/* * Copyright 2005 Werner Guttmann * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * $Id$ */ package org.castor.persist.proxy; import java.io.NotSerializableException; import java.io.Serializable; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.castor.persist.ProposedEntity; import org.castor.persist.TransactionContext; import org.exolab.castor.jdo.ObjectNotFoundException; import org.exolab.castor.jdo.PersistenceException; import org.exolab.castor.mapping.AccessMode; import org.exolab.castor.persist.ClassMolder; import org.exolab.castor.persist.spi.Identity; public final class SingleProxy implements MethodInterceptor, Serializable { /** SerialVersionUID. */ private static final long serialVersionUID = -1498354553937679053L; private static final Log LOG = LogFactory.getLog(SingleProxy.class); private TransactionContext _tx; private ClassMolder _classMolder; private Class _clazz; private Identity _identity; private Object _object; private AccessMode _accessMode; private boolean _hasMaterialized = false; /** * Creates an instance of SingleProxy. * @param tx Actual TransactionContext. * @param classMolder Associated ClassMolder. * @param clazz Associated Class instance. * @param identity Identity object. * @param object Object to be lazy-loaded. * @param accessMode Access mode identifier. */ private SingleProxy(final TransactionContext tx, final ClassMolder classMolder, final Class clazz, final Identity identity, final Object object, final AccessMode accessMode) { _tx = tx; _classMolder = classMolder; _clazz = clazz; _identity = identity; _object = object; _accessMode = accessMode; if (LOG.isDebugEnabled()) { LOG.debug("Created new SingleProxy for an instance of '" + classMolder.getName() + "' with id '" + identity + "'"); } } /** * Factory method to create SingleProxy instance. * @param tx Actual TransactionContext. * @param classMolder Associated ClassMolder. * @param identity Identity object. * @param object Object to be lazy-loaded. * @param accessMode Access mode identifier. * @return A SingleProxy instance. * @throws ObjectNotFoundException */ public static synchronized Object getProxy(final TransactionContext tx, final ClassMolder classMolder, final Identity identity, final Object object, final AccessMode accessMode) throws ObjectNotFoundException { try { Class clazz = Class.forName(classMolder.getName()); SingleProxy sp = new SingleProxy(tx, classMolder, clazz, identity, object, accessMode); return Enhancer.create(clazz, new Class[] {LazyCGLIB.class}, sp); } catch (Throwable ex) { if (LOG.isErrorEnabled()) { String msg = "error on enhance class"; if (classMolder != null) { msg += " " + classMolder.getName(); } LOG.error(msg, ex); } throw new ObjectNotFoundException("lazy loading error - " + ex.getMessage(), ex); } } /** * {@inheritDoc} * @see net.sf.cglib.proxy.MethodInterceptor #intercept(java.lang.Object, * java.lang.reflect.Method, java.lang.Object[], * net.sf.cglib.proxy.MethodProxy) */ public Object intercept(final Object obj, final Method method, final Object[] args, final MethodProxy proxy) throws Throwable { String methodName = method.getName(); // to not load if method geClass() or finalize() if ("writeReplace".equals(methodName)) { if (LOG.isDebugEnabled()) { LOG.debug("writeReplacing " + _classMolder.getName() + " with identity " + _identity); } if (!_hasMaterialized) { try { _object = loadOnly(); } catch (ObjectNotFoundException e) { String msg = "Object with identity " + _identity + " does not exist"; LOG.error(msg, e); throw new NotSerializableException(msg); } catch (PersistenceException e) { String msg = "Problem serializing object with identity " + _identity; LOG.error(msg, e); throw new NotSerializableException(msg); } } if (LOG.isDebugEnabled()) { LOG.debug("Serializing instance of " + _object.getClass().getName()); LOG.debug("_object = " + _object); } return _object; } else if ("interceptedClass".equals(methodName)) { return _clazz; } else if ("interceptedHasMaterialized".equals(methodName)) { return new Boolean(_hasMaterialized); } else if ("interceptedIdentity".equals(methodName)) { return _identity; } else if ("interceptedClassMolder".equals(methodName)) { return _classMolder; } else if ("getClass".equals(methodName)) { return method.invoke(obj, args); } else if ("finalize".equals(methodName)) { return method.invoke(obj, args); } // load object, if not previous loaded if (_object == null) { _object = load(obj); } // object found? if (_object == null) { return null; } // invoke original method in loaded object return method.invoke(_object, args); } /** * Tool method to load the underlying (proxied) object instance. * @return The actual object * @throws ObjectNotFoundException * @throws PersistenceException */ private Object loadOnly() throws PersistenceException { Object instance = null; if (LOG.isDebugEnabled() && _classMolder != null) { LOG.debug("load object " + _classMolder.getName() + " with id " + _identity); } ProposedEntity proposedValue = new ProposedEntity(_classMolder); proposedValue.setProposedEntityClass(_clazz); proposedValue.setEntity(_object); instance = _tx.load(_identity, proposedValue, _accessMode); _hasMaterialized = true; return instance; } private Object load(final Object proxiedObject) throws PersistenceException, IllegalAccessException, InstantiationException { Object instance = null; try { instance = loadOnly(); } catch (ObjectNotFoundException ex) { if (LOG.isDebugEnabled()) { LOG.debug("object not found -> " + ex.toString()); } // if a ObjectNotFoundException occur then create a empty instance if (proxiedObject instanceof net.sf.cglib.proxy.Factory) { instance = proxiedObject.getClass().getSuperclass() .newInstance(); } } return instance; } }