001: package org.apache.ojb.broker.core;
002:
003: /* Copyright 2002-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import java.util.Map;
019:
020: import org.apache.ojb.broker.Identity;
021: import org.apache.ojb.broker.IdentityFactory;
022: import org.apache.ojb.broker.OJBRuntimeException;
023: import org.apache.ojb.broker.PersistenceBroker;
024: import org.apache.ojb.broker.PersistenceBrokerException;
025: import org.apache.ojb.broker.PBStateListener;
026: import org.apache.ojb.broker.PBStateEvent;
027: import org.apache.ojb.broker.core.proxy.IndirectionHandler;
028: import org.apache.ojb.broker.core.proxy.ProxyHelper;
029: import org.apache.ojb.broker.metadata.ClassDescriptor;
030: import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
031: import org.apache.ojb.broker.metadata.FieldDescriptor;
032: import org.apache.ojb.broker.util.BrokerHelper;
033: import org.apache.ojb.broker.util.sequence.SequenceManager;
034: import org.apache.ojb.broker.util.sequence.SequenceManagerTransientImpl;
035: import org.apache.commons.lang.SystemUtils;
036: import org.apache.commons.lang.ArrayUtils;
037: import org.apache.commons.lang.exception.ExceptionUtils;
038: import org.apache.commons.collections.map.ReferenceIdentityMap;
039:
040: /**
041: * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
042: * @version $Id: IdentityFactoryImpl.java,v 1.2.2.5 2005/12/21 22:25:01 tomdz Exp $
043: * @see org.apache.ojb.broker.IdentityFactory
044: */
045: public class IdentityFactoryImpl implements IdentityFactory,
046: PBStateListener {
047: private PersistenceBroker broker;
048: //private boolean activeTx;
049: private Map objectToIdentityMap;
050: private SequenceManager transientSequenceManager;
051:
052: public IdentityFactoryImpl(PersistenceBroker broker) {
053: this .broker = broker;
054: this .objectToIdentityMap = new ReferenceIdentityMap(
055: ReferenceIdentityMap.WEAK, ReferenceIdentityMap.HARD,
056: true);
057: this .transientSequenceManager = new SequenceManagerTransientImpl(
058: broker);
059: broker.addListener(this , true);
060: }
061:
062: /**
063: * This methods creates a new transient (if at least one PK field is 'null') or persistent
064: * (if the PK fields are populated) {@link org.apache.ojb.broker.Identity} instance. If the specified object
065: * is transient and former call for the same object returns already a transient Identity, the same transient
066: * Identity object will be returned.
067: */
068: protected Identity createTransientOrRealIdentity(
069: ClassDescriptor cld, Object objOrProxy) {
070: if (objOrProxy == null)
071: throw new OJBRuntimeException(
072: "Can't create Identity for 'null'-object");
073: Identity result = null;
074: Class topLevelClass = null;
075: Class realClass = null;
076: Object[] pks = null;
077: try {
078: final IndirectionHandler handler = ProxyHelper
079: .getIndirectionHandler(objOrProxy);
080:
081: synchronized (objOrProxy) {
082: if (handler != null) {
083: result = handler.getIdentity();
084: } else {
085: // now we are sure that the specified object is not a proxy
086: realClass = objOrProxy.getClass();
087: topLevelClass = broker.getTopLevelClass(objOrProxy
088: .getClass());
089: if (cld == null) {
090: cld = broker.getClassDescriptor(objOrProxy
091: .getClass());
092: }
093: BrokerHelper helper = broker.serviceBrokerHelper();
094:
095: FieldDescriptor[] fields = cld.getPkFields();
096: pks = new Object[fields.length];
097: FieldDescriptor fld;
098: for (int i = 0; i < fields.length; i++) {
099: fld = fields[i];
100: /*
101: we check all PK fields for 'null'-values
102: */
103: Object value = fld.getPersistentField().get(
104: objOrProxy);
105: if (helper.representsNull(fld, value)) {
106: result = (Identity) objectToIdentityMap
107: .get(objOrProxy);
108: if (result == null) {
109: pks[i] = transientSequenceManager
110: .getUniqueValue(fld);
111: result = new Identity(realClass,
112: topLevelClass, pks, true);
113: //if(activeTx) objectToIdentityMap.put(objOrProxy, result);
114: objectToIdentityMap.put(objOrProxy,
115: result);
116: }
117: break;
118: } else {
119: pks[i] = value;
120: }
121: }
122: if (result == null) {
123: result = new Identity(realClass, topLevelClass,
124: pks, false);
125: }
126: }
127: }
128: } catch (ClassNotPersistenceCapableException e) {
129: throw e;
130: } catch (Exception e) {
131: throw createException(e,
132: "Can not init Identity for given object.",
133: objOrProxy, topLevelClass, realClass, pks);
134: }
135: return result;
136: }
137:
138: /** @see org.apache.ojb.broker.IdentityFactory#buildIdentity(Object) */
139: public Identity buildIdentity(Object obj) {
140: return createTransientOrRealIdentity(broker
141: .getClassDescriptor(ProxyHelper.getRealClass(obj)), obj);
142: }
143:
144: /** @see org.apache.ojb.broker.IdentityFactory#buildIdentity(Object) */
145: public Identity buildIdentity(ClassDescriptor cld, Object obj) {
146: return createTransientOrRealIdentity(cld, obj);
147: }
148:
149: /** @see org.apache.ojb.broker.IdentityFactory#buildIdentity(Class, Class, String[], Object[]) */
150: public Identity buildIdentity(Class realClass, Class topLevelClass,
151: String[] pkFieldNames, Object[] pkValues) {
152: Object[] orderedPKValues = pkValues;
153: if (pkValues == null) {
154: throw new NullPointerException(
155: "Given primary key value array can't be null");
156: }
157: if (pkValues.length == 1
158: && (pkFieldNames == null || pkFieldNames.length == 1)) {
159: /*
160: we assume only a single PK field is defined and do no further checks,
161: we have nothing to do
162: */
163: } else {
164: // in other more complex cases we do several check
165: FieldDescriptor[] flds = broker.getClassDescriptor(
166: realClass).getPkFields();
167: if (!isOrdered(flds, pkFieldNames)) {
168: orderedPKValues = reorderFieldValues(flds,
169: pkFieldNames, pkValues);
170: }
171: }
172: return new Identity(realClass, topLevelClass, orderedPKValues);
173: }
174:
175: /**
176: * This method orders the specified field values based on the
177: * specified {@link org.apache.ojb.broker.metadata.FieldDescriptor}.
178: *
179: * @param flds The {@link org.apache.ojb.broker.metadata.FieldDescriptor} array.
180: * @param fieldNames The field names.
181: * @param fieldValues The field values.
182: * @return The ordered field values.
183: */
184: private Object[] reorderFieldValues(FieldDescriptor[] flds,
185: String[] fieldNames, Object[] fieldValues) {
186: String fieldName;
187: Object[] orderedValues = new Object[flds.length];
188: for (int i = 0; i < flds.length; i++) {
189: fieldName = flds[i].getPersistentField().getName();
190: int realPosition = findIndexForName(fieldNames, fieldName);
191: orderedValues[i] = fieldValues[realPosition];
192: }
193: return orderedValues;
194: }
195:
196: /**
197: * Find the index of the specified name in field name array.
198: */
199: private int findIndexForName(String[] fieldNames, String searchName) {
200: for (int i = 0; i < fieldNames.length; i++) {
201: if (searchName.equals(fieldNames[i])) {
202: return i;
203: }
204: }
205: throw new PersistenceBrokerException("Can't find field name '"
206: + searchName + "' in given array of field names");
207: }
208:
209: /** Checks length and compare order of field names with declared PK fields in metadata. */
210: private boolean isOrdered(FieldDescriptor[] flds,
211: String[] pkFieldNames) {
212: if ((flds.length > 1 && pkFieldNames == null)
213: || flds.length != pkFieldNames.length) {
214: throw new PersistenceBrokerException(
215: "pkFieldName length does not match number of defined PK fields."
216: + " Expected number of PK fields is "
217: + flds.length
218: + ", given number was "
219: + (pkFieldNames != null ? pkFieldNames.length
220: : 0));
221: }
222: boolean result = true;
223: for (int i = 0; i < flds.length; i++) {
224: FieldDescriptor fld = flds[i];
225: result = result
226: && fld.getPersistentField().getName().equals(
227: pkFieldNames[i]);
228: }
229: return result;
230: }
231:
232: /** @see org.apache.ojb.broker.IdentityFactory#buildIdentity(Class, String[], Object[]) */
233: public Identity buildIdentity(Class realClass,
234: String[] pkFieldNames, Object[] pkValues) {
235: return buildIdentity(realClass, broker
236: .getTopLevelClass(realClass), pkFieldNames, pkValues);
237: }
238:
239: /** @see org.apache.ojb.broker.IdentityFactory#buildIdentity(Class, String[], Object[]) */
240: public Identity buildIdentity(Class realClass, Class topLevelClass,
241: Object[] pkValues) {
242: return new Identity(realClass, topLevelClass, pkValues);
243: }
244:
245: /** @see org.apache.ojb.broker.IdentityFactory#buildIdentity(Class, Object) */
246: public Identity buildIdentity(Class realClass, Object pkValue) {
247: return buildIdentity(realClass, (String[]) null,
248: new Object[] { pkValue });
249: }
250:
251: /**
252: * Helper method which supports creation of proper error messages.
253: *
254: * @param ex An exception to include or <em>null</em>.
255: * @param message The error message or <em>null</em>.
256: * @param objectToIdentify The current used object or <em>null</em>.
257: * @param topLevelClass The object top-level class or <em>null</em>.
258: * @param realClass The object real class or <em>null</em>.
259: * @param pks The associated PK values of the object or <em>null</em>.
260: * @return The generated exception.
261: */
262: private PersistenceBrokerException createException(
263: final Exception ex, String message,
264: final Object objectToIdentify, Class topLevelClass,
265: Class realClass, Object[] pks) {
266: final String eol = SystemUtils.LINE_SEPARATOR;
267: StringBuffer msg = new StringBuffer();
268: if (message == null) {
269: msg.append("Unexpected error: ");
270: } else {
271: msg.append(message).append(" :");
272: }
273: if (topLevelClass != null)
274: msg.append(eol).append("objectTopLevelClass=").append(
275: topLevelClass.getName());
276: if (realClass != null)
277: msg.append(eol).append("objectRealClass=").append(
278: realClass.getName());
279: if (pks != null)
280: msg.append(eol).append("pkValues=").append(
281: ArrayUtils.toString(pks));
282: if (objectToIdentify != null)
283: msg.append(eol).append("object to identify: ").append(
284: objectToIdentify);
285: if (ex != null) {
286: // add causing stack trace
287: Throwable rootCause = ExceptionUtils.getRootCause(ex);
288: if (rootCause != null) {
289: msg.append(eol).append("The root stack trace is --> ");
290: String rootStack = ExceptionUtils
291: .getStackTrace(rootCause);
292: msg.append(eol).append(rootStack);
293: }
294:
295: return new PersistenceBrokerException(msg.toString(), ex);
296: } else {
297: return new PersistenceBrokerException(msg.toString());
298: }
299: }
300:
301: //===================================================================
302: // PBStateListener interface
303: //===================================================================
304: public void afterBegin(PBStateEvent event) {
305: }
306:
307: public void afterCommit(PBStateEvent event) {
308: if (objectToIdentityMap.size() > 0)
309: objectToIdentityMap.clear();
310: }
311:
312: public void afterRollback(PBStateEvent event) {
313: if (objectToIdentityMap.size() > 0)
314: objectToIdentityMap.clear();
315: }
316:
317: public void beforeClose(PBStateEvent event) {
318: if (objectToIdentityMap.size() > 0)
319: objectToIdentityMap.clear();
320: }
321:
322: public void beforeRollback(PBStateEvent event) {
323: }
324:
325: public void afterOpen(PBStateEvent event) {
326: }
327:
328: public void beforeBegin(PBStateEvent event) {
329: }
330:
331: public void beforeCommit(PBStateEvent event) {
332: }
333: }
|