KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > Identity


1 package org.apache.ojb.broker;
2
3 /* Copyright 2002-2005 The Apache Software Foundation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 import org.apache.ojb.broker.metadata.ClassDescriptor;
19 import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
20 import org.apache.ojb.broker.core.ValueContainer;
21 import org.apache.ojb.broker.core.proxy.IndirectionHandler;
22 import org.apache.ojb.broker.core.proxy.ProxyHelper;
23 import org.apache.ojb.broker.util.BrokerHelper;
24 import org.apache.commons.lang.SystemUtils;
25 import org.apache.commons.lang.ArrayUtils;
26
27 import java.io.ByteArrayInputStream JavaDoc;
28 import java.io.ByteArrayOutputStream JavaDoc;
29 import java.io.ObjectInputStream JavaDoc;
30 import java.io.ObjectOutputStream JavaDoc;
31 import java.io.Serializable JavaDoc;
32 import java.util.Arrays JavaDoc;
33 import java.util.zip.GZIPInputStream JavaDoc;
34 import java.util.zip.GZIPOutputStream JavaDoc;
35
36 /**
37  * Represents the identity of an object.
38  * <br/>
39  * It's composed of:
40  * <ul>
41  * <li>
42  * class of the real object
43  * </li>
44  * <li>
45  * top-level class of the real object (could be an abstract class or interface or the
46  * class of the object itself), used to make an object unique across extent classes
47  * </li>
48  * <li>
49  * an array of all primary key value objects
50  * </li>
51  * <li>
52  * a flag which indicates whether this is a <em>transient Identity</em>
53  * (identity of a non-persistent, "new" object) or a <em>persistent Identity</em> (identity object
54  * of a persistent, "already written to datastore" object).
55  * </li>
56  * </ul>
57  * <p>
58  * To create <code>Identity</code> objects it's strongly recommended to use the {@link IdentityFactory}, because
59  * in future releases of OJB the <code>Identity</code> constructors will be no longer reachable or forbidden to use.
60  * </p>
61  * <p>
62  * NOTE: An <em>Identity</em> object must be unique
63  * accross extents. Means all objects with the same top-level class need unique
64  * PK values.
65  * </p>
66  * @see org.apache.ojb.broker.IdentityFactory
67
68  * @author Thomas Mahler
69  * @version $Id: Identity.java,v 1.36.2.14 2005/12/21 22:22:07 tomdz Exp $
70  */

71 public class Identity implements Serializable JavaDoc
72 {
73     /** Unique id for serialization purposes. */
74     private static final long serialVersionUID = 3182285550574178710L;
75
76     private static final int IS_TRANSIENT = 3;
77     private static final int IS_PERMANENT = 17;
78     /**
79      * Used for hashCode calculation.
80      */

81     private static final int iConstant = 37;
82
83     /**
84      * The top-level Class of the identified object, ie. an interface.
85      */

86     private Class JavaDoc m_objectsTopLevelClass;
87
88     /**
89      * The real Class of the identified object, ie. the implementing class.
90      */

91     private Class JavaDoc m_objectsRealClass = null;
92
93     /**
94      * The ordered list of primary key values maintaining the objects identity in the underlying RDBMS.
95      */

96     private Object JavaDoc[] m_pkValues;
97
98     private final int isTransient;
99
100     /*
101     the hashcode of different objects has to be unique across different
102     JVM and have to be the same for the same object in different JVM.
103
104     In distributed enviroments the Identity object have to recalculate the
105     hashCode and toString values, because the hash code of the Class object
106     differs in different JVM
107     */

108     private transient String JavaDoc m_stringRepresentation = null;
109     private transient Integer JavaDoc m_hashCode;
110
111     /**
112      * For internal use only!
113      */

114     protected Identity()
115     {
116         isTransient = IS_TRANSIENT;
117     }
118
119     /**
120      * For internal use only!. Creates an em from a class and the objects primary key values.
121      * used for the definition of proxies.
122      * <br/>
123      * OJB user have to use {@link IdentityFactory} to create object identity.
124      *
125      *
126      * @param realClass the concrete class of the object, or null if not known.
127      * @param topLevel the highest persistence-capable class or
128      * interface (in the inheritance hierarchy) that the identified object is an instance of
129      * @param pkValues (unique across the extents !)
130      * @param isTransient If <em>true</em>
131      */

132     public Identity(final Class JavaDoc realClass, final Class JavaDoc topLevel, final Object JavaDoc[] pkValues, final boolean isTransient)
133     {
134         m_objectsTopLevelClass = topLevel;
135         m_objectsRealClass = realClass;
136         m_pkValues = pkValues;
137         this.isTransient = isTransient ? IS_TRANSIENT : IS_PERMANENT;
138         checkForPrimaryKeys(null);
139     }
140
141     /**
142      * For internal use only! Creates an Identity from a class and the objects primary key values.
143      * used for the definition of proxies.
144      * <br/>
145      * OJB user have to use {@link IdentityFactory} to create object identity.
146      *
147      * @param realClass the concrete class of the object, or null if not known.
148      * @param topLevel the highest persistence-capable class or
149      * interface (in the inheritance hierarchy) that the identified object is an instance of
150      * @param pkValues (unique across the extents !)
151      */

152     public Identity(final Class JavaDoc realClass, final Class JavaDoc topLevel, final Object JavaDoc[] pkValues)
153     {
154         m_objectsTopLevelClass = topLevel;
155         m_objectsRealClass = realClass;
156         m_pkValues = pkValues;
157         this.isTransient = IS_PERMANENT;
158         checkForPrimaryKeys(null);
159     }
160
161     /**
162      * Constructor for internal use. Use {@link IdentityFactory} to create an object identity.
163      *
164      * @param objectToIdentitify The object for which to create the identity
165      * @param targetBroker The persistence broker
166      */

167     public Identity(final Object JavaDoc objectToIdentitify, final PersistenceBroker targetBroker)
168     {
169         this.isTransient = IS_PERMANENT;
170         init(objectToIdentitify, targetBroker, null);
171     }
172
173     /**
174      * Constructor for internal use. Use {@link IdentityFactory} to create an object identity.
175      *
176      * @param objectToIdentitify The object for which to create the identity
177      * @param targetBroker The persistence broker
178      * @param cld The class descriptor
179      */

180     public Identity(final Object JavaDoc objectToIdentitify, final PersistenceBroker targetBroker, final ClassDescriptor cld)
181     {
182         this.isTransient = IS_PERMANENT;
183         init(objectToIdentitify, targetBroker, cld);
184     }
185
186     private void init(final Object JavaDoc objectToIdentify, final PersistenceBroker targetBroker, ClassDescriptor cld)
187     {
188         if(objectToIdentify == null) throw new OJBRuntimeException("Can't create Identity for 'null'-object");
189         try
190         {
191             final IndirectionHandler handler = ProxyHelper.getIndirectionHandler(objectToIdentify);
192
193             synchronized(objectToIdentify)
194             {
195                 if (handler != null)
196                 {
197                     final Identity sourceOID = handler.getIdentity();
198                     m_objectsTopLevelClass = sourceOID.m_objectsTopLevelClass;
199                     m_objectsRealClass = sourceOID.m_objectsRealClass;
200                     m_pkValues = sourceOID.m_pkValues;
201                 }
202                 else
203                 {
204                     if (cld == null)
205                     {
206                         cld = targetBroker.getClassDescriptor(objectToIdentify.getClass());
207                     }
208
209                     // identities must be unique accross extents !
210
m_objectsTopLevelClass = targetBroker.getTopLevelClass(objectToIdentify.getClass());
211                     m_objectsRealClass = objectToIdentify.getClass();
212
213                     // BRJ: definitely do NOT convertToSql
214
// conversion is done when binding the sql-statement
215
final BrokerHelper helper = targetBroker.serviceBrokerHelper();
216                     final ValueContainer[] pkValues = helper.getValuesForObject(cld.getPkFields(), objectToIdentify, false, true);
217                     if (pkValues == null || pkValues.length == 0)
218                     {
219                         throw createException("Can't extract PK value fields", objectToIdentify, null);
220                     }
221                     m_pkValues = helper.extractValueArray(pkValues);
222                 }
223             }
224
225             checkForPrimaryKeys(objectToIdentify);
226         }
227         catch (ClassNotPersistenceCapableException e)
228         {
229             throw e;
230         }
231         catch (Exception JavaDoc e)
232         {
233             throw createException("Can not init Identity for given object.", objectToIdentify, e);
234         }
235     }
236
237     /**
238      * Factory method that returns an Identity object created from a serializated representation.
239      *
240      * @param anArray The serialized representation
241      * @return The identity
242      * @see {@link #serialize}.
243      * @deprecated
244      */

245     public static Identity fromByteArray(final byte[] anArray) throws PersistenceBrokerException
246     {
247         // reverse of the serialize() algorithm:
248
// read from byte[] with a ByteArrayInputStream, decompress with
249
// a GZIPInputStream and then deserialize by reading from the ObjectInputStream
250
try
251         {
252             final ByteArrayInputStream JavaDoc bais = new ByteArrayInputStream JavaDoc(anArray);
253             final GZIPInputStream JavaDoc gis = new GZIPInputStream JavaDoc(bais);
254             final ObjectInputStream JavaDoc ois = new ObjectInputStream JavaDoc(gis);
255             final Identity result = (Identity) ois.readObject();
256             ois.close();
257             gis.close();
258             bais.close();
259             return result;
260         }
261         catch (Exception JavaDoc ex)
262         {
263             throw new PersistenceBrokerException(ex);
264         }
265     }
266
267     /**
268      * Determines whether the identity is transient.
269      *
270      * @return <code>true</code> if the identity is transient
271      */

272     public boolean isTransient()
273     {
274         return isTransient == IS_TRANSIENT;
275     }
276
277     /**
278      * Returns the top-level class of the real subject (base class,
279      * base interface denoted in the repository or
280      * objects real class if no top-level was found).
281      *
282      * @return The top level class
283      */

284     public Class JavaDoc getObjectsTopLevelClass()
285     {
286         return m_objectsTopLevelClass;
287     }
288
289     /**
290      * Return the "real" class of the real subject.
291      *
292      * @return The real class
293      */

294     public Class JavaDoc getObjectsRealClass()
295     {
296         return m_objectsRealClass;
297     }
298
299     /**
300      * Set the real class of the subject.
301      *
302      * @param objectsRealClass The real class
303      */

304     public void setObjectsRealClass(final Class JavaDoc objectsRealClass)
305     {
306         this.m_objectsRealClass = objectsRealClass;
307     }
308
309     /**
310      * Return the serialized form of this Identity.
311      *
312      * @return The serialized representation
313      * @see #fromByteArray
314      * @deprecated
315      */

316     public byte[] serialize() throws PersistenceBrokerException
317     {
318         // Identity is serialized and written to an ObjectOutputStream
319
// This ObjectOutputstream is compressed by a GZIPOutputStream
320
// and finally written to a ByteArrayOutputStream.
321
// the resulting byte[] is returned
322
try
323         {
324             final ByteArrayOutputStream JavaDoc bao = new ByteArrayOutputStream JavaDoc();
325             final GZIPOutputStream JavaDoc gos = new GZIPOutputStream JavaDoc(bao);
326             final ObjectOutputStream JavaDoc oos = new ObjectOutputStream JavaDoc(gos);
327             oos.writeObject(this);
328             oos.close();
329             gos.close();
330             bao.close();
331             return bao.toByteArray();
332         }
333         catch (Exception JavaDoc ignored)
334         {
335             throw new PersistenceBrokerException(ignored);
336         }
337     }
338
339     /**
340      * return a String representation.
341      * @return java.lang.String
342      */

343     public String JavaDoc toString()
344     {
345         if (m_stringRepresentation == null)
346         {
347             final StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
348             buf.append(m_objectsTopLevelClass.getName());
349             for (int i = 0; i < m_pkValues.length; i++)
350             {
351                 buf.append((i == 0) ? "{" : ",");
352                 buf.append(m_pkValues[i]);
353             }
354             buf.append("}");
355             if(isTransient == IS_TRANSIENT) buf.append("-transient");
356             m_stringRepresentation = buf.toString();
357         }
358         return m_stringRepresentation;
359     }
360
361
362     /**
363      * OJB can handle only classes that declare at least one primary key attribute,
364      * this method checks this condition.
365      *
366      * @param realObject The real object to check
367      * @throws ClassNotPersistenceCapableException thrown if no primary key is specified for the objects class
368      */

369     protected void checkForPrimaryKeys(final Object JavaDoc realObject) throws ClassNotPersistenceCapableException
370     {
371         // if no PKs are specified OJB can't handle this class !
372
if (m_pkValues == null || m_pkValues.length == 0)
373         {
374             throw createException("OJB needs at least one primary key attribute for class: ", realObject, null);
375         }
376 // arminw: should never happen
377
// if(m_pkValues[0] instanceof ValueContainer)
378
// throw new OJBRuntimeException("Can't handle pk values of type "+ValueContainer.class.getName());
379
}
380
381     /**
382      * Returns the primary key values of the real subject.
383      *
384      * @return The pk values
385      */

386     public Object JavaDoc[] getPrimaryKeyValues()
387     {
388         return m_pkValues;
389     }
390
391     /**
392      * {@inheritDoc}
393      */

394     public boolean equals(final Object JavaDoc obj)
395     {
396         if(this == obj) return true;
397
398         boolean result = false;
399         if (obj instanceof Identity)
400         {
401             final Identity id = (Identity) obj;
402             result = m_objectsTopLevelClass.equals(id.m_objectsTopLevelClass) && isTransient == id.isTransient;
403             if(result)
404             {
405                 final Object JavaDoc[] otherPkValues = id.m_pkValues;
406                 result = m_pkValues.length == otherPkValues.length;
407                 if(result)
408                 {
409                     for (int i = 0; result && i < m_pkValues.length; i++)
410                     {
411                         result = (m_pkValues[i] == null) ? (otherPkValues[i] == null)
412                                 : m_pkValues[i].equals(otherPkValues[i]);
413
414                         // special treatment for byte[]
415
if (!result && m_pkValues[i] instanceof byte[] && otherPkValues[i] instanceof byte[])
416                         {
417                             result = Arrays.equals((byte[]) m_pkValues[i], (byte[]) otherPkValues[i]);
418                         }
419                     }
420                 }
421             }
422         }
423         return result;
424     }
425
426     /**
427      * {@inheritDoc}
428      */

429     public int hashCode()
430     {
431         /*
432         arminw:
433         identity is quasi immutable (toplevel class and PK fields
434         never change), thus we can note hashCode
435         */

436         if(m_hashCode == null)
437         {
438             int iTotal = isTransient;
439             Object JavaDoc obj;
440             for (int i = 0; i < m_pkValues.length; i++)
441             {
442                 obj = m_pkValues[i];
443                 if(obj instanceof byte[])
444                 {
445                     iTotal = iTotal * iConstant + ((byte[]) obj).length;
446             }
447                 else
448                 {
449                     iTotal = iTotal * iConstant + (obj != null ? obj.hashCode() : 0);
450         }
451             }
452             iTotal = iTotal * iConstant + m_objectsTopLevelClass.hashCode();
453             m_hashCode = new Integer JavaDoc(iTotal);
454         }
455         return m_hashCode.intValue();
456     }
457
458     private ClassNotPersistenceCapableException createException(String JavaDoc msg, final Object JavaDoc objectToIdentify, final Exception JavaDoc e)
459     {
460         final String JavaDoc eol = SystemUtils.LINE_SEPARATOR;
461         if(msg == null)
462         {
463             msg = "Unexpected error:";
464         }
465         if(e != null)
466         {
467             return new ClassNotPersistenceCapableException(msg + eol +
468                         "objectTopLevelClass=" + (m_objectsTopLevelClass != null ? m_objectsTopLevelClass.getName() : null) + eol +
469                         "objectRealClass=" + (m_objectsRealClass != null ? m_objectsRealClass.getName() : null) + eol +
470                         "pkValues=" + (m_pkValues != null ? ArrayUtils.toString(m_pkValues) : null) +
471                         (objectToIdentify != null ? (eol + "object to identify: " + objectToIdentify) : ""), e);
472         }
473         else
474         {
475             return new ClassNotPersistenceCapableException(msg + eol +
476                         "objectTopLevelClass=" + (m_objectsTopLevelClass != null ? m_objectsTopLevelClass.getName() : null) + eol +
477                         "objectRealClass=" + (m_objectsRealClass != null ? m_objectsRealClass.getName() : null) + eol +
478                         "pkValues=" + (m_pkValues != null ? ArrayUtils.toString(m_pkValues) : null) +
479                         eol + "object to identify: " + objectToIdentify);
480         }
481     }
482 }
Popular Tags