/*==========================================================================*\ | $Id: EOManager.java,v 1.5 2011/12/25 02:24:53 stedwar2 Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2006-2011 Virginia Tech | | This file is part of Web-CAT. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU Affero General Public License as published | by the Free Software Foundation; either version 3 of the License, or | (at your option) any later version. | | Web-CAT is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General Public License for more details. | | You should have received a copy of the GNU Affero General Public License | along with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package org.webcat.core; import org.apache.log4j.Logger; import org.webcat.woextensions.WCEC; import com.webobjects.eoaccess.EOUtilities; import com.webobjects.eocontrol.EOEditingContext; import com.webobjects.eocontrol.EOEnterpriseObject; import com.webobjects.eocontrol.EORelationshipManipulation; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSKeyValueCoding; import com.webobjects.foundation.NSMutableArray; import com.webobjects.foundation.NSMutableDictionary; import er.extensions.eof.ERXEC; //------------------------------------------------------------------------- /** * This interface defines common features for classes that encapsulate an EO * that is managed in its own editing context so that it can be saved/managed * independently of the client editing context from which it is being * accessed. The intent is to allow a single object (managed by this * container) to be independently saved to the database, separate from all * the other objects related to it. * * @author Stephen Edwards * @author Last changed by $Author: stedwar2 $ * @version $Revision: 1.5 $, $Date: 2011/12/25 02:24:53 $ */ public interface EOManager extends NSKeyValueCoding, EORelationshipManipulation { // ---------------------------------------------------------- /** * This inner class encapsulates an internal editing context that * might need to change over time, if the old editing context accumulates * errors that need to be dumped. */ public static class ECManager { // ---------------------------------------------------------- /** * Create a new ECManager with a new internal editing context. */ public ECManager() { ec = WCEC.newAutoLockingEditingContext(); ec.setSharedEditingContext(null); } // ---------------------------------------------------------- /** * Get a copy of the given object in the given editing context. * @param <T> The type of the object to be localized, which could * be a concrete EO class * @param context The editing context to localize to * @param object The EO to transfer into the EC. If the given * object is not an EOEnterpriseObject, it is returned unchanged. * If the object is an NSArray or NSDictionary, a copy with * localized internal values is returned. * @return A local instance of the given object */ public static <T> T localize(EOEditingContext context, T object) { if (object == null) { return object; } else if (object instanceof EOEnterpriseObject) { if (((EOEnterpriseObject)object).editingContext() == context) { return object; } else { @SuppressWarnings("unchecked") T resultAsT = (T)EOUtilities.localInstanceOfObject( context, (EOEnterpriseObject)object); return resultAsT; } } else if (object instanceof NSDictionary) { NSMutableDictionary<?, ?> result = ((NSDictionary<?, ?>)object).mutableClone(); for (Object key : result.allKeys()) { result.takeValueForKey( localize(context, result.valueForKey((String)key)), (String)key); } @SuppressWarnings("unchecked") T resultAsT = (T)result; return resultAsT; } else if (object instanceof NSArray) { @SuppressWarnings("unchecked") NSMutableArray<Object> result = ((NSArray<Object>)object).mutableClone(); for (int i = 0; i < result.count(); i++) { result.set(i, localize(context, result.objectAtIndex(i))); } @SuppressWarnings("unchecked") T resultAsT = (T)result; return resultAsT; } else { return object; } } // ---------------------------------------------------------- /** * Get a copy of the given object in the internal editing context. * @param <T> The type of the object to be localized, which could * be a concrete EO class * @param object The EO to transfer into the EC. If the given * object is not an EOEnterpriseObject, it is returned unchanged. * If the object is an NSArray or NSDictionary, a copy with * localized internal values is returned. * @return A local instance of the given object */ protected <T> T localize(T object) { return localize(ec, object); } // ---------------------------------------------------------- /** * Calls saveChanges() on the internal editing context, returning * null on success or an appropriate Exception object on failure. * This method allows the caller to decide what to do when saving * fails. * @return The exception that occurred, if saving fails, or null * on success. */ public Exception tryToSaveChanges() { Exception result = null; try { ec.saveChanges(); } catch (Exception e) { result = e; } return result; } // ---------------------------------------------------------- /** * Calls saveChanges() on the internal editing context. If any * error occurs, it throws away the old editing context and * creates a new one. Assumes that the editing context is currently * locked by the caller, and leaves it locked after completion. * If a new editing context is created, the given EO (which presumably * already exists in the current EC) is imported into the new * context, and then returned. Otherwise, the EO is returned * unchanged. * @param eo The primary EO of interest * @return eo in the current editing context (imported into a * new editing context if the old one is thrown away). */ public EOEnterpriseObject saveChanges(EOEnterpriseObject eo) { Exception err = tryToSaveChanges(); if (err != null) { log.info("Exception in saveChanges(); throwing away old EC", err); // Something happened, so try replacing the old context // with a new one. ERXEC newContext = WCEC.newAutoLockingEditingContext(); newContext.setSharedEditingContext(null); eo = EOUtilities.localInstanceOfObject(newContext, eo); newContext.refreshObject(eo); // Now try to clean up the old one try { // Try to clean up the broken editing context, if possible ec.dispose(); } catch (Exception ee) { // if there is an error, ignore it since we're not going // to use this ec any more anyway } // Finally do the replacement ec = newContext; } return eo; } // ---------------------------------------------------------- /** * Revert the internal editing context, throwing away any * pending changes. */ public void revert() { ec.revert(); } // ---------------------------------------------------------- /** * Refresh all objects in the internal editing context. */ public void refreshAllObjects() { ec.refreshAllObjects(); } // ---------------------------------------------------------- /** * Releases the inner EOEditContext. */ public void dispose() { ec.dispose(); } //~ Instance/static variables ......................................... private ERXEC ec; static Logger log = Logger.getLogger(ECManager.class); } }