Add cayenne-java-modules example
diff --git a/cayenne-java-modules/.gitignore b/cayenne-java-modules/.gitignore
new file mode 100644
index 0000000..bb172de
--- /dev/null
+++ b/cayenne-java-modules/.gitignore
@@ -0,0 +1,15 @@
+target
+out
+.classpath
+.project
+.settings/
+.externalToolBuilders
+.DS_Store
+derby.log
+.metadata
+bin/
+.idea
+*.iml
+.factorypath
+build
+.gradle
\ No newline at end of file
diff --git a/cayenne-java-modules/README.md b/cayenne-java-modules/README.md
new file mode 100644
index 0000000..d9bdfde
--- /dev/null
+++ b/cayenne-java-modules/README.md
@@ -0,0 +1,22 @@
+Apache Cayenne example with java modules
+=============
+
+Example shows how to use Apache Cayenne in application with java modules.
+
+- Clone:
+```bash
+git clone https://github.com/apache/cayenne-examples
+cd cayenne-java-modules
+```
+
+- Run instance of mysql database and change url, username and password in cayenne-project.xml. 
+
+- Build:
+```bash
+mvn clean install
+```
+
+- Run:
+```bash
+java --module-path target/cayenne-java-modules-1.0-SNAPSHOT.jar:target/lib/cayenne-server-4.1.B2.jar:target/lib/cayenne-di-4.1.B2.jar:target/lib/slf4j-api-1.7.25.jar:target/lib/mysql-connector-java-8.0.17.jar:target/lib/protobuf-java-3.6.1.jar:target/lib/slf4j-simple-1.7.25.jar --module cayenne.java.module/org.apache.cayenne.example.Main
+```
\ No newline at end of file
diff --git a/cayenne-java-modules/pom.xml b/cayenne-java-modules/pom.xml
new file mode 100644
index 0000000..421f286
--- /dev/null
+++ b/cayenne-java-modules/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <packaging>jar</packaging>
+
+    <groupId>org.apache.cayenne.example</groupId>
+    <artifactId>cayenne-java-modules</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <properties>
+        <cayenne.version>4.1.B2</cayenne.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.cayenne</groupId>
+            <artifactId>cayenne-server</artifactId>
+            <version>${cayenne.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>1.7.25</version>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>8.0.17</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>11</source>
+                    <target>11</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>copy-dependencies</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
+                            <overWriteReleases>false</overWriteReleases>
+                            <overWriteSnapshots>false</overWriteSnapshots>
+                            <overWriteIfNewer>true</overWriteIfNewer>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <!-- Build an executable JAR -->
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>3.1.0</version>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <mainClass>org.apache.cayenne.example.Main</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/cayenne-java-modules/src/main/java/module-info.java b/cayenne-java-modules/src/main/java/module-info.java
new file mode 100644
index 0000000..f063342
--- /dev/null
+++ b/cayenne-java-modules/src/main/java/module-info.java
@@ -0,0 +1,5 @@
+module cayenne.java.module {
+    requires java.sql;
+    requires cayenne.server;
+    exports org.apache.cayenne.example.persistent;
+}
\ No newline at end of file
diff --git a/cayenne-java-modules/src/main/java/org/apache/cayenne/example/Main.java b/cayenne-java-modules/src/main/java/org/apache/cayenne/example/Main.java
new file mode 100644
index 0000000..5a8a7cd
--- /dev/null
+++ b/cayenne-java-modules/src/main/java/org/apache/cayenne/example/Main.java
@@ -0,0 +1,34 @@
+package org.apache.cayenne.example;
+
+import java.util.List;
+
+import org.apache.cayenne.example.persistent.Artist;
+import org.apache.cayenne.example.persistent.Painting;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.apache.cayenne.query.ObjectSelect;
+
+public class Main {
+    public static void main(String[] args) {
+        ServerRuntime cayenneRuntime = ServerRuntime.builder()
+                .addConfig("cayenne-project.xml")
+                .build();
+        ObjectContext context = cayenneRuntime.newContext();
+        Artist artist = context.newObject(Artist.class);
+        artist.setArtistName("Pablo Picasso");
+
+        Painting painting = context.newObject(Painting.class);
+        painting.setPaintingTitle("Girl Reading at a Table");
+
+        artist.addToPaintings(painting);
+
+        context.commitChanges();
+
+        List<Artist> artists = ObjectSelect.query(Artist.class)
+                .prefetch(Artist.PAINTINGS.disjoint())
+                .select(context);
+
+        System.out.println(artists.get(0).getArtistName());
+        System.out.println(artists.get(0).getPaintings().get(0).getPaintingTitle());
+    }
+}
diff --git a/cayenne-java-modules/src/main/java/org/apache/cayenne/example/persistent/Artist.java b/cayenne-java-modules/src/main/java/org/apache/cayenne/example/persistent/Artist.java
new file mode 100644
index 0000000..900b2c5
--- /dev/null
+++ b/cayenne-java-modules/src/main/java/org/apache/cayenne/example/persistent/Artist.java
@@ -0,0 +1,9 @@
+package org.apache.cayenne.example.persistent;
+
+import org.apache.cayenne.example.persistent.auto._Artist;
+
+public class Artist extends _Artist {
+
+    private static final long serialVersionUID = 1L;
+
+}
diff --git a/cayenne-java-modules/src/main/java/org/apache/cayenne/example/persistent/Painting.java b/cayenne-java-modules/src/main/java/org/apache/cayenne/example/persistent/Painting.java
new file mode 100644
index 0000000..0cc90de
--- /dev/null
+++ b/cayenne-java-modules/src/main/java/org/apache/cayenne/example/persistent/Painting.java
@@ -0,0 +1,9 @@
+package org.apache.cayenne.example.persistent;
+
+import org.apache.cayenne.example.persistent.auto._Painting;
+
+public class Painting extends _Painting {
+
+    private static final long serialVersionUID = 1L;
+
+}
diff --git a/cayenne-java-modules/src/main/java/org/apache/cayenne/example/persistent/auto/_Artist.java b/cayenne-java-modules/src/main/java/org/apache/cayenne/example/persistent/auto/_Artist.java
new file mode 100644
index 0000000..20648ab
--- /dev/null
+++ b/cayenne-java-modules/src/main/java/org/apache/cayenne/example/persistent/auto/_Artist.java
@@ -0,0 +1,110 @@
+package org.apache.cayenne.example.persistent.auto;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.List;
+
+import org.apache.cayenne.BaseDataObject;
+import org.apache.cayenne.example.persistent.Painting;
+import org.apache.cayenne.exp.Property;
+
+/**
+ * Class _Artist was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _Artist extends BaseDataObject {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final Property<String> ARTIST_NAME = Property.create("artistName", String.class);
+    public static final Property<List<Painting>> PAINTINGS = Property.create("paintings", List.class);
+
+    protected String artistName;
+
+    protected Object paintings;
+
+    public void setArtistName(String artistName) {
+        beforePropertyWrite("artistName", this.artistName, artistName);
+        this.artistName = artistName;
+    }
+
+    public String getArtistName() {
+        beforePropertyRead("artistName");
+        return this.artistName;
+    }
+
+    public void addToPaintings(Painting obj) {
+        addToManyTarget("paintings", obj, true);
+    }
+
+    public void removeFromPaintings(Painting obj) {
+        removeToManyTarget("paintings", obj, true);
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<Painting> getPaintings() {
+        return (List<Painting>)readProperty("paintings");
+    }
+
+    @Override
+    public Object readPropertyDirectly(String propName) {
+        if(propName == null) {
+            throw new IllegalArgumentException();
+        }
+
+        switch(propName) {
+            case "artistName":
+                return this.artistName;
+            case "paintings":
+                return this.paintings;
+            default:
+                return super.readPropertyDirectly(propName);
+        }
+    }
+
+    @Override
+    public void writePropertyDirectly(String propName, Object val) {
+        if(propName == null) {
+            throw new IllegalArgumentException();
+        }
+
+        switch (propName) {
+            case "artistName":
+                this.artistName = (String)val;
+                break;
+            case "paintings":
+                this.paintings = val;
+                break;
+            default:
+                super.writePropertyDirectly(propName, val);
+        }
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        writeSerialized(out);
+    }
+
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        readSerialized(in);
+    }
+
+    @Override
+    protected void writeState(ObjectOutputStream out) throws IOException {
+        super.writeState(out);
+        out.writeObject(this.artistName);
+        out.writeObject(this.paintings);
+    }
+
+    @Override
+    protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        super.readState(in);
+        this.artistName = (String)in.readObject();
+        this.paintings = in.readObject();
+    }
+
+}
diff --git a/cayenne-java-modules/src/main/java/org/apache/cayenne/example/persistent/auto/_Painting.java b/cayenne-java-modules/src/main/java/org/apache/cayenne/example/persistent/auto/_Painting.java
new file mode 100644
index 0000000..c3961d0
--- /dev/null
+++ b/cayenne-java-modules/src/main/java/org/apache/cayenne/example/persistent/auto/_Painting.java
@@ -0,0 +1,104 @@
+package org.apache.cayenne.example.persistent.auto;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.apache.cayenne.BaseDataObject;
+import org.apache.cayenne.example.persistent.Artist;
+import org.apache.cayenne.exp.Property;
+
+/**
+ * Class _Painting was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _Painting extends BaseDataObject {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final Property<String> PAINTING_TITLE = Property.create("paintingTitle", String.class);
+    public static final Property<Artist> TO_ARTIST = Property.create("toArtist", Artist.class);
+
+    protected String paintingTitle;
+
+    protected Object toArtist;
+
+    public void setPaintingTitle(String paintingTitle) {
+        beforePropertyWrite("paintingTitle", this.paintingTitle, paintingTitle);
+        this.paintingTitle = paintingTitle;
+    }
+
+    public String getPaintingTitle() {
+        beforePropertyRead("paintingTitle");
+        return this.paintingTitle;
+    }
+
+    public void setToArtist(Artist toArtist) {
+        setToOneTarget("toArtist", toArtist, true);
+    }
+
+    public Artist getToArtist() {
+        return (Artist)readProperty("toArtist");
+    }
+
+    @Override
+    public Object readPropertyDirectly(String propName) {
+        if(propName == null) {
+            throw new IllegalArgumentException();
+        }
+
+        switch(propName) {
+            case "paintingTitle":
+                return this.paintingTitle;
+            case "toArtist":
+                return this.toArtist;
+            default:
+                return super.readPropertyDirectly(propName);
+        }
+    }
+
+    @Override
+    public void writePropertyDirectly(String propName, Object val) {
+        if(propName == null) {
+            throw new IllegalArgumentException();
+        }
+
+        switch (propName) {
+            case "paintingTitle":
+                this.paintingTitle = (String)val;
+                break;
+            case "toArtist":
+                this.toArtist = val;
+                break;
+            default:
+                super.writePropertyDirectly(propName, val);
+        }
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        writeSerialized(out);
+    }
+
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        readSerialized(in);
+    }
+
+    @Override
+    protected void writeState(ObjectOutputStream out) throws IOException {
+        super.writeState(out);
+        out.writeObject(this.paintingTitle);
+        out.writeObject(this.toArtist);
+    }
+
+    @Override
+    protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
+        super.readState(in);
+        this.paintingTitle = (String)in.readObject();
+        this.toArtist = in.readObject();
+    }
+
+}
diff --git a/cayenne-java-modules/src/main/resources/cayenne-project.xml b/cayenne-java-modules/src/main/resources/cayenne-project.xml
new file mode 100644
index 0000000..ff1373b
--- /dev/null
+++ b/cayenne-java-modules/src/main/resources/cayenne-project.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<domain xmlns="http://cayenne.apache.org/schema/10/domain"
+	 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	 xsi:schemaLocation="http://cayenne.apache.org/schema/10/domain http://cayenne.apache.org/schema/10/domain.xsd"
+	 project-version="10">
+	<map name="datamap"/>
+	<node name="datanode"
+		 factory="org.apache.cayenne.configuration.server.XMLPoolingDataSourceFactory"
+		 schema-update-strategy="org.apache.cayenne.access.dbsync.CreateIfNoSchemaStrategy">
+		<map-ref name="datamap"/>
+		<data-source>
+			<driver value="com.mysql.cj.jdbc.Driver"/>
+			<url value="jdbc:mysql://localhost:3306/test"/>
+			<connectionPool min="1" max="1"/>
+			<login userName="root" password="root"/>
+		</data-source>
+	</node>
+</domain>
diff --git a/cayenne-java-modules/src/main/resources/datamap.map.xml b/cayenne-java-modules/src/main/resources/datamap.map.xml
new file mode 100644
index 0000000..c869081
--- /dev/null
+++ b/cayenne-java-modules/src/main/resources/datamap.map.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<data-map xmlns="http://cayenne.apache.org/schema/10/modelMap"
+	 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	 xsi:schemaLocation="http://cayenne.apache.org/schema/10/modelMap http://cayenne.apache.org/schema/10/modelMap.xsd"
+	 project-version="10">
+	<property name="defaultPackage" value="com.bulackiy.persistent"/>
+	<db-entity name="ARTIST" catalog="test">
+		<db-attribute name="ARTIST_NAME" type="VARCHAR" length="100"/>
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true" length="10"/>
+	</db-entity>
+	<db-entity name="PAINTING">
+		<db-attribute name="ARTIST_ID" type="INTEGER"/>
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+		<db-attribute name="PAINTING_TITLE" type="VARCHAR" length="255"/>
+	</db-entity>
+	<obj-entity name="Artist" className="org.apache.cayenne.example.persistent.Artist" dbEntityName="ARTIST">
+		<obj-attribute name="artistName" type="java.lang.String" db-attribute-path="ARTIST_NAME"/>
+	</obj-entity>
+	<obj-entity name="Painting" className="org.apache.cayenne.example.persistent.Painting" dbEntityName="PAINTING">
+		<obj-attribute name="paintingTitle" type="java.lang.String" db-attribute-path="PAINTING_TITLE"/>
+	</obj-entity>
+	<db-relationship name="paintings" source="ARTIST" target="PAINTING" toMany="true">
+		<db-attribute-pair source="ID" target="ARTIST_ID"/>
+	</db-relationship>
+	<db-relationship name="toArtist" source="PAINTING" target="ARTIST">
+		<db-attribute-pair source="ARTIST_ID" target="ID"/>
+	</db-relationship>
+	<obj-relationship name="paintings" source="Artist" target="Painting" deleteRule="Deny" db-relationship-path="paintings"/>
+	<obj-relationship name="toArtist" source="Painting" target="Artist" deleteRule="Nullify" db-relationship-path="toArtist"/>
+</data-map>