cayenne-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ntimof...@apache.org
Subject [09/16] cayenne git commit: CAY-2335: New XML loading/saving mechanics with support of plugable handlers - new XML loader for DataMap - new project version - updated test projects
Date Tue, 25 Jul 2017 12:25:24 GMT
CAY-2335: New XML loading/saving mechanics with support of plugable handlers
  - new XML loader for DataMap
  - new project version
  - updated test projects


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/c58b6f40
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/c58b6f40
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/c58b6f40

Branch: refs/heads/master
Commit: c58b6f406e81de4d90abe888b763a1292c77feb8
Parents: 2c9896c
Author: Nikita Timofeev <stariy95@gmail.com>
Authored: Tue Jul 25 12:35:50 2017 +0300
Committer: Nikita Timofeev <stariy95@gmail.com>
Committed: Tue Jul 25 12:35:50 2017 +0300

----------------------------------------------------------------------
 .../upgrade/v6/XMLDataMapLoader_V3_0_0_1.java   |   51 -
 .../project/upgrade/v8/ProjectUpgrader_V8.java  |   42 -
 .../configuration/DataChannelDescriptor.java    |   46 +-
 .../configuration/DataNodeDescriptor.java       |   39 +-
 .../EmptyConfigurationNodeVisitor.java          |  108 ++
 .../configuration/SAXNestedTagHandler.java      |  131 --
 .../XMLDataChannelDescriptorLoader.java         |  389 ------
 .../cayenne/configuration/XMLDataMapLoader.java |   57 -
 .../configuration/server/ServerModule.java      |   11 +-
 .../xml/DataChannelChildrenHandler.java         |  133 ++
 .../configuration/xml/DataChannelHandler.java   |   78 ++
 .../configuration/xml/DataChannelMetaData.java  |   64 +
 .../configuration/xml/DataMapHandler.java       |  142 ++
 .../xml/DataMapLoaderListener.java              |   31 +
 .../xml/DataNodeChildrenHandler.java            |   71 +
 .../xml/DataSourceChildrenHandler.java          |  175 +++
 .../configuration/xml/DbEntityHandler.java      |  146 ++
 .../xml/DbKeyGeneratorHandler.java              |  117 ++
 .../xml/DbRelationshipHandler.java              |   97 ++
 .../xml/DefaultDataChannelMetaData.java         |   91 ++
 .../xml/DefaultHandlerFactory.java              |   44 +
 .../xml/EmbeddableAttributeHandler.java         |   69 +
 .../configuration/xml/EmbeddableHandler.java    |   75 ++
 .../configuration/xml/HandlerFactory.java       |   30 +
 .../configuration/xml/LoaderContext.java        |   63 +
 .../xml/NamespaceAwareNestedTagHandler.java     |   97 ++
 .../xml/NoopDataChannelMetaData.java            |   42 +
 .../configuration/xml/ObjEntityHandler.java     |  210 +++
 .../xml/ObjRelationshipHandler.java             |  109 ++
 .../configuration/xml/ProcedureHandler.java     |  114 ++
 .../xml/QueryDescriptorHandler.java             |  191 +++
 .../configuration/xml/RootDataMapHandler.java   |   51 +
 .../configuration/xml/SAXNestedTagHandler.java  |  175 +++
 .../xml/XMLDataChannelDescriptorLoader.java     |  149 +++
 .../configuration/xml/XMLDataMapLoader.java     |  102 ++
 .../org/apache/cayenne/conn/DataSourceInfo.java |   47 +-
 .../java/org/apache/cayenne/exp/Expression.java |   10 +-
 .../java/org/apache/cayenne/map/Attribute.java  |    4 +-
 .../org/apache/cayenne/map/CallbackMap.java     |    6 +-
 .../java/org/apache/cayenne/map/DataMap.java    |  131 +-
 .../org/apache/cayenne/map/DbAttribute.java     |   32 +-
 .../java/org/apache/cayenne/map/DbEntity.java   |   33 +-
 .../java/org/apache/cayenne/map/DbJoin.java     |   24 +-
 .../org/apache/cayenne/map/DbKeyGenerator.java  |   29 +-
 .../org/apache/cayenne/map/DbRelationship.java  |   28 +-
 .../cayenne/map/EJBQLQueryDescriptor.java       |   19 +-
 .../java/org/apache/cayenne/map/Embeddable.java |   20 +-
 .../apache/cayenne/map/EmbeddableAttribute.java |   26 +-
 .../apache/cayenne/map/EmbeddedAttribute.java   |   33 +-
 .../org/apache/cayenne/map/EntityListener.java  |    4 +-
 .../java/org/apache/cayenne/map/MapLoader.java  | 1261 ------------------
 .../org/apache/cayenne/map/ObjAttribute.java    |   32 +-
 .../java/org/apache/cayenne/map/ObjEntity.java  |   86 +-
 .../org/apache/cayenne/map/ObjRelationship.java |   49 +-
 .../java/org/apache/cayenne/map/Procedure.java  |   39 +-
 .../apache/cayenne/map/ProcedureParameter.java  |   47 +-
 .../cayenne/map/ProcedureQueryDescriptor.java   |   36 +-
 .../org/apache/cayenne/map/QueryDescriptor.java |   58 +-
 .../cayenne/map/QueryDescriptorLoader.java      |   32 +-
 .../cayenne/map/SQLTemplateDescriptor.java      |   53 +-
 .../cayenne/map/SelectQueryDescriptor.java      |   46 +-
 .../apache/cayenne/query/BaseQueryMetadata.java |   38 +-
 .../org/apache/cayenne/query/EJBQLQuery.java    |   25 +-
 .../java/org/apache/cayenne/query/Ordering.java |   26 +-
 .../apache/cayenne/query/PrefetchTreeNode.java  |   34 +-
 .../apache/cayenne/query/ProcedureQuery.java    |   52 +-
 .../org/apache/cayenne/query/SQLTemplate.java   |   91 +-
 .../org/apache/cayenne/query/SelectQuery.java   |   72 +-
 .../reflect/LifecycleCallbackEventHandler.java  |   19 +-
 .../cayenne/remote/IncrementalSelectQuery.java  |    5 -
 .../org/apache/cayenne/util/XMLEncoder.java     |  326 +++--
 .../apache/cayenne/util/XMLSerializable.java    |    4 +-
 .../org/apache/cayenne/schema/10/dbimport.xsd   |  106 ++
 .../org/apache/cayenne/schema/10/domain.xsd     |  130 ++
 .../org/apache/cayenne/schema/10/info.xsd       |   29 +
 .../org/apache/cayenne/schema/10/modelMap.xsd   |  356 +++++
 .../XMLDataChannelDescriptorLoaderTest.java     |  165 ---
 .../configuration/xml/BaseHandlerTest.java      |   76 ++
 .../configuration/xml/DbEntityHandlerTest.java  |   82 ++
 .../xml/DbKeyGeneratorHandlerTest.java          |   51 +
 .../xml/DbRelationshipHandlerTest.java          |   58 +
 .../configuration/xml/ObjEntityHandlerTest.java |   86 ++
 .../xml/ObjRelationshipHandlerTest.java         |   61 +
 .../xml/XMLDataChannelDescriptorLoaderTest.java |  171 +++
 .../configuration/xml/XMLDataMapLoaderTest.java |  158 +++
 .../org/apache/cayenne/map/DataMapTest.java     |   41 +-
 .../apache/cayenne/map/MapLoaderLoadTest.java   |  175 ---
 .../org/apache/cayenne/map/MockAttribute.java   |    4 +-
 .../java/org/apache/cayenne/map/MockEntity.java |    4 +-
 .../apache/cayenne/map/MockRelationship.java    |    4 +-
 .../apache/cayenne/map/ObjRelationshipIT.java   |    6 +-
 .../cayenne/map/QueryDescriptorLoaderTest.java  |    6 +-
 .../org/apache/cayenne/query/EJBQLQueryIT.java  |   30 +-
 .../cayenne/unit/di/server/DBCleaner.java       |    2 +-
 .../cayenne/unit/di/server/SchemaBuilder.java   |   16 +-
 .../unit/di/server/ServerCaseModule.java        |    8 +-
 .../src/test/resources/array-type.map.xml       |   32 +-
 .../src/test/resources/binary-pk.map.xml        |   64 +-
 .../src/test/resources/cay-2032.map.xml         |   13 +-
 .../test/resources/cay743/cayenne-domain.xml    |    2 +-
 .../src/test/resources/cay743/map1.map.xml      |    6 +-
 .../src/test/resources/cay743/map2.map.xml      |    6 +-
 .../src/test/resources/cayenne-array-type.xml   |    5 +-
 .../src/test/resources/cayenne-binary-pk.xml    |    5 +-
 .../src/test/resources/cayenne-cay-2032.xml     |    3 +-
 .../src/test/resources/cayenne-compound.xml     |    5 +-
 .../src/test/resources/cayenne-date-time.xml    |    5 +-
 .../src/test/resources/cayenne-delete-rules.xml |    5 +-
 .../src/test/resources/cayenne-embeddable.xml   |    5 +-
 .../src/test/resources/cayenne-empty.xml        |    5 +-
 .../src/test/resources/cayenne-enum.xml         |    3 +-
 .../test/resources/cayenne-extended-type.xml    |    5 +-
 .../src/test/resources/cayenne-generated.xml    |    5 +-
 .../src/test/resources/cayenne-generic.xml      |    5 +-
 .../cayenne-inheritance-single-table1.xml       |    3 +-
 .../resources/cayenne-inheritance-vertical.xml  |    3 +-
 .../src/test/resources/cayenne-inheritance.xml  |    5 +-
 .../cayenne-lifecycle-callbacks-order.xml       |    5 +-
 .../src/test/resources/cayenne-lifecycles.xml   |    5 +-
 .../src/test/resources/cayenne-lob.xml          |    5 +-
 .../src/test/resources/cayenne-locking.xml      |    3 +-
 .../src/test/resources/cayenne-map-to-many.xml  |    5 +-
 .../test/resources/cayenne-meaningful-pk.xml    |    5 +-
 .../src/test/resources/cayenne-misc-types.xml   |    5 +-
 .../cayenne-mixed-persistence-strategy.xml      |    5 +-
 .../src/test/resources/cayenne-multi-tier.xml   |    3 +-
 .../src/test/resources/cayenne-multinode.xml    |    3 +-
 .../src/test/resources/cayenne-no-pk.xml        |    5 +-
 .../test/resources/cayenne-numeric-types.xml    |    5 +-
 .../src/test/resources/cayenne-oneway-rels.xml  |    3 +-
 ...nne.configuration.rop.server.test-config.xml |    4 +-
 .../src/test/resources/cayenne-people.xml       |    3 +-
 .../src/test/resources/cayenne-primitive.xml    |    5 +-
 .../src/test/resources/cayenne-qualified.xml    |    3 +-
 .../resources/cayenne-quoted-identifiers.xml    |    3 +-
 .../src/test/resources/cayenne-reflexive.xml    |    5 +-
 .../cayenne-relationships-activity.xml          |    3 +-
 .../cayenne-relationships-child-master.xml      |    5 +-
 .../resources/cayenne-relationships-clob.xml    |    3 +-
 ...cayenne-relationships-collection-to-many.xml |    5 +-
 .../cayenne-relationships-delete-rules.xml      |    5 +-
 .../cayenne-relationships-flattened.xml         |    3 +-
 .../cayenne-relationships-set-to-many.xml       |    5 +-
 .../cayenne-relationships-to-many-fk.xml        |    5 +-
 .../cayenne-relationships-to-one-fk.xml         |    5 +-
 .../test/resources/cayenne-relationships.xml    |    3 +-
 .../src/test/resources/cayenne-return-types.xml |    5 +-
 .../src/test/resources/cayenne-soft-delete.xml  |    5 +-
 .../src/test/resources/cayenne-sus.xml          |    3 +-
 .../test/resources/cayenne-table-primitives.xml |    5 +-
 .../src/test/resources/cayenne-testmap.xml      |    2 +-
 .../src/test/resources/cayenne-things.xml       |    5 +-
 .../src/test/resources/cayenne-toone.xml        |    5 +-
 .../cayenne-unsupported-distinct-types.xml      |    3 +-
 .../src/test/resources/cayenne-uuid.xml         |    5 +-
 .../test/resources/cayenne-weighted-sort.xml    |    3 +-
 .../src/test/resources/compound.map.xml         |  124 +-
 .../src/test/resources/custom-name-file.xml     |    2 +-
 .../src/test/resources/date-time.map.xml        |   64 +-
 .../src/test/resources/delete-rules.map.xml     |  124 +-
 .../src/test/resources/embeddable.map.xml       |    6 +-
 cayenne-server/src/test/resources/empty.map.xml |   14 +-
 cayenne-server/src/test/resources/enum.map.xml  |    6 +-
 .../src/test/resources/extended-type.map.xml    |   32 +-
 .../src/test/resources/generated.map.xml        |  180 ++-
 .../src/test/resources/generic.map.xml          |    8 +-
 .../resources/inheritance-single-table1.map.xml |   18 +-
 .../test/resources/inheritance-vertical.map.xml |   59 +-
 .../src/test/resources/inheritance.map.xml      |   86 +-
 .../resources/lifecycle-callbacks-order.map.xml |   28 +-
 .../src/test/resources/lifecycles.map.xml       |   28 +-
 cayenne-server/src/test/resources/lob.map.xml   |  102 +-
 .../src/test/resources/locking.map.xml          |   10 +-
 .../src/test/resources/map-db1.map.xml          |    6 +-
 .../src/test/resources/map-db2.map.xml          |   10 +-
 .../src/test/resources/map-to-many.map.xml      |   99 +-
 .../src/test/resources/meaningful-pk.map.xml    |   74 +-
 .../src/test/resources/misc-types.map.xml       |   72 +-
 .../mixed-persistence-strategy.map.xml          |   68 +-
 .../src/test/resources/multi-tier.map.xml       |   23 +-
 cayenne-server/src/test/resources/no-pk.map.xml |   30 +-
 .../src/test/resources/numeric-types.map.xml    |  154 +--
 .../src/test/resources/oneway-rels.map.xml      |   17 +-
 .../configuration/cayenne-testConfig1.xml       |    4 -
 .../configuration/cayenne-testConfig2.xml       |    4 -
 .../configuration/cayenne-testConfig3.xml       |   14 -
 .../configuration/testConfigMap2.map.xml        |    7 -
 .../configuration/testConfigMap3_1.map.xml      |    7 -
 .../configuration/testConfigMap3_2.map.xml      |    7 -
 .../configuration/xml/DbEntityHandlerTest.xml   |   25 +
 .../xml/DbKeyGeneratorHandlerTest.xml           |   24 +
 .../xml/DbRelationshipHandlerTest.xml           |   22 +
 .../configuration/xml/ObjEntityHandlerTest.xml  |   38 +
 .../xml/ObjRelationshipHandlerTest.xml          |   20 +
 .../configuration/xml/cayenne-testConfig1.xml   |    4 +
 .../configuration/xml/cayenne-testConfig2.xml   |    4 +
 .../configuration/xml/cayenne-testConfig3.xml   |   14 +
 .../configuration/xml/testConfigMap2.map.xml    |   26 +
 .../configuration/xml/testConfigMap3_1.map.xml  |   26 +
 .../configuration/xml/testConfigMap3_2.map.xml  |   26 +
 .../configuration/xml/testConfigMap4.map.xml    |  314 +++++
 .../src/test/resources/people.map.xml           |   16 +-
 .../src/test/resources/primitive.map.xml        |   36 +-
 .../src/test/resources/qualified.map.xml        |    8 +-
 .../test/resources/quoted-identifiers.map.xml   |   10 +-
 .../src/test/resources/reflexive.map.xml        |   46 +-
 .../resources/relationships-activity.map.xml    |    6 +-
 .../relationships-child-master.map.xml          |   52 +-
 .../test/resources/relationships-clob.map.xml   |    8 +-
 .../relationships-collection-to-many.map.xml    |   46 +-
 .../relationships-delete-rules.map.xml          |  133 +-
 .../resources/relationships-flattened.map.xml   |   29 +-
 .../resources/relationships-set-to-many.map.xml |   46 +-
 .../resources/relationships-to-many-fk.map.xml  |   92 +-
 .../resources/relationships-to-one-fk.map.xml   |   46 +-
 .../src/test/resources/relationships.map.xml    |   17 +-
 .../src/test/resources/return-types.map.xml     |  180 +--
 .../src/test/resources/soft-delete.map.xml      |   32 +-
 .../src/test/resources/sus-map.map.xml          |    6 +-
 .../src/test/resources/table-primitives.map.xml |   32 +-
 .../src/test/resources/testmap.map.xml          |   39 +-
 .../src/test/resources/things.map.xml           |  216 +--
 cayenne-server/src/test/resources/toone.map.xml |   48 +-
 .../unsupported-distinct-types.map.xml          |   42 +-
 .../resources/url conversion/cayenne-empty.xml  |    4 +
 .../test/resources/url conversion/empty.map.xml |    9 +
 cayenne-server/src/test/resources/uuid.map.xml  |   44 +-
 .../src/test/resources/weighted-sort.map.xml    |   14 +-
 228 files changed, 6936 insertions(+), 4946 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/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
deleted file mode 100644
index 6dc3d52..0000000
--- a/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v6/XMLDataMapLoader_V3_0_0_1.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.project.upgrade.v6;
-
-import java.io.InputStream;
-import java.net.URL;
-
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.MapLoader;
-import org.apache.cayenne.resource.Resource;
-import org.xml.sax.InputSource;
-
-/**
- * @since 3.1
- */
-class XMLDataMapLoader_V3_0_0_1 {
-
-	public DataMap load(Resource configurationResource) throws CayenneRuntimeException {
-
-		MapLoader mapLoader = new MapLoader();
-		URL url = configurationResource.getURL();
-
-		DataMap map;
-
-		try (InputStream in = url.openStream();) {
-
-			map = mapLoader.loadDataMap(new InputSource(in));
-		} catch (Exception e) {
-			throw new CayenneRuntimeException("Error loading configuration from %s", e, url);
-		}
-
-		return map;
-	}
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v8/ProjectUpgrader_V8.java
----------------------------------------------------------------------
diff --git a/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v8/ProjectUpgrader_V8.java b/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v8/ProjectUpgrader_V8.java
deleted file mode 100644
index 461d118..0000000
--- a/cayenne-project/src/main/java/org/apache/cayenne/project/upgrade/v8/ProjectUpgrader_V8.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.project.upgrade.v8;
-
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.di.Injector;
-import org.apache.cayenne.project.upgrade.ProjectUpgrader;
-import org.apache.cayenne.project.upgrade.UpgradeHandler;
-import org.apache.cayenne.resource.Resource;
-
-/**
- * A ProjectUpgrader that handles project upgrades from version 4.0.M3 and 7
- * to version 8.
- */
-public class ProjectUpgrader_V8 implements ProjectUpgrader {
-
-    @Inject
-    protected Injector injector;
-
-    @Override
-    public UpgradeHandler getUpgradeHandler(Resource projectSource) {
-        UpgradeHandler_V8 handler = new UpgradeHandler_V8(projectSource);
-        injector.injectMembers(handler);
-        return handler;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/DataChannelDescriptor.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/DataChannelDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/DataChannelDescriptor.java
index bc3929b..92bd8bb 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/DataChannelDescriptor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/DataChannelDescriptor.java
@@ -40,6 +40,11 @@ public class DataChannelDescriptor implements ConfigurationNode, Serializable, X
 
 	private static final long serialVersionUID = 6567527544207035602L;
 
+	/**
+	 * The namespace in which the data map XML file will be created.
+	 */
+	public static final String SCHEMA_XSD = "http://cayenne.apache.org/schema/10/domain";
+
 	protected String name;
 	protected Map<String, String> properties;
 	protected Collection<DataMap> dataMaps;
@@ -53,58 +58,41 @@ public class DataChannelDescriptor implements ConfigurationNode, Serializable, X
 		nodeDescriptors = new ArrayList<>(3);
 	}
 
-	public void encodeAsXML(XMLEncoder encoder) {
+	@Override
+	public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
 
-		encoder.print("<domain");
-		encoder.printProjectVersion();
-		encoder.println(">");
-
-		encoder.indent(1);
-		boolean breakNeeded = false;
+		encoder.start("domain")
+				.attribute("xmlns", SCHEMA_XSD)
+				.attribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance", true)
+				.attribute("xsi:schemaLocation", SCHEMA_XSD + " " + SCHEMA_XSD + ".xsd", true)
+				.projectVersion();
 
 		if (!properties.isEmpty()) {
-			breakNeeded = true;
-
 			List<String> keys = new ArrayList<>(properties.keySet());
 			Collections.sort(keys);
 
 			for (String key : keys) {
-				encoder.printProperty(key, properties.get(key));
+				encoder.property(key, properties.get(key));
 			}
 		}
 
 		if (!dataMaps.isEmpty()) {
-			if (breakNeeded) {
-				encoder.println();
-			} else {
-				breakNeeded = true;
-			}
-
 			List<DataMap> maps = new ArrayList<>(this.dataMaps);
 			Collections.sort(maps);
 
 			for (DataMap dataMap : maps) {
-
-				encoder.print("<map");
-				encoder.printAttribute("name", dataMap.getName().trim());
-				encoder.println("/>");
+				encoder.start("map").attribute("name", dataMap.getName().trim()).end();
 			}
 		}
 
 		if (!nodeDescriptors.isEmpty()) {
-			if (breakNeeded) {
-				encoder.println();
-			} else {
-				breakNeeded = true;
-			}
-
 			List<DataNodeDescriptor> nodes = new ArrayList<>(nodeDescriptors);
 			Collections.sort(nodes);
-			encoder.print(nodes);
+			encoder.nested(nodes, delegate);
 		}
 
-		encoder.indent(-1);
-		encoder.println("</domain>");
+		delegate.visitDataChannelDescriptor(this);
+		encoder.end();
 	}
 
 	public <T> T acceptVisitor(ConfigurationNodeVisitor<T> visitor) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/DataNodeDescriptor.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/DataNodeDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/DataNodeDescriptor.java
index 7192d0d..6ece619 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/DataNodeDescriptor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/DataNodeDescriptor.java
@@ -81,57 +81,50 @@ public class DataNodeDescriptor implements ConfigurationNode, XMLSerializable,
         this.dataChannelDescriptor = dataChannelDescriptor;
     }
 
+    @Override
     public int compareTo(DataNodeDescriptor o) {
         String o1 = getName();
         String o2 = o.getName();
 
         if (o1 == null) {
             return (o2 != null) ? -1 : 0;
-        }
-        else if (o2 == null) {
+        } else if (o2 == null) {
             return 1;
         }
-        else {
-            return o1.compareTo(o2);
-        }
+        return o1.compareTo(o2);
     }
 
+    @Override
     public <T> T acceptVisitor(ConfigurationNodeVisitor<T> visitor) {
         return visitor.visitDataNodeDescriptor(this);
     }
 
-    public void encodeAsXML(XMLEncoder encoder) {
-        encoder.print("<node");
-        encoder.printlnAttribute("name", name);
-        encoder.indent(1);
-
-        encoder.printlnAttribute("adapter", adapterType);
-        encoder.printlnAttribute("factory", dataSourceFactoryType);
+    @Override
+    public void encodeAsXML(XMLEncoder encoder, ConfigurationNodeVisitor delegate) {
+        encoder.start("node")
+                .attribute("name", name, false)
+                .attribute("adapter", adapterType, true)
+                .attribute("factory", dataSourceFactoryType, true);
         
         if (!XMLPoolingDataSourceFactory.class.getName().equals(dataSourceFactoryType)) {
-            encoder.printlnAttribute("parameters", parameters);
+            encoder.attribute("parameters", parameters);
         }
-        encoder.printlnAttribute("schema-update-strategy", schemaUpdateStrategyType);
-        encoder.println(">");
+        encoder.attribute("schema-update-strategy", schemaUpdateStrategyType, true);
 
         if (!dataMapNames.isEmpty()) {
-
             List<String> names = new ArrayList<>(dataMapNames);
             Collections.sort(names);
-
             for (String mapName : names) {
-                encoder.print("<map-ref");
-                encoder.printAttribute("name", mapName);
-                encoder.println("/>");
+                encoder.start("map-ref").attribute("name", mapName).end();
             }
         }
 
         if (dataSourceDescriptor != null && XMLPoolingDataSourceFactory.class.getName().equals(dataSourceFactoryType)) {
-            dataSourceDescriptor.encodeAsXML(encoder);
+            dataSourceDescriptor.encodeAsXML(encoder, delegate);
         }
 
-        encoder.indent(-1);
-        encoder.println("</node>");
+        delegate.visitDataNodeDescriptor(this);
+        encoder.end();
     }
 
     public String getName() {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/EmptyConfigurationNodeVisitor.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/EmptyConfigurationNodeVisitor.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/EmptyConfigurationNodeVisitor.java
new file mode 100644
index 0000000..84289d2
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/EmptyConfigurationNodeVisitor.java
@@ -0,0 +1,108 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration;
+
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.Embeddable;
+import org.apache.cayenne.map.EmbeddableAttribute;
+import org.apache.cayenne.map.ObjAttribute;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.map.Procedure;
+import org.apache.cayenne.map.ProcedureParameter;
+import org.apache.cayenne.map.QueryDescriptor;
+
+/**
+ * @since 4.1
+ */
+public class EmptyConfigurationNodeVisitor implements ConfigurationNodeVisitor<Void> {
+    @Override
+    public Void visitDataChannelDescriptor(DataChannelDescriptor channelDescriptor) {
+        return null;
+    }
+
+    @Override
+    public Void visitDataNodeDescriptor(DataNodeDescriptor nodeDescriptor) {
+        return null;
+    }
+
+    @Override
+    public Void visitDataMap(DataMap dataMap) {
+        return null;
+    }
+
+    @Override
+    public Void visitObjEntity(ObjEntity entity) {
+        return null;
+    }
+
+    @Override
+    public Void visitDbEntity(DbEntity entity) {
+        return null;
+    }
+
+    @Override
+    public Void visitEmbeddable(Embeddable embeddable) {
+        return null;
+    }
+
+    @Override
+    public Void visitEmbeddableAttribute(EmbeddableAttribute attribute) {
+        return null;
+    }
+
+    @Override
+    public Void visitObjAttribute(ObjAttribute attribute) {
+        return null;
+    }
+
+    @Override
+    public Void visitDbAttribute(DbAttribute attribute) {
+        return null;
+    }
+
+    @Override
+    public Void visitObjRelationship(ObjRelationship relationship) {
+        return null;
+    }
+
+    @Override
+    public Void visitDbRelationship(DbRelationship relationship) {
+        return null;
+    }
+
+    @Override
+    public Void visitProcedure(Procedure procedure) {
+        return null;
+    }
+
+    @Override
+    public Void visitProcedureParameter(ProcedureParameter parameter) {
+        return null;
+    }
+
+    @Override
+    public Void visitQuery(QueryDescriptor query) {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/SAXNestedTagHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/SAXNestedTagHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/SAXNestedTagHandler.java
deleted file mode 100644
index 3891e83..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/SAXNestedTagHandler.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.configuration;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.Locator;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.DefaultHandler;
-
-/**
- * A superclass of nested tag handlers for parsing of XML documents with SAX.
- * 
- * @since 3.1
- */
-public class SAXNestedTagHandler extends DefaultHandler {
-
-    private final static Locator NOOP_LOCATOR = new Locator() {
-
-        public int getColumnNumber() {
-            return -1;
-        }
-
-        public int getLineNumber() {
-            return -1;
-        }
-
-        public String getPublicId() {
-            return "<unknown>";
-        }
-
-        public String getSystemId() {
-            return "<unknown>";
-        }
-    };
-
-    protected XMLReader parser;
-    protected ContentHandler parentHandler;
-    protected Locator locator;
-
-    public SAXNestedTagHandler(XMLReader parser, SAXNestedTagHandler parentHandler) {
-        this.parentHandler = parentHandler;
-        this.parser = parser;
-
-        if (parentHandler != null) {
-            locator = parentHandler.locator;
-        }
-
-        if (locator == null) {
-            locator = NOOP_LOCATOR;
-        }
-    }
-
-    protected String unexpectedTagMessage(String tagFound, String... tagsExpected) {
-
-        List<String> expected = tagsExpected != null
-                ? Arrays.asList(tagsExpected)
-                : Collections.<String> emptyList();
-
-        return String
-                .format(
-                        "tag <%s> is unexpected at [%d,%d]. The following tags are allowed here: %s",
-                        tagFound,
-                        locator.getColumnNumber(),
-                        locator.getLineNumber(),
-                        expected);
-    }
-
-    protected ContentHandler createChildTagHandler(
-            String namespaceURI,
-            String localName,
-            String qName,
-            Attributes attributes) {
-
-        // loose handling of unrecognized tags - just ignore them
-        return new SAXNestedTagHandler(parser, this);
-    }
-
-    protected void stop() {
-        // pop self from the handler stack
-        parser.setContentHandler(parentHandler);
-    }
-
-    @Override
-    public final void startElement(
-            String namespaceURI,
-            String localName,
-            String qName,
-            Attributes attributes) throws SAXException {
-
-        // push child handler to the stack...
-        ContentHandler childHandler = createChildTagHandler(
-                namespaceURI,
-                localName,
-                qName,
-                attributes);
-        parser.setContentHandler(childHandler);
-    }
-
-    @Override
-    public void endElement(String namespaceURI, String localName, String qName)
-            throws SAXException {
-        stop();
-    }
-
-    @Override
-    public void setDocumentLocator(Locator locator) {
-        this.locator = locator;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/XMLDataChannelDescriptorLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/XMLDataChannelDescriptorLoader.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/XMLDataChannelDescriptorLoader.java
deleted file mode 100644
index ad9341a..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/XMLDataChannelDescriptorLoader.java
+++ /dev/null
@@ -1,389 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.configuration;
-
-import org.apache.cayenne.ConfigurationException;
-import org.apache.cayenne.conn.DataSourceInfo;
-import org.apache.cayenne.di.AdhocObjectFactory;
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.resource.Resource;
-import org.apache.cayenne.util.Util;
-import org.slf4j.LoggerFactory;
-import org.slf4j.Logger;
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.InputSource;
-import org.xml.sax.XMLReader;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-/**
- * @since 3.1
- */
-public class XMLDataChannelDescriptorLoader implements DataChannelDescriptorLoader {
-
-	private static Logger logger = LoggerFactory.getLogger(XMLDataChannelDescriptorLoader.class);
-
-	static final String DOMAIN_TAG = "domain";
-	static final String MAP_TAG = "map";
-	static final String NODE_TAG = "node";
-	static final String PROPERTY_TAG = "property";
-	static final String MAP_REF_TAG = "map-ref";
-	static final String DATA_SOURCE_TAG = "data-source";
-
-	/**
-	 * @deprecated the caller should use password resolving strategy instead of
-	 *             resolving the password on the spot. For one thing this can be
-	 *             used in the Modeler and no password may be available.
-	 */
-	@Deprecated
-	private static String passwordFromURL(URL url) {
-		InputStream inputStream = null;
-		String password = null;
-
-		try {
-			inputStream = url.openStream();
-			password = passwordFromInputStream(inputStream);
-		} catch (IOException exception) {
-			// Log the error while trying to open the stream. A null
-			// password will be returned as a result.
-			logger.warn(exception.getMessage(), exception);
-		}
-
-		return password;
-	}
-
-	/**
-	 * @deprecated the caller should use password resolving strategy instead of
-	 *             resolving the password on the spot. For one thing this can be
-	 *             used in the Modeler and no password may be available.
-	 */
-	@Deprecated
-	private static String passwordFromInputStream(InputStream inputStream) {
-		String password = null;
-
-		try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));) {
-
-			password = bufferedReader.readLine();
-		} catch (IOException exception) {
-			logger.warn(exception.getMessage(), exception);
-		} finally {
-
-			try {
-				inputStream.close();
-			} catch (IOException exception) {
-			}
-		}
-
-		return password;
-	}
-
-	@Inject
-	protected DataMapLoader dataMapLoader;
-
-	@Inject
-	protected ConfigurationNameMapper nameMapper;
-
-	@Inject
-	protected AdhocObjectFactory objectFactory;
-
-	@Override
-	public ConfigurationTree<DataChannelDescriptor> load(Resource configurationResource) throws ConfigurationException {
-
-		if (configurationResource == null) {
-			throw new NullPointerException("Null configurationResource");
-		}
-
-		URL configurationURL = configurationResource.getURL();
-
-		logger.info("Loading XML configuration resource from " + configurationURL);
-
-		DataChannelDescriptor descriptor = new DataChannelDescriptor();
-		descriptor.setConfigurationSource(configurationResource);
-		descriptor.setName(nameMapper.configurationNodeName(DataChannelDescriptor.class, configurationResource));
-
-		DataChannelHandler rootHandler;
-
-		InputStream in = null;
-
-		try {
-			in = configurationURL.openStream();
-			XMLReader parser = Util.createXmlReader();
-
-			rootHandler = new DataChannelHandler(descriptor, parser);
-			parser.setContentHandler(rootHandler);
-			parser.setErrorHandler(rootHandler);
-			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);
-			}
-		}
-
-		// TODO: andrus 03/10/2010 - actually provide load failures here...
-		return new ConfigurationTree<DataChannelDescriptor>(descriptor, null);
-	}
-
-	final class DataChannelHandler extends SAXNestedTagHandler {
-
-		private DataChannelDescriptor descriptor;
-
-		DataChannelHandler(DataChannelDescriptor dataChannelDescriptor, XMLReader parser) {
-			super(parser, null);
-			this.descriptor = dataChannelDescriptor;
-		}
-
-		@Override
-		protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String name,
-				Attributes attributes) {
-
-			if (localName.equals(DOMAIN_TAG)) {
-				return new DataChannelChildrenHandler(parser, this);
-			}
-
-			logger.info(unexpectedTagMessage(localName, DOMAIN_TAG));
-			return super.createChildTagHandler(namespaceURI, localName, name, attributes);
-		}
-	}
-
-	final class DataChannelChildrenHandler extends SAXNestedTagHandler {
-
-		private DataChannelDescriptor descriptor;
-
-		DataChannelChildrenHandler(XMLReader parser, DataChannelHandler parentHandler) {
-			super(parser, parentHandler);
-			this.descriptor = parentHandler.descriptor;
-		}
-
-		@Override
-		protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String name,
-				Attributes attributes) {
-
-			if (localName.equals(PROPERTY_TAG)) {
-
-				String key = attributes.getValue("", "name");
-				String value = attributes.getValue("", "value");
-				if (key != null && value != null) {
-					descriptor.getProperties().put(key, value);
-				}
-			} else if (localName.equals(MAP_TAG)) {
-
-				String dataMapName = attributes.getValue("", "name");
-				Resource baseResource = descriptor.getConfigurationSource();
-
-				String dataMapLocation = nameMapper.configurationLocation(DataMap.class, dataMapName);
-
-				Resource dataMapResource = baseResource.getRelativeResource(dataMapLocation);
-
-				logger.info("Loading XML DataMap resource from " + dataMapResource.getURL());
-
-				DataMap dataMap = dataMapLoader.load(dataMapResource);
-				dataMap.setName(dataMapName);
-				dataMap.setLocation(dataMapLocation);
-				dataMap.setConfigurationSource(dataMapResource);
-				dataMap.setDataChannelDescriptor(descriptor);
-
-                descriptor.getDataMaps().add(dataMap);
-            } else if (localName.equals(NODE_TAG)) {
-
-				String nodeName = attributes.getValue("", "name");
-				if (nodeName == null) {
-					throw new ConfigurationException("Error: <node> without 'name'.");
-				}
-
-				DataNodeDescriptor nodeDescriptor = new DataNodeDescriptor();
-				nodeDescriptor.setConfigurationSource(descriptor.getConfigurationSource());
-				descriptor.getNodeDescriptors().add(nodeDescriptor);
-
-				nodeDescriptor.setName(nodeName);
-				nodeDescriptor.setAdapterType(attributes.getValue("", "adapter"));
-
-				String parameters = attributes.getValue("", "parameters");
-				nodeDescriptor.setParameters(parameters);
-
-				String dataSourceFactory = attributes.getValue("", "factory");
-				nodeDescriptor.setDataSourceFactoryType(dataSourceFactory);
-				nodeDescriptor.setSchemaUpdateStrategyType(attributes.getValue("", "schema-update-strategy"));
-				nodeDescriptor.setDataChannelDescriptor(descriptor);
-
-				return new DataNodeChildrenHandler(parser, this, nodeDescriptor);
-			}
-
-			return super.createChildTagHandler(namespaceURI, localName, name, attributes);
-		}
-	}
-
-	final class DataNodeChildrenHandler extends SAXNestedTagHandler {
-
-		private DataNodeDescriptor nodeDescriptor;
-
-		DataNodeChildrenHandler(XMLReader parser, SAXNestedTagHandler parentHandler, DataNodeDescriptor nodeDescriptor) {
-			super(parser, parentHandler);
-			this.nodeDescriptor = nodeDescriptor;
-		}
-
-		@Override
-		protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String name,
-				Attributes attributes) {
-
-			if (localName.equals(MAP_REF_TAG)) {
-
-				String mapName = attributes.getValue("", "name");
-				nodeDescriptor.getDataMapNames().add(mapName);
-			} else if (localName.equals(DATA_SOURCE_TAG)) {
-
-				DataSourceInfo dataSourceDescriptor = new DataSourceInfo();
-				nodeDescriptor.setDataSourceDescriptor(dataSourceDescriptor);
-				return new DataSourceChildrenHandler(parser, this, dataSourceDescriptor);
-			}
-
-			return super.createChildTagHandler(namespaceURI, localName, name, attributes);
-		}
-	}
-
-	class DataSourceChildrenHandler extends SAXNestedTagHandler {
-
-		private DataSourceInfo dataSourceDescriptor;
-
-		DataSourceChildrenHandler(XMLReader parser, DataNodeChildrenHandler parentHandler,
-				DataSourceInfo dataSourceDescriptor) {
-			super(parser, parentHandler);
-			this.dataSourceDescriptor = dataSourceDescriptor;
-		}
-
-		@Override
-		protected ContentHandler createChildTagHandler(String namespaceURI, String localName, String name,
-				Attributes attributes) {
-
-			if (localName.equals("driver")) {
-				String className = attributes.getValue("", "value");
-				dataSourceDescriptor.setJdbcDriver(className);
-			} else if (localName.equals("login")) {
-
-				logger.info("loading user name and password.");
-
-				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);
-				}
-
-				String encoderType = dataSourceDescriptor.getPasswordEncoderClass();
-				PasswordEncoding passwordEncoder = null;
-				if (encoderType != null) {
-					passwordEncoder = objectFactory.newInstance(PasswordEncoding.class, encoderType);
-				}
-
-				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 {
-							logger.error("Could not find resource in CLASSPATH: " + passwordSource);
-						}
-					} else if (passwordLocation.equals(DataSourceInfo.PASSWORD_LOCATION_URL)) {
-						try {
-							password = passwordFromURL(new URL(passwordSource));
-						} catch (MalformedURLException exception) {
-							logger.warn(exception.getMessage(), exception);
-						}
-					} 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) {
-								logger.warn(exception.getMessage(), exception);
-							} catch (InterruptedException exception) {
-								logger.warn(exception.getMessage(), exception);
-							}
-						}
-					}
-				}
-
-				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) {
-						logger.info("Non-numeric 'min' attribute", 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) {
-						logger.info("Non-numeric 'max' attribute", 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/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/XMLDataMapLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/XMLDataMapLoader.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/XMLDataMapLoader.java
deleted file mode 100644
index d5cce91..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/XMLDataMapLoader.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.configuration;
-
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.MapLoader;
-import org.apache.cayenne.resource.Resource;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.xml.sax.InputSource;
-
-import java.io.InputStream;
-import java.net.URL;
-
-/**
- * @since 3.1
- */
-public class XMLDataMapLoader implements DataMapLoader {
-
-    private static Logger logger = LoggerFactory.getLogger(XMLDataMapLoader.class);
-
-    public DataMap load(Resource configurationResource) throws CayenneRuntimeException {
-
-        // TODO: andrus 11.27.2009 - deprecate MapLoader and implement a loader
-        // here. MapLoader is in the wrong place, exposes ContentHandler methods and
-        // implements if/else contextless matching of tags... should use something like
-        // SAXNestedTagHandler instead.
-        MapLoader mapLoader = new MapLoader();
-        URL url = configurationResource.getURL();
-
-        try (InputStream in = url.openStream()) {
-            return mapLoader.loadDataMap(new InputSource(in));
-        } catch (Exception e) {
-            throw new CayenneRuntimeException(
-                    "Error loading configuration from %s",
-                    e,
-                    url);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
index 962bba9..c86371d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
@@ -71,8 +71,12 @@ import org.apache.cayenne.configuration.DefaultRuntimeProperties;
 import org.apache.cayenne.configuration.ObjectContextFactory;
 import org.apache.cayenne.configuration.ObjectStoreFactory;
 import org.apache.cayenne.configuration.RuntimeProperties;
-import org.apache.cayenne.configuration.XMLDataChannelDescriptorLoader;
-import org.apache.cayenne.configuration.XMLDataMapLoader;
+import org.apache.cayenne.configuration.xml.DataChannelMetaData;
+import org.apache.cayenne.configuration.xml.DefaultHandlerFactory;
+import org.apache.cayenne.configuration.xml.HandlerFactory;
+import org.apache.cayenne.configuration.xml.NoopDataChannelMetaData;
+import org.apache.cayenne.configuration.xml.XMLDataChannelDescriptorLoader;
+import org.apache.cayenne.configuration.xml.XMLDataMapLoader;
 import org.apache.cayenne.dba.db2.DB2Sniffer;
 import org.apache.cayenne.dba.derby.DerbySniffer;
 import org.apache.cayenne.dba.firebird.FirebirdSniffer;
@@ -403,5 +407,8 @@ public class ServerModule implements Module {
         binder.bind(RowReaderFactory.class).to(DefaultRowReaderFactory.class);
 
         binder.bind(SQLTemplateProcessor.class).to(VelocitySQLTemplateProcessor.class);
+
+        binder.bind(HandlerFactory.class).to(DefaultHandlerFactory.class);
+        binder.bind(DataChannelMetaData.class).to(NoopDataChannelMetaData.class);
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataChannelChildrenHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataChannelChildrenHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataChannelChildrenHandler.java
new file mode 100644
index 0000000..9dc65af
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataChannelChildrenHandler.java
@@ -0,0 +1,133 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.ConfigurationException;
+import org.apache.cayenne.configuration.DataChannelDescriptor;
+import org.apache.cayenne.configuration.DataNodeDescriptor;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.resource.Resource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+
+/**
+ * @since 4.1
+ */
+final class DataChannelChildrenHandler extends SAXNestedTagHandler {
+
+    private static Logger logger = LoggerFactory.getLogger(XMLDataChannelDescriptorLoader.class);
+
+    static final String OLD_MAP_TAG = "map";
+    static final String NODE_TAG = "node";
+    static final String PROPERTY_TAG = "property";
+    static final String DATA_MAP_TAG = "data-map";
+
+
+    private XMLDataChannelDescriptorLoader xmlDataChannelDescriptorLoader;
+    private DataChannelDescriptor descriptor;
+
+    private DataNodeDescriptor nodeDescriptor;
+
+    DataChannelChildrenHandler(XMLDataChannelDescriptorLoader xmlDataChannelDescriptorLoader, DataChannelHandler parentHandler) {
+        super(parentHandler);
+        this.xmlDataChannelDescriptorLoader = xmlDataChannelDescriptorLoader;
+        this.descriptor = parentHandler.descriptor;
+    }
+
+    @Override
+    protected boolean processElement(String namespaceURI, String localName, Attributes attributes) {
+        switch (localName) {
+            case PROPERTY_TAG:
+                addProperty(attributes);
+                return true;
+
+            case OLD_MAP_TAG:
+                addMap(attributes);
+                return true;
+
+            case NODE_TAG:
+                addNode(attributes);
+                return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    protected ContentHandler createChildTagHandler(String namespaceURI, String localName,
+                                                   String name, Attributes attributes) {
+        if (NODE_TAG.equals(localName)) {
+            nodeDescriptor = new DataNodeDescriptor();
+            return new DataNodeChildrenHandler(xmlDataChannelDescriptorLoader, this, nodeDescriptor);
+        }
+
+        if (DATA_MAP_TAG.equals(localName)) {
+            return new DataMapHandler(loaderContext);
+        }
+
+        return super.createChildTagHandler(namespaceURI, localName, name, attributes);
+    }
+
+    private void addProperty(Attributes attributes) {
+        String key = attributes.getValue("name");
+        String value = attributes.getValue("value");
+        if (key != null && value != null) {
+            descriptor.getProperties().put(key, value);
+        }
+    }
+
+    private void addMap(Attributes attributes) {
+        String dataMapName = attributes.getValue("name");
+        Resource baseResource = descriptor.getConfigurationSource();
+
+        String dataMapLocation = xmlDataChannelDescriptorLoader.nameMapper.configurationLocation(DataMap.class, dataMapName);
+
+        Resource dataMapResource = baseResource.getRelativeResource(dataMapLocation);
+
+        logger.info("Loading XML DataMap resource from " + dataMapResource.getURL());
+
+        DataMap dataMap = xmlDataChannelDescriptorLoader.dataMapLoader.load(dataMapResource);
+        dataMap.setName(dataMapName);
+        dataMap.setLocation(dataMapLocation);
+        dataMap.setConfigurationSource(dataMapResource);
+        dataMap.setDataChannelDescriptor(descriptor);
+
+        descriptor.getDataMaps().add(dataMap);
+    }
+
+    private void addNode(Attributes attributes) {
+        String nodeName = attributes.getValue("name");
+        if (nodeName == null) {
+            throw new ConfigurationException("Error: <node> without 'name'.");
+        }
+
+        nodeDescriptor.setConfigurationSource(descriptor.getConfigurationSource());
+        nodeDescriptor.setName(nodeName);
+        nodeDescriptor.setAdapterType(attributes.getValue("adapter"));
+        nodeDescriptor.setParameters(attributes.getValue("parameters"));
+        nodeDescriptor.setDataSourceFactoryType(attributes.getValue("factory"));
+        nodeDescriptor.setSchemaUpdateStrategyType(attributes.getValue("schema-update-strategy"));
+        nodeDescriptor.setDataChannelDescriptor(descriptor);
+
+        descriptor.getNodeDescriptors().add(nodeDescriptor);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataChannelHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataChannelHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataChannelHandler.java
new file mode 100644
index 0000000..e5e4c71
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataChannelHandler.java
@@ -0,0 +1,78 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.configuration.DataChannelDescriptor;
+import org.apache.cayenne.util.LocalizedStringsHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * @since 4.1
+ */
+final class DataChannelHandler extends SAXNestedTagHandler {
+
+    private static Logger logger = LoggerFactory.getLogger(XMLDataChannelDescriptorLoader.class);
+
+    static final String DOMAIN_TAG = "domain";
+
+    private XMLDataChannelDescriptorLoader xmlDataChannelDescriptorLoader;
+    DataChannelDescriptor descriptor;
+
+    DataChannelHandler(XMLDataChannelDescriptorLoader xmlDataChannelDescriptorLoader, DataChannelDescriptor dataChannelDescriptor, LoaderContext loaderContext) {
+        super(loaderContext);
+        this.xmlDataChannelDescriptorLoader = xmlDataChannelDescriptorLoader;
+        this.descriptor = dataChannelDescriptor;
+    }
+
+    @Override
+    protected boolean processElement(String namespaceURI, String localName, Attributes attributes) throws SAXException {
+        switch (localName) {
+            case DOMAIN_TAG:
+                validateVersion(attributes);
+                return true;
+        }
+        return super.processElement(namespaceURI, localName, attributes);
+    }
+
+    protected void validateVersion(Attributes attributes) {
+        String version = attributes.getValue("project-version");
+        if(!XMLDataChannelDescriptorLoader.CURRENT_PROJECT_VERSION.equals(version)) {
+            throw new CayenneRuntimeException("Unsupported project version: %s, please upgrade project using Modeler v%s",
+                    version, LocalizedStringsHandler.getString("cayenne.version"));
+        }
+    }
+
+    @Override
+    protected ContentHandler createChildTagHandler(String namespaceURI, String localName,
+                                                   String name, Attributes attributes) {
+
+        if (localName.equals(DOMAIN_TAG)) {
+            return new DataChannelChildrenHandler(xmlDataChannelDescriptorLoader, this);
+        }
+
+        logger.info(unexpectedTagMessage(localName, DOMAIN_TAG));
+        return super.createChildTagHandler(namespaceURI, localName, name, attributes);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataChannelMetaData.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataChannelMetaData.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataChannelMetaData.java
new file mode 100644
index 0000000..3e0cd20
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataChannelMetaData.java
@@ -0,0 +1,64 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.configuration.ConfigurationNode;
+
+/**
+ * <p>
+ * Storage for all kind of meta data that is not required for runtime.
+ * </p>
+ * <p>
+ * Currently used by Modeler and cli tools (e.g. Maven, Ant and Gradle) to store project extra data.
+ * </p>
+ *
+ * <p>
+ * Usage: <pre>
+ *      // attach custom information to data map
+ *      metaData.add(dataMap, myObject);
+ *
+ *      // read data
+ *      MyObject obj = metaData.get(dataMap, MyObject.class);
+ * </pre>
+ * </p>
+ *
+ * @since 4.1
+ */
+public interface DataChannelMetaData {
+
+    /**
+     * Store data for object.
+     *
+     * @param key object for which we want to store data
+     * @param value data to store
+     */
+    void add(ConfigurationNode key, Object value);
+
+    /**
+     *
+     * Get meta data for object
+     *
+     * @param key object for wich we want meta data
+     * @param type meta data type class
+     * @param <T> meta data type
+     * @return value or {@code null} if no data available
+     */
+    <T> T get(ConfigurationNode key, Class<T> type);
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataMapHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataMapHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataMapHandler.java
new file mode 100644
index 0000000..ea508f5
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataMapHandler.java
@@ -0,0 +1,142 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.cayenne.map.DataMap;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * @since 4.1
+ */
+public class DataMapHandler extends NamespaceAwareNestedTagHandler {
+
+    /* This constants must be in sync with dataMap.xsd schema */
+    private static final String DATA_MAP_TAG         = "data-map";
+    private static final String PROPERTY_TAG         = "property";
+    private static final String DB_ENTITY_TAG        = "db-entity";
+    private static final String OBJ_ENTITY_TAG       = "obj-entity";
+    private static final String DB_RELATIONSHIP_TAG  = "db-relationship";
+    private static final String OBJ_RELATIONSHIP_TAG = "obj-relationship";
+    private static final String EMBEDDABLE_TAG       = "embeddable";
+    private static final String PROCEDURE_TAG        = "procedure";
+    private static final String QUERY_TAG            = "query";
+
+    public static final String TRUE = "true";
+
+    private DataMap dataMap;
+
+    private Map<String, Object> mapProperties;
+
+    public DataMapHandler(NamespaceAwareNestedTagHandler parentHandler) {
+        super(parentHandler);
+    }
+
+    public DataMapHandler(LoaderContext loaderContext) {
+        super(loaderContext);
+        setTargetNamespace(DataMap.SCHEMA_XSD);
+    }
+
+    @Override
+    protected boolean processElement(String namespaceURI, String localName,
+                                     Attributes attributes) throws SAXException {
+        switch (localName) {
+            case PROPERTY_TAG:
+                addProperty(attributes);
+                return true;
+
+            case DATA_MAP_TAG:
+                this.dataMap = new DataMap();
+                return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    protected ContentHandler createChildTagHandler(String namespaceURI, String localName,
+                                                   String qName, Attributes attributes) {
+
+        if(namespaceURI.equals(targetNamespace)) {
+            switch (localName) {
+                case DB_ENTITY_TAG:
+                    return new DbEntityHandler(this, dataMap);
+
+                case OBJ_ENTITY_TAG:
+                    return new ObjEntityHandler(this, dataMap);
+
+                case DB_RELATIONSHIP_TAG:
+                    return new DbRelationshipHandler(this, dataMap);
+
+                case OBJ_RELATIONSHIP_TAG:
+                    return new ObjRelationshipHandler(this, dataMap);
+
+                case PROCEDURE_TAG:
+                    return new ProcedureHandler(this, dataMap);
+
+                case QUERY_TAG:
+                    return new QueryDescriptorHandler(this, dataMap);
+
+                case EMBEDDABLE_TAG:
+                    return new EmbeddableHandler(this, dataMap);
+            }
+        }
+
+        return super.createChildTagHandler(namespaceURI, localName, qName, attributes);
+    }
+
+    @Override
+    protected void beforeScopeEnd() {
+        dataMap.initWithProperties(mapProperties);
+        loaderContext.dataMapLoaded(dataMap);
+    }
+
+    private void addProperty(Attributes attributes) throws SAXException {
+        String name = attributes.getValue("name");
+        if (null == name) {
+            throw new SAXException("MapLoader::processStartDataMapProperty(), no property name.");
+        }
+
+        String value = attributes.getValue("value");
+        if (null == value) {
+            throw new SAXException("MapLoader::processStartDataMapProperty(), no property value.");
+        }
+
+        // special meaning for <property name="name" .../>
+        if("name".equals(name)) {
+            dataMap.setName(value);
+            return;
+        }
+
+        if (mapProperties == null) {
+            mapProperties = new TreeMap<>();
+        }
+
+        mapProperties.put(name, value);
+    }
+
+    public DataMap getDataMap() {
+        return dataMap;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataMapLoaderListener.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataMapLoaderListener.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataMapLoaderListener.java
new file mode 100644
index 0000000..c9f4ccd
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataMapLoaderListener.java
@@ -0,0 +1,31 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.map.DataMap;
+
+/**
+ * @since 4.1
+ */
+public interface DataMapLoaderListener {
+
+    void onDataMapLoaded(DataMap dataMap);
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataNodeChildrenHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataNodeChildrenHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataNodeChildrenHandler.java
new file mode 100644
index 0000000..b98bd73
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataNodeChildrenHandler.java
@@ -0,0 +1,71 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import org.apache.cayenne.configuration.DataNodeDescriptor;
+import org.apache.cayenne.conn.DataSourceInfo;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+
+/**
+ * @since 4.1
+ */
+final class DataNodeChildrenHandler extends SAXNestedTagHandler {
+
+    static final String DATA_SOURCE_TAG = "data-source";
+    static final String MAP_REF_TAG = "map-ref";
+
+    private XMLDataChannelDescriptorLoader xmlDataChannelDescriptorLoader;
+    private DataNodeDescriptor nodeDescriptor;
+
+    private DataSourceInfo dataSourceDescriptor;
+
+    DataNodeChildrenHandler(XMLDataChannelDescriptorLoader xmlDataChannelDescriptorLoader, SAXNestedTagHandler parentHandler, DataNodeDescriptor nodeDescriptor) {
+        super(parentHandler);
+        this.xmlDataChannelDescriptorLoader = xmlDataChannelDescriptorLoader;
+        this.nodeDescriptor = nodeDescriptor;
+    }
+
+    @Override
+    protected boolean processElement(String namespaceURI, String localName, Attributes attributes) {
+        switch (localName) {
+            case MAP_REF_TAG:
+                nodeDescriptor.getDataMapNames().add(attributes.getValue("name"));
+                return true;
+
+            case DATA_SOURCE_TAG:
+                nodeDescriptor.setDataSourceDescriptor(dataSourceDescriptor);
+                return true;
+        }
+
+        return false;
+    }
+
+    @Override
+    protected ContentHandler createChildTagHandler(String namespaceURI, String localName,
+                                                   String name, Attributes attributes) {
+        if (DATA_SOURCE_TAG.equals(localName)) {
+            dataSourceDescriptor = new DataSourceInfo();
+            return new DataSourceChildrenHandler(xmlDataChannelDescriptorLoader, this, dataSourceDescriptor);
+        }
+
+        return super.createChildTagHandler(namespaceURI, localName, name, attributes);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/c58b6f40/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataSourceChildrenHandler.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataSourceChildrenHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataSourceChildrenHandler.java
new file mode 100644
index 0000000..b860b1f
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/xml/DataSourceChildrenHandler.java
@@ -0,0 +1,175 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.configuration.xml;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.cayenne.ConfigurationException;
+import org.apache.cayenne.configuration.PasswordEncoding;
+import org.apache.cayenne.conn.DataSourceInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.Attributes;
+
+/**
+ * @since 4.1
+ */
+class DataSourceChildrenHandler extends SAXNestedTagHandler {
+
+    private static Logger logger = LoggerFactory.getLogger(XMLDataChannelDescriptorLoader.class);
+
+    static final String DRIVER_TAG = "driver";
+    static final String LOGIN_TAG = "login";
+    static final String URL_TAG = "url";
+    static final String CONNECTION_POOL_TAG = "connectionPool";
+
+
+    private XMLDataChannelDescriptorLoader xmlDataChannelDescriptorLoader;
+    private DataSourceInfo dataSourceDescriptor;
+
+    DataSourceChildrenHandler(XMLDataChannelDescriptorLoader xmlDataChannelDescriptorLoader,
+                              DataNodeChildrenHandler parentHandler,
+                              DataSourceInfo dataSourceDescriptor) {
+        super(parentHandler);
+        this.xmlDataChannelDescriptorLoader = xmlDataChannelDescriptorLoader;
+        this.dataSourceDescriptor = dataSourceDescriptor;
+    }
+
+    @Override
+    protected boolean processElement(String namespaceURI, String localName, Attributes attributes) {
+        switch (localName) {
+            case DRIVER_TAG:
+                dataSourceDescriptor.setJdbcDriver(attributes.getValue("value"));
+                return true;
+
+            case LOGIN_TAG:
+                configureCredentials(attributes);
+                return true;
+
+            case URL_TAG:
+                dataSourceDescriptor.setDataSourceUrl(attributes.getValue("value"));
+                return true;
+
+            case CONNECTION_POOL_TAG:
+                configureConnectionPool(attributes);
+                return true;
+        }
+
+        return false;
+    }
+
+    void configureCredentials(Attributes attributes) {
+        logger.info("loading user name and password.");
+
+        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);
+        }
+
+        String encoderType = dataSourceDescriptor.getPasswordEncoderClass();
+        PasswordEncoding passwordEncoder = null;
+        if (encoderType != null) {
+            passwordEncoder = xmlDataChannelDescriptorLoader.objectFactory.newInstance(PasswordEncoding.class, encoderType);
+        }
+
+        if (passwordLocation != null) {
+            switch (passwordLocation) {
+                case DataSourceInfo.PASSWORD_LOCATION_CLASSPATH:
+
+                    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+                    URL url = classLoader.getResource(username);
+                    if (url != null) {
+                        password = XMLDataChannelDescriptorLoader.passwordFromURL(url);
+                    } else {
+                        logger.error("Could not find resource in CLASSPATH: " + passwordSource);
+                    }
+                    break;
+                case DataSourceInfo.PASSWORD_LOCATION_URL:
+                    try {
+                        password = XMLDataChannelDescriptorLoader.passwordFromURL(new URL(passwordSource));
+                    } catch (MalformedURLException exception) {
+                        logger.warn(exception.getMessage(), exception);
+                    }
+                    break;
+                case DataSourceInfo.PASSWORD_LOCATION_EXECUTABLE:
+                    if (passwordSource != null) {
+                        try {
+                            Process process = Runtime.getRuntime().exec(passwordSource);
+                            password = XMLDataChannelDescriptorLoader.passwordFromInputStream(process.getInputStream());
+                            process.waitFor();
+                        } catch (IOException | InterruptedException exception) {
+                            logger.warn(exception.getMessage(), exception);
+                        }
+                    }
+                    break;
+            }
+        }
+
+        if (password != null && passwordEncoder != null) {
+            dataSourceDescriptor.setPassword(passwordEncoder.decodePassword(password, encoderKey));
+        }
+    }
+
+    void configureConnectionPool(Attributes attributes) {
+        String min = attributes.getValue("min");
+        if (min != null) {
+            try {
+                dataSourceDescriptor.setMinConnections(Integer.parseInt(min));
+            } catch (NumberFormatException nfex) {
+                logger.info("Non-numeric 'min' attribute", 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) {
+                logger.info("Non-numeric 'max' attribute", nfex);
+                throw new ConfigurationException("Non-numeric 'max' attribute '%s'", nfex, max);
+            }
+        }
+    }
+}


Mime
View raw message