cayenne-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From aadamc...@apache.org
Subject cayenne git commit: CAY-2030 Capturing a stream of commit changes
Date Fri, 23 Oct 2015 13:17:07 GMT
Repository: cayenne
Updated Branches:
  refs/heads/master f03867873 -> 28c7db937


CAY-2030 Capturing a stream of commit changes

* adding builder method to control whether PostCommitFilter is executed inside or outside
  the main transaction (default is INSIDE)


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

Branch: refs/heads/master
Commit: 28c7db937a767a2b8630b8c5d29614ac988fa302
Parents: f038678
Author: aadamchik <aadamchik@apache.org>
Authored: Fri Oct 23 14:10:28 2015 +0300
Committer: aadamchik <aadamchik@apache.org>
Committed: Fri Oct 23 16:12:53 2015 +0300

----------------------------------------------------------------------
 .../postcommit/PostCommitModuleBuilder.java     | 40 +++++++--
 .../apache/cayenne/lifecycle/db/AuditLog.java   |  9 +++
 .../cayenne/lifecycle/db/auto/_AuditLog.java    | 27 +++++++
 .../PostCommitFilter_OutsideTxIT.java           | 85 ++++++++++++++++++++
 .../postcommit/PostCommitFilter_TxIT.java       | 84 +++++++++++++++++++
 .../lifecycle/unit/AuditableServerCase.java     |  6 ++
 .../src/test/resources/lifecycle-map.map.xml    |  7 ++
 7 files changed, 250 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/28c7db93/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/postcommit/PostCommitModuleBuilder.java
----------------------------------------------------------------------
diff --git a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/postcommit/PostCommitModuleBuilder.java
b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/postcommit/PostCommitModuleBuilder.java
index 99b3181..7c1eb5d 100644
--- a/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/postcommit/PostCommitModuleBuilder.java
+++ b/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/postcommit/PostCommitModuleBuilder.java
@@ -30,6 +30,9 @@ import org.apache.cayenne.lifecycle.postcommit.meta.AuditablePostCommitEntityFac
 import org.apache.cayenne.lifecycle.postcommit.meta.IncludeAllPostCommitEntityFactory;
 import org.apache.cayenne.lifecycle.postcommit.meta.PostCommitEntity;
 import org.apache.cayenne.lifecycle.postcommit.meta.PostCommitEntityFactory;
+import org.apache.cayenne.tx.TransactionFilter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 
 /**
  * A builder of a module that integrates {@link PostCommitFilter} and
@@ -39,6 +42,8 @@ import org.apache.cayenne.lifecycle.postcommit.meta.PostCommitEntityFactory;
  */
 public class PostCommitModuleBuilder {
 
+	private static final Log LOGGER = LogFactory.getLog(PostCommitModuleBuilder.class);
+
 	public static PostCommitModuleBuilder builder() {
 		return new PostCommitModuleBuilder();
 	}
@@ -46,6 +51,7 @@ public class PostCommitModuleBuilder {
 	private Class<? extends PostCommitEntityFactory> entityFactoryType;
 	private Collection<Class<? extends PostCommitListener>> listenerTypes;
 	private Collection<PostCommitListener> listenerInstances;
+	private boolean excludeFromTransaction;
 
 	PostCommitModuleBuilder() {
 		this.entityFactoryType = IncludeAllPostCommitEntityFactory.class;
@@ -64,6 +70,16 @@ public class PostCommitModuleBuilder {
 	}
 
 	/**
+	 * If called, events will be dispatched outside of the main commit
+	 * transaction. By default events are dispatched within the transaction, so
+	 * listeners can commit their code together with the main commit.
+	 */
+	public PostCommitModuleBuilder excludeFromTransaction() {
+		this.excludeFromTransaction = true;
+		return this;
+	}
+
+	/**
 	 * Installs entity filter that would only include entities annotated with
 	 * {@link Auditable} on the callbacks. Also {@link Auditable#confidential()}
 	 * properties will be obfuscated and {@link Auditable#ignoredProperties()} -
@@ -94,9 +110,15 @@ public class PostCommitModuleBuilder {
 			@Override
 			public void configure(Binder binder) {
 
-				ListBuilder<PostCommitListener> listeners = binder
-						.<PostCommitListener> bindList(PostCommitFilter.POST_COMMIT_LISTENERS_LIST)
-						.addAll(listenerInstances);
+				if (listenerTypes.isEmpty() && listenerInstances.isEmpty()) {
+					LOGGER.info("No listeners configured. Skipping PostCommitFilter registration");
+					return;
+				}
+
+				binder.bind(PostCommitEntityFactory.class).to(entityFactoryType);
+
+				ListBuilder<PostCommitListener> listeners = binder.<PostCommitListener> bindList(
+						PostCommitFilter.POST_COMMIT_LISTENERS_LIST).addAll(listenerInstances);
 
 				// types have to be added one-by-one
 				for (Class type : listenerTypes) {
@@ -110,11 +132,13 @@ public class PostCommitModuleBuilder {
 
 				binder.bind(PostCommitFilter.class).to(PostCommitFilter.class);
 
-				// TODO: should be ordering the filter to go inside transaction
-				// once the corresponding Jiras are available in Cayenne
-				binder.bindList(Constants.SERVER_DOMAIN_FILTERS_LIST).add(PostCommitFilter.class);
-
-				binder.bind(PostCommitEntityFactory.class).to(entityFactoryType);
+				if (excludeFromTransaction) {
+					binder.bindList(Constants.SERVER_DOMAIN_FILTERS_LIST).add(PostCommitFilter.class)
+							.after(TransactionFilter.class);
+				} else {
+					binder.bindList(Constants.SERVER_DOMAIN_FILTERS_LIST).add(PostCommitFilter.class)
+							.before(TransactionFilter.class);
+				}
 			}
 		};
 	}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/28c7db93/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditLog.java
----------------------------------------------------------------------
diff --git a/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditLog.java
b/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditLog.java
new file mode 100644
index 0000000..64cab94
--- /dev/null
+++ b/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/AuditLog.java
@@ -0,0 +1,9 @@
+package org.apache.cayenne.lifecycle.db;
+
+import org.apache.cayenne.lifecycle.db.auto._AuditLog;
+
+public class AuditLog extends _AuditLog {
+
+    private static final long serialVersionUID = 1L; 
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/28c7db93/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditLog.java
----------------------------------------------------------------------
diff --git a/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditLog.java
b/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditLog.java
new file mode 100644
index 0000000..6c870d6
--- /dev/null
+++ b/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/db/auto/_AuditLog.java
@@ -0,0 +1,27 @@
+package org.apache.cayenne.lifecycle.db.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.exp.Property;
+
+/**
+ * Class _AuditLog 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 _AuditLog extends CayenneDataObject {
+
+    private static final long serialVersionUID = 1L; 
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final Property<String> LOG = new Property<String>("log");
+
+    public void setLog(String log) {
+        writeProperty("log", log);
+    }
+    public String getLog() {
+        return (String)readProperty("log");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/28c7db93/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/postcommit/PostCommitFilter_OutsideTxIT.java
----------------------------------------------------------------------
diff --git a/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/postcommit/PostCommitFilter_OutsideTxIT.java
b/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/postcommit/PostCommitFilter_OutsideTxIT.java
new file mode 100644
index 0000000..b9b3278
--- /dev/null
+++ b/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/postcommit/PostCommitFilter_OutsideTxIT.java
@@ -0,0 +1,85 @@
+/*****************************************************************
+ *   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.lifecycle.postcommit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.server.ServerRuntimeBuilder;
+import org.apache.cayenne.lifecycle.changemap.ChangeMap;
+import org.apache.cayenne.lifecycle.changemap.ObjectChange;
+import org.apache.cayenne.lifecycle.db.AuditLog;
+import org.apache.cayenne.lifecycle.db.Auditable2;
+import org.apache.cayenne.lifecycle.unit.AuditableServerCase;
+import org.apache.cayenne.tx.BaseTransaction;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PostCommitFilter_OutsideTxIT extends AuditableServerCase {
+
+	protected ObjectContext context;
+	protected PostCommitListener listener;
+
+	@Override
+	protected ServerRuntimeBuilder configureCayenne() {
+		this.listener = new PostCommitListener() {
+
+			@Override
+			public void onPostCommit(ObjectContext originatingContext, ChangeMap changes) {
+
+				// assert we are inside transaction
+				assertNull(BaseTransaction.getThreadTransaction());
+
+				for (ObjectChange c : changes.getUniqueChanges()) {
+					AuditLog log = runtime.newContext().newObject(AuditLog.class);
+					log.setLog("DONE: " + c.getPostCommitId());
+					log.getObjectContext().commitChanges();
+				}
+			}
+		};
+		return super.configureCayenne().addModule(
+				PostCommitModuleBuilder.builder().auditableEntitiesOnly().excludeFromTransaction().listener(listener)
+						.build());
+	}
+
+	@Before
+	public void before() {
+		this.context = runtime.newContext();
+	}
+
+	@Test
+	public void testCommitLog() throws SQLException {
+		Auditable2 a1 = context.newObject(Auditable2.class);
+		a1.setCharProperty1("yy");
+		a1.setCharProperty2("zz");
+
+		Auditable2 a2 = context.newObject(Auditable2.class);
+		a2.setCharProperty1("yy");
+		a2.setCharProperty2("zz");
+		context.commitChanges();
+
+		List<Object[]> logs = auditLog.selectAll();
+		assertEquals(2, logs.size());
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/28c7db93/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/postcommit/PostCommitFilter_TxIT.java
----------------------------------------------------------------------
diff --git a/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/postcommit/PostCommitFilter_TxIT.java
b/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/postcommit/PostCommitFilter_TxIT.java
new file mode 100644
index 0000000..8b62be6
--- /dev/null
+++ b/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/postcommit/PostCommitFilter_TxIT.java
@@ -0,0 +1,84 @@
+/*****************************************************************
+ *   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.lifecycle.postcommit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.server.ServerRuntimeBuilder;
+import org.apache.cayenne.lifecycle.changemap.ChangeMap;
+import org.apache.cayenne.lifecycle.changemap.ObjectChange;
+import org.apache.cayenne.lifecycle.db.AuditLog;
+import org.apache.cayenne.lifecycle.db.Auditable2;
+import org.apache.cayenne.lifecycle.unit.AuditableServerCase;
+import org.apache.cayenne.tx.BaseTransaction;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PostCommitFilter_TxIT extends AuditableServerCase {
+
+	protected ObjectContext context;
+	protected PostCommitListener listener;
+
+	@Override
+	protected ServerRuntimeBuilder configureCayenne() {
+		this.listener = new PostCommitListener() {
+
+			@Override
+			public void onPostCommit(ObjectContext originatingContext, ChangeMap changes) {
+
+				// assert we are inside transaction
+				assertNotNull(BaseTransaction.getThreadTransaction());
+
+				for (ObjectChange c : changes.getUniqueChanges()) {
+					AuditLog log = runtime.newContext().newObject(AuditLog.class);
+					log.setLog("DONE: " + c.getPostCommitId());
+					log.getObjectContext().commitChanges();
+				}
+			}
+		};
+		return super.configureCayenne().addModule(
+				PostCommitModuleBuilder.builder().auditableEntitiesOnly().listener(listener).build());
+	}
+
+	@Before
+	public void before() {
+		this.context = runtime.newContext();
+	}
+
+	@Test
+	public void testCommitLog() throws SQLException {
+		Auditable2 a1 = context.newObject(Auditable2.class);
+		a1.setCharProperty1("yy");
+		a1.setCharProperty2("zz");
+
+		Auditable2 a2 = context.newObject(Auditable2.class);
+		a2.setCharProperty1("yy");
+		a2.setCharProperty2("zz");
+		context.commitChanges();
+
+		List<Object[]> logs = auditLog.selectAll();
+		assertEquals(2, logs.size());
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/28c7db93/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/unit/AuditableServerCase.java
----------------------------------------------------------------------
diff --git a/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/unit/AuditableServerCase.java
b/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/unit/AuditableServerCase.java
index ec8024a..6513273 100644
--- a/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/unit/AuditableServerCase.java
+++ b/cayenne-lifecycle/src/test/java/org/apache/cayenne/lifecycle/unit/AuditableServerCase.java
@@ -43,12 +43,16 @@ public abstract class AuditableServerCase {
 	protected TableHelper auditable3;
 	protected TableHelper auditable4;
 
+	protected TableHelper auditLog;
+
 	@Before
 	public void startCayenne() throws Exception {
 		this.runtime = configureCayenne().build();
 
 		DBHelper dbHelper = new DBHelper(runtime.getDataSource());
 
+		this.auditLog = new TableHelper(dbHelper, "AUDIT_LOG").setColumns("ID", "LOG");
+
 		this.auditable1 = new TableHelper(dbHelper, "AUDITABLE1").setColumns("ID", "CHAR_PROPERTY1");
 
 		this.auditableChild1 = new TableHelper(dbHelper, "AUDITABLE_CHILD1").setColumns("ID", "AUDITABLE1_ID",
@@ -77,6 +81,8 @@ public abstract class AuditableServerCase {
 		this.auditableChildUuid.deleteAll();
 		this.auditable4.deleteAll();
 		this.auditable3.deleteAll();
+
+		this.auditLog.deleteAll();
 	}
 
 	protected ServerRuntimeBuilder configureCayenne() {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/28c7db93/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml
----------------------------------------------------------------------
diff --git a/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml b/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml
index c04bc31..92a6ab5 100644
--- a/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml
+++ b/cayenne-lifecycle/src/test/resources/lifecycle-map.map.xml
@@ -46,6 +46,10 @@
 		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
 		<db-attribute name="UUID" type="VARCHAR" length="200"/>
 	</db-entity>
+	<db-entity name="AUDIT_LOG">
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+		<db-attribute name="LOG" type="CLOB"/>
+	</db-entity>
 	<db-entity name="E1">
 		<db-attribute name="ID" type="BIGINT" isPrimaryKey="true" isMandatory="true"/>
 	</db-entity>
@@ -73,6 +77,9 @@
 		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
 		<db-attribute name="UUID" type="VARCHAR" length="200"/>
 	</db-entity>
+	<obj-entity name="AuditLog" className="org.apache.cayenne.lifecycle.db.AuditLog" dbEntityName="AUDIT_LOG">
+		<obj-attribute name="log" type="java.lang.String" db-attribute-path="LOG"/>
+	</obj-entity>
 	<obj-entity name="Auditable1" className="org.apache.cayenne.lifecycle.db.Auditable1"
dbEntityName="AUDITABLE1">
 		<obj-attribute name="charProperty1" type="java.lang.String" db-attribute-path="CHAR_PROPERTY1"/>
 	</obj-entity>


Mime
View raw message