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();
+                       }
+               }
+       }
 }

Reply via email to