001: /*
002: * Copyright (c) 2001 Silvere Martin-Michiellot All Rights Reserved.
003: *
004: * Silvere Martin-Michiellot grants you ("Licensee") a non-exclusive,
005: * royalty free, license to use, modify and redistribute this
006: * software in source and binary code form,
007: * provided that i) this copyright notice and license appear on all copies of
008: * the software; and ii) Licensee does not utilize the software in a manner
009: * which is disparaging to Silvere Martin-Michiellot.
010: *
011: * This software is provided "AS IS," without a warranty of any kind. ALL
012: * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
013: * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
014: * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. Silvere Martin-Michiellot
015: * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
016: * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
017: * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
018: * Silvere Martin-Michiellot OR ITS LICENSORS BE LIABLE
019: * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
020: * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
021: * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
022: * OR INABILITY TO USE SOFTWARE, EVEN IF Silvere Martin-Michiellot HAS BEEN
023: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
024: *
025: * This software is not designed or intended for use in on-line control of
026: * aircraft, air traffic, aircraft navigation or aircraft communications; or in
027: * the design, construction, operation or maintenance of any nuclear
028: * facility. Licensee represents and warrants that it will not use or
029: * redistribute the Software for such purposes.
030: *
031: * @Author: Silvere Martin-Michiellot
032: *
033: */
034:
035: package com.db.server;
036:
037: import javax.jdo.*;
038: import java.util.*;
039:
040: /**
041: * This is a simple banking system. It is currently not as safe as a professionnal banking system.
042: */
043:
044: //Many different things can be sold: land, backgrounds, objects, avatars skins, service, pseudo-cash (an object on which is imprinted the value of its price but not secure
045: //since someone can build up objects with an imprinted value different from the price).
046: //
047: //Buying and selling can either occur directly between two participants putting money against the other's services. It can also occur with a group of
048: //participants in a merchant exchange in which people pool and draw objects with the existing agreement of some other one doing the same.
049: //
050: //Credit card can be used. Public key system is here to enforce security as well as receipts and certificates.
051: //merchant center system
052: //it is actually meaningless to have only one avatar in a pool because transactions can't occur
053: //Pools auto administer
054: //pools are also used as a succession system
055: //when an avatar is removed its belongings must be either destroyed or owning must be transfered (see avatar for details)
056:
057: //PLEASE NOTE:
058: //Dates are computed using the Java System time NOT the UniverseServer time (which is basically the same apart from an offset)
059: //there is no real time update of the Pool therefore there might be little gaps while objects boughts have their actual ownership changed
060: public class Pool extends Object {
061:
062: private UniverseServer universeServer;
063:
064: private HashSet avatarPool;
065: private Vector virtualElementPool;
066:
067: private Trade trade;
068:
069: private Pool() {
070:
071: throw new java.lang.IllegalArgumentException(
072: "A pool must have a universeServer, an avatar and a valid trade.");
073:
074: }
075:
076: public Pool(UniverseServer universeServer, Avatar avatar,
077: Trade trade) {
078:
079: super ();
080:
081: if ((universeServer != null) && (avatar != null)
082: && (trade != null)) {
083: this .universeServer = universeServer;
084: this .avatarPool = new HashSet();
085: this .virtualElementPool = new Vector();
086: this .avatarPool.add(avatar);
087: this .trade = trade;
088: } else {
089: throw new java.lang.IllegalArgumentException(
090: "A pool must have a valid universeServer, a valid avatar and a valid trade.");
091: }
092:
093: }
094:
095: public final PersistenceManager getPersistenceManager() {
096:
097: return this .universeServer.getPersistenceManager();
098:
099: }
100:
101: public final Trade getTrade() {
102:
103: return this .trade;
104:
105: }
106:
107: protected final void setTrade(Trade trade) {
108:
109: this .trade = trade;
110:
111: }
112:
113: //to get the trade used in this pool please query the corresponding worldspot
114:
115: public final void comeIntoPool(Avatar avatar) {
116:
117: if (avatar != null) {
118: this .avatarPool.add(avatar);
119: }
120:
121: }
122:
123: //pools are closed by managing worldSpot when there is no more avatars
124: protected final boolean checkPool() {
125:
126: return this .avatarPool.size() != 0;
127:
128: }
129:
130: public final boolean isInPool(Avatar avatar) {
131:
132: return this .avatarPool.contains(avatar);
133:
134: }
135:
136: protected final Avatar[] avatarsInPool() {
137:
138: return (Avatar[]) this .avatarPool.toArray();
139:
140: }
141:
142: protected final boolean closablePool() {
143:
144: boolean found;
145: int i;
146: TaggedVirtualElement taggedVirtualElement;
147:
148: found = false;
149: i = 0;
150: while ((i < virtualElementPool.size()) && (!found)) {
151: taggedVirtualElement = (TaggedVirtualElement) virtualElementPool
152: .get(i);
153: found = (taggedVirtualElement.getAvatar().getStatus() == Avatar.PAWN);
154: }
155:
156: return found;
157:
158: }
159:
160: public final boolean isInPool(VirtualElement virtualElement) {
161:
162: int i;
163: boolean found;
164: TaggedVirtualElement taggedVirtualElement;
165:
166: found = false;
167: i = 0;
168:
169: while ((i < virtualElementPool.size()) && (!found)) {
170: taggedVirtualElement = (TaggedVirtualElement) virtualElementPool
171: .get(i);
172: found = (taggedVirtualElement.getVirtualElement() == virtualElement);
173: i++;
174: }
175:
176: return found;
177:
178: }
179:
180: //objects from pool at the time of the request are listed with the current proposed prices from each avatar
181: //vector consists of TaggedVirtualElements
182: public final Vector readPool(Avatar avatar) {
183:
184: this .updatePool();
185:
186: if (this .avatarPool.contains(avatar)) {
187:
188: return this .virtualElementPool;
189:
190: }
191:
192: else {
193: return null;
194: }
195:
196: }
197:
198: //Avatars objects not drawn out before the user leaves are drawn out and given back to the user
199: //uncompleted avatar's offers are cancelled if he leaves pool
200: //synchronized
201: //pawns can't leave pool
202: public final void leavePool(Avatar avatar) {
203:
204: HashSet virtualElements;
205: TaggedVirtualElement taggedVirtualElement;
206: VirtualElement currentVirtualElement;
207: Iterator iterator;
208: int i;
209:
210: if ((avatar != null) && (avatarPool.contains(avatar))) {
211: if (avatar.getStatus() != Avatar.PAWN) {
212: synchronized (avatar) {
213: virtualElements = new HashSet();
214: this .avatarPool.remove(avatar);
215: for (i = 0; i < virtualElementPool.size(); i++) {
216: taggedVirtualElement = (TaggedVirtualElement) virtualElementPool
217: .get(i);
218: if (taggedVirtualElement.getVirtualElement()
219: .getUserOwner() == avatar) {
220: virtualElements.add(taggedVirtualElement
221: .getVirtualElement());
222: taggedVirtualElement.getVirtualElement()
223: .doCancelSale();
224: this .virtualElementPool.removeElementAt(i);
225: }
226: }
227:
228: }
229:
230: synchronized (virtualElementPool) {
231:
232: iterator = virtualElements.iterator();
233:
234: while (iterator.hasNext()) {
235: i = 0;
236: currentVirtualElement = (VirtualElement) iterator
237: .next();
238: while (i < virtualElementPool.size()) {
239: taggedVirtualElement = (TaggedVirtualElement) virtualElementPool
240: .get(i);
241: if (taggedVirtualElement
242: .getVirtualElement() == currentVirtualElement) {
243: this .virtualElementPool
244: .removeElementAt(i);
245: } else {
246: i++;
247: }
248: }
249: }
250:
251: }
252: }
253:
254: }
255: this .updatePool();
256:
257: }
258:
259: //synchronized
260: protected final void updatePool() {
261:
262: int i;
263: TaggedVirtualElement taggedVirtualElement;
264: Date date;
265: Ticket[] tickets;
266: HashSet virtualElements;
267: Iterator iterator;
268: VirtualElement virtualElement;
269: Avatar seller;
270: double bestPrice;
271: double askedPrice;
272: double currentPrice;
273: int index;
274:
275: synchronized (virtualElementPool) {
276:
277: date = new Date(System.currentTimeMillis());
278: i = 0;
279: virtualElements = new HashSet();
280:
281: //remove unwanted objects
282: while (i < virtualElementPool.size()) {
283: taggedVirtualElement = (TaggedVirtualElement) virtualElementPool
284: .get(i);
285: if (taggedVirtualElement.getDate().before(date)) {
286: if (taggedVirtualElement.getAvatar() == taggedVirtualElement
287: .getVirtualElement().getUserOwner()) {
288: virtualElements.add(taggedVirtualElement);
289: i++;
290: } else {
291: this .virtualElementPool.removeElementAt(i);
292: }
293: } else {
294: i++;
295: }
296: }
297: //sale some objects at their best price
298: iterator = virtualElements.iterator();
299:
300: while (iterator.hasNext()) {
301:
302: taggedVirtualElement = (TaggedVirtualElement) iterator
303: .next();
304: virtualElement = taggedVirtualElement
305: .getVirtualElement();
306: //or seller = virtualElement.getUserOwner();
307: seller = taggedVirtualElement.getAvatar();
308: askedPrice = this .trade.convertPrice(
309: taggedVirtualElement.getCurrency(),
310: Trade.US_DOLLAR, taggedVirtualElement
311: .getPrice());
312: i = 0;
313: bestPrice = -1;
314: index = -1;
315: while (i < virtualElementPool.size()) {
316: taggedVirtualElement = (TaggedVirtualElement) virtualElementPool
317: .get(i);
318: if ((taggedVirtualElement.getVirtualElement() == virtualElement)
319: && (taggedVirtualElement.getAvatar() != seller)) {
320: //date has already been checked
321: currentPrice = this .trade.convertPrice(
322: taggedVirtualElement.getCurrency(),
323: Trade.US_DOLLAR, taggedVirtualElement
324: .getPrice());
325: if (currentPrice > bestPrice) {
326: bestPrice = currentPrice;
327: index = i;
328: }
329: }
330: i++;
331: }
332:
333: if (bestPrice > askedPrice) {
334: taggedVirtualElement = (TaggedVirtualElement) virtualElementPool
335: .get(index);
336: //decrease V-Cash account
337: //does a doBuy()
338: tickets = this .getTrade().generateTransaction(
339: seller, taggedVirtualElement.getAvatar(),
340: taggedVirtualElement.getVirtualElement(),
341: taggedVirtualElement.getPrice(),
342: taggedVirtualElement.getCurrency());
343: this .virtualElementPool.removeElementAt(index);
344: } else {
345: virtualElement.doCancelSale();
346: }
347:
348: //remove all concerning virtualElement
349: i = 0;
350: while (i < virtualElementPool.size()) {
351: taggedVirtualElement = (TaggedVirtualElement) virtualElementPool
352: .get(i);
353: if (taggedVirtualElement.getVirtualElement() == virtualElement) {
354: this .virtualElementPool.removeElementAt(i);
355: } else {
356: i++;
357: }
358: }
359:
360: }
361:
362: }
363:
364: }
365:
366: //ensure an object won't be sold under a given price (the object is removed from the pool after the date if noone wants it otherwise it is sold to the best price given at the given date for those who can pay)
367: //each new call with the same virtualElement overrides preceding calls
368: //avatar must first be added to the pool
369: //prices must be positive
370: //avatar can put multiple objects in the pool at different time (multiple calls)
371: //avatar must own the objects he sells
372: //(use other method if you want to sell the objects you own at their price and currency)
373: public final void dontSellUnder(Avatar owner,
374: VirtualElement virtualElement, double price,
375: String currency, Date date) {
376:
377: TaggedVirtualElement taggedVirtualElement;
378: int i;
379: boolean found;
380:
381: if ((owner != null) && (this .avatarPool.contains(owner))
382: && (virtualElement != null)) {
383:
384: if ((virtualElement.getUserOwner() == owner)
385: && (price >= 0) && (currency != null)
386: && (currency.length() > 0)) {
387: //updatePool manages invalid dates
388:
389: found = false;
390: i = 0;
391:
392: while ((i < virtualElementPool.size()) && (!found)) {
393: taggedVirtualElement = (TaggedVirtualElement) virtualElementPool
394: .get(i);
395: found = ((taggedVirtualElement.getVirtualElement() == virtualElement) && (taggedVirtualElement
396: .getAvatar() == owner));
397: i++;
398: }
399:
400: taggedVirtualElement = new TaggedVirtualElement(
401: virtualElement, owner, price, currency, date);
402: virtualElement.doSale(this );
403: if (found) {
404: this .virtualElementPool.removeElementAt(i - 1);
405: }
406: this .virtualElementPool.add(taggedVirtualElement);
407:
408: }
409:
410: }
411:
412: }
413:
414: //uses virtualElement price and currency
415: public final void dontSellUnder(Avatar owner,
416: VirtualElement virtualElement, Date date) {
417:
418: this .dontSellUnder(owner, virtualElement, virtualElement
419: .getPrice(), virtualElement.getCurrency(), date);
420:
421: }
422:
423: //offerer proposed to buy a virtualElement in the pool at a given price until a given date
424: //offerer can make multiple offers with different dates for the same object
425: //virtualElement must be first put in the pool
426: //avatar must be in the pool
427: //you can't make an offer for your own virtualElements
428: //people can try to beat each other by putting many single requests on same objects
429:
430: //virtualElements are drawn out automatically depending on owner's expiration date and offers at that date
431: //converted prices (at the time of access) must be at least equal with the proposed prices of the pool
432: //uses avatar v-cash-card to process transaction
433: public final void makeAnOffer(Avatar offerer,
434: VirtualElement virtualElement, float price,
435: String currency, Date date) {
436:
437: TaggedVirtualElement taggedVirtualElement;
438: int i;
439: boolean found;
440:
441: updatePool();
442:
443: if ((offerer != null) && (virtualElement != null)) {
444:
445: if (virtualElement.getUserOwner() != offerer) {
446:
447: found = false;
448: i = 0;
449:
450: while ((i < virtualElementPool.size()) && (!found)) {
451: taggedVirtualElement = (TaggedVirtualElement) virtualElementPool
452: .get(i);
453: found = (taggedVirtualElement.getVirtualElement() == virtualElement);
454: i++;
455: }
456: if (found) {
457: //add offer only if not existing: same offerer for same virtualElement at the same date
458:
459: found = false;
460: i = 0;
461: while ((i < virtualElementPool.size()) && (!found)) {
462: taggedVirtualElement = (TaggedVirtualElement) virtualElementPool
463: .get(i);
464: found = ((taggedVirtualElement.getAvatar() == offerer)
465: && (taggedVirtualElement
466: .getVirtualElement() == virtualElement) && (taggedVirtualElement
467: .getDate() == date));
468: i++;
469: }
470: taggedVirtualElement = new TaggedVirtualElement(
471: virtualElement, offerer, price, currency,
472: date);
473:
474: if (found) {
475: this .virtualElementPool.removeElementAt(i - 1);
476: }
477: this .virtualElementPool.add(taggedVirtualElement);
478: virtualElement.doOffer(taggedVirtualElement);
479: }
480: }
481:
482: }
483:
484: }
485:
486: //when the owner finally decides not to sell a virtualElement
487: public final void cancelSale(Avatar owner,
488: VirtualElement virtualElement) {
489:
490: TaggedVirtualElement taggedVirtualElement;
491: int i;
492:
493: updatePool();
494:
495: if ((owner != null) && (this .avatarPool.contains(owner))
496: && (virtualElement != null)) {
497:
498: if (virtualElement.getUserOwner() == owner) {
499:
500: i = 0;
501:
502: while ((i < virtualElementPool.size())) {
503: taggedVirtualElement = (TaggedVirtualElement) virtualElementPool
504: .get(i);
505: if (taggedVirtualElement.getVirtualElement() == virtualElement) {
506: virtualElement.doCancelSale();
507: this .virtualElementPool.removeElementAt(i);
508: } else {
509: i++;
510: }
511: }
512:
513: }
514:
515: }
516:
517: }
518:
519: //when an offerer doesn't want anymore to buy the object
520: public final void cancelOffer(Avatar offerer,
521: VirtualElement virtualElement, double price,
522: String currency, Date date) {
523:
524: TaggedVirtualElement taggedVirtualElement;
525: int i;
526:
527: updatePool();
528:
529: if ((offerer != null) && (this .avatarPool.contains(offerer))
530: && (virtualElement != null)) {
531:
532: if (virtualElement.getUserOwner() != offerer) {
533:
534: i = 0;
535:
536: while ((i < virtualElementPool.size())) {
537: taggedVirtualElement = (TaggedVirtualElement) virtualElementPool
538: .get(i);
539: if ((taggedVirtualElement.getVirtualElement() == virtualElement)
540: && (taggedVirtualElement.getAvatar() == offerer)
541: && (taggedVirtualElement.getPrice() == price)
542: && (taggedVirtualElement.getCurrency() == currency)
543: && (taggedVirtualElement.getDate() == date)) {
544: this .virtualElementPool.removeElementAt(i);
545: virtualElement
546: .doCancelOffer(taggedVirtualElement);
547: } else {
548: i++;
549: }
550: }
551:
552: }
553:
554: }
555:
556: }
557:
558: //when an offerer doesn't want anymore to buy the object
559: //if you really want to cancel all your offers just leave the pool
560: public final void cancelAllOffers(Avatar offerer,
561: VirtualElement virtualElement) {
562:
563: TaggedVirtualElement taggedVirtualElement;
564: int i;
565:
566: updatePool();
567:
568: if ((offerer != null) && (this .avatarPool.contains(offerer))
569: && (virtualElement != null)) {
570:
571: if (virtualElement.getUserOwner() != offerer) {
572:
573: i = 0;
574:
575: while ((i < virtualElementPool.size())) {
576: taggedVirtualElement = (TaggedVirtualElement) virtualElementPool
577: .get(i);
578: if ((taggedVirtualElement.getVirtualElement() == virtualElement)
579: && (taggedVirtualElement.getAvatar() == offerer)) {
580: this.virtualElementPool.removeElementAt(i);
581: virtualElement
582: .doCancelOffer(taggedVirtualElement);
583: } else {
584: i++;
585: }
586: }
587:
588: }
589:
590: }
591:
592: }
593:
594: }
|