View Javadoc

1   /*
2    * This file is part of Domingo
3    * an Open Source Java-API to Lotus Notes/Domino
4    * hosted at http://domingo.sourceforge.net
5    *
6    * Copyright (c) 2003-2007 Beck et al. projects GmbH Munich, Germany (http://www.bea.de)
7    *
8    * This library is free software; you can redistribute it and/or
9    * modify it under the terms of the GNU Lesser General Public
10   * License as published by the Free Software Foundation; either
11   * version 2.1 of the License, or (at your option) any later version.
12   *
13   * This library is distributed in the hope that it will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   * Lesser General Public License for more details.
17   *
18   * You should have received a copy of the GNU Lesser General Public
19   * License along with this library; if not, write to the Free Software
20   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21   */
22  
23  package de.bea.domingo.proxy;
24  
25  import java.lang.ref.WeakReference;
26  import java.util.ArrayList;
27  import java.util.Calendar;
28  import java.util.Collections;
29  import java.util.List;
30  import java.util.Vector;
31  
32  import lotus.domino.Base;
33  import lotus.domino.DateRange;
34  import lotus.domino.DateTime;
35  import lotus.domino.Document;
36  import lotus.domino.DocumentCollection;
37  import lotus.domino.NotesException;
38  import de.bea.domingo.DBase;
39  import de.bea.domingo.DNotesException;
40  import de.bea.domingo.DNotesIterator;
41  import de.bea.domingo.DNotesMonitor;
42  import de.bea.domingo.DNotesRuntimeException;
43  import de.bea.domingo.exception.DominoException;
44  import de.bea.domingo.i18n.ResourceManager;
45  import de.bea.domingo.i18n.Resources;
46  import de.bea.domingo.monitor.AbstractMonitorEnabled;
47  
48  /***
49   * Abstract base class for all implementations of interfaces derived from
50   * <code>DBase</code>.
51   *
52   * @author <a href=mailto:kriede@users.sourceforge.net>Kurt Riede</a>
53   */
54  public abstract class BaseProxy extends AbstractMonitorEnabled implements DBase {
55  
56      /*** Internationalized resources. */
57      protected static final Resources RESOURCES = ResourceManager.getPackageResources(NotesProxyFactory.class);
58  
59      ////////////////////////////////////////////////
60      // constants
61      ////////////////////////////////////////////////
62  
63      /*** Number of characters needed to represent a date/time value. */
64      protected static final int DATETIME_STRING_LENGTH = 20;
65  
66      /*** Maximum number of items supported in method getItemValues(). */
67      protected static final int NUM_DATETIME_VALUES = 1000;
68  
69      /*** maximum number of characters to parse in method getItemValues(). */
70      protected static final int MAX_DATETIME_LENGTH = NUM_DATETIME_VALUES * DATETIME_STRING_LENGTH;
71  
72      /*** The empty string. */
73      protected static final String EMPTY_STRING = "";
74  
75      ////////////////////////////////////////////////
76      // instance attributes
77      ////////////////////////////////////////////////
78  
79      /*** Reference to the parent object. */
80      private final DBase parent;
81  
82      /***
83       * Reference to the notes object.
84       *
85       * <p>This reference can be either a notes object directly or a
86       * <code>WeakReference</code> to a notes object, if the notes object
87       * can be recycled.
88       *
89       * @see de.bea.domingo.proxy.BaseProxy#getNotesObject()
90       */
91      private Object ref;
92  
93      /*** Reference to the factory which controls this instance. */
94      private final NotesProxyFactory factory;
95  
96      ////////////////////////////////////////////////
97      // creation
98      ////////////////////////////////////////////////
99  
100     /***
101      * Creates a new DBaseImpl object.
102      *
103      * @param theFactory the controlling factory
104      * @param theParent the parent object
105      * @param object the notes object
106      * @param monitor the monitor
107      */
108     protected BaseProxy(final NotesProxyFactory theFactory, final DBase theParent,
109                         final Base object, final DNotesMonitor monitor) {
110         super(monitor);
111         this.factory = theFactory;
112         this.parent = theParent;
113         this.ref = object;
114         //getMonitor().debug("initialize " + super.toString());
115     }
116 
117     ////////////////////////////////////////////////
118     // protected utility methods for sub classes
119     ////////////////////////////////////////////////
120 
121     /***
122      * Returns the Notes object associated with an instance.
123      *
124      * @return associated Notes object
125      */
126     protected final Base getNotesObject() {
127         if (ref instanceof WeakReference) {
128             return (Base) ((WeakReference) ref).get();
129         }
130         return (Base) ref;
131     }
132 
133     /***
134      * Clears the associated notes object, used during dispose/recycle operations.
135      */
136     protected final void clearNotesObject() {
137         ref = null;
138     }
139 
140     /***
141      * Returns the parent object.
142      *
143      * @return the parent object or <code>null</code> if no parent available
144      */
145     protected final DBase getParent() {
146         return parent;
147     }
148 
149     /***
150      * Returns the factory corresponding to this instance.
151      *
152      * @return the corresponding factory
153      */
154     protected final NotesProxyFactory getFactory() {
155         return factory;
156     }
157 
158     /***
159      * Utility method for the toString method of derived classes.
160      * Returns a string representation of the object.
161      *
162      * @return string representation of the object
163      *
164      * @see java.lang.Object#toString()
165      */
166     public final String toStringGeneric() {
167         final Base base = getNotesObject();
168         if (base != null) {
169             return base.toString();
170         }
171         return super.toString();
172     }
173 
174     /***
175      * Convert List to Vector.
176      *
177      * <p>If the implementation of the <code>List</code> interface is a Vector,
178      * this vector is used unchanged, else the references of elements of the
179      * <code>List</code> are copied to  a new result <code>Vector</code>.
180      *
181      * @param list a list
182      * @return a Vector containing the elements of the list argument
183      */
184     protected final Vector convertListToVector(final List list) {
185         if (list == null) {
186             return null;
187         }
188         if (list instanceof Vector) {
189             return (Vector) list;
190         }
191         final Vector v = new Vector();
192         for (int i = 0; i < list.size(); i++) {
193             v.addElement(list.get(i));
194         }
195         return v;
196     }
197 
198     /***
199      * Convert Vector to List.
200      *
201      * A Vector does also implement the <code>List</code> interface. But it is
202      * synchronized. Therefore this conversion method provides a real List
203      * that is not synchronized.
204      *
205      * @param vector a vector
206      * @return a List
207      * @deprecated the usage of the method convertVectorToList() is not needed and impacts performance
208      */
209     protected final List convertVectorToList(final Vector vector) {
210         if (vector == null) {
211             return null;
212         }
213         final List list = new ArrayList();
214         list.addAll(vector);
215         return Collections.unmodifiableList(list);
216     }
217 
218     /***
219      * Converts every occurrence of a <code>DateTime</code> object to a
220      * <code>java.util.Calendar</code> instance.
221      *
222      * <p>The created DateTime objects must get recycled somewhen later, when
223      * they are not needed anymore. To ensure this, the calling method must call
224      * the method recycleDateTimeList() after usage.</p>
225      * <p>Lists are processed recursivly; this is e.g. needed for column values
226      * that might contain value lists in the list of column values.</p>
227      *
228      * @param list the list to convert its content
229      * @return List a new List with the same elements in same order,
230      *         but converted dates
231      */
232     protected final List convertNotesDateTimesToCalendar(final List list) {
233         if (list == null) {
234             return null;
235         }
236         final List result = new ArrayList();
237         for (int i = 0; i < list.size(); i++) {
238             final Object object = list.get(i);
239             if (object instanceof DateTime) {
240                 final DateTime dateTime = (DateTime) object;
241                 checkSession(dateTime);
242                 result.add(createCalendar(dateTime));
243             } else if (object instanceof List) {
244                 result.add(convertNotesDateTimesToCalendar((List) object));
245             } else {
246                 result.add(object);
247             }
248         }
249         return Collections.unmodifiableList(result);
250     }
251 
252     /***
253      * Converts every occurrence of a <code>java.util.Calendar</code> or
254      * <code>java.util.Date</code> in a List to a <code>DateTime</code> instance.
255      *
256      * <p><b>ATTENTION:</b><br/>
257      * The created DateTime objects must get recycled somewhen later, when
258      * they are not needed anymore. To ensure this, the calling method must call
259      * the method recycleDateTimeList() after usage.</p>
260      *
261      * <p><b>Example</b><br/>
262      * <pre>
263      * List keys;
264      * // initialize and fill list with Date objects and others
265      * List convertedList = convertDatesToNotesDateTime(keys);
266      * // now the list contains Notes DateTime objects.
267      * // do something with the list
268      * recycleDateTimeList(convertedKeys);
269      * </pre>
270      * </p>
271      *
272      * @param list the list to convert its content
273      * @return List a new List with the same elements in same order,
274      *         but converted dates
275      */
276     protected final List convertCalendarsToNotesDateTime(final List list) {
277         if (list == null) {
278             return null;
279         }
280         final List result = new ArrayList();
281         for (int i = 0; i < list.size(); i++) {
282             final Object object = list.get(i);
283             if (object instanceof Calendar) {
284                 final DateTime date = createDateTime((Calendar) object);
285                 result.add(date);
286             } else {
287                 result.add(object);
288             }
289         }
290         return Collections.unmodifiableList(result);
291     }
292 
293     /***
294      * Recycles all Notes DateTime objects in a list.
295      *
296      * <p>The list can have mixed content and must not contain only DateTime
297      * objects. But only DateTime objects are recycled.</p>
298      *
299      * @param list any list implementation
300      */
301     protected final void recycleDateTimeList(final List list) {
302         if (list == null) {
303             return;
304         }
305         for (int i = 0; i < list.size(); i++) {
306             final Object object = list.get(i);
307             if (object instanceof DateTime) {
308                 getFactory().recycle(object);
309             }
310         }
311     }
312 
313     /***
314      * Returns the Domingo session that created the current object.
315      *
316      * @return Domingo session of current object
317      */
318     protected final SessionProxy getDSession() {
319         BaseProxy proxy = this;
320         while (!(proxy instanceof SessionProxy)) {
321             proxy = (BaseProxy) proxy.getParent();
322         }
323         return (SessionProxy) proxy;
324     }
325 
326     /***
327      * Creates a Notes <code>DateTime</code> instance from a
328      * <code>java.util.Calendar</code>.
329      *
330      * Adapter to InternationalProxy.
331      * This is a convenience method to allow every object to convert dates.
332      *
333      * @param calendar the calendar to convert
334      * @return DateTime object representing the same point in time
335      */
336     protected final DateTime createDateTime(final Calendar calendar) {
337         return getDSession().getInternational().createDateTime(calendar);
338     }
339 
340     /***
341      * Creates a Notes <code>DateRange</code> instance from two
342      * <code>java.util.Calendar</code>s.
343      *
344      * Adapter to InternationalProxy.
345      * This is a convenience method to allow every object to convert dates.
346      *
347      * @param calendar1 the start calendar to convert
348      * @param calendar2 the end calendar to convert
349      * @return DateTime object representing the same point in time
350      * @see de.bea.domingo.proxy.InternationalProxy#createDateRange(Calendar, Calendar)
351      */
352     protected final DateRange createDateRange(final Calendar calendar1, final Calendar calendar2) {
353         return getDSession().getInternational().createDateRange(calendar1, calendar2);
354     }
355 
356     /***
357      * Converts a Notes DateTime object into a Calendar.
358      * <p>Milli seconds are cleared in all cases.</p>
359      *
360      * Adapter to InternationalProxy.
361      * This is a convenience method to allow every object to convert dates.
362      *
363      * @param dateTime a Notes DateTime object
364      * @return a Calendar
365      * @see de.bea.domingo.proxy.InternationalProxy#createCalendar(DateTime)
366      */
367     protected final Calendar createCalendar(final DateTime dateTime) {
368         return getDSession().getInternational().createCalendar(dateTime);
369     }
370 
371     /***
372      * Checks and monitors an error if a <code>DateTime</code> object doesn't
373      * have a parent session.
374      *
375      * @param dateTime the DateTime object to check
376      */
377     protected final void checkSession(final DateTime dateTime) {
378         try {
379             if (dateTime.getParent() == null) {
380                 getMonitor().error("created a DateTime object without parent session");
381             }
382         } catch (NotesException e) {
383             getMonitor().error("created a DateTime object with parent session not available", e);
384         }
385     }
386 
387 
388     ////////////////////////////////////////////////
389     //    interface java.lang.Object
390     ////////////////////////////////////////////////
391 
392     /***
393      * @see java.lang.Object#finalize()
394      * @throws Throwable the <code>Exception</code> raised by this method
395      */
396     protected final void finalize() throws Throwable {
397         factory.getBaseCache().remove(getNotesObject());
398         getFactory().recycleLater(this);
399         super.finalize();
400     }
401 
402     /***
403      * Returns the hashCode of the referenced Notes object.
404      * @return hashCode of referenced Notes object
405      *
406      * @see java.lang.Object#hashCode()
407      */
408     public final int refereceHashCode() {
409         if (ref != null) {
410             return ref.hashCode();
411         } else {
412             return 0;
413         }
414     }
415 
416     ////////////////////////////////////////////////
417     //    static helper methods
418     ////////////////////////////////////////////////
419 
420     /***
421      * Returns a string representation of the object. This method
422      * returns a string that "textually represents" this object.
423      * The result is a concise but informative representation that
424      * is easy for a person to read.
425      *
426      * <p>The <code>toStringIntern</code> method
427      * returns a string consisting of the name of the class of which the
428      * object is an instance, the at-sign character `<code>@</code>', and
429      * the unsigned hexadecimal representation of the hash code of the
430      * object. In other words, this method returns a string equal to the
431      * value of:
432      * <blockquote>
433      * <pre>
434      * object.getClass().getName() + '@' + Integer.toHexString(object.hashCode())
435      * </pre></blockquote>
436      *
437      * @param object the reference object to use.
438      * @return a string representation of the object.
439      * @see java.lang.Object#toString()
440      */
441     public static String toStringIntern(final Object object) {
442         return object.getClass().getName() + "@" + Integer.toHexString(object.hashCode());
443     }
444 
445     /*** Counter for number of currently existing DateTime objects. */
446     private static int countDateTime = 0;
447 
448     /***
449      * Returns the global counter for DateTime objects.
450      *
451      * @return global counter for DateTime objects.
452      */
453     public static int getCountDateTime() {
454         return countDateTime;
455     }
456 
457     /***
458      * Increments the global counter for DateTime objects.
459      *
460      * @return new value of the counter after incrementation
461      */
462     public static int  incrementDateTimeCounter() {
463         return ++countDateTime;
464     }
465 
466     /***
467      * Decrements the global counter for DateTime objects.
468      *
469      * @return new value of the counter after decrementation
470      */
471     public static int decrementDateTimeCounter() {
472         return --countDateTime;
473     }
474 
475     ////////////////////////////////////////////////
476     //    inner classes
477     ////////////////////////////////////////////////
478 
479     /***
480      * Iterator for collections of documents.
481      *
482      * <p>A Mapper instance is used to map Notes document into a business
483      * object. If no Mapper is provided, a simple default mapper is used that
484      * maps the Notes document into a simple data structure that implements
485      * the DDocument interface.</p>
486      *
487      * @see de.bea.domingo.DDocument
488      *
489      * @author <a href=mailto:kriede@users.sourceforge.net>Kurt Riede</a>
490      */
491     public static final class DocumentCollectionIterator extends BaseProxy implements DNotesIterator {
492 
493         /*** serial version ID for serialization. */
494         private static final long serialVersionUID = 3258412845845524528L;
495 
496         /*** Reference to current document within the view. */
497         private BaseDocumentProxy document = null;
498 
499         /***
500          * Constructor.
501          *
502          * @param theFactory the controlling factory
503          * @param theParent the parent database
504          * @param monitor the monitor
505          * @param theCollection the Notes document collection
506          */
507         protected DocumentCollectionIterator(final NotesProxyFactory theFactory,
508                                              final DBase theParent,
509                                              final DocumentCollection theCollection,
510                                              final DNotesMonitor monitor) {
511             super(theFactory, theParent, theCollection, monitor);
512             initialize(theFactory);
513         }
514 
515         /***
516          * initialization.
517          * @param theFactory the controlling factory
518          */
519         private void initialize(final NotesProxyFactory theFactory) {
520             getFactory().preprocessMethod();
521             try {
522                 if (getCollection() != null && getCollection().getCount() > 0) {
523                     final Document notesDocument = getCollection().getFirstDocument();
524                     document = BaseDocumentProxy.getInstance(theFactory, getParent(), notesDocument, getMonitor());
525                 }
526             } catch (NotesException e) {
527                 getMonitor().warn("Cannot create Iterator: ", e);
528                 return;
529             }
530         }
531 
532         /***
533          * Returns the associated document collection.
534          *
535          * @return the associated document collection
536          */
537         private DocumentCollection getCollection() {
538             return (DocumentCollection) getNotesObject();
539         }
540 
541         /***
542          * {@inheritDoc}
543          * @see java.util.Iterator#hasNext()
544          */
545         public boolean hasNext() {
546             return document != null;
547         }
548 
549         /***
550          * {@inheritDoc}
551          * @see java.util.Iterator#next()
552          */
553         public Object next() {
554             getFactory().preprocessMethod();
555             final Object result = document;
556             try {
557                 final Document nextNotesDoc = getCollection().getNextDocument((Document) document.getNotesObject());
558                 document = null;
559                 document = BaseDocumentProxy.getInstance(getFactory(), getParent(), nextNotesDoc, getMonitor());
560             } catch (NotesException e) {
561                 document = null;
562             }
563             return result;
564         }
565 
566         /***
567          * The <tt>remove</tt> operation is not supported by this Iterator.
568          * An <code>UnsupportedOperationException</code> is thrown if this
569          * method is called.
570          *
571          * @see java.util.Iterator#remove()
572          */
573         public void remove() {
574             throw new UnsupportedOperationException();
575         }
576 
577         /***
578          * {@inheritDoc}
579          * @see de.bea.domingo.proxy.BaseProxy#toString()
580          */
581         public String toString() {
582             return BaseProxy.toStringIntern(this);
583         }
584 
585         /***
586          * {@inheritDoc}
587          * @see de.bea.domingo.DNotesIterator#getSize()
588          */
589         public int getSize() {
590             try {
591                 return getCollection().getCount();
592             } catch (NotesException e) {
593                 throw newRuntimeException("Cannot get size of iterator", e);
594             }
595         }
596     }
597 
598     /***
599      * Creates and returns a new exception wrapping a given
600      * NotesException.
601      * @param e the NotesException, can be null
602      * @return new DNotesException
603      */
604     protected final DNotesException newException(final NotesException e) {
605         return newException(null, e);
606     }
607 
608     /***
609      * Creates and returns a new exception with the given message.
610      * @param message the message
611      * @return new DNotesException
612      */
613     protected final DNotesException newException(final String message) {
614         return newException(message, null);
615     }
616 
617     /***
618      * Creates and returns a new exception wrapping a given NotesException.
619      * @param message the message
620      * @param e the NotesException, can be null
621      * @return new DNotesException
622      */
623     protected final DNotesException newException(final String message, final NotesException e) {
624         final DominoException d = e != null ? new DominoException(e) : null;
625         return new NotesProxyException(getFullMessage(message, d), d);
626     }
627 
628     /***
629      * Creates and returns a new runtime exception wrapping a given
630      * NotesException.
631      * @param e the NotesException, can be null
632      * @return new DNotesRuntimeException
633      */
634     protected final DNotesRuntimeException newRuntimeException(final NotesException e) {
635         return newRuntimeException(null, e);
636     }
637 
638     /***
639      * Creates and returns a new runtime exception with the given message.
640      * @param message the message
641      * @return new DNotesRuntimeException
642      */
643     protected final DNotesRuntimeException newRuntimeException(final String message) {
644         return newRuntimeException(message, null);
645     }
646 
647     /***
648      * Creates and returns a new runtime exception with the given message.
649      * @param message the message
650      * @param e the NotesException, can be null
651      * @return new DNotesRuntimeException
652      */
653     protected final DNotesRuntimeException newRuntimeException(final String message, final Exception e) {
654         final Exception cause = e instanceof NotesException ? new DominoException((NotesException) e) : e;
655         return new NotesProxyRuntimeException(getFullMessage(message, cause), cause);
656     }
657 
658     /***
659      * Returns a message for a domino exception.
660      * @param message a message string
661      * @param a @link{de.bea.domingo.proxy.DominoException DominoException}
662      * @return combined full message
663      */
664     private String getFullMessage(final String message, final Exception d) {
665         final String msg = message != null ? message : (d != null ? d.getMessage() : "Unknown problem");
666         final String fullMessage = this.getClass().getName() + ": " + msg;
667         return fullMessage;
668     }
669 }