001: package org.apache.ojb.broker.core.proxy;
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.lang.reflect.Constructor;
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Modifier;
021: import java.util.Collection;
022: import java.util.List;
023: import java.util.Set;
024:
025: import org.apache.ojb.broker.Identity;
026: import org.apache.ojb.broker.ManageableCollection;
027: import org.apache.ojb.broker.PBKey;
028: import org.apache.ojb.broker.PersistenceBrokerException;
029: import org.apache.ojb.broker.metadata.MetadataException;
030: import org.apache.ojb.broker.query.Query;
031: import org.apache.ojb.broker.util.configuration.impl.OjbConfigurator;
032: import org.apache.ojb.broker.util.logging.Logger;
033: import org.apache.ojb.broker.util.logging.LoggerFactory;
034:
035: /**
036: * Abstract implementation for the ProxyFactory
037: *
038: * @author andrew.clute
039: * @version $Id:
040: */
041: public abstract class AbstractProxyFactory implements ProxyFactory {
042: /*
043: arminw:
044: Do we need a serializable ProxyFactory implementation? If yes, we have to fix this class.
045: TODO: ProxyFactory is declared as Serializable but Constructor class is not, but used without transient keyword.
046: */
047:
048: private static Logger log = LoggerFactory
049: .getLogger(AbstractProxyFactory.class);
050: private static transient ProxyFactory singleton;
051:
052: /** The indirection handler class */
053: private Class _indirectionHandlerClass;
054: /** The constructor used for creating indirection handler instances (shortcut) */
055: private transient Constructor _indirectionHandlerConstructor;
056: /** The constructor used for creating list proxies */
057: private Constructor _listProxyConstructor;
058: /** The constructor used for creating set proxies */
059: private Constructor _setProxyConstructor;
060: /** The constructor used for creating collection proxies */
061: private Constructor _collectionProxyConstructor;
062:
063: private static ProxyConfiguration getProxyConfiguration() {
064: return (ProxyConfiguration) OjbConfigurator.getInstance()
065: .getConfigurationFor(null);
066: }
067:
068: /**
069: * Returns the constructor of the indirection handler class.
070: *
071: * @return The constructor for indirection handlers
072: */
073: private synchronized Constructor getIndirectionHandlerConstructor() {
074: if (_indirectionHandlerConstructor == null) {
075: Class[] paramType = { PBKey.class, Identity.class };
076:
077: try {
078: _indirectionHandlerConstructor = getIndirectionHandlerClass()
079: .getConstructor(paramType);
080: } catch (NoSuchMethodException ex) {
081: throw new MetadataException(
082: "The class "
083: + _indirectionHandlerClass.getName()
084: + " specified for IndirectionHandlerClass"
085: + " is required to have a public constructor with signature ("
086: + PBKey.class.getName() + ", "
087: + Identity.class.getName() + ").");
088: }
089: }
090: return _indirectionHandlerConstructor;
091: }
092:
093: /**
094: * Returns the indirection handler class.
095: *
096: * @return The class for indirection handlers
097: */
098: public Class getIndirectionHandlerClass() {
099: if (_indirectionHandlerClass == null) {
100: setIndirectionHandlerClass(getProxyConfiguration()
101: .getIndirectionHandlerClass());
102: }
103:
104: return _indirectionHandlerClass;
105: }
106:
107: /**
108: * Sets the indirection handler class.
109: *
110: * @param indirectionHandlerClass The class for indirection handlers
111: */
112: public void setIndirectionHandlerClass(Class indirectionHandlerClass) {
113: if (indirectionHandlerClass == null) {
114: //throw new MetadataException("No IndirectionHandlerClass specified.");
115: /**
116: * andrew.clute
117: * Allow the default IndirectionHandler for the given ProxyFactory implementation
118: * when the parameter is not given
119: */
120: indirectionHandlerClass = getDefaultIndirectionHandlerClass();
121: }
122: if (indirectionHandlerClass.isInterface()
123: || Modifier.isAbstract(indirectionHandlerClass
124: .getModifiers())
125: || !getIndirectionHandlerBaseClass().isAssignableFrom(
126: indirectionHandlerClass)) {
127: throw new MetadataException(
128: "Illegal class "
129: + indirectionHandlerClass.getName()
130: + " specified for IndirectionHandlerClass. Must be a concrete subclass of "
131: + getIndirectionHandlerBaseClass()
132: .getName());
133: }
134: _indirectionHandlerClass = indirectionHandlerClass;
135: }
136:
137: /**
138: * Creates a new indirection handler instance.
139: *
140: * @param brokerKey The associated {@link PBKey}.
141: * @param id The subject's ids
142: * @return The new instance
143: */
144: public IndirectionHandler createIndirectionHandler(PBKey brokerKey,
145: Identity id) {
146: Object args[] = { brokerKey, id };
147:
148: try {
149: return (IndirectionHandler) getIndirectionHandlerConstructor()
150: .newInstance(args);
151: } catch (InvocationTargetException ex) {
152: throw new PersistenceBrokerException(
153: "Exception while creating a new indirection handler instance",
154: ex);
155: } catch (InstantiationException ex) {
156: throw new PersistenceBrokerException(
157: "Exception while creating a new indirection handler instance",
158: ex);
159: } catch (IllegalAccessException ex) {
160: throw new PersistenceBrokerException(
161: "Exception while creating a new indirection handler instance",
162: ex);
163: }
164: }
165:
166: /**
167: * Retrieves the constructor that is used by OJB to create instances of the given collection proxy
168: * class.
169: *
170: * @param proxyClass The proxy class
171: * @param baseType The required base type of the proxy class
172: * @param typeDesc The type of collection proxy
173: * @return The constructor
174: */
175: private static Constructor retrieveCollectionProxyConstructor(
176: Class proxyClass, Class baseType, String typeDesc) {
177: if (proxyClass == null) {
178: throw new MetadataException("No " + typeDesc
179: + " specified.");
180: }
181: if (proxyClass.isInterface()
182: || Modifier.isAbstract(proxyClass.getModifiers())
183: || !baseType.isAssignableFrom(proxyClass)) {
184: throw new MetadataException("Illegal class "
185: + proxyClass.getName() + " specified for "
186: + typeDesc + ". Must be a concrete subclass of "
187: + baseType.getName());
188: }
189:
190: Class[] paramType = { PBKey.class, Class.class, Query.class };
191:
192: try {
193: return proxyClass.getConstructor(paramType);
194: } catch (NoSuchMethodException ex) {
195: throw new MetadataException(
196: "The class "
197: + proxyClass.getName()
198: + " specified for "
199: + typeDesc
200: + " is required to have a public constructor with signature ("
201: + PBKey.class.getName() + ", "
202: + Class.class.getName() + ", "
203: + Query.class.getName() + ").");
204: }
205: }
206:
207: /**
208: * Returns the list proxy class.
209: *
210: * @return The class used for list proxies
211: */
212: public Class getListProxyClass() {
213: return getListProxyConstructor().getDeclaringClass();
214: }
215:
216: /**
217: * Returns the constructor of the list proxy class.
218: *
219: * @return The constructor for list proxies
220: */
221: private Constructor getListProxyConstructor() {
222: if (_listProxyConstructor == null) {
223: setListProxyClass(getProxyConfiguration()
224: .getListProxyClass());
225: }
226: return _listProxyConstructor;
227: }
228:
229: /**
230: * Dets the proxy class to use for collection classes that implement the {@link java.util.List} interface.
231: * Notes that the proxy class must implement the {@link java.util.List} interface, and have a constructor
232: * of the signature ({@link org.apache.ojb.broker.PBKey}, {@link java.lang.Class}, {@link org.apache.ojb.broker.query.Query}).
233: *
234: * @param listProxyClass The proxy class
235: */
236: public void setListProxyClass(Class listProxyClass) {
237: _listProxyConstructor = retrieveCollectionProxyConstructor(
238: listProxyClass, List.class, "ListProxyClass");
239: }
240:
241: /**
242: * Returns the set proxy class.
243: *
244: * @return The class used for set proxies
245: */
246: public Class getSetProxyClass() {
247: return getSetProxyConstructor().getDeclaringClass();
248: }
249:
250: /**
251: * Returns the constructor of the set proxy class.
252: *
253: * @return The constructor for set proxies
254: */
255: private Constructor getSetProxyConstructor() {
256: if (_setProxyConstructor == null) {
257: setSetProxyClass(getProxyConfiguration().getSetProxyClass());
258: }
259: return _setProxyConstructor;
260: }
261:
262: /**
263: * Dets the proxy class to use for collection classes that implement the {@link Set} interface.
264: *
265: * @param setProxyClass The proxy class
266: */
267: public void setSetProxyClass(Class setProxyClass) {
268: _setProxyConstructor = retrieveCollectionProxyConstructor(
269: setProxyClass, Set.class, "SetProxyClass");
270: }
271:
272: /**
273: * Returns the collection proxy class.
274: *
275: * @return The class used for collection proxies
276: */
277: public Class getCollectionProxyClass() {
278: return getCollectionProxyConstructor().getDeclaringClass();
279: }
280:
281: /**
282: * Returns the constructor of the generic collection proxy class.
283: *
284: * @return The constructor for collection proxies
285: */
286: private Constructor getCollectionProxyConstructor() {
287: if (_collectionProxyConstructor == null) {
288: setCollectionProxyClass(getProxyConfiguration()
289: .getCollectionProxyClass());
290: }
291: return _collectionProxyConstructor;
292: }
293:
294: /**
295: * Dets the proxy class to use for generic collection classes implementing the {@link java.util.Collection} interface.
296: *
297: * @param collectionProxyClass The proxy class
298: */
299: public void setCollectionProxyClass(Class collectionProxyClass) {
300: _collectionProxyConstructor = retrieveCollectionProxyConstructor(
301: collectionProxyClass, Collection.class,
302: "CollectionProxyClass");
303: // we also require the class to be a subclass of ManageableCollection
304: if (!ManageableCollection.class
305: .isAssignableFrom(collectionProxyClass)) {
306: throw new MetadataException(
307: "Illegal class "
308: + collectionProxyClass.getName()
309: + " specified for CollectionProxyClass. Must be a concrete subclass of "
310: + ManageableCollection.class.getName());
311: }
312: }
313:
314: /**
315: * Determines which proxy to use for the given collection class (list, set or generic collection proxy).
316: *
317: * @param collectionClass The collection class
318: * @return The constructor of the proxy class
319: */
320: private Constructor getCollectionProxyConstructor(
321: Class collectionClass) {
322: if (List.class.isAssignableFrom(collectionClass)) {
323: return getListProxyConstructor();
324: } else if (Set.class.isAssignableFrom(collectionClass)) {
325: return getSetProxyConstructor();
326: } else {
327: return getCollectionProxyConstructor();
328: }
329: }
330:
331: /**
332: * Create a Collection Proxy for a given query.
333: *
334: * @param brokerKey The key of the persistence broker
335: * @param query The query
336: * @param collectionClass The class to build the proxy for
337: * @return The collection proxy
338: */
339: public ManageableCollection createCollectionProxy(PBKey brokerKey,
340: Query query, Class collectionClass) {
341: Object args[] = { brokerKey, collectionClass, query };
342:
343: try {
344: return (ManageableCollection) getCollectionProxyConstructor(
345: collectionClass).newInstance(args);
346: } catch (InstantiationException ex) {
347: throw new PersistenceBrokerException(
348: "Exception while creating a new collection proxy instance",
349: ex);
350: } catch (InvocationTargetException ex) {
351: throw new PersistenceBrokerException(
352: "Exception while creating a new collection proxy instance",
353: ex);
354: } catch (IllegalAccessException ex) {
355: throw new PersistenceBrokerException(
356: "Exception while creating a new collection proxy instance",
357: ex);
358: }
359: }
360:
361: /**
362: * Get the real Object
363: *
364: * @param objectOrProxy
365: * @return Object
366: */
367: public final Object getRealObject(Object objectOrProxy) {
368: if (isNormalOjbProxy(objectOrProxy)) {
369: String msg;
370:
371: try {
372: return getIndirectionHandler(objectOrProxy)
373: .getRealSubject();
374: } catch (ClassCastException e) {
375: // shouldn't happen but still ...
376: msg = "The InvocationHandler for the provided Proxy was not an instance of "
377: + IndirectionHandler.class.getName();
378: log.error(msg);
379: throw new PersistenceBrokerException(msg, e);
380: } catch (IllegalArgumentException e) {
381: msg = "Could not retrieve real object for given Proxy: "
382: + objectOrProxy;
383: log.error(msg);
384: throw new PersistenceBrokerException(msg, e);
385: } catch (PersistenceBrokerException e) {
386: log
387: .error("Could not retrieve real object for given Proxy: "
388: + objectOrProxy);
389: throw e;
390: }
391: } else if (isVirtualOjbProxy(objectOrProxy)) {
392: try {
393: return ((VirtualProxy) objectOrProxy).getRealSubject();
394: } catch (PersistenceBrokerException e) {
395: log
396: .error("Could not retrieve real object for VirtualProxy: "
397: + objectOrProxy);
398: throw e;
399: }
400: } else {
401: return objectOrProxy;
402: }
403: }
404:
405: /**
406: * Get the real Object for already materialized Handler
407: *
408: * @param objectOrProxy
409: * @return Object or null if the Handel is not materialized
410: */
411: public Object getRealObjectIfMaterialized(Object objectOrProxy) {
412: if (isNormalOjbProxy(objectOrProxy)) {
413: String msg;
414:
415: try {
416: IndirectionHandler handler = getIndirectionHandler(objectOrProxy);
417:
418: return handler.alreadyMaterialized() ? handler
419: .getRealSubject() : null;
420: } catch (ClassCastException e) {
421: // shouldn't happen but still ...
422: msg = "The InvocationHandler for the provided Proxy was not an instance of "
423: + IndirectionHandler.class.getName();
424: log.error(msg);
425: throw new PersistenceBrokerException(msg, e);
426: } catch (IllegalArgumentException e) {
427: msg = "Could not retrieve real object for given Proxy: "
428: + objectOrProxy;
429: log.error(msg);
430: throw new PersistenceBrokerException(msg, e);
431: } catch (PersistenceBrokerException e) {
432: log
433: .error("Could not retrieve real object for given Proxy: "
434: + objectOrProxy);
435: throw e;
436: }
437: } else if (isVirtualOjbProxy(objectOrProxy)) {
438: try {
439: VirtualProxy proxy = (VirtualProxy) objectOrProxy;
440:
441: return proxy.alreadyMaterialized() ? proxy
442: .getRealSubject() : null;
443: } catch (PersistenceBrokerException e) {
444: log
445: .error("Could not retrieve real object for VirtualProxy: "
446: + objectOrProxy);
447: throw e;
448: }
449: } else {
450: return objectOrProxy;
451: }
452: }
453:
454: /**
455: * Get the real Class
456: *
457: * @param objectOrProxy
458: * @return Class
459: */
460: public Class getRealClass(Object objectOrProxy) {
461: IndirectionHandler handler;
462:
463: if (isNormalOjbProxy(objectOrProxy)) {
464: String msg;
465:
466: try {
467: handler = getIndirectionHandler(objectOrProxy);
468: /*
469: arminw:
470: think we should return the real class
471: */
472: // return handler.getIdentity().getObjectsTopLevelClass();
473: return handler.getIdentity().getObjectsRealClass();
474: } catch (ClassCastException e) {
475: // shouldn't happen but still ...
476: msg = "The InvocationHandler for the provided Proxy was not an instance of "
477: + IndirectionHandler.class.getName();
478: log.error(msg);
479: throw new PersistenceBrokerException(msg, e);
480: } catch (IllegalArgumentException e) {
481: msg = "Could not retrieve real object for given Proxy: "
482: + objectOrProxy;
483: log.error(msg);
484: throw new PersistenceBrokerException(msg, e);
485: }
486: } else if (isVirtualOjbProxy(objectOrProxy)) {
487: handler = VirtualProxy
488: .getIndirectionHandler((VirtualProxy) objectOrProxy);
489: /*
490: arminw:
491: think we should return the real class
492: */
493: // return handler.getIdentity().getObjectsTopLevelClass();
494: return handler.getIdentity().getObjectsRealClass();
495: } else {
496: return objectOrProxy.getClass();
497: }
498: }
499:
500: /**
501: * Determines whether the given object is an OJB proxy.
502: *
503: * @return <code>true</code> if the object is an OJB proxy
504: */
505: public boolean isNormalOjbProxy(Object proxyOrObject) {
506: return proxyOrObject instanceof OJBProxy;
507: }
508:
509: /**
510: * Determines whether the given object is an OJB virtual proxy.
511: *
512: * @return <code>true</code> if the object is an OJB virtual proxy
513: */
514: public boolean isVirtualOjbProxy(Object proxyOrObject) {
515: return proxyOrObject instanceof VirtualProxy;
516: }
517:
518: /**
519: * Returns <tt>true</tt> if the given object is a {@link java.lang.reflect.Proxy}
520: * or a {@link VirtualProxy} instance.
521: */
522: public boolean isProxy(Object proxyOrObject) {
523: return isNormalOjbProxy(proxyOrObject)
524: || isVirtualOjbProxy(proxyOrObject);
525: }
526:
527: /**
528: * Returns the IndirectionHandler associated with a dynamic proxy. Each
529: * subclass is responsible for it's execution
530: */
531: protected abstract IndirectionHandler getDynamicIndirectionHandler(
532: Object obj);
533:
534: /**
535: * Returns the invocation handler object of the given proxy object.
536: *
537: * @param obj The object
538: * @return The invocation handler if the object is an OJB proxy, or <code>null</code>
539: * otherwise
540: */
541: public IndirectionHandler getIndirectionHandler(Object obj) {
542: if (obj == null) {
543: return null;
544: } else if (isNormalOjbProxy(obj)) {
545: return getDynamicIndirectionHandler(obj);
546: } else if (isVirtualOjbProxy(obj)) {
547: return VirtualProxy
548: .getIndirectionHandler((VirtualProxy) obj);
549: } else {
550: return null;
551: }
552:
553: }
554:
555: /**
556: * Determines whether the object is a materialized object, i.e. no proxy or a
557: * proxy that has already been loaded from the database.
558: *
559: * @param object The object to test
560: * @return <code>true</code> if the object is materialized
561: */
562: public boolean isMaterialized(Object object) {
563: IndirectionHandler handler = getIndirectionHandler(object);
564:
565: return handler == null || handler.alreadyMaterialized();
566: }
567:
568: /** Return CollectionProxy for item is item is a CollectionProxy, otherwise return null */
569: public CollectionProxy getCollectionProxy(Object item) {
570: if (isCollectionProxy(item)) {
571: return (CollectionProxy) item;
572: } else {
573: return null;
574: }
575: }
576:
577: /**
578: * Reports if item is a CollectionProxy.
579: *
580: * TODO: Provide handling for pluggable collection proxy implementations
581: */
582: public boolean isCollectionProxy(Object item) {
583: return (item instanceof CollectionProxy);
584: }
585:
586: /**
587: * Materialization-safe version of toString. If the object is a yet-unmaterialized proxy,
588: * then only the text "unmaterialized proxy for ..." is returned and the proxy is NOT
589: * materialized. Otherwise, the normal toString method is called. This useful e.g. for
590: * logging etc.
591: *
592: * @param proxy The object for which a string representation shall be generated
593: * @return The string representation
594: */
595: public String toString(Object proxy) {
596: IndirectionHandler handler = getIndirectionHandler(proxy);
597: if ((handler != null) && handler.alreadyMaterialized()) {
598: return "unmaterialized proxy for " + handler.getIdentity();
599: } else {
600: return proxy.toString();
601: }
602: }
603:
604: public synchronized static ProxyFactory getProxyFactory() {
605: /*
606: TODO: Check usage of singleton.
607: arminw: Think the ProxyFactory instance can be a singleton, because the proxy stuff
608: will never change while runtime.
609: */
610: if (singleton == null) {
611: Class proxyFactoryClass = null;
612: try {
613: proxyFactoryClass = getProxyConfiguration()
614: .getProxyFactoryClass();
615: singleton = (ProxyFactory) proxyFactoryClass
616: .newInstance();
617: } catch (InstantiationException e) {
618: throw new MetadataException("Illegal class "
619: + proxyFactoryClass.getName()
620: + " specified for ProxyFactoryClass.");
621: } catch (IllegalAccessException e) {
622: throw new MetadataException("Illegal class "
623: + proxyFactoryClass.getName()
624: + " specified for ProxyFactoryClass.");
625: }
626: }
627: return singleton;
628: }
629:
630: }
|