cayenne-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From aadamc...@apache.org
Subject [13/17] cayenne git commit: CAY-2026 Java 7
Date Sat, 12 Sep 2015 10:02:41 GMT
http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/DataSourceInfoLoader_3_0_0_1.java
----------------------------------------------------------------------
diff --git a/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/DataSourceInfoLoader_3_0_0_1.java b/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/DataSourceInfoLoader_3_0_0_1.java
index 594f3d4..dc52a50 100644
--- a/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/DataSourceInfoLoader_3_0_0_1.java
+++ b/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/DataSourceInfoLoader_3_0_0_1.java
@@ -37,235 +37,192 @@ import org.xml.sax.InputSource;
 import org.xml.sax.XMLReader;
 
 /**
- * A loader of XML for the {@link DataSourceInfo} object. The loader is compatible with
- * project version 3.0.0.1 and earlier.
+ * A loader of XML for the {@link DataSourceInfo} object. The loader is
+ * compatible with project version 3.0.0.1 and earlier.
  * 
  * @since 3.1
  */
 // TODO: andrus 12.13.2009 - unused yet.. will be used in upgrade manager
 class DataSourceInfoLoader_3_0_0_1 {
 
-    public DataSourceInfo load(Resource configurationResource) throws Exception {
-
-        if (configurationResource == null) {
-            throw new NullPointerException("Null configurationResource");
-        }
-
-        DataSourceInfo dataSourceDescriptor = new DataSourceInfo();
-
-        XMLReader parser = Util.createXmlReader();
-
-        DriverHandler handler = new DriverHandler(dataSourceDescriptor, parser);
-        parser.setContentHandler(handler);
-        parser.setErrorHandler(handler);
-        parser.parse(new InputSource(configurationResource.getURL().openStream()));
-
-        return dataSourceDescriptor;
-    }
-
-    private static String passwordFromURL(URL url) {
-        InputStream inputStream = null;
-        String password = null;
-
-        try {
-            inputStream = url.openStream();
-            password = passwordFromInputStream(inputStream);
-        }
-        catch (IOException exception) {
-            // ignore
-        }
-
-        return password;
-    }
-
-    private static String passwordFromInputStream(InputStream inputStream) {
-        BufferedReader bufferedReader = null;
-        String password = null;
-
-        try {
-            bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
-            password = bufferedReader.readLine();
-        }
-        catch (IOException exception) {
-            // ignoring...
-        }
-        finally {
-            try {
-                if (bufferedReader != null) {
-                    bufferedReader.close();
-                }
-            }
-            catch (Exception exception) {
-            }
-
-            try {
-                inputStream.close();
-            }
-            catch (IOException exception) {
-            }
-        }
-
-        return password;
-    }
-
-    private class DriverHandler extends SAXNestedTagHandler {
-
-        private DataSourceInfo dataSourceDescriptor;
-
-        DriverHandler(DataSourceInfo dataSourceDescriptor, XMLReader parser) {
-            super(parser, null);
-            this.dataSourceDescriptor = dataSourceDescriptor;
-        }
-
-        @Override
-        protected ContentHandler createChildTagHandler(
-                String namespaceURI,
-                String localName,
-                String name,
-                Attributes attributes) {
-
-            if (localName.equals("driver")) {
-                String className = attributes.getValue("", "class");
-                dataSourceDescriptor.setJdbcDriver(className);
-                return new DriverChildrenHandler(parser, this);
-            }
-
-            return super.createChildTagHandler(namespaceURI, localName, name, attributes);
-        }
-    }
-
-    private class DriverChildrenHandler extends SAXNestedTagHandler {
-
-        private DataSourceInfo dataSourceDescriptor;
-
-        DriverChildrenHandler(XMLReader parser, DriverHandler parentHandler) {
-            super(parser, parentHandler);
-            this.dataSourceDescriptor = parentHandler.dataSourceDescriptor;
-        }
-
-        @Override
-        protected ContentHandler createChildTagHandler(
-                String namespaceURI,
-                String localName,
-                String name,
-                Attributes attributes) {
-
-            if (localName.equals("login")) {
-
-                String encoderClass = attributes.getValue("encoderClass");
-
-                String encoderKey = attributes.getValue("encoderKey");
-                if (encoderKey == null) {
-                    encoderKey = attributes.getValue("encoderSalt");
-                }
-
-                String password = attributes.getValue("password");
-                String passwordLocation = attributes.getValue("passwordLocation");
-                String passwordSource = attributes.getValue("passwordSource");
-                if (passwordSource == null) {
-                    passwordSource = DataSourceInfo.PASSWORD_LOCATION_MODEL;
-                }
-
-                String username = attributes.getValue("userName");
-
-                dataSourceDescriptor.setPasswordEncoderClass(encoderClass);
-                dataSourceDescriptor.setPasswordEncoderKey(encoderKey);
-                dataSourceDescriptor.setPasswordLocation(passwordLocation);
-                dataSourceDescriptor.setPasswordSource(passwordSource);
-                dataSourceDescriptor.setUserName(username);
-
-                // Replace {} in passwordSource with encoderSalt -- useful for EXECUTABLE
-                // & URL options
-                if (encoderKey != null) {
-                    passwordSource = passwordSource.replaceAll("\\{\\}", encoderKey);
-                }
-
-                PasswordEncoding passwordEncoder = dataSourceDescriptor
-                        .getPasswordEncoder();
-
-                if (passwordLocation != null) {
-                    if (passwordLocation
-                            .equals(DataSourceInfo.PASSWORD_LOCATION_CLASSPATH)) {
-
-                        ClassLoader classLoader = Thread
-                                .currentThread()
-                                .getContextClassLoader();
-                        URL url = classLoader.getResource(username);
-                        if (url != null) {
-                            password = passwordFromURL(url);
-                        }
-                        else {
-                            // ignoring..
-                        }
-                    }
-                    else if (passwordLocation
-                            .equals(DataSourceInfo.PASSWORD_LOCATION_URL)) {
-                        try {
-                            password = passwordFromURL(new URL(passwordSource));
-                        }
-                        catch (MalformedURLException exception) {
-                            // ignoring...
-                        }
-                    }
-                    else if (passwordLocation
-                            .equals(DataSourceInfo.PASSWORD_LOCATION_EXECUTABLE)) {
-                        if (passwordSource != null) {
-                            try {
-                                Process process = Runtime.getRuntime().exec(
-                                        passwordSource);
-                                password = passwordFromInputStream(process
-                                        .getInputStream());
-                                process.waitFor();
-                            }
-                            catch (IOException exception) {
-                                // ignoring...
-                            }
-                            catch (InterruptedException exception) {
-                                // ignoring...
-                            }
-                        }
-                    }
-                }
-
-                if (password != null && passwordEncoder != null) {
-                    dataSourceDescriptor.setPassword(passwordEncoder.decodePassword(
-                            password,
-                            encoderKey));
-                }
-            }
-            else if (localName.equals("url")) {
-                dataSourceDescriptor.setDataSourceUrl(attributes.getValue("value"));
-            }
-            else if (localName.equals("connectionPool")) {
-                String min = attributes.getValue("min");
-                if (min != null) {
-                    try {
-                        dataSourceDescriptor.setMinConnections(Integer.parseInt(min));
-                    }
-                    catch (NumberFormatException nfex) {
-                        throw new ConfigurationException(
-                                "Non-numeric 'min' attribute '%s'",
-                                nfex,
-                                min);
-                    }
-                }
-
-                String max = attributes.getValue("max");
-                if (max != null) {
-                    try {
-                        dataSourceDescriptor.setMaxConnections(Integer.parseInt(max));
-                    }
-                    catch (NumberFormatException nfex) {
-                        throw new ConfigurationException(
-                                "Non-numeric 'max' attribute '%s'",
-                                nfex,
-                                max);
-                    }
-                }
-            }
-
-            return super.createChildTagHandler(namespaceURI, localName, name, attributes);
-        }
-    }
+	public DataSourceInfo load(Resource configurationResource) throws Exception {
+
+		if (configurationResource == null) {
+			throw new NullPointerException("Null configurationResource");
+		}
+
+		DataSourceInfo dataSourceDescriptor = new DataSourceInfo();
+
+		XMLReader parser = Util.createXmlReader();
+
+		DriverHandler handler = new DriverHandler(dataSourceDescriptor, parser);
+		parser.setContentHandler(handler);
+		parser.setErrorHandler(handler);
+		parser.parse(new InputSource(configurationResource.getURL().openStream()));
+
+		return dataSourceDescriptor;
+	}
+
+	private static String passwordFromURL(URL url) {
+		InputStream inputStream = null;
+		String password = null;
+
+		try {
+			inputStream = url.openStream();
+			password = passwordFromInputStream(inputStream);
+		} catch (IOException exception) {
+			// ignore
+		}
+
+		return password;
+	}
+
+	private static String passwordFromInputStream(InputStream inputStream) {
+		String password = null;
+
+		try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));) {
+
+			password = bufferedReader.readLine();
+		} catch (IOException exception) {
+			// ignoring...
+		} finally {
+
+			try {
+				inputStream.close();
+			} catch (IOException exception) {
+			}
+		}
+
+		return password;
+	}
+
+	private class DriverHandler extends SAXNestedTagHandler {
+
+		private DataSourceInfo dataSourceDescriptor;
+
+		DriverHandler(DataSourceInfo dataSourceDescriptor, XMLReader parser) {
+			super(parser, null);
+			this.dataSourceDescriptor = dataSourceDescriptor;
+		}
+
+		@Override
+		protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String name,
+				Attributes attributes) {
+
+			if (localName.equals("driver")) {
+				String className = attributes.getValue("", "class");
+				dataSourceDescriptor.setJdbcDriver(className);
+				return new DriverChildrenHandler(parser, this);
+			}
+
+			return super.createChildTagHandler(namespaceURI, localName, name, attributes);
+		}
+	}
+
+	private class DriverChildrenHandler extends SAXNestedTagHandler {
+
+		private DataSourceInfo dataSourceDescriptor;
+
+		DriverChildrenHandler(XMLReader parser, DriverHandler parentHandler) {
+			super(parser, parentHandler);
+			this.dataSourceDescriptor = parentHandler.dataSourceDescriptor;
+		}
+
+		@Override
+		protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String name,
+				Attributes attributes) {
+
+			if (localName.equals("login")) {
+
+				String encoderClass = attributes.getValue("encoderClass");
+
+				String encoderKey = attributes.getValue("encoderKey");
+				if (encoderKey == null) {
+					encoderKey = attributes.getValue("encoderSalt");
+				}
+
+				String password = attributes.getValue("password");
+				String passwordLocation = attributes.getValue("passwordLocation");
+				String passwordSource = attributes.getValue("passwordSource");
+				if (passwordSource == null) {
+					passwordSource = DataSourceInfo.PASSWORD_LOCATION_MODEL;
+				}
+
+				String username = attributes.getValue("userName");
+
+				dataSourceDescriptor.setPasswordEncoderClass(encoderClass);
+				dataSourceDescriptor.setPasswordEncoderKey(encoderKey);
+				dataSourceDescriptor.setPasswordLocation(passwordLocation);
+				dataSourceDescriptor.setPasswordSource(passwordSource);
+				dataSourceDescriptor.setUserName(username);
+
+				// Replace {} in passwordSource with encoderSalt -- useful for
+				// EXECUTABLE
+				// & URL options
+				if (encoderKey != null) {
+					passwordSource = passwordSource.replaceAll("\\{\\}", encoderKey);
+				}
+
+				PasswordEncoding passwordEncoder = dataSourceDescriptor.getPasswordEncoder();
+
+				if (passwordLocation != null) {
+					if (passwordLocation.equals(DataSourceInfo.PASSWORD_LOCATION_CLASSPATH)) {
+
+						ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+						URL url = classLoader.getResource(username);
+						if (url != null) {
+							password = passwordFromURL(url);
+						} else {
+							// ignoring..
+						}
+					} else if (passwordLocation.equals(DataSourceInfo.PASSWORD_LOCATION_URL)) {
+						try {
+							password = passwordFromURL(new URL(passwordSource));
+						} catch (MalformedURLException exception) {
+							// ignoring...
+						}
+					} else if (passwordLocation.equals(DataSourceInfo.PASSWORD_LOCATION_EXECUTABLE)) {
+						if (passwordSource != null) {
+							try {
+								Process process = Runtime.getRuntime().exec(passwordSource);
+								password = passwordFromInputStream(process.getInputStream());
+								process.waitFor();
+							} catch (IOException exception) {
+								// ignoring...
+							} catch (InterruptedException exception) {
+								// ignoring...
+							}
+						}
+					}
+				}
+
+				if (password != null && passwordEncoder != null) {
+					dataSourceDescriptor.setPassword(passwordEncoder.decodePassword(password, encoderKey));
+				}
+			} else if (localName.equals("url")) {
+				dataSourceDescriptor.setDataSourceUrl(attributes.getValue("value"));
+			} else if (localName.equals("connectionPool")) {
+				String min = attributes.getValue("min");
+				if (min != null) {
+					try {
+						dataSourceDescriptor.setMinConnections(Integer.parseInt(min));
+					} catch (NumberFormatException nfex) {
+						throw new ConfigurationException("Non-numeric 'min' attribute '%s'", nfex, min);
+					}
+				}
+
+				String max = attributes.getValue("max");
+				if (max != null) {
+					try {
+						dataSourceDescriptor.setMaxConnections(Integer.parseInt(max));
+					} catch (NumberFormatException nfex) {
+						throw new ConfigurationException("Non-numeric 'max' attribute '%s'", nfex, max);
+					}
+				}
+			}
+
+			return super.createChildTagHandler(namespaceURI, localName, name, attributes);
+		}
+	}
 
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v6/XMLDataChannelDescriptorLoader_V3_0_0_1.java
----------------------------------------------------------------------
diff --git a/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v6/XMLDataChannelDescriptorLoader_V3_0_0_1.java b/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v6/XMLDataChannelDescriptorLoader_V3_0_0_1.java
index fef935f..21de9dc 100644
--- a/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v6/XMLDataChannelDescriptorLoader_V3_0_0_1.java
+++ b/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v6/XMLDataChannelDescriptorLoader_V3_0_0_1.java
@@ -18,7 +18,6 @@
  ****************************************************************/
 package org.apache.cayenne.project.upgrade.v6;
 
-import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
 import java.util.ArrayList;
@@ -90,10 +89,9 @@ class XMLDataChannelDescriptorLoader_V3_0_0_1 {
 		URL configurationURL = configurationSource.getURL();
 
 		List<DataChannelDescriptor> domains = new ArrayList<DataChannelDescriptor>();
-		InputStream in = null;
 
-		try {
-			in = configurationURL.openStream();
+		try (InputStream in = configurationURL.openStream();) {
+
 			XMLReader parser = Util.createXmlReader();
 
 			DomainsHandler rootHandler = new DomainsHandler(configurationSource, domains, parser);
@@ -102,14 +100,6 @@ class XMLDataChannelDescriptorLoader_V3_0_0_1 {
 			parser.parse(new InputSource(in));
 		} catch (Exception e) {
 			throw new ConfigurationException("Error loading configuration from %s", e, configurationURL);
-		} finally {
-			try {
-				if (in != null) {
-					in.close();
-				}
-			} catch (IOException ioex) {
-				logger.info("failure closing input stream for " + configurationURL + ", ignoring", ioex);
-			}
 		}
 
 		return domains;

http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v6/XMLDataMapLoader_V3_0_0_1.java
----------------------------------------------------------------------
diff --git a/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v6/XMLDataMapLoader_V3_0_0_1.java b/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v6/XMLDataMapLoader_V3_0_0_1.java
index 6e49410..6dc3d52 100644
--- a/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v6/XMLDataMapLoader_V3_0_0_1.java
+++ b/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v6/XMLDataMapLoader_V3_0_0_1.java
@@ -18,17 +18,13 @@
  ****************************************************************/
 package org.apache.cayenne.project.upgrade.v6;
 
-import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
 
 import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.configuration.XMLDataMapLoader;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.MapLoader;
 import org.apache.cayenne.resource.Resource;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
 import org.xml.sax.InputSource;
 
 /**
@@ -36,41 +32,20 @@ import org.xml.sax.InputSource;
  */
 class XMLDataMapLoader_V3_0_0_1 {
 
-    private static Log logger = LogFactory.getLog(XMLDataMapLoader.class);
+	public DataMap load(Resource configurationResource) throws CayenneRuntimeException {
 
-    public DataMap load(Resource configurationResource) throws CayenneRuntimeException {
+		MapLoader mapLoader = new MapLoader();
+		URL url = configurationResource.getURL();
 
-        MapLoader mapLoader = new MapLoader();
-        URL url = configurationResource.getURL();
+		DataMap map;
 
-        InputStream in = null;
+		try (InputStream in = url.openStream();) {
 
-        DataMap map;
+			map = mapLoader.loadDataMap(new InputSource(in));
+		} catch (Exception e) {
+			throw new CayenneRuntimeException("Error loading configuration from %s", e, url);
+		}
 
-        try {
-            in = url.openStream();
-
-            map = mapLoader.loadDataMap(new InputSource(in));
-        }
-        catch (Exception e) {
-            throw new CayenneRuntimeException(
-                    "Error loading configuration from %s",
-                    e,
-                    url);
-        }
-        finally {
-            try {
-                if (in != null) {
-                    in.close();
-                }
-            }
-            catch (IOException ioex) {
-                logger.info(
-                        "failure closing input stream for " + url + ", ignoring",
-                        ioex);
-            }
-        }
-
-        return map;
-    }
+		return map;
+	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/BaseContext.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/BaseContext.java b/cayenne-server/src/main/java/org/apache/cayenne/BaseContext.java
index 174d266..4d59e7a 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/BaseContext.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/BaseContext.java
@@ -18,6 +18,13 @@
  ****************************************************************/
 package org.apache.cayenne;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
 import org.apache.cayenne.cache.NestedQueryCache;
 import org.apache.cayenne.cache.QueryCache;
 import org.apache.cayenne.configuration.CayenneRuntime;
@@ -43,13 +50,6 @@ import org.apache.cayenne.reflect.ToManyProperty;
 import org.apache.cayenne.reflect.ToOneProperty;
 import org.apache.cayenne.util.ObjectContextGraphAction;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
 /**
  * A common base superclass for Cayenne ObjectContext implementors.
  * 
@@ -57,630 +57,630 @@ import java.util.concurrent.ConcurrentHashMap;
  */
 public abstract class BaseContext implements ObjectContext {
 
-    /**
-     * A holder of a ObjectContext bound to the current thread.
-     * 
-     * @since 3.0
-     */
-    protected static final ThreadLocal<ObjectContext> threadObjectContext = new ThreadLocal<ObjectContext>();
-
-    /**
-     * Returns the ObjectContext bound to the current thread.
-     * 
-     * @since 3.0
-     * @return the ObjectContext associated with caller thread.
-     * @throws IllegalStateException
-     *             if there is no ObjectContext bound to the current thread.
-     */
-    public static ObjectContext getThreadObjectContext() throws IllegalStateException {
-        ObjectContext context = threadObjectContext.get();
-        if (context == null) {
-            throw new IllegalStateException("Current thread has no bound ObjectContext.");
-        }
-
-        return context;
-    }
-
-    /**
-     * Binds a ObjectContext to the current thread. ObjectContext can later be
-     * retrieved by users in the same thread by calling
-     * {@link BaseContext#getThreadObjectContext}. Using null parameter will
-     * unbind currently bound ObjectContext.
-     * 
-     * @since 3.0
-     */
-    public static void bindThreadObjectContext(ObjectContext context) {
-        threadObjectContext.set(context);
-    }
-
-    // transient variables that should be reinitialized on deserialization from
-    // the
-    // registry
-    protected transient DataChannel channel;
-    protected transient QueryCache queryCache;
-    protected transient EntityResolver entityResolver;
-
-    protected boolean validatingObjectsOnCommit = true;
-
-    /**
-     * Graph action that handles property changes
-     * 
-     * @since 3.1
-     */
-    protected ObjectContextGraphAction graphAction;
-
-    /**
-     * Stores user defined properties associated with this DataContext.
-     * 
-     * @since 3.0
-     */
-    protected volatile Map<String, Object> userProperties;
-
-    protected BaseContext() {
-        graphAction = new ObjectContextGraphAction(this);
-    }
-
-    /**
-     * Checks whether this context is attached to Cayenne runtime stack and if
-     * not, attempts to attach itself to the runtime using Injector returned
-     * from the call to {@link CayenneRuntime#getThreadInjector()}. If thread
-     * Injector is not available and the context is not attached, throws
-     * CayenneRuntimeException.
-     * <p>
-     * This method is called internally by the context before access to
-     * transient variables to allow the context to attach to the stack lazily
-     * following deserialization.
-     * 
-     * @return true if the context successfully attached to the thread runtime,
-     *         false - if it was already attached.
-     * @since 3.1
-     */
-    protected boolean attachToRuntimeIfNeeded() {
-        if (channel != null) {
-            return false;
-        }
-
-        Injector injector = CayenneRuntime.getThreadInjector();
-        if (injector == null) {
-            throw new CayenneRuntimeException("Can't attach to Cayenne runtime. "
-                    + "Null injector returned from CayenneRuntime.getThreadInjector()");
-        }
-
-        attachToRuntime(injector);
-        return true;
-    }
-
-    /**
-     * Attaches this context to the CayenneRuntime whose Injector is passed as
-     * an argument to this method.
-     * 
-     * @since 3.1
-     */
-    protected void attachToRuntime(Injector injector) {
-
-        // TODO: nested contexts handling??
-        attachToChannel(injector.getInstance(DataChannel.class));
-        setQueryCache(new NestedQueryCache(injector.getInstance(QueryCache.class)));
-    }
-
-    /**
-     * Attaches to a provided DataChannel.
-     * 
-     * @since 3.1
-     */
-    protected void attachToChannel(DataChannel channel) {
-        if (channel == null) {
-            throw new NullPointerException("Null channel");
-        }
-
-        setChannel(channel);
-        setEntityResolver(channel.getEntityResolver());
-    }
-
-    @Override
-    public abstract void commitChanges();
-
-    @Override
-    public abstract void commitChangesToParent();
-    
-    @Override
-    public void deleteObject(Object object) throws DeleteDenyException {
-    	deleteObjects(object);
-    }
-
-    @Override
-    public abstract Collection<?> deletedObjects();
-
-    @Override
-    public DataChannel getChannel() {
-        attachToRuntimeIfNeeded();
-        return channel;
-    }
-
-    /**
-     * Sets a new DataChannel for this context.
-     * 
-     * @since 3.1
-     */
-    public void setChannel(DataChannel channel) {
-        this.channel = channel;
-    }
-
-    @Override
-    public EntityResolver getEntityResolver() {
-        attachToRuntimeIfNeeded();
-        return entityResolver;
-    }
-
-    /**
-     * @since 3.1
-     */
-    public void setEntityResolver(EntityResolver entityResolver) {
-        this.entityResolver = entityResolver;
-    }
-
-    /**
-     * Returns whether this ObjectContext performs object validation before
-     * commit is executed.
-     * 
-     * @since 1.1
-     */
-    public boolean isValidatingObjectsOnCommit() {
-        return validatingObjectsOnCommit;
-    }
-
-    /**
-     * Sets the property defining whether this ObjectContext should perform
-     * object validation before commit is executed.
-     * 
-     * @since 1.1
-     */
-    public void setValidatingObjectsOnCommit(boolean flag) {
-        this.validatingObjectsOnCommit = flag;
-    }
-
-    /**
-     * @since 3.1
-     */
-    @Override
-    public <T extends Persistent> T localObject(T objectFromAnotherContext) {
-
-        if (objectFromAnotherContext == null) {
-            throw new NullPointerException("Null object argument");
-        }
-
-        ObjectId id = objectFromAnotherContext.getObjectId();
-
-        // first look for the ID in the local GraphManager
-        synchronized (getGraphManager()) {
-            T localObject = (T) getGraphManager().getNode(id);
-            if (localObject != null) {
-                return localObject;
-            }
-
-            // create a hollow object, optimistically assuming that the ID we
-            // got from
-            // 'objectFromAnotherContext' is a valid ID either in the parent
-            // context or in
-            // the DB. This essentially defers possible FaultFailureExceptions.
-
-            ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(id.getEntityName());
-            Persistent persistent = (Persistent) descriptor.createObject();
-
-            persistent.setObjectContext(this);
-            persistent.setObjectId(id);
-            persistent.setPersistenceState(PersistenceState.HOLLOW);
-
-            getGraphManager().registerNode(id, persistent);
-
-            return (T) persistent;
-        }
-    }
-
-    @Override
-    public abstract GraphManager getGraphManager();
-
-    @Override
-    public abstract Collection<?> modifiedObjects();
-
-    @Override
-    public abstract <T> T newObject(Class<T> persistentClass);
-
-    @Override
-    public abstract void registerNewObject(Object object);
-
-    @Override
-    public abstract Collection<?> newObjects();
-
-    @Override
-    public abstract QueryResponse performGenericQuery(Query query);
-
-    @Override
-    public abstract List performQuery(Query query);
-
-    /**
-     * @since 4.0
-     */
-    @SuppressWarnings("unchecked")
-    @Override
-    public <T> List<T> select(Select<T> query) {
-        return performQuery(query);
-    }
-
-    /**
-     * @since 4.0
-     */
-    @Override
-    public <T> T selectOne(Select<T> query) {
-        List<T> objects = select(query);
-
-        if (objects.size() == 0) {
-            return null;
-        } else if (objects.size() > 1) {
-            throw new CayenneRuntimeException("Expected zero or one object, instead query matched: " + objects.size());
-        }
-
-        return objects.get(0);
-    }
-
-    /**
-     * @since 4.0
-     */
-    @Override
-    public <T> T selectFirst(Select<T> query) {
-        List<T> objects = select(query);
-
-        return (objects == null || objects.isEmpty()) ? null : objects.get(0);
-    }
-
-    /**
-     * @since 4.0
-     */
-    @Override
-    public <T> void iterate(Select<T> query, ResultIteratorCallback<T> callback) {
-        ResultIterator<T> it = iterator(query);
-        try {
-        	for(T t : it) {
-        		callback.next(t);
-        	}
-        } finally {
-            it.close();
-        }
-    }
-
-    @Override
-    public abstract <T> ResultIterator<T> iterator(Select<T> query);
-
-    @Override
-    public <T> ResultBatchIterator<T> batchIterator(Select<T> query, int size) {
-        return new ResultBatchIterator<T>(iterator(query), size);
-    }
-
-    @Override
-    public void prepareForAccess(Persistent object, String property, boolean lazyFaulting) {
-        if (object.getPersistenceState() == PersistenceState.HOLLOW) {
-
-            ObjectId oid = object.getObjectId();
-            List<?> objects = performQuery(new ObjectIdQuery(oid, false, ObjectIdQuery.CACHE));
-
-            if (objects.size() == 0) {
-                throw new FaultFailureException(
-                        "Error resolving fault, no matching row exists in the database for ObjectId: " + oid);
-            } else if (objects.size() > 1) {
-                throw new FaultFailureException(
-                        "Error resolving fault, more than one row exists in the database for ObjectId: " + oid);
-            }
-
-            // 5/28/2013 - Commented out this block to allow for modifying
-            // objects in the postLoad callback
-            // sanity check...
-            // if (object.getPersistenceState() != PersistenceState.COMMITTED) {
-            //
-            // String state =
-            // PersistenceState.persistenceStateName(object.getPersistenceState());
-            //
-            // // TODO: andrus 4/13/2006, modified and deleted states are
-            // // possible due to
-            // // a race condition, should we handle them here?
-            // throw new
-            // FaultFailureException("Error resolving fault for ObjectId: " +
-            // oid + " and state (" + state
-            // +
-            // "). Possible cause - matching row is missing from the database.");
-            // }
-        }
-
-        // resolve relationship fault
-        if (lazyFaulting && property != null) {
-            ClassDescriptor classDescriptor = getEntityResolver().getClassDescriptor(
-                    object.getObjectId().getEntityName());
-            PropertyDescriptor propertyDescriptor = classDescriptor.getProperty(property);
-
-            // If we don't have a property descriptor, there's not much we can
-            // do.
-            // Let the caller know that the specified property could not be
-            // found and list
-            // all of the properties that could be so the caller knows what can
-            // be used.
-            if (propertyDescriptor == null) {
-                final StringBuilder errorMessage = new StringBuilder();
-
-                errorMessage.append(String.format("Property '%s' is not declared for entity '%s'.", property, object
-                        .getObjectId().getEntityName()));
-
-                errorMessage.append(" Declared properties are: ");
-
-                // Grab each of the declared properties.
-                final List<String> properties = new ArrayList<String>();
-                classDescriptor.visitProperties(new PropertyVisitor() {
-                    @Override
-                    public boolean visitAttribute(final AttributeProperty property) {
-                        properties.add(property.getName());
-
-                        return true;
-                    }
-                    @Override
-                    public boolean visitToOne(final ToOneProperty property) {
-                        properties.add(property.getName());
-
-                        return true;
-                    }
-                    @Override
-                    public boolean visitToMany(final ToManyProperty property) {
-                        properties.add(property.getName());
-
-                        return true;
-                    }
-                });
-
-                // Now add the declared property names to the error message.
-                boolean first = true;
-                for (String declaredProperty : properties) {
-                    if (first) {
-                        errorMessage.append(String.format("'%s'", declaredProperty));
-
-                        first = false;
-                    } else {
-                        errorMessage.append(String.format(", '%s'", declaredProperty));
-                    }
-                }
-
-                errorMessage.append(".");
-
-                throw new CayenneRuntimeException(errorMessage.toString());
-            }
-
-            // this should trigger fault resolving
-            propertyDescriptor.readProperty(object);
-        }
-    }
-    
-    @Override
-    public void propertyChanged(Persistent object, String property, Object oldValue, Object newValue) {
-        graphAction.handlePropertyChange(object, property, oldValue, newValue);
-    }
-
-    @Override
-    public abstract void rollbackChanges();
-
-    @Override
-    public abstract void rollbackChangesLocally();
-
-    @Override
-    public abstract Collection<?> uncommittedObjects();
-
-    public QueryCache getQueryCache() {
-        attachToRuntimeIfNeeded();
-        return queryCache;
-    }
-
-    /**
-     * Sets a QueryCache to be used for storing cached query results.
-     */
-    public void setQueryCache(QueryCache queryCache) {
-        this.queryCache = queryCache;
-    }
-
-    /**
-     * Returns EventManager associated with the ObjectStore.
-     * 
-     * @since 1.2
-     */
-    @Override
-    public EventManager getEventManager() {
-        return channel != null ? channel.getEventManager() : null;
-    }
-
-    @Override
-    public GraphDiff onSync(ObjectContext originatingContext, GraphDiff changes, int syncType) {
-        switch (syncType) {
-        case DataChannel.ROLLBACK_CASCADE_SYNC:
-            return onContextRollback(originatingContext);
-        case DataChannel.FLUSH_NOCASCADE_SYNC:
-            return onContextFlush(originatingContext, changes, false);
-        case DataChannel.FLUSH_CASCADE_SYNC:
-            return onContextFlush(originatingContext, changes, true);
-        default:
-            throw new CayenneRuntimeException("Unrecognized SyncMessage type: " + syncType);
-        }
-    }
-
-    GraphDiff onContextRollback(ObjectContext originatingContext) {
-        rollbackChanges();
-        return new CompoundDiff();
-    }
-
-    protected abstract GraphDiff onContextFlush(ObjectContext originatingContext, GraphDiff changes, boolean cascade);
-
-    /**
-     * @since 1.2
-     */
-    protected void fireDataChannelCommitted(Object postedBy, GraphDiff changes) {
-        EventManager manager = getEventManager();
-
-        if (manager != null) {
-            GraphEvent e = new GraphEvent(this, postedBy, changes);
-            manager.postEvent(e, DataChannel.GRAPH_FLUSHED_SUBJECT);
-        }
-    }
-
-    /**
-     * @since 1.2
-     */
-    protected void fireDataChannelRolledback(Object postedBy, GraphDiff changes) {
-        EventManager manager = getEventManager();
-
-        if (manager != null) {
-            GraphEvent e = new GraphEvent(this, postedBy, changes);
-            manager.postEvent(e, DataChannel.GRAPH_ROLLEDBACK_SUBJECT);
-        }
-    }
-
-    /**
-     * @since 1.2
-     */
-    protected void fireDataChannelChanged(Object postedBy, GraphDiff changes) {
-        EventManager manager = getEventManager();
-
-        if (manager != null) {
-            GraphEvent e = new GraphEvent(this, postedBy, changes);
-            manager.postEvent(e, DataChannel.GRAPH_CHANGED_SUBJECT);
-        }
-    }
-
-    @Override
-    public void invalidateObjects(Collection<?> objects) {
-
-        // don't allow null collections as a matter of coding discipline
-        if (objects == null) {
-            throw new NullPointerException("Null collection of objects to invalidate");
-        }
-
-        if (!objects.isEmpty()) {
-            performGenericQuery(new RefreshQuery(objects));
-        }
-    }
-
-    /**
-     * @since 3.1
-     */
-    @Override
-    public <T> void invalidateObjects(T... objects) {
-        if (objects != null && objects.length > 0) {
-            performGenericQuery(new RefreshQuery(Arrays.asList(objects)));
-        }
-    }
-
-    /**
-     * Returns a map of user-defined properties associated with this
-     * DataContext.
-     * 
-     * @since 3.0
-     */
-    protected Map<String, Object> getUserProperties() {
-
-        // as not all users will take advantage of properties, creating the
-        // map on demand to keep the context lean...
-        if (userProperties == null) {
-            synchronized (this) {
-                if (userProperties == null) {
-                    userProperties = new ConcurrentHashMap<String, Object>();
-                }
-            }
-        }
-
-        return userProperties;
-    }
-
-    /**
-     * Returns a user-defined property previously set via 'setUserProperty'.
-     * Note that it is a caller responsibility to synchronize access to
-     * properties.
-     * 
-     * @since 3.0
-     */
-    @Override
-    public Object getUserProperty(String key) {
-        return getUserProperties().get(key);
-    }
-
-    /**
-     * Sets a user-defined property. Note that it is a caller responsibility to
-     * synchronize access to properties.
-     * 
-     * @since 3.0
-     */
-    @Override
-    public void setUserProperty(String key, Object value) {
-        getUserProperties().put(key, value);
-    }
-
-    /**
-     * If ObjEntity qualifier is set, asks it to inject initial value to an
-     * object. Also performs all Persistent initialization operations
-     */
-    protected void injectInitialValue(Object obj) {
-        // must follow this exact order of property initialization per CAY-653,
-        // i.e. have
-        // the id and the context in place BEFORE setPersistence is called
-
-        Persistent object = (Persistent) obj;
-
-        object.setObjectContext(this);
-        object.setPersistenceState(PersistenceState.NEW);
-
-        GraphManager graphManager = getGraphManager();
-        synchronized (graphManager) {
-            graphManager.registerNode(object.getObjectId(), object);
-            graphManager.nodeCreated(object.getObjectId());
-        }
-
-        ObjEntity entity;
-        try {
-            entity = getEntityResolver().getObjEntity(object.getClass());
-        } catch (CayenneRuntimeException ex) {
-            // ObjEntity cannot be fetched, ignored
-            entity = null;
-        }
-
-        if (entity != null) {
-            if (entity.getDeclaredQualifier() instanceof ValueInjector) {
-                ((ValueInjector) entity.getDeclaredQualifier()).injectValue(object);
-            }
-        }
-
-        // invoke callbacks
-        getEntityResolver().getCallbackRegistry().performCallbacks(LifecycleEvent.POST_ADD, object);
-    }
-
-    /**
-     * @since 3.1
-     */
-    @Override
-    public <T> void deleteObjects(T... objects) throws DeleteDenyException {
-        if (objects == null || objects.length == 0) {
-            return;
-        }
-
-        ObjectContextDeleteAction action = new ObjectContextDeleteAction(this);
-
-        for (Object object : objects) {
-            action.performDelete((Persistent) object);
-        }
-    }
-
-    @Override
-    public void deleteObjects(Collection<?> objects) throws DeleteDenyException {
-        if (objects.isEmpty()) {
-            return;
-        }
-
-        ObjectContextDeleteAction action = new ObjectContextDeleteAction(this);
-
-        // Make a copy to iterate over to avoid ConcurrentModificationException.
-        List<Object> copy = new ArrayList<Object>(objects);
-        for (Object object : copy) {
-            action.performDelete((Persistent) object);
-        }
-    }
+	/**
+	 * A holder of a ObjectContext bound to the current thread.
+	 * 
+	 * @since 3.0
+	 */
+	protected static final ThreadLocal<ObjectContext> threadObjectContext = new ThreadLocal<ObjectContext>();
+
+	/**
+	 * Returns the ObjectContext bound to the current thread.
+	 * 
+	 * @since 3.0
+	 * @return the ObjectContext associated with caller thread.
+	 * @throws IllegalStateException
+	 *             if there is no ObjectContext bound to the current thread.
+	 */
+	public static ObjectContext getThreadObjectContext() throws IllegalStateException {
+		ObjectContext context = threadObjectContext.get();
+		if (context == null) {
+			throw new IllegalStateException("Current thread has no bound ObjectContext.");
+		}
+
+		return context;
+	}
+
+	/**
+	 * Binds a ObjectContext to the current thread. ObjectContext can later be
+	 * retrieved by users in the same thread by calling
+	 * {@link BaseContext#getThreadObjectContext}. Using null parameter will
+	 * unbind currently bound ObjectContext.
+	 * 
+	 * @since 3.0
+	 */
+	public static void bindThreadObjectContext(ObjectContext context) {
+		threadObjectContext.set(context);
+	}
+
+	// transient variables that should be reinitialized on deserialization from
+	// the
+	// registry
+	protected transient DataChannel channel;
+	protected transient QueryCache queryCache;
+	protected transient EntityResolver entityResolver;
+
+	protected boolean validatingObjectsOnCommit = true;
+
+	/**
+	 * Graph action that handles property changes
+	 * 
+	 * @since 3.1
+	 */
+	protected ObjectContextGraphAction graphAction;
+
+	/**
+	 * Stores user defined properties associated with this DataContext.
+	 * 
+	 * @since 3.0
+	 */
+	protected volatile Map<String, Object> userProperties;
+
+	protected BaseContext() {
+		graphAction = new ObjectContextGraphAction(this);
+	}
+
+	/**
+	 * Checks whether this context is attached to Cayenne runtime stack and if
+	 * not, attempts to attach itself to the runtime using Injector returned
+	 * from the call to {@link CayenneRuntime#getThreadInjector()}. If thread
+	 * Injector is not available and the context is not attached, throws
+	 * CayenneRuntimeException.
+	 * <p>
+	 * This method is called internally by the context before access to
+	 * transient variables to allow the context to attach to the stack lazily
+	 * following deserialization.
+	 * 
+	 * @return true if the context successfully attached to the thread runtime,
+	 *         false - if it was already attached.
+	 * @since 3.1
+	 */
+	protected boolean attachToRuntimeIfNeeded() {
+		if (channel != null) {
+			return false;
+		}
+
+		Injector injector = CayenneRuntime.getThreadInjector();
+		if (injector == null) {
+			throw new CayenneRuntimeException("Can't attach to Cayenne runtime. "
+					+ "Null injector returned from CayenneRuntime.getThreadInjector()");
+		}
+
+		attachToRuntime(injector);
+		return true;
+	}
+
+	/**
+	 * Attaches this context to the CayenneRuntime whose Injector is passed as
+	 * an argument to this method.
+	 * 
+	 * @since 3.1
+	 */
+	protected void attachToRuntime(Injector injector) {
+
+		// TODO: nested contexts handling??
+		attachToChannel(injector.getInstance(DataChannel.class));
+		setQueryCache(new NestedQueryCache(injector.getInstance(QueryCache.class)));
+	}
+
+	/**
+	 * Attaches to a provided DataChannel.
+	 * 
+	 * @since 3.1
+	 */
+	protected void attachToChannel(DataChannel channel) {
+		if (channel == null) {
+			throw new NullPointerException("Null channel");
+		}
+
+		setChannel(channel);
+		setEntityResolver(channel.getEntityResolver());
+	}
+
+	@Override
+	public abstract void commitChanges();
+
+	@Override
+	public abstract void commitChangesToParent();
+
+	@Override
+	public void deleteObject(Object object) throws DeleteDenyException {
+		deleteObjects(object);
+	}
+
+	@Override
+	public abstract Collection<?> deletedObjects();
+
+	@Override
+	public DataChannel getChannel() {
+		attachToRuntimeIfNeeded();
+		return channel;
+	}
+
+	/**
+	 * Sets a new DataChannel for this context.
+	 * 
+	 * @since 3.1
+	 */
+	public void setChannel(DataChannel channel) {
+		this.channel = channel;
+	}
+
+	@Override
+	public EntityResolver getEntityResolver() {
+		attachToRuntimeIfNeeded();
+		return entityResolver;
+	}
+
+	/**
+	 * @since 3.1
+	 */
+	public void setEntityResolver(EntityResolver entityResolver) {
+		this.entityResolver = entityResolver;
+	}
+
+	/**
+	 * Returns whether this ObjectContext performs object validation before
+	 * commit is executed.
+	 * 
+	 * @since 1.1
+	 */
+	public boolean isValidatingObjectsOnCommit() {
+		return validatingObjectsOnCommit;
+	}
+
+	/**
+	 * Sets the property defining whether this ObjectContext should perform
+	 * object validation before commit is executed.
+	 * 
+	 * @since 1.1
+	 */
+	public void setValidatingObjectsOnCommit(boolean flag) {
+		this.validatingObjectsOnCommit = flag;
+	}
+
+	/**
+	 * @since 3.1
+	 */
+	@Override
+	public <T extends Persistent> T localObject(T objectFromAnotherContext) {
+
+		if (objectFromAnotherContext == null) {
+			throw new NullPointerException("Null object argument");
+		}
+
+		ObjectId id = objectFromAnotherContext.getObjectId();
+
+		// first look for the ID in the local GraphManager
+		synchronized (getGraphManager()) {
+			T localObject = (T) getGraphManager().getNode(id);
+			if (localObject != null) {
+				return localObject;
+			}
+
+			// create a hollow object, optimistically assuming that the ID we
+			// got from
+			// 'objectFromAnotherContext' is a valid ID either in the parent
+			// context or in
+			// the DB. This essentially defers possible FaultFailureExceptions.
+
+			ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(id.getEntityName());
+			Persistent persistent = (Persistent) descriptor.createObject();
+
+			persistent.setObjectContext(this);
+			persistent.setObjectId(id);
+			persistent.setPersistenceState(PersistenceState.HOLLOW);
+
+			getGraphManager().registerNode(id, persistent);
+
+			return (T) persistent;
+		}
+	}
+
+	@Override
+	public abstract GraphManager getGraphManager();
+
+	@Override
+	public abstract Collection<?> modifiedObjects();
+
+	@Override
+	public abstract <T> T newObject(Class<T> persistentClass);
+
+	@Override
+	public abstract void registerNewObject(Object object);
+
+	@Override
+	public abstract Collection<?> newObjects();
+
+	@Override
+	public abstract QueryResponse performGenericQuery(Query query);
+
+	@Override
+	public abstract List performQuery(Query query);
+
+	/**
+	 * @since 4.0
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public <T> List<T> select(Select<T> query) {
+		return performQuery(query);
+	}
+
+	/**
+	 * @since 4.0
+	 */
+	@Override
+	public <T> T selectOne(Select<T> query) {
+		List<T> objects = select(query);
+
+		if (objects.size() == 0) {
+			return null;
+		} else if (objects.size() > 1) {
+			throw new CayenneRuntimeException("Expected zero or one object, instead query matched: " + objects.size());
+		}
+
+		return objects.get(0);
+	}
+
+	/**
+	 * @since 4.0
+	 */
+	@Override
+	public <T> T selectFirst(Select<T> query) {
+		List<T> objects = select(query);
+
+		return (objects == null || objects.isEmpty()) ? null : objects.get(0);
+	}
+
+	/**
+	 * @since 4.0
+	 */
+	@Override
+	public <T> void iterate(Select<T> query, ResultIteratorCallback<T> callback) {
+
+		try (ResultIterator<T> it = iterator(query);) {
+			for (T t : it) {
+				callback.next(t);
+			}
+		}
+	}
+
+	@Override
+	public abstract <T> ResultIterator<T> iterator(Select<T> query);
+
+	@Override
+	public <T> ResultBatchIterator<T> batchIterator(Select<T> query, int size) {
+		return new ResultBatchIterator<T>(iterator(query), size);
+	}
+
+	@Override
+	public void prepareForAccess(Persistent object, String property, boolean lazyFaulting) {
+		if (object.getPersistenceState() == PersistenceState.HOLLOW) {
+
+			ObjectId oid = object.getObjectId();
+			List<?> objects = performQuery(new ObjectIdQuery(oid, false, ObjectIdQuery.CACHE));
+
+			if (objects.size() == 0) {
+				throw new FaultFailureException(
+						"Error resolving fault, no matching row exists in the database for ObjectId: " + oid);
+			} else if (objects.size() > 1) {
+				throw new FaultFailureException(
+						"Error resolving fault, more than one row exists in the database for ObjectId: " + oid);
+			}
+
+			// 5/28/2013 - Commented out this block to allow for modifying
+			// objects in the postLoad callback
+			// sanity check...
+			// if (object.getPersistenceState() != PersistenceState.COMMITTED) {
+			//
+			// String state =
+			// PersistenceState.persistenceStateName(object.getPersistenceState());
+			//
+			// // TODO: andrus 4/13/2006, modified and deleted states are
+			// // possible due to
+			// // a race condition, should we handle them here?
+			// throw new
+			// FaultFailureException("Error resolving fault for ObjectId: " +
+			// oid + " and state (" + state
+			// +
+			// "). Possible cause - matching row is missing from the database.");
+			// }
+		}
+
+		// resolve relationship fault
+		if (lazyFaulting && property != null) {
+			ClassDescriptor classDescriptor = getEntityResolver().getClassDescriptor(
+					object.getObjectId().getEntityName());
+			PropertyDescriptor propertyDescriptor = classDescriptor.getProperty(property);
+
+			// If we don't have a property descriptor, there's not much we can
+			// do.
+			// Let the caller know that the specified property could not be
+			// found and list
+			// all of the properties that could be so the caller knows what can
+			// be used.
+			if (propertyDescriptor == null) {
+				final StringBuilder errorMessage = new StringBuilder();
+
+				errorMessage.append(String.format("Property '%s' is not declared for entity '%s'.", property, object
+						.getObjectId().getEntityName()));
+
+				errorMessage.append(" Declared properties are: ");
+
+				// Grab each of the declared properties.
+				final List<String> properties = new ArrayList<String>();
+				classDescriptor.visitProperties(new PropertyVisitor() {
+					@Override
+					public boolean visitAttribute(final AttributeProperty property) {
+						properties.add(property.getName());
+
+						return true;
+					}
+
+					@Override
+					public boolean visitToOne(final ToOneProperty property) {
+						properties.add(property.getName());
+
+						return true;
+					}
+
+					@Override
+					public boolean visitToMany(final ToManyProperty property) {
+						properties.add(property.getName());
+
+						return true;
+					}
+				});
+
+				// Now add the declared property names to the error message.
+				boolean first = true;
+				for (String declaredProperty : properties) {
+					if (first) {
+						errorMessage.append(String.format("'%s'", declaredProperty));
+
+						first = false;
+					} else {
+						errorMessage.append(String.format(", '%s'", declaredProperty));
+					}
+				}
+
+				errorMessage.append(".");
+
+				throw new CayenneRuntimeException(errorMessage.toString());
+			}
+
+			// this should trigger fault resolving
+			propertyDescriptor.readProperty(object);
+		}
+	}
+
+	@Override
+	public void propertyChanged(Persistent object, String property, Object oldValue, Object newValue) {
+		graphAction.handlePropertyChange(object, property, oldValue, newValue);
+	}
+
+	@Override
+	public abstract void rollbackChanges();
+
+	@Override
+	public abstract void rollbackChangesLocally();
+
+	@Override
+	public abstract Collection<?> uncommittedObjects();
+
+	public QueryCache getQueryCache() {
+		attachToRuntimeIfNeeded();
+		return queryCache;
+	}
+
+	/**
+	 * Sets a QueryCache to be used for storing cached query results.
+	 */
+	public void setQueryCache(QueryCache queryCache) {
+		this.queryCache = queryCache;
+	}
+
+	/**
+	 * Returns EventManager associated with the ObjectStore.
+	 * 
+	 * @since 1.2
+	 */
+	@Override
+	public EventManager getEventManager() {
+		return channel != null ? channel.getEventManager() : null;
+	}
+
+	@Override
+	public GraphDiff onSync(ObjectContext originatingContext, GraphDiff changes, int syncType) {
+		switch (syncType) {
+		case DataChannel.ROLLBACK_CASCADE_SYNC:
+			return onContextRollback(originatingContext);
+		case DataChannel.FLUSH_NOCASCADE_SYNC:
+			return onContextFlush(originatingContext, changes, false);
+		case DataChannel.FLUSH_CASCADE_SYNC:
+			return onContextFlush(originatingContext, changes, true);
+		default:
+			throw new CayenneRuntimeException("Unrecognized SyncMessage type: " + syncType);
+		}
+	}
+
+	GraphDiff onContextRollback(ObjectContext originatingContext) {
+		rollbackChanges();
+		return new CompoundDiff();
+	}
+
+	protected abstract GraphDiff onContextFlush(ObjectContext originatingContext, GraphDiff changes, boolean cascade);
+
+	/**
+	 * @since 1.2
+	 */
+	protected void fireDataChannelCommitted(Object postedBy, GraphDiff changes) {
+		EventManager manager = getEventManager();
+
+		if (manager != null) {
+			GraphEvent e = new GraphEvent(this, postedBy, changes);
+			manager.postEvent(e, DataChannel.GRAPH_FLUSHED_SUBJECT);
+		}
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	protected void fireDataChannelRolledback(Object postedBy, GraphDiff changes) {
+		EventManager manager = getEventManager();
+
+		if (manager != null) {
+			GraphEvent e = new GraphEvent(this, postedBy, changes);
+			manager.postEvent(e, DataChannel.GRAPH_ROLLEDBACK_SUBJECT);
+		}
+	}
+
+	/**
+	 * @since 1.2
+	 */
+	protected void fireDataChannelChanged(Object postedBy, GraphDiff changes) {
+		EventManager manager = getEventManager();
+
+		if (manager != null) {
+			GraphEvent e = new GraphEvent(this, postedBy, changes);
+			manager.postEvent(e, DataChannel.GRAPH_CHANGED_SUBJECT);
+		}
+	}
+
+	@Override
+	public void invalidateObjects(Collection<?> objects) {
+
+		// don't allow null collections as a matter of coding discipline
+		if (objects == null) {
+			throw new NullPointerException("Null collection of objects to invalidate");
+		}
+
+		if (!objects.isEmpty()) {
+			performGenericQuery(new RefreshQuery(objects));
+		}
+	}
+
+	/**
+	 * @since 3.1
+	 */
+	@Override
+	public <T> void invalidateObjects(T... objects) {
+		if (objects != null && objects.length > 0) {
+			performGenericQuery(new RefreshQuery(Arrays.asList(objects)));
+		}
+	}
+
+	/**
+	 * Returns a map of user-defined properties associated with this
+	 * DataContext.
+	 * 
+	 * @since 3.0
+	 */
+	protected Map<String, Object> getUserProperties() {
+
+		// as not all users will take advantage of properties, creating the
+		// map on demand to keep the context lean...
+		if (userProperties == null) {
+			synchronized (this) {
+				if (userProperties == null) {
+					userProperties = new ConcurrentHashMap<String, Object>();
+				}
+			}
+		}
+
+		return userProperties;
+	}
+
+	/**
+	 * Returns a user-defined property previously set via 'setUserProperty'.
+	 * Note that it is a caller responsibility to synchronize access to
+	 * properties.
+	 * 
+	 * @since 3.0
+	 */
+	@Override
+	public Object getUserProperty(String key) {
+		return getUserProperties().get(key);
+	}
+
+	/**
+	 * Sets a user-defined property. Note that it is a caller responsibility to
+	 * synchronize access to properties.
+	 * 
+	 * @since 3.0
+	 */
+	@Override
+	public void setUserProperty(String key, Object value) {
+		getUserProperties().put(key, value);
+	}
+
+	/**
+	 * If ObjEntity qualifier is set, asks it to inject initial value to an
+	 * object. Also performs all Persistent initialization operations
+	 */
+	protected void injectInitialValue(Object obj) {
+		// must follow this exact order of property initialization per CAY-653,
+		// i.e. have
+		// the id and the context in place BEFORE setPersistence is called
+
+		Persistent object = (Persistent) obj;
+
+		object.setObjectContext(this);
+		object.setPersistenceState(PersistenceState.NEW);
+
+		GraphManager graphManager = getGraphManager();
+		synchronized (graphManager) {
+			graphManager.registerNode(object.getObjectId(), object);
+			graphManager.nodeCreated(object.getObjectId());
+		}
+
+		ObjEntity entity;
+		try {
+			entity = getEntityResolver().getObjEntity(object.getClass());
+		} catch (CayenneRuntimeException ex) {
+			// ObjEntity cannot be fetched, ignored
+			entity = null;
+		}
+
+		if (entity != null) {
+			if (entity.getDeclaredQualifier() instanceof ValueInjector) {
+				((ValueInjector) entity.getDeclaredQualifier()).injectValue(object);
+			}
+		}
+
+		// invoke callbacks
+		getEntityResolver().getCallbackRegistry().performCallbacks(LifecycleEvent.POST_ADD, object);
+	}
+
+	/**
+	 * @since 3.1
+	 */
+	@Override
+	public <T> void deleteObjects(T... objects) throws DeleteDenyException {
+		if (objects == null || objects.length == 0) {
+			return;
+		}
+
+		ObjectContextDeleteAction action = new ObjectContextDeleteAction(this);
+
+		for (Object object : objects) {
+			action.performDelete((Persistent) object);
+		}
+	}
+
+	@Override
+	public void deleteObjects(Collection<?> objects) throws DeleteDenyException {
+		if (objects.isEmpty()) {
+			return;
+		}
+
+		ObjectContextDeleteAction action = new ObjectContextDeleteAction(this);
+
+		// Make a copy to iterate over to avoid ConcurrentModificationException.
+		List<Object> copy = new ArrayList<Object>(objects);
+		for (Object object : copy) {
+			action.performDelete((Persistent) object);
+		}
+	}
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/ResultBatchIterator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/ResultBatchIterator.java b/cayenne-server/src/main/java/org/apache/cayenne/ResultBatchIterator.java
index 118c5e5..835d2c7 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/ResultBatchIterator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/ResultBatchIterator.java
@@ -19,7 +19,6 @@
 
 package org.apache.cayenne;
 
-import java.io.Closeable;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -32,12 +31,12 @@ import java.util.List;
  *
  * @since 4.0
  */
-public class ResultBatchIterator<T> implements Iterable<List<T>>, Iterator<List<T>>, Closeable {
+public class ResultBatchIterator<T> implements Iterable<List<T>>, Iterator<List<T>>, AutoCloseable {
 
-    private final ResultIterator delegate;
+    private final ResultIterator<T> delegate;
     private final int size;
 
-    public ResultBatchIterator(ResultIterator delegate, int size) {
+    public ResultBatchIterator(ResultIterator<T> delegate, int size) {
         this.delegate = delegate;
         this.size = size;
     }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/ResultIterator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/ResultIterator.java b/cayenne-server/src/main/java/org/apache/cayenne/ResultIterator.java
index 3de04f0..a5840f4 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/ResultIterator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/ResultIterator.java
@@ -19,7 +19,6 @@
 
 package org.apache.cayenne;
 
-import java.io.Closeable;
 import java.util.List;
 
 /**
@@ -32,7 +31,7 @@ import java.util.List;
  *
  * @since 3.0
  */
-public interface ResultIterator<T> extends Iterable<T>, Closeable {
+public interface ResultIterator<T> extends Iterable<T>, AutoCloseable {
 
     /**
      * Returns all yet unread rows from ResultSet without closing it.

http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
index a0fce92..5ee7531 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
@@ -562,12 +562,12 @@ class DataDomainQueryAction implements QueryRouter, OperationObserver {
     }
 
     @Override
-    public void nextRows(Query q, ResultIterator it) {
+    public void nextRows(Query q, ResultIterator<?> it) {
         throw new CayenneRuntimeException("Invalid attempt to fetch a cursor.");
     }
 
     @Override
-    public void nextGeneratedRows(Query query, ResultIterator keys, ObjectId idToUpdate) {
+    public void nextGeneratedRows(Query query, ResultIterator<?> keys, ObjectId idToUpdate) {
         if (keys != null) {
             try {
                 nextRows(query, keys.allRows());

http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/access/DataPort.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataPort.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataPort.java
index 0691124..8dbad91 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataPort.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataPort.java
@@ -150,7 +150,7 @@ public class DataPort {
 			if (delegate != null) {
 				query = delegate.willCleanData(this, entity, query);
 			}
-			
+
 			final int[] count = new int[] { -1 };
 
 			// perform delete query
@@ -205,10 +205,10 @@ public class DataPort {
 			Query query = (delegate != null) ? delegate.willPortEntity(this, entity, select) : select;
 
 			sourceNode.performQueries(Collections.singletonList(query), observer);
-			ResultIterator result = observer.getResultIterator();
+
 			InsertBatchQuery insert = new InsertBatchQuery(entity, INSERT_BATCH_SIZE);
 
-			try {
+			try (ResultIterator<?> result = observer.getResultIterator();) {
 
 				// Split insertions into the same table into batches.
 				// This will allow to process tables of arbitrary size
@@ -243,10 +243,6 @@ public class DataPort {
 				if (delegate != null) {
 					delegate.didPortEntity(this, entity, currentRow);
 				}
-			} finally {
-
-				// don't forget to close ResultIterator
-				result.close();
 			}
 		}
 	}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/access/DbGenerator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DbGenerator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DbGenerator.java
index f9558ca..b56cae0 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DbGenerator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DbGenerator.java
@@ -269,9 +269,7 @@ public class DbGenerator {
 	public void runGenerator(DataSource ds) throws Exception {
 		this.failures = null;
 
-		Connection connection = ds.getConnection();
-
-		try {
+		try (Connection connection = ds.getConnection();) {
 
 			// drop tables
 			if (shouldDropTables) {
@@ -328,8 +326,6 @@ public class DbGenerator {
 			}
 
 			new DbGeneratorPostprocessor().execute(connection, getAdapter());
-		} finally {
-			connection.close();
 		}
 	}
 
@@ -340,9 +336,8 @@ public class DbGenerator {
 	 * @since 1.1
 	 */
 	protected boolean safeExecute(Connection connection, String sql) throws SQLException {
-		Statement statement = connection.createStatement();
 
-		try {
+		try (Statement statement = connection.createStatement();) {
 			jdbcEventLogger.logQuery(sql, null);
 			statement.execute(sql);
 			return true;
@@ -354,8 +349,6 @@ public class DbGenerator {
 			failures.addFailure(new SimpleValidationFailure(sql, ex.getMessage()));
 			jdbcEventLogger.logQueryError(ex);
 			return false;
-		} finally {
-			statement.close();
 		}
 	}
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/cayenne-server/src/main/java/org/apache/cayenne/access/DbGeneratorPostprocessor.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DbGeneratorPostprocessor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DbGeneratorPostprocessor.java
index d46c9bc..cc1eb58 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DbGeneratorPostprocessor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DbGeneratorPostprocessor.java
@@ -24,53 +24,47 @@ import java.sql.SQLException;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.cayenne.dba.AutoAdapter;
 import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.dba.hsqldb.HSQLDBAdapter;
 
 /**
- * A helper class that handles postprocessing after the schema generation operation. E.g.
- * some databases require a checkpoint command to be run for the schema changes to be
- * flushed to disk.
+ * A helper class that handles postprocessing after the schema generation
+ * operation. E.g. some databases require a checkpoint command to be run for the
+ * schema changes to be flushed to disk.
  * 
  */
 class DbGeneratorPostprocessor {
 
-    private static final Map<String, HSQLDBPostprocessor> postprocessors;
+	private static final Map<String, HSQLDBPostprocessor> postprocessors;
 
-    static {
-        postprocessors = new HashMap<String, HSQLDBPostprocessor>();
-        postprocessors.put(HSQLDBAdapter.class.getName(), new HSQLDBPostprocessor());
-    }
+	static {
+		postprocessors = new HashMap<String, HSQLDBPostprocessor>();
+		postprocessors.put(HSQLDBAdapter.class.getName(), new HSQLDBPostprocessor());
+	}
 
-    void execute(Connection connection, DbAdapter adapter) throws SQLException {
+	void execute(Connection connection, DbAdapter adapter) throws SQLException {
 
-        if (adapter != null) {
-            Postprocessor postprocessor = postprocessors.get(adapter
-                    .getClass()
-                    .getName());
-            if (postprocessor != null) {
-                postprocessor.execute(connection);
-            }
-        }
-    }
+		if (adapter != null) {
+			Postprocessor postprocessor = postprocessors.get(adapter.getClass().getName());
+			if (postprocessor != null) {
+				postprocessor.execute(connection);
+			}
+		}
+	}
 
-    static abstract class Postprocessor {
+	static abstract class Postprocessor {
 
-        abstract void execute(Connection c) throws SQLException;
-    }
+		abstract void execute(Connection c) throws SQLException;
+	}
 
-    static class HSQLDBPostprocessor extends Postprocessor {
+	static class HSQLDBPostprocessor extends Postprocessor {
 
-        @Override
-        void execute(Connection c) throws SQLException {
-            PreparedStatement st = c.prepareStatement("CHECKPOINT");
-            try {
-                st.execute();
-            }
-            finally {
-                st.close();
-            }
-        }
-    }
+		@Override
+		void execute(Connection c) throws SQLException {
+
+			try (PreparedStatement st = c.prepareStatement("CHECKPOINT");) {
+				st.execute();
+			}
+		}
+	}
 }


Mime
View raw message