Home » apache-openjpa-1.1.0-source » org.apache.openjpa » kernel » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    * http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.    
   18    */
   19   package org.apache.openjpa.kernel;
   20   
   21   import java.util.Collection;
   22   import java.util.Collections;
   23   import java.util.Iterator;
   24   import java.util.LinkedList;
   25   import java.util.List;
   26   import java.util.Map;
   27   import java.util.Set;
   28   
   29   import org.apache.commons.collections.map.IdentityMap;
   30   import org.apache.openjpa.enhance.PersistenceCapable;
   31   import org.apache.openjpa.event.CallbackModes;
   32   import org.apache.openjpa.event.LifecycleEvent;
   33   import org.apache.openjpa.lib.util.Localizer;
   34   import org.apache.openjpa.meta.ClassMetaData;
   35   import org.apache.openjpa.meta.ValueMetaData;
   36   import org.apache.openjpa.util.CallbackException;
   37   import org.apache.openjpa.util.Exceptions;
   38   import org.apache.openjpa.util.OpenJPAException;
   39   import org.apache.openjpa.util.OptimisticException;
   40   import org.apache.openjpa.util.ProxyManager;
   41   import org.apache.openjpa.util.UserException;
   42   import org.apache.openjpa.util.ImplHelper;
   43   
   44   /**
   45    * Handles attaching instances.
   46    *
   47    * @author Marc Prud'hommeaux
   48    */
   49   class AttachManager {
   50   
   51       private static final Localizer _loc = Localizer.forPackage
   52           (AttachManager.class);
   53   
   54       private final BrokerImpl _broker;
   55       private final ProxyManager _proxy;
   56       private final OpCallbacks _call;
   57       private final boolean _copyNew;
   58       private final boolean _failFast;
   59       private final IdentityMap _attached = new IdentityMap();
   60   
   61       // reusable strategies
   62       private AttachStrategy _version = null;
   63       private AttachStrategy _detach = null;
   64   
   65       /**
   66        * Constructor. Supply broker attaching to.
   67        */
   68       public AttachManager(BrokerImpl broker, boolean copyNew, OpCallbacks call) {
   69           _broker = broker;
   70           _proxy = broker.getConfiguration().getProxyManagerInstance();
   71           _call = call;
   72           _copyNew = copyNew;
   73           _failFast = (broker.getConfiguration().getMetaDataRepositoryInstance().
   74               getMetaDataFactory().getDefaults().getCallbackMode()
   75               & CallbackModes.CALLBACK_FAIL_FAST) != 0;
   76       }
   77   
   78       /**
   79        * Return the behavior supplied on construction.
   80        */
   81       public OpCallbacks getBehavior() {
   82           return _call;
   83       }
   84   
   85       /**
   86        * Return whether to copy new instances being persisted.
   87        */
   88       public boolean getCopyNew() {
   89           return _copyNew;
   90       }
   91   
   92       /**
   93        * Return an attached version of the given instance.
   94        */
   95       public Object attach(Object pc) {
   96           if (pc == null)
   97               return null;
   98   
   99           CallbackException excep = null;
  100           try {
  101               return attach(pc, null, null, null, true);
  102           } catch (CallbackException ce) {
  103               excep = ce;
  104               return null; // won't be reached as the exceps will be rethrown
  105           } finally {
  106               List exceps = null;
  107               if (excep == null || !_failFast)
  108                   exceps = invokeAfterAttach(null);
  109               else
  110                   exceps = Collections.singletonList(excep);
  111               _attached.clear();
  112               throwExceptions(exceps, null, false);
  113           }
  114       }
  115   
  116       /**
  117        * Return attached versions of the given instances.
  118        */
  119       public Object[] attachAll(Collection instances) {
  120           Object[] attached = new Object[instances.size()];
  121           List exceps = null;
  122           List failed = null;
  123           boolean opt = true;
  124           boolean failFast = false;
  125           try {
  126               int i = 0;
  127               for (Iterator itr = instances.iterator(); itr.hasNext(); i++) {
  128                   try {
  129                       attached[i] = attach(itr.next(), null, null, null, true);
  130                   } catch (OpenJPAException ke) {
  131                       // track exceptions and optimistic failed objects
  132                       if (opt && !(ke instanceof OptimisticException))
  133                           opt = false;
  134                       if (opt && ke.getFailedObject() != null)
  135                           failed = add(failed, ke.getFailedObject());
  136                       exceps = add(exceps, ke);
  137   
  138                       if (ke instanceof CallbackException && _failFast) {
  139                           failFast = true;
  140                           break;
  141                       }
  142                   }
  143                   catch (RuntimeException re) {
  144                       exceps = add(exceps, re);
  145                   }
  146               }
  147           } finally {
  148               // invoke post callbacks unless all failed
  149               if (!failFast && (exceps == null
  150                   || exceps.size() < instances.size()))
  151                   exceps = invokeAfterAttach(exceps);
  152               _attached.clear();
  153           }
  154           throwExceptions(exceps, failed, opt);
  155           return attached;
  156       }
  157   
  158       /**
  159        * Invoke postAttach() on any attached instances that implement
  160        * PostAttachCallback. This will be done after the entire graph has
  161        * been attached.
  162        */
  163       private List invokeAfterAttach(List exceps) {
  164           Set entries = _attached.entrySet();
  165           for (Iterator i = entries.iterator(); i.hasNext();) {
  166               Map.Entry entry = (Map.Entry) i.next();
  167               Object attached = entry.getValue();
  168               StateManagerImpl sm = _broker.getStateManagerImpl(attached, true);
  169               if (sm.isNew())
  170                   continue;
  171               try {
  172                   _broker.fireLifecycleEvent(attached, entry.getKey(),
  173                       sm.getMetaData(), LifecycleEvent.AFTER_ATTACH);
  174               } catch (RuntimeException re) {
  175                   exceps = add(exceps, re);
  176                   if (_failFast && re instanceof CallbackException)
  177                       break;
  178               }
  179           }
  180           return exceps;
  181       }
  182   
  183       /**
  184        * Add an object to the list.
  185        */
  186       private List add(List list, Object obj) {
  187           if (list == null)
  188               list = new LinkedList();
  189           list.add(obj);
  190           return list;
  191       }
  192   
  193       /**
  194        * Throw exception for failures.
  195        */
  196       private void throwExceptions(List exceps, List failed, boolean opt) {
  197           if (exceps == null)
  198               return;
  199           if (exceps.size() == 1)
  200               throw (RuntimeException) exceps.get(0);
  201   
  202           Throwable[] t = (Throwable[]) exceps.toArray
  203               (new Throwable[exceps.size()]);
  204           if (opt && failed != null)
  205               throw new OptimisticException(failed, t);
  206           if (opt)
  207               throw new OptimisticException(t);
  208           throw new UserException(_loc.get("nested-exceps")).
  209               setNestedThrowables(t);
  210       }
  211   
  212       /**
  213        * Attach.
  214        *
  215        * @param toAttach the detached object
  216        * @param into the instance we're attaching into
  217        * @param owner state manager for <code>into</code>
  218        * @param ownerMeta the field we traversed to find <code>toAttach</code>
  219        * @param explicit whether to make new instances explicitly persistent
  220        */
  221       Object attach(Object toAttach, PersistenceCapable into,
  222           OpenJPAStateManager owner, ValueMetaData ownerMeta, boolean explicit) {
  223           if (toAttach == null)
  224               return null;
  225   
  226           // check if already attached
  227           Object attached = _attached.get(toAttach);
  228           if (attached != null)
  229               return attached;
  230   
  231           //### need to handle ACT_CASCADE
  232           int action = processArgument(toAttach);
  233           if ((action & OpCallbacks.ACT_RUN) == 0)
  234               return toAttach;
  235   
  236           //### need to handle ACT_RUN without also ACT_CASCADE
  237           ClassMetaData meta = _broker.getConfiguration().
  238               getMetaDataRepositoryInstance().getMetaData(
  239                   ImplHelper.getManagedInstance(toAttach).getClass(),
  240                   _broker.getClassLoader(), true);
  241           return getStrategy(toAttach).attach(this, toAttach, meta, into,
  242               owner, ownerMeta, explicit);
  243       }
  244   
  245       /**
  246        * Determine the action to take on the given argument.
  247        */
  248       private int processArgument(Object obj) {
  249           if (_call == null)
  250               return OpCallbacks.ACT_RUN;
  251           return _call.processArgument(OpCallbacks.OP_ATTACH, obj,
  252               _broker.getStateManager(obj));
  253       }
  254   
  255       /**
  256        * Calculate proper attach strategy for instance.
  257        */
  258       private AttachStrategy getStrategy(Object toAttach) {
  259           PersistenceCapable pc = ImplHelper.toPersistenceCapable(toAttach,
  260               getBroker().getConfiguration());
  261           if (pc.pcGetStateManager() instanceof AttachStrategy)
  262               return (AttachStrategy) pc.pcGetStateManager();
  263   
  264           Object obj = pc.pcGetDetachedState();
  265           if (obj instanceof AttachStrategy)
  266               return (AttachStrategy) obj;
  267           if (obj == null || obj == PersistenceCapable.DESERIALIZED) {
  268               // new or detached without state
  269               if (_version == null)
  270                   _version = new VersionAttachStrategy();
  271               return _version;
  272           }
  273   
  274           // detached state
  275           if (_detach == null)
  276               _detach = new DetachedStateAttachStrategy();
  277           return _detach;
  278       }
  279   
  280       /**
  281        * Owning broker.
  282        */
  283       BrokerImpl getBroker() {
  284           return _broker;
  285       }
  286   
  287       /**
  288        * System proxy manager.
  289        */
  290       ProxyManager getProxyManager() {
  291           return _proxy;
  292       }
  293   
  294       /**
  295        * If the passed in argument has already been attached, return
  296        * the (cached) attached copy.
  297        */
  298       PersistenceCapable getAttachedCopy(Object pc) {
  299           return ImplHelper.toPersistenceCapable(_attached.get(pc),
  300               getBroker().getConfiguration());
  301       }
  302   
  303       /**
  304        * Record the attached copy in the cache.
  305        */
  306       void setAttachedCopy(Object from, PersistenceCapable into) {
  307           _attached.put(from, into);
  308       }
  309   
  310       /**
  311        * Fire before-attach event.
  312        */
  313       void fireBeforeAttach(Object pc, ClassMetaData meta) {
  314           _broker.fireLifecycleEvent(pc, null, meta,
  315               LifecycleEvent.BEFORE_ATTACH);
  316       }
  317   
  318       /**
  319        * Return the detached oid of the given instance.
  320        */
  321       Object getDetachedObjectId(Object pc) {
  322           if (pc == null)
  323               return null;
  324           return getStrategy(pc).getDetachedObjectId(this, pc);
  325       }
  326   
  327       /**
  328        * Throw an exception if the given object is not managed; otherwise
  329        * return its state manager.
  330        */
  331       StateManagerImpl assertManaged(Object obj) {
  332           StateManagerImpl sm = _broker.getStateManagerImpl(obj, true);
  333           if (sm == null)
  334               throw new UserException(_loc.get("not-managed",
  335                   Exceptions.toString(obj))).setFailedObject (obj);
  336   		return sm;
  337   	}
  338   }

Save This Page
Home » apache-openjpa-1.1.0-source » org.apache.openjpa » kernel » [javadoc | source]