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