You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2017/03/14 14:32:00 UTC

[1/3] cayenne git commit: CAY-2266 Move EventBridge implementations into autoloadable modules

Repository: cayenne
Updated Branches:
  refs/heads/master 52e7ede9d -> ba3c7eadf


http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPBridgeProvider.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPBridgeProvider.java b/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPBridgeProvider.java
new file mode 100644
index 0000000..38c9874
--- /dev/null
+++ b/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPBridgeProvider.java
@@ -0,0 +1,50 @@
+/*****************************************************************
+ *   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.event;
+
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.access.DataRowStore;
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.di.DIRuntimeException;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.di.Provider;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class XMPPBridgeProvider implements Provider<EventBridge> {
+
+    @Inject
+    protected DataDomain dataDomain;
+
+    @Inject(XMPPModule.XMPP_BRIDGE_PROPERTIES_MAP)
+    Map<String, String> properties;
+
+    @Override
+    public EventBridge get() throws DIRuntimeException {
+        EventSubject snapshotEventSubject = EventSubject.getSubject(DataRowStore.class.getClass(), dataDomain.getName());
+
+        return new XMPPBridge(
+                Collections.singleton(snapshotEventSubject),
+                EventBridge.convertToExternalSubject(snapshotEventSubject),
+                properties);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPModule.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPModule.java b/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPModule.java
new file mode 100644
index 0000000..6726992
--- /dev/null
+++ b/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPModule.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.event;
+
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.MapBuilder;
+import org.apache.cayenne.di.Module;
+
+/**
+ * @since 4.0
+ */
+public class XMPPModule implements Module {
+
+    /**
+     * A DI container key for the Map&lt;String, String&gt; storing
+     * {@link org.apache.cayenne.event.XMPPBridge} properties
+     *
+     * @since 4.0
+     */
+    public static final String XMPP_BRIDGE_PROPERTIES_MAP = "cayenne.server.xmpp_bridge";
+
+    public static void contributeHost(Binder binder, String host) {
+        contributeProperties(binder).put(XMPPBridge.XMPP_HOST_PROPERTY, host);
+    }
+
+    public static void contributePort(Binder binder, int port) {
+        contributeProperties(binder).put(XMPPBridge.XMPP_PORT_PROPERTY, Integer.toString(port));
+    }
+
+    public static void contributeLogin(Binder binder, String login, String password) {
+        contributeProperties(binder).put(XMPPBridge.XMPP_LOGIN_PROPERTY, login);
+        contributeProperties(binder).put(XMPPBridge.XMPP_PASSWORD_PROPERTY, password);
+    }
+
+    public static void contributeChatService(Binder binder, String chatService) {
+        contributeProperties(binder).put(XMPPBridge.XMPP_CHAT_SERVICE_PROPERTY, chatService);
+    }
+
+    public static void contributeSecureConnection(Binder binder, boolean secure) {
+        contributeProperties(binder).put(XMPPBridge.XMPP_SECURE_CONNECTION_PROPERTY, Boolean.toString(secure));
+    }
+
+    private static MapBuilder<String> contributeProperties(Binder binder) {
+        return binder.bindMap(XMPP_BRIDGE_PROPERTIES_MAP);
+    }
+
+    @Override
+    public void configure(Binder binder) {
+        // init properties' defaults
+        contributeChatService(binder, XMPPBridge.DEFAULT_CHAT_SERVICE);
+
+        binder.bind(EventBridge.class).toProvider(XMPPBridgeProvider.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPModuleProvider.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPModuleProvider.java b/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPModuleProvider.java
new file mode 100644
index 0000000..852cedb
--- /dev/null
+++ b/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPModuleProvider.java
@@ -0,0 +1,50 @@
+/*****************************************************************
+ *   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.event;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.cayenne.configuration.server.ServerModule;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.di.spi.ModuleProvider;
+
+/**
+ * @since 4.0
+ */
+public class XMPPModuleProvider implements ModuleProvider {
+
+    @Override
+    public Module module() {
+        return new XMPPModule();
+    }
+
+    @Override
+    public Class<? extends Module> moduleType() {
+        return XMPPModule.class;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Collection<Class<? extends Module>> overrides() {
+        Collection modules = Collections.singletonList(ServerModule.class);
+        return modules;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-xmpp/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-xmpp/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider b/eventbridges/cayenne-xmpp/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
new file mode 100644
index 0000000..c1730ce
--- /dev/null
+++ b/eventbridges/cayenne-xmpp/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
@@ -0,0 +1,20 @@
+##################################################################
+#   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.
+##################################################################
+
+org.apache.cayenne.event.XMPPModuleProvider
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/CayenneXMPPModuleProviderTest.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/CayenneXMPPModuleProviderTest.java b/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/CayenneXMPPModuleProviderTest.java
new file mode 100644
index 0000000..3adf631
--- /dev/null
+++ b/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/CayenneXMPPModuleProviderTest.java
@@ -0,0 +1,35 @@
+/*****************************************************************
+ *   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.event;
+
+import org.apache.cayenne.unit.util.ModuleProviderChecker;
+import org.junit.Test;
+
+/**
+ * @since 4.0
+ */
+public class CayenneXMPPModuleProviderTest {
+
+    @Test
+    public void testAutoLoadable() {
+        ModuleProviderChecker.testProviderPresent(XMPPModuleProvider.class);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeFactoryTest.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeFactoryTest.java b/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeFactoryTest.java
new file mode 100644
index 0000000..b5aa80a
--- /dev/null
+++ b/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeFactoryTest.java
@@ -0,0 +1,72 @@
+/*****************************************************************
+ *   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.event;
+
+import org.junit.Test;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class XMPPBridgeFactoryTest {
+
+    protected Collection<EventSubject> subjects = Collections.singleton(new EventSubject("test"));
+    protected String externalSubject = "subject";
+
+    @Test
+    public void testCreateEventBridge() {
+        EventBridge bridge = new XMPPBridgeFactory().createEventBridge(
+                subjects,
+                externalSubject,
+                Collections.<String, String>emptyMap());
+
+        assertTrue(bridge instanceof XMPPBridge);
+        assertEquals(subjects, bridge.getLocalSubjects());
+        assertEquals(externalSubject, bridge.getExternalSubject());
+    }
+
+    @Test
+    public void testUseMapPropertiesSetter() throws Exception {
+        XMPPBridgeFactory bridgeFactory = new XMPPBridgeFactory();
+        Map<String, String> properties = new HashMap<>();
+
+        properties.put(XMPPBridge.XMPP_HOST_PROPERTY, XMPPBridgeProviderTest.HOST_TEST);
+        properties.put(XMPPBridge.XMPP_CHAT_SERVICE_PROPERTY, XMPPBridgeProviderTest.CHAT_SERVICE_TEST);
+        properties.put(XMPPBridge.XMPP_LOGIN_PROPERTY, XMPPBridgeProviderTest.LOGIN_TEST);
+        properties.put(XMPPBridge.XMPP_PASSWORD_PROPERTY, XMPPBridgeProviderTest.PASSWORD_TEST);
+        properties.put(XMPPBridge.XMPP_SECURE_CONNECTION_PROPERTY, String.valueOf(XMPPBridgeProviderTest.SECURE_CONNECTION_TEST));
+        properties.put(XMPPBridge.XMPP_PORT_PROPERTY, String.valueOf(XMPPBridgeProviderTest.PORT_TEST));
+
+        XMPPBridge bridge = (XMPPBridge) bridgeFactory.createEventBridge(subjects,
+                externalSubject,
+                properties);
+
+        assertEquals(bridge.getXmppHost(), XMPPBridgeProviderTest.HOST_TEST);
+        assertEquals(bridge.getChatService(), XMPPBridgeProviderTest.CHAT_SERVICE_TEST);
+        assertEquals(bridge.getLoginId(), XMPPBridgeProviderTest.LOGIN_TEST);
+        assertEquals(bridge.getPassword(), XMPPBridgeProviderTest.PASSWORD_TEST);
+        assertEquals(bridge.getXmppPort(), XMPPBridgeProviderTest.PORT_TEST);
+        assertEquals(bridge.isSecureConnection(), XMPPBridgeProviderTest.SECURE_CONNECTION_TEST);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeProviderTest.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeProviderTest.java b/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeProviderTest.java
new file mode 100644
index 0000000..14794f4
--- /dev/null
+++ b/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeProviderTest.java
@@ -0,0 +1,107 @@
+/*****************************************************************
+ *   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.event;
+
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.configuration.DefaultRuntimeProperties;
+import org.apache.cayenne.configuration.RuntimeProperties;
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.DIBootstrap;
+import org.apache.cayenne.di.Injector;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.log.CommonsJdbcEventLogger;
+import org.apache.cayenne.log.JdbcEventLogger;
+import org.apache.cayenne.tx.DefaultTransactionFactory;
+import org.apache.cayenne.tx.DefaultTransactionManager;
+import org.apache.cayenne.tx.TransactionFactory;
+import org.apache.cayenne.tx.TransactionManager;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class XMPPBridgeProviderTest {
+
+    private final DataDomain DOMAIN = new DataDomain("test");
+    private final EventManager EVENT_MANAGER = new DefaultEventManager();
+    protected static final String HOST_TEST = "somehost.com";
+    protected static final String CHAT_SERVICE_TEST = "conference";
+    protected static final String LOGIN_TEST = "login";
+    protected static final String PASSWORD_TEST = "password";
+    protected static final boolean SECURE_CONNECTION_TEST = true;
+    protected static final int PORT_TEST = 12345;
+
+    @Test
+    public void testGetXMPPBridge() throws Exception {
+        Injector injector = DIBootstrap.createInjector(new DefaultBindings(), new XMPPModule());
+        EventBridge bridge = injector.getInstance(EventBridge.class);
+
+        assertNotNull(bridge);
+        assertTrue(bridge instanceof XMPPBridge);
+    }
+
+    @Test
+    public void testUseProperties() throws Exception {
+        Module module = new Module() {
+            public void configure(Binder binder) {
+                XMPPModule.contributeSecureConnection(binder, SECURE_CONNECTION_TEST);
+                XMPPModule.contributeHost(binder, HOST_TEST);
+                XMPPModule.contributePort(binder, PORT_TEST);
+                XMPPModule.contributeLogin(binder, LOGIN_TEST, PASSWORD_TEST);
+                XMPPModule.contributeChatService(binder, CHAT_SERVICE_TEST);
+            }
+        };
+
+        Injector injector = DIBootstrap.createInjector(new DefaultBindings(), new XMPPModule(), module);
+        XMPPBridge bridge = (XMPPBridge) injector.getInstance(EventBridge.class);
+
+        assertEquals(HOST_TEST, bridge.getXmppHost());
+        assertEquals(CHAT_SERVICE_TEST, bridge.getChatService());
+        assertEquals(LOGIN_TEST, bridge.getLoginId());
+        assertEquals(PASSWORD_TEST, bridge.getPassword());
+        assertEquals(SECURE_CONNECTION_TEST, bridge.isSecureConnection());
+        assertEquals(PORT_TEST, bridge.getXmppPort());
+    }
+
+    @Test
+    public void testUseDefaultProperties() throws Exception {
+        Injector injector = DIBootstrap.createInjector(new DefaultBindings(), new XMPPModule());
+        XMPPBridge bridge = (XMPPBridge) injector.getInstance(EventBridge.class);
+
+        assertEquals(XMPPBridge.DEFAULT_CHAT_SERVICE, bridge.getChatService());
+        assertEquals(0, bridge.getXmppPort());
+        assertEquals(false, bridge.isSecureConnection());
+    }
+
+    class DefaultBindings implements Module {
+        @Override
+        public void configure(Binder binder) {
+            binder.bindMap(Constants.PROPERTIES_MAP);
+            binder.bind(DataDomain.class).toInstance(DOMAIN);
+            binder.bind(EventManager.class).toInstance(EVENT_MANAGER);
+            binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
+            binder.bind(TransactionFactory.class).to(DefaultTransactionFactory.class);
+            binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
+            binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeTest.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeTest.java b/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeTest.java
new file mode 100644
index 0000000..5422c3a
--- /dev/null
+++ b/eventbridges/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeTest.java
@@ -0,0 +1,54 @@
+/*****************************************************************
+ *   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.event;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ */
+public class XMPPBridgeTest {
+
+    @Test
+    public void testEventSerialization() throws Exception {
+        Map<String, String> info = new HashMap<>();
+        info.put("a", "b");
+        CayenneEvent e = new CayenneEvent(this, this, info);
+
+        String string = XMPPBridge.serializeToString(e);
+        assertNotNull(string);
+
+        Object copy = XMPPBridge.deserializeFromString(string);
+        assertNotNull(copy);
+        assertTrue(copy instanceof CayenneEvent);
+
+        CayenneEvent e2 = (CayenneEvent) copy;
+        assertEquals(info, e2.getInfo());
+        assertNull(e2.getPostedBy());
+        assertNull(e2.getSource());
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/pom.xml
----------------------------------------------------------------------
diff --git a/eventbridges/pom.xml b/eventbridges/pom.xml
new file mode 100644
index 0000000..d15339b
--- /dev/null
+++ b/eventbridges/pom.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~   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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<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">
+    <parent>
+        <artifactId>cayenne-parent</artifactId>
+        <groupId>org.apache.cayenne</groupId>
+        <version>4.0.M6-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>cayenne-eventbridges-parent</artifactId>
+    <name>cayenne-eventbridges-parent: Event bridges implementations parent</name>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>cayenne-jgroups</module>
+        <module>cayenne-jms</module>
+        <module>cayenne-xmpp</module>
+    </modules>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.cayenne</groupId>
+            <artifactId>cayenne-server</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <!-- Test dependencies -->
+        <dependency>
+            <groupId>org.apache.cayenne</groupId>
+            <artifactId>cayenne-server</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- This ensures LICENSE and NOTICE inclusion in all jars -->
+            <plugin>
+                <artifactId>maven-remote-resources-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>process</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 7d404cb..6895ce8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,7 +67,8 @@
 		<module>maven-plugins</module>
 		<module>tutorials</module>
 		<module>docs</module>
-    </modules>
+		<module>eventbridges</module>
+	</modules>
 	<issueManagement>
 		<system>jira</system>
 		<url>https://issues.apache.org/jira/browse/CAY</url>
@@ -357,29 +358,6 @@
 				<version>2.7.0</version>
 			</dependency>
 			<dependency>
-				<groupId>jgroups</groupId>
-				<artifactId>jgroups-all</artifactId>
-				<version>2.2.7</version>
-				<scope>provided</scope>
-			</dependency>
-			<dependency>
-				<groupId>org.apache.geronimo.specs</groupId>
-				<artifactId>geronimo-jms_1.1_spec</artifactId>
-				<version>1.1</version>
-				<scope>provided</scope>
-			</dependency>
-			<dependency>
-				<groupId>org.apache.geronimo.specs</groupId>
-				<artifactId>geronimo-jta_1.1_spec</artifactId>
-				<version>1.0</version>
-			</dependency>
-			<dependency>
-				<groupId>org.apache.geronimo.components</groupId>
-				<artifactId>geronimo-transaction</artifactId>
-				<version>2.0.1</version>
-				<scope>test</scope>
-			</dependency>
-			<dependency>
 				<groupId>mockrunner</groupId>
 				<artifactId>mockrunner</artifactId>
 				<version>0.4.1</version>
@@ -403,18 +381,6 @@
 				<scope>provided</scope>
 			</dependency>
 			<dependency>
-				<groupId>jivesoftware</groupId>
-				<artifactId>smack</artifactId>
-				<version>2.2.1</version>
-				<scope>provided</scope>
-			</dependency>
-			<dependency>
-				<groupId>jivesoftware</groupId>
-				<artifactId>smackx</artifactId>
-				<version>2.2.1</version>
-				<scope>provided</scope>
-			</dependency>
-			<dependency>
 				<groupId>org.springframework</groupId>
 				<artifactId>spring-core</artifactId>
 				<version>1.2.6</version>


[3/3] cayenne git commit: CAY-2266 Move EventBridge implementations into autoloadable modules

Posted by nt...@apache.org.
CAY-2266 Move EventBridge implementations into autoloadable modules


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

Branch: refs/heads/master
Commit: ba3c7eadff7e49afc453c2d757bff7bf687d80c9
Parents: 52e7ede
Author: Nikita Timofeev <st...@gmail.com>
Authored: Tue Mar 14 17:31:19 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Tue Mar 14 17:31:19 2017 +0300

----------------------------------------------------------------------
 assembly/pom.xml                                |  18 +
 .../resources/assemblies/assembly-generic.xml   |   3 +
 .../main/resources/assemblies/assembly-mac.xml  |   3 +
 .../resources/assemblies/assembly-windows.xml   |   3 +
 cayenne-server/pom.xml                          |  24 --
 .../apache/cayenne/configuration/Constants.java |  24 --
 .../org/apache/cayenne/event/JMSBridge.java     | 280 ----------------
 .../apache/cayenne/event/JMSBridgeFactory.java  |  39 ---
 .../apache/cayenne/event/JMSBridgeProvider.java |  50 ---
 .../apache/cayenne/event/JavaGroupsBridge.java  | 230 -------------
 .../cayenne/event/JavaGroupsBridgeFactory.java  |  49 ---
 .../cayenne/event/JavaGroupsBridgeProvider.java |  50 ---
 .../org/apache/cayenne/event/XMPPBridge.java    | 331 -------------------
 .../apache/cayenne/event/XMPPBridgeFactory.java |  41 ---
 .../cayenne/event/XMPPBridgeProvider.java       |  50 ---
 .../cayenne/event/JMSBridgeFactoryTest.java     |  77 -----
 .../cayenne/event/JMSBridgeProviderTest.java    | 119 -------
 .../event/JavaGroupsBridgeFactoryTest.java      |  84 -----
 .../event/JavaGroupsBridgeProviderTest.java     | 123 -------
 .../cayenne/event/XMPPBridgeFactoryTest.java    |  72 ----
 .../cayenne/event/XMPPBridgeProviderTest.java   | 130 --------
 .../apache/cayenne/event/XMPPBridgeTest.java    |  54 ---
 docs/doc/pom.xml                                |  15 -
 docs/doc/src/main/resources/RELEASE-NOTES.txt   |   3 +-
 docs/doc/src/main/resources/UPGRADE.txt         |  28 ++
 eventbridges/cayenne-jgroups/pom.xml            |  44 +++
 .../org/apache/cayenne/event/JGroupsModule.java |  63 ++++
 .../cayenne/event/JGroupsModuleProvider.java    |  50 +++
 .../apache/cayenne/event/JavaGroupsBridge.java  | 231 +++++++++++++
 .../cayenne/event/JavaGroupsBridgeFactory.java  |  49 +++
 .../cayenne/event/JavaGroupsBridgeProvider.java |  50 +++
 .../org.apache.cayenne.di.spi.ModuleProvider    |  20 ++
 .../event/CayenneJGroupsModuleProviderTest.java |  35 ++
 .../event/JavaGroupsBridgeFactoryTest.java      |  84 +++++
 .../event/JavaGroupsBridgeProviderTest.java     |  99 ++++++
 eventbridges/cayenne-jms/pom.xml                |  44 +++
 .../org/apache/cayenne/event/JMSBridge.java     | 280 ++++++++++++++++
 .../apache/cayenne/event/JMSBridgeFactory.java  |  39 +++
 .../apache/cayenne/event/JMSBridgeProvider.java |  50 +++
 .../org/apache/cayenne/event/JMSModule.java     |  54 +++
 .../apache/cayenne/event/JMSModuleProvider.java |  50 +++
 .../org.apache.cayenne.di.spi.ModuleProvider    |  20 ++
 .../event/CayenneJMSModuleProviderTest.java     |  35 ++
 .../cayenne/event/JMSBridgeFactoryTest.java     |  77 +++++
 .../cayenne/event/JMSBridgeProviderTest.java    |  92 ++++++
 eventbridges/cayenne-xmpp/pom.xml               |  50 +++
 .../org/apache/cayenne/event/XMPPBridge.java    | 330 ++++++++++++++++++
 .../apache/cayenne/event/XMPPBridgeFactory.java |  41 +++
 .../cayenne/event/XMPPBridgeProvider.java       |  50 +++
 .../org/apache/cayenne/event/XMPPModule.java    |  71 ++++
 .../cayenne/event/XMPPModuleProvider.java       |  50 +++
 .../org.apache.cayenne.di.spi.ModuleProvider    |  20 ++
 .../event/CayenneXMPPModuleProviderTest.java    |  35 ++
 .../cayenne/event/XMPPBridgeFactoryTest.java    |  72 ++++
 .../cayenne/event/XMPPBridgeProviderTest.java   | 107 ++++++
 .../apache/cayenne/event/XMPPBridgeTest.java    |  54 +++
 eventbridges/pom.xml                            |  79 +++++
 pom.xml                                         |  38 +--
 58 files changed, 2484 insertions(+), 1879 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/assembly/pom.xml
----------------------------------------------------------------------
diff --git a/assembly/pom.xml b/assembly/pom.xml
index e183224..b35ee1a 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -114,6 +114,24 @@
 			<version>${project.version}</version>
 		</dependency>
 
+		<dependency>
+			<groupId>org.apache.cayenne</groupId>
+			<artifactId>cayenne-jgroups</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.cayenne</groupId>
+			<artifactId>cayenne-jms</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.cayenne</groupId>
+			<artifactId>cayenne-xmpp</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+
 	</dependencies>
 
 	<build>

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/assembly/src/main/resources/assemblies/assembly-generic.xml
----------------------------------------------------------------------
diff --git a/assembly/src/main/resources/assemblies/assembly-generic.xml b/assembly/src/main/resources/assemblies/assembly-generic.xml
index ac3f98b..e688faa 100644
--- a/assembly/src/main/resources/assemblies/assembly-generic.xml
+++ b/assembly/src/main/resources/assemblies/assembly-generic.xml
@@ -90,6 +90,9 @@
 				<include>org.apache.cayenne:cayenne-dbcp2</include>
 				<include>org.apache.cayenne:cayenne-java8</include>
 				<include>org.apache.cayenne:cayenne-jcache</include>
+				<include>org.apache.cayenne:cayenne-jgroups</include>
+				<include>org.apache.cayenne:cayenne-jms</include>
+				<include>org.apache.cayenne:cayenne-xmpp</include>
 			</includes>
 		</dependencySet>
 		<dependencySet>

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/assembly/src/main/resources/assemblies/assembly-mac.xml
----------------------------------------------------------------------
diff --git a/assembly/src/main/resources/assemblies/assembly-mac.xml b/assembly/src/main/resources/assemblies/assembly-mac.xml
index 6b78adad..45efd5d 100644
--- a/assembly/src/main/resources/assemblies/assembly-mac.xml
+++ b/assembly/src/main/resources/assemblies/assembly-mac.xml
@@ -90,6 +90,9 @@
 				<include>org.apache.cayenne:cayenne-dbcp2</include>
 				<include>org.apache.cayenne:cayenne-java8</include>
 				<include>org.apache.cayenne:cayenne-jcache</include>
+				<include>org.apache.cayenne:cayenne-jgroups</include>
+				<include>org.apache.cayenne:cayenne-jms</include>
+				<include>org.apache.cayenne:cayenne-xmpp</include>
 			</includes>
 		</dependencySet>
 		<dependencySet>

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/assembly/src/main/resources/assemblies/assembly-windows.xml
----------------------------------------------------------------------
diff --git a/assembly/src/main/resources/assemblies/assembly-windows.xml b/assembly/src/main/resources/assemblies/assembly-windows.xml
index 563b9b3..25f1099 100644
--- a/assembly/src/main/resources/assemblies/assembly-windows.xml
+++ b/assembly/src/main/resources/assemblies/assembly-windows.xml
@@ -90,6 +90,9 @@
 				<include>org.apache.cayenne:cayenne-dbcp2</include>
 				<include>org.apache.cayenne:cayenne-java8</include>
 				<include>org.apache.cayenne:cayenne-jcache</include>
+				<include>org.apache.cayenne:cayenne-jgroups</include>
+				<include>org.apache.cayenne:cayenne-jms</include>
+				<include>org.apache.cayenne:cayenne-xmpp</include>
 			</includes>
 		</dependencySet>
 		<dependencySet>

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/pom.xml
----------------------------------------------------------------------
diff --git a/cayenne-server/pom.xml b/cayenne-server/pom.xml
index 1288be2..0622a58 100644
--- a/cayenne-server/pom.xml
+++ b/cayenne-server/pom.xml
@@ -62,24 +62,6 @@
 			<optional>true</optional>
 		</dependency>
 		<dependency>
-			<groupId>jgroups</groupId>
-			<artifactId>jgroups-all</artifactId>
-			<scope>provided</scope>
-			<optional>true</optional>
-		</dependency>
-		<dependency>
-			<groupId>jivesoftware</groupId>
-			<artifactId>smack</artifactId>
-			<scope>provided</scope>
-			<optional>true</optional>
-		</dependency>
-		<dependency>
-			<groupId>jivesoftware</groupId>
-			<artifactId>smackx</artifactId>
-			<scope>provided</scope>
-			<optional>true</optional>
-		</dependency>
-		<dependency>
 			<groupId>opensymphony</groupId>
 			<artifactId>oscache</artifactId>
 			<scope>provided</scope>
@@ -92,12 +74,6 @@
 			<optional>true</optional>
 		</dependency>
 		<dependency>
-			<groupId>org.apache.geronimo.specs</groupId>
-			<artifactId>geronimo-jms_1.1_spec</artifactId>
-			<scope>provided</scope>
-			<optional>true</optional>
-		</dependency>
-		<dependency>
 			<groupId>javax.servlet</groupId>
 			<artifactId>servlet-api</artifactId>
 			<scope>provided</scope>

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java
index 9761790..18c42cd 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java
@@ -272,28 +272,4 @@ public interface Constants {
      */
     public static final String DATA_ROW_STORE_PROPERTIES_MAP = "cayenne.server.data_row_store";
 
-    /**
-     * A DI container key for the Map&lt;String, String&gt; storing
-     * {@link org.apache.cayenne.event.JMSBridge} properties
-     *
-     * @since 4.0
-     */
-    public static final String JMS_BRIDGE_PROPERTIES_MAP = "cayenne.server.jms_bridge";
-
-    /**
-     * A DI container key for the Map&lt;String, String&gt; storing
-     * {@link org.apache.cayenne.event.JavaGroupsBridge} properties
-     *
-     * @since 4.0
-     */
-    public static final String JAVA_GROUPS_BRIDGE_PROPERTIES_MAP = "cayenne.server.java_group_bridge";
-
-    /**
-     * A DI container key for the Map&lt;String, String&gt; storing
-     * {@link org.apache.cayenne.event.XMPPBridge} properties
-     *
-     * @since 4.0
-     */
-    public static final String XMPP_BRIDGE_PROPERTIES_MAP = "cayenne.server.xmpp_bridge";
-
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/main/java/org/apache/cayenne/event/JMSBridge.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/event/JMSBridge.java b/cayenne-server/src/main/java/org/apache/cayenne/event/JMSBridge.java
deleted file mode 100644
index 0b746fc..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/event/JMSBridge.java
+++ /dev/null
@@ -1,280 +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.event;
-
-import org.apache.cayenne.util.IDUtil;
-
-import javax.jms.Message;
-import javax.jms.MessageFormatException;
-import javax.jms.MessageListener;
-import javax.jms.ObjectMessage;
-import javax.jms.Session;
-import javax.jms.Topic;
-import javax.jms.TopicConnection;
-import javax.jms.TopicConnectionFactory;
-import javax.jms.TopicPublisher;
-import javax.jms.TopicSession;
-import javax.jms.TopicSubscriber;
-import javax.naming.Context;
-import javax.naming.InitialContext;
-import javax.naming.NameNotFoundException;
-import javax.naming.NamingException;
-import java.io.Serializable;
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * Implementation of EventBridge that passes and receives events via JMS (Java Messaging
- * Service). JMSBridge uses "publish/subscribe" model for communication with external
- * agents.
- * 
- * @since 1.1
- */
-public class JMSBridge extends EventBridge implements MessageListener {
-
-    // this is an OpenJMS default for the factory name. Likely it won't work with
-    // anything else
-    public static final String TOPIC_CONNECTION_FACTORY_DEFAULT = "JmsTopicConnectionFactory";
-
-    public static final String TOPIC_CONNECTION_FACTORY_PROPERTY = "cayenne.JMSBridge.topic.connection.factory";
-
-    static final String VM_ID = new String(IDUtil.pseudoUniqueByteSequence16());
-    static final String VM_ID_PROPERTY = "VM_ID";
-
-    protected String topicConnectionFactoryName;
-
-    protected TopicConnection sendConnection;
-    protected TopicSession sendSession;
-    protected TopicConnection receivedConnection;
-    protected TopicPublisher publisher;
-    protected TopicSubscriber subscriber;
-
-    public JMSBridge(EventSubject localSubject, String externalSubject) {
-        super(localSubject, externalSubject);
-    }
-
-    /**
-     * @since 1.2
-     */
-    public JMSBridge(Collection<EventSubject> localSubjects, String externalSubject) {
-        super(localSubjects, externalSubject);
-    }
-
-    /**
-     * @since 4.0
-     */
-    public JMSBridge(Collection<EventSubject> localSubjects, String externalSubject, Map<String, String> properties) {
-        super(localSubjects, externalSubject);
-
-        // configure properties
-        String topicConnectionFactory = properties
-                .get(TOPIC_CONNECTION_FACTORY_PROPERTY);
-
-        this.topicConnectionFactoryName = (topicConnectionFactory != null)
-                ? topicConnectionFactory
-                : TOPIC_CONNECTION_FACTORY_DEFAULT;
-    }
-
-    /**
-     * JMS MessageListener implementation. Injects received events to the EventManager
-     * local event queue.
-     */
-    public void onMessage(Message message) {
-
-        try {
-            Object vmID = message.getObjectProperty(JMSBridge.VM_ID_PROPERTY);
-            if (JMSBridge.VM_ID.equals(vmID)) {
-                return;
-            }
-
-            if (!(message instanceof ObjectMessage)) {
-                return;
-            }
-
-            ObjectMessage objectMessage = (ObjectMessage) message;
-            CayenneEvent event = messageObjectToEvent(objectMessage.getObject());
-            if (event != null) {
-                onExternalEvent(event);
-            }
-
-        }
-        catch (MessageFormatException mfex) {
-            // TODO: Andrus, 2/8/2006 logging... Log4J was removed to make this usable on
-            // the client
-        }
-        catch (Exception ex) {
-            // TODO: Andrus, 2/8/2006 logging... Log4J was removed to make this usable on
-            // the client
-        }
-    }
-
-    /**
-     * @return Name of javax.jms.TopicConnectionFactory accessible via JNDI.
-     */
-    public String getTopicConnectionFactoryName() {
-        return topicConnectionFactoryName;
-    }
-
-    public void setTopicConnectionFactoryName(String name) {
-        this.topicConnectionFactoryName = name;
-    }
-
-    /**
-     * Starts up JMS machinery for "publish/subscribe" model.
-     */
-    @Override
-    protected void startupExternal() throws Exception {
-        Context jndiContext = new InitialContext();
-        TopicConnectionFactory connectionFactory = (TopicConnectionFactory) jndiContext
-                .lookup(topicConnectionFactoryName);
-
-        Topic topic = null;
-
-        try {
-            topic = (Topic) jndiContext.lookup(externalSubject);
-        }
-        catch (NameNotFoundException ex) {
-            // can't find topic, try to create it
-            topic = topicNotFound(jndiContext, ex);
-
-            if (topic == null) {
-                throw ex;
-            }
-        }
-
-        // config publisher
-        if (receivesLocalEvents()) {
-            this.sendConnection = connectionFactory.createTopicConnection();
-            this.sendSession = sendConnection.createTopicSession(
-                    false,
-                    Session.AUTO_ACKNOWLEDGE);
-            this.publisher = sendSession.createPublisher(topic);
-        }
-
-        // config subscriber
-        if (receivesExternalEvents()) {
-            this.receivedConnection = connectionFactory.createTopicConnection();
-            this.subscriber = receivedConnection.createTopicSession(
-                    false,
-                    Session.AUTO_ACKNOWLEDGE).createSubscriber(topic);
-            this.subscriber.setMessageListener(this);
-            this.receivedConnection.start();
-        }
-    }
-
-    /**
-     * Attempts to create missing Topic. Since Topic creation is JMS-implementation
-     * specific, this task is left to subclasses. Current implementation simply rethrows
-     * the exception.
-     */
-    protected Topic topicNotFound(Context jndiContext, NamingException ex)
-            throws Exception {
-        throw ex;
-    }
-
-    /**
-     * Closes all resources used to communicate via JMS.
-     */
-    @Override
-    protected void shutdownExternal() throws Exception {
-        Exception lastException = null;
-
-        if (publisher != null) {
-            try {
-                publisher.close();
-            }
-            catch (Exception ex) {
-                lastException = ex;
-            }
-        }
-
-        if (subscriber != null) {
-            try {
-                subscriber.close();
-            }
-            catch (Exception ex) {
-                lastException = ex;
-            }
-        }
-
-        if (receivedConnection != null) {
-            try {
-                receivedConnection.close();
-            }
-            catch (Exception ex) {
-                lastException = ex;
-            }
-        }
-
-        if (sendSession != null) {
-            try {
-                sendSession.close();
-            }
-            catch (Exception ex) {
-                lastException = ex;
-            }
-        }
-
-        if (sendConnection != null) {
-            try {
-                sendConnection.close();
-            }
-            catch (Exception ex) {
-                lastException = ex;
-            }
-        }
-
-        publisher = null;
-        subscriber = null;
-        receivedConnection = null;
-        sendConnection = null;
-        sendSession = null;
-
-        if (lastException != null) {
-            throw lastException;
-        }
-    }
-
-    @Override
-    protected void sendExternalEvent(CayenneEvent localEvent) throws Exception {
-        ObjectMessage message = sendSession
-                .createObjectMessage(eventToMessageObject(localEvent));
-        message.setObjectProperty(JMSBridge.VM_ID_PROPERTY, JMSBridge.VM_ID);
-        publisher.publish(message);
-    }
-
-    /**
-     * Converts CayenneEvent to a serializable object that will be sent via JMS. Default
-     * implementation simply returns the event, but subclasses can customize this
-     * behavior.
-     */
-    protected Serializable eventToMessageObject(CayenneEvent event) throws Exception {
-        return event;
-    }
-
-    /**
-     * Converts a Serializable instance to CayenneEvent. Returns null if the object is not
-     * supported. Default implementation simply tries to cast the object to CayenneEvent,
-     * but subclasses can customize this behavior.
-     */
-    protected CayenneEvent messageObjectToEvent(Serializable object) throws Exception {
-        return (object instanceof CayenneEvent) ? (CayenneEvent) object : null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/main/java/org/apache/cayenne/event/JMSBridgeFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/event/JMSBridgeFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/event/JMSBridgeFactory.java
deleted file mode 100644
index b7772d8..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/event/JMSBridgeFactory.java
+++ /dev/null
@@ -1,39 +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.event;
-
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * Factory to create JMSBridge instances.
- * 
- * @since 1.1
- */
-public class JMSBridgeFactory implements EventBridgeFactory {
-
-    /**
-     * @since 1.2
-     */
-    public EventBridge createEventBridge(Collection<EventSubject> localSubjects, String externalSubject, Map<String, String> properties) {
-        return new JMSBridge(localSubjects, externalSubject, properties);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/main/java/org/apache/cayenne/event/JMSBridgeProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/event/JMSBridgeProvider.java b/cayenne-server/src/main/java/org/apache/cayenne/event/JMSBridgeProvider.java
deleted file mode 100644
index fbd5468..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/event/JMSBridgeProvider.java
+++ /dev/null
@@ -1,50 +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.event;
-
-import org.apache.cayenne.access.DataDomain;
-import org.apache.cayenne.access.DataRowStore;
-import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.di.DIRuntimeException;
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.di.Provider;
-
-import java.util.Collections;
-import java.util.Map;
-
-public class JMSBridgeProvider implements Provider<EventBridge> {
-
-    @Inject
-    protected DataDomain dataDomain;
-
-    @Inject(Constants.JMS_BRIDGE_PROPERTIES_MAP)
-    Map<String, String> properties;
-
-    @Override
-    public EventBridge get() throws DIRuntimeException {
-        EventSubject snapshotEventSubject = EventSubject.getSubject(DataRowStore.class, dataDomain.getName());
-
-        return new JMSBridge(
-                Collections.singleton(snapshotEventSubject),
-                EventBridge.convertToExternalSubject(snapshotEventSubject),
-                properties);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/main/java/org/apache/cayenne/event/JavaGroupsBridge.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/event/JavaGroupsBridge.java b/cayenne-server/src/main/java/org/apache/cayenne/event/JavaGroupsBridge.java
deleted file mode 100644
index 44ea4f0..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/event/JavaGroupsBridge.java
+++ /dev/null
@@ -1,230 +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.event;
-
-import org.jgroups.Channel;
-import org.jgroups.JChannel;
-import org.jgroups.Message;
-import org.jgroups.MessageListener;
-import org.jgroups.blocks.PullPushAdapter;
-
-import java.io.Serializable;
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * Implementation of EventBridge that passes and receives events via JavaGroups
- * communication software.
- * 
- * @since 1.1
- */
-public class JavaGroupsBridge extends EventBridge implements MessageListener {
-
-    public static final String MCAST_ADDRESS_DEFAULT = "228.0.0.5";
-    public static final String MCAST_PORT_DEFAULT = "22222";
-
-    public static final String MCAST_ADDRESS_PROPERTY = "cayenne.JavaGroupsBridge.mcast.address";
-    public static final String MCAST_PORT_PROPERTY = "cayenne.JavaGroupsBridge.mcast.port";
-
-    /**
-     * Defines a property for JavaGroups XML configuration file.
-     */
-    public static final String JGROUPS_CONFIG_URL_PROPERTY = "javagroupsbridge.config.url";
-
-    // TODO: Meaning of "state" in JGroups is not yet clear to me
-    protected byte[] state;
-
-    protected Channel channel;
-    protected PullPushAdapter adapter;
-    protected String multicastAddress;
-    protected String multicastPort;
-    protected String configURL;
-
-    /**
-     * Creates new instance of JavaGroupsBridge.
-     */
-    public JavaGroupsBridge(EventSubject localSubject, String externalSubject) {
-        super(localSubject, externalSubject);
-    }
-
-    /**
-     * @since 1.2
-     */
-    public JavaGroupsBridge(Collection<EventSubject> localSubjects, String externalSubject) {
-        super(localSubjects, externalSubject);
-    }
-
-    /**
-     * @since 4.0
-     */
-    public JavaGroupsBridge(Collection<EventSubject> localSubjects, String externalSubject, Map<String, String> properties) {
-        super(localSubjects, externalSubject);
-
-        // configure properties
-        String multicastAddress = properties.get(MCAST_ADDRESS_PROPERTY);
-        String multicastPort = properties.get(MCAST_PORT_PROPERTY);
-
-        this.configURL = properties.get(JGROUPS_CONFIG_URL_PROPERTY);
-        this.multicastAddress = (multicastAddress != null) ? multicastAddress : MCAST_ADDRESS_DEFAULT;
-        this.multicastPort = (multicastPort != null) ? multicastPort : MCAST_PORT_DEFAULT;
-    }
-
-    public String getConfigURL() {
-        return configURL;
-    }
-
-    public void setConfigURL(String configURL) {
-        this.configURL = configURL;
-    }
-
-    public String getMulticastAddress() {
-        return multicastAddress;
-    }
-
-    public void setMulticastAddress(String multicastAddress) {
-        this.multicastAddress = multicastAddress;
-    }
-
-    public String getMulticastPort() {
-        return multicastPort;
-    }
-
-    public void setMulticastPort(String multicastPort) {
-        this.multicastPort = multicastPort;
-    }
-
-    public byte[] getState() {
-        return state;
-    }
-
-    public void setState(byte[] state) {
-        this.state = state;
-    }
-
-    /**
-     * Implementation of org.javagroups.MessageListener - a callback method to process
-     * incoming messages.
-     */
-    public void receive(Message message) {
-        try {
-            CayenneEvent event = messageObjectToEvent((Serializable) message.getObject());
-            if (event != null) {
-
-                onExternalEvent(event);
-            }
-        }
-        catch (Exception ex) {
-            // TODO: Andrus, 2/8/2006 logging... Log4J was removed to make this usable on
-            // the client
-        }
-    }
-
-    @Override
-    protected void startupExternal() throws Exception {
-        // TODO: need to do more research to figure out the best default transport
-        // settings
-        // to avoid fragmentation, etc.
-
-        // if config file is set, use it, otherwise use a default
-        // set of properties, trying to configure multicast address and port
-        if (configURL != null) {
-            channel = new JChannel(configURL);
-        }
-        else {
-            String configString = buildConfigString();
-            channel = new JChannel(configString);
-        }
-
-        // Important - discard messages from self
-        channel.setOpt(Channel.LOCAL, Boolean.FALSE);
-        channel.connect(externalSubject);
-
-        if (receivesExternalEvents()) {
-            adapter = new PullPushAdapter(channel, this);
-        }
-    }
-
-    /**
-     * Creates JavaGroups configuration String, using preconfigured multicast port and
-     * address.
-     */
-    protected String buildConfigString() {
-        if (multicastAddress == null) {
-            throw new IllegalStateException("'multcastAddress' is not set");
-        }
-
-        if (multicastPort == null) {
-            throw new IllegalStateException("'multcastPort' is not set");
-        }
-
-        return "UDP(mcast_addr="
-                + multicastAddress
-                + ";mcast_port="
-                + multicastPort
-                + ";ip_ttl=32):"
-                + "PING(timeout=3000;num_initial_members=6):"
-                + "FD(timeout=3000):"
-                + "VERIFY_SUSPECT(timeout=1500):"
-                + "pbcast.NAKACK(gc_lag=10;retransmit_timeout=600,1200,2400,4800):"
-                + "pbcast.STABLE(desired_avg_gossip=10000):"
-                + "FRAG:"
-                + "pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;"
-                + "shun=true;print_local_addr=false)";
-    }
-
-    @Override
-    protected void shutdownExternal() throws Exception {
-        try {
-            if (adapter != null) {
-                adapter.stop();
-            }
-
-            channel.close();
-        }
-        finally {
-            adapter = null;
-            channel = null;
-        }
-    }
-
-    @Override
-    protected void sendExternalEvent(CayenneEvent localEvent) throws Exception {
-        Message message = new Message(null, null, eventToMessageObject(localEvent));
-        channel.send(message);
-    }
-
-    /**
-     * Converts CayenneEvent to a serializable object that will be sent via JMS. Default
-     * implementation simply returns the event, but subclasses can customize this
-     * behavior.
-     */
-    protected Serializable eventToMessageObject(CayenneEvent event) throws Exception {
-        return event;
-    }
-
-    /**
-     * Converts a Serializable instance to CayenneEvent. Returns null if the object is not
-     * supported. Default implementation simply tries to cast the object to CayenneEvent,
-     * but subclasses can customize this behavior.
-     */
-    protected CayenneEvent messageObjectToEvent(Serializable object) throws Exception {
-        return (object instanceof CayenneEvent) ? (CayenneEvent) object : null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeFactory.java
deleted file mode 100644
index 302e5ca..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeFactory.java
+++ /dev/null
@@ -1,49 +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.event;
-
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * Factory to create JavaGroupsBridge instances. If JavaGroups library is not installed
- * this factory will return a noop EventBridge as a failover mechanism.
- * <p/>
- * For further information about JavaGroups consult the <a href="http://www.jgroups.org/">documentation</a>.
- *
- * @since 1.1
- */
-public class JavaGroupsBridgeFactory implements EventBridgeFactory {
-
-    /**
-     * Creates a JavaGroupsBridge instance. Since JavaGroups is not shipped with Cayenne
-     * and should be installed separately, a common misconfiguration problem may be the
-     * absence of JavaGroups jar file. This factory returns a dummy noop EventBridge, if
-     * this is the case. This would allow the application to continue to run, but without
-     * remote notifications.
-     */
-    public EventBridge createEventBridge(
-            Collection<EventSubject> localSubjects,
-            String externalSubject,
-            Map<String, String> properties) {
-        return new JavaGroupsBridge(localSubjects, externalSubject, properties);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeProvider.java b/cayenne-server/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeProvider.java
deleted file mode 100644
index c2e184f..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeProvider.java
+++ /dev/null
@@ -1,50 +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.event;
-
-import org.apache.cayenne.access.DataDomain;
-import org.apache.cayenne.access.DataRowStore;
-import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.di.DIRuntimeException;
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.di.Provider;
-
-import java.util.Collections;
-import java.util.Map;
-
-public class JavaGroupsBridgeProvider implements Provider<EventBridge> {
-
-    @Inject
-    protected DataDomain dataDomain;
-
-    @Inject(Constants.JAVA_GROUPS_BRIDGE_PROPERTIES_MAP)
-    Map<String, String> properties;
-
-    @Override
-    public EventBridge get() throws DIRuntimeException {
-        EventSubject snapshotEventSubject = EventSubject.getSubject(DataRowStore.class, dataDomain.getName());
-
-        return new JavaGroupsBridge(
-                Collections.singleton(snapshotEventSubject),
-                EventBridge.convertToExternalSubject(snapshotEventSubject),
-                properties);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/main/java/org/apache/cayenne/event/XMPPBridge.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/event/XMPPBridge.java b/cayenne-server/src/main/java/org/apache/cayenne/event/XMPPBridge.java
deleted file mode 100644
index 10a1386..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/event/XMPPBridge.java
+++ /dev/null
@@ -1,331 +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.event;
-
-import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.util.Base64Codec;
-import org.apache.cayenne.util.Util;
-import org.jivesoftware.smack.GroupChat;
-import org.jivesoftware.smack.PacketListener;
-import org.jivesoftware.smack.SSLXMPPConnection;
-import org.jivesoftware.smack.XMPPConnection;
-import org.jivesoftware.smack.XMPPException;
-import org.jivesoftware.smack.packet.Message;
-import org.jivesoftware.smack.packet.Packet;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-
-/**
- * An EventBridge implementation based on XMPP protocol and Smack XMPP client library.
- * What's good about XMPP (Extensible Messaging and Presence Protocol, an IETF standard
- * protocol that grew up from Jabber IM) is that generally it has fewer or no deployment
- * limitations (unlike JMS and JGroups that are generally a good solution for local
- * controlled networks). Also it provides lots of additional information for free, such as
- * presence, and much more.
- * <p>
- * This implementation is based on Smack XMPP client library from JiveSoftware.
- * </p>
- * 
- * @since 1.2
- */
-public class XMPPBridge extends EventBridge {
-
-    public static final String XMPP_HOST_PROPERTY = "cayenne.XMPPBridge.xmppHost";
-
-    /**
-     * An optional property, port 5222 is used as default XMPP port.
-     */
-    public static final String XMPP_PORT_PROPERTY = "cayenne.XMPPBridge.xmppPort";
-
-    /**
-     * An optional property, "conference" is used as default chat service.
-     */
-    public static final String XMPP_CHAT_SERVICE_PROPERTY = "cayenne.XMPPBridge.xmppChatService";
-
-    public static final String XMPP_SECURE_CONNECTION_PROPERTY = "cayenne.XMPPBridge.xmppSecure";
-    public static final String XMPP_LOGIN_PROPERTY = "cayenne.XMPPBridge.xmppLogin";
-    public static final String XMPP_PASSWORD_PROPERTY = "cayenne.XMPPBridge.xmppPassword";
-
-    static final String DEFAULT_CHAT_SERVICE = "conference";
-    static final int DEFAULT_XMPP_PORT = 5222;
-    static final int DEFAULT_XMPP_SECURE_PORT = 5223;
-
-    protected boolean secureConnection;
-    protected String loginId;
-    protected String password;
-    protected String xmppHost;
-    protected int xmppPort;
-    protected String chatService;
-    protected String sessionHandle;
-
-    protected XMPPConnection connection;
-    protected GroupChat groupChat;
-    protected boolean connected;
-
-    /**
-     * Creates an XMPPBridge. External subject will be used as the chat group name.
-     */
-    public XMPPBridge(EventSubject localSubject, String externalSubject) {
-        this(Collections.singleton(localSubject), externalSubject);
-    }
-
-    /**
-     * Creates an XMPPBridge. External subject will be used as the chat group name.
-     */
-    public XMPPBridge(Collection<EventSubject> localSubjects, String externalSubject) {
-        super(localSubjects, externalSubject);
-
-        // generate a unique session handle... users can override it to use a specific
-        // handle...
-        this.sessionHandle = "cayenne-xmpp-" + System.currentTimeMillis();
-    }
-
-    public XMPPBridge(Collection<EventSubject> localSubjects, String externalSubject, Map<String, String> properties) {
-        this(localSubjects, externalSubject);
-
-        this.chatService = properties.get(XMPP_CHAT_SERVICE_PROPERTY);
-        this.xmppHost = properties.get(XMPP_HOST_PROPERTY);
-
-        this.loginId = properties.get(XMPP_LOGIN_PROPERTY);
-        this.password = properties.get(XMPP_PASSWORD_PROPERTY);
-
-        String secureConnectionString = properties.get(XMPP_SECURE_CONNECTION_PROPERTY);
-        secureConnection = "true".equalsIgnoreCase(secureConnectionString);
-
-        String portString = properties.get(XMPP_PORT_PROPERTY);
-        int port = -1;
-        if (portString != null) {
-
-            try {
-                this.xmppPort = Integer.parseInt(portString);
-            }
-            catch (NumberFormatException e) {
-                throw new CayenneRuntimeException("Invalid port: " + portString);
-            }
-        }
-    }
-
-    public String getXmppHost() {
-        return xmppHost;
-    }
-
-    public void setXmppHost(String xmppHost) {
-        this.xmppHost = xmppHost;
-    }
-
-    public int getXmppPort() {
-        return xmppPort;
-    }
-
-    public void setXmppPort(int xmppPort) {
-        this.xmppPort = xmppPort;
-    }
-
-    public String getLoginId() {
-        return loginId;
-    }
-
-    public void setLoginId(String loginId) {
-        this.loginId = loginId;
-    }
-
-    public String getPassword() {
-        return password;
-    }
-
-    public void setPassword(String password) {
-        this.password = password;
-    }
-
-    public boolean isSecureConnection() {
-        return secureConnection;
-    }
-
-    public void setSecureConnection(boolean secureConnection) {
-        this.secureConnection = secureConnection;
-    }
-
-    public String getChatService() {
-        return chatService;
-    }
-
-    public void setChatService(String chatService) {
-        this.chatService = chatService;
-    }
-
-    public String getSessionHandle() {
-        return sessionHandle;
-    }
-
-    public void setSessionHandle(String sessionHandle) {
-        this.sessionHandle = sessionHandle;
-    }
-
-    @Override
-    protected void startupExternal() throws Exception {
-
-        // validate settings
-        if (xmppHost == null) {
-            throw new CayenneRuntimeException("Null 'xmppHost', can't start XMPPBridge");
-        }
-
-        // shutdown old bridge
-        if (connected) {
-            shutdownExternal();
-        }
-
-        try {
-            // connect and log in to chat
-            if (secureConnection) {
-                int port = xmppPort > 0 ? xmppPort : DEFAULT_XMPP_SECURE_PORT;
-                this.connection = new SSLXMPPConnection(xmppHost, port);
-            }
-            else {
-                int port = xmppPort > 0 ? xmppPort : DEFAULT_XMPP_PORT;
-                this.connection = new XMPPConnection(xmppHost, port);
-            }
-
-            if (loginId != null) {
-                // it is important to provide a (pseudo) globally unique string as the
-                // third argument ("sessionHandle" is such string); without it same
-                // loginId can not be reused from the same machine.
-                connection.login(loginId, password, sessionHandle);
-            }
-            else {
-                connection.loginAnonymously();
-            }
-        }
-        catch (XMPPException e) {
-            throw new CayenneRuntimeException("Error connecting to XMPP Server"
-                    + e.getLocalizedMessage());
-        }
-
-        String service = this.chatService != null
-                ? this.chatService
-                : DEFAULT_CHAT_SERVICE;
-
-        try {
-            this.groupChat = connection.createGroupChat(externalSubject
-                    + '@'
-                    + service
-                    + "."
-                    + connection.getHost());
-
-            groupChat.join(sessionHandle);
-            groupChat.addMessageListener(new XMPPListener());
-        }
-        catch (XMPPException e) {
-            throw new CayenneRuntimeException("Error setting up a group chat: "
-                    + e.getLocalizedMessage());
-        }
-
-        this.connected = true;
-    }
-
-    @Override
-    protected void shutdownExternal() throws Exception {
-        if (groupChat != null) {
-            groupChat.leave();
-            groupChat = null;
-        }
-
-        if (connection != null) {
-            connection.close();
-            connection = null;
-        }
-
-        connected = false;
-    }
-
-    @Override
-    protected void sendExternalEvent(CayenneEvent localEvent) throws Exception {
-
-        Message message = groupChat.createMessage();
-        message.setBody(serializeToString(localEvent));
-
-        // set thread to our session handle to be able to discard messages from self
-        message.setThread(sessionHandle);
-
-        groupChat.sendMessage(message);
-    }
-
-    class XMPPListener implements PacketListener {
-
-        public void processPacket(Packet packet) {
-
-            if (packet instanceof Message) {
-                Message message = (Message) packet;
-
-                // filter our own messages
-                if (sessionHandle.equals(message.getThread())) {
-                    // discarding
-                }
-                else {
-
-                    String payload = message.getBody();
-
-                    try {
-                        Object event = deserializeFromString(payload);
-                        if (event instanceof CayenneEvent) {
-                            onExternalEvent((CayenneEvent) event);
-                        }
-                    }
-                    catch (Exception ex) {
-                        // ignore for now... need to add logging.
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Decodes the String (assuming it is using Base64 encoding), and then deserializes
-     * object from the byte array.
-     */
-    static Object deserializeFromString(String string) throws Exception {
-        if (Util.isEmptyString(string)) {
-            return null;
-        }
-
-        byte[] bytes = Base64Codec.decodeBase64(string.getBytes());
-        ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
-        Object object = in.readObject();
-        in.close();
-        return object;
-    }
-
-    /**
-     * Serializes object and then encodes it using Base64 encoding.
-     */
-    static String serializeToString(Object object) throws Exception {
-        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-        ObjectOutputStream out = new ObjectOutputStream(bytes);
-        out.writeObject(object);
-        out.close();
-
-        return new String(Base64Codec.encodeBase64(bytes.toByteArray()));
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/main/java/org/apache/cayenne/event/XMPPBridgeFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/event/XMPPBridgeFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/event/XMPPBridgeFactory.java
deleted file mode 100644
index 7934752..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/event/XMPPBridgeFactory.java
+++ /dev/null
@@ -1,41 +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.event;
-
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * A factory of XMPPBridge. Note that to deploy an XMPPBridge, you need to have
- * <em>smack.jar</em> library in the runtime.
- * 
- * @since 1.2
- */
-public class XMPPBridgeFactory implements EventBridgeFactory {
-
-    @Override
-    public EventBridge createEventBridge(
-            Collection<EventSubject> localSubjects,
-            String externalSubject,
-            Map<String, String> properties) {
-        return new XMPPBridge(localSubjects, externalSubject, properties);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/main/java/org/apache/cayenne/event/XMPPBridgeProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/event/XMPPBridgeProvider.java b/cayenne-server/src/main/java/org/apache/cayenne/event/XMPPBridgeProvider.java
deleted file mode 100644
index 2870edf..0000000
--- a/cayenne-server/src/main/java/org/apache/cayenne/event/XMPPBridgeProvider.java
+++ /dev/null
@@ -1,50 +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.event;
-
-import org.apache.cayenne.access.DataDomain;
-import org.apache.cayenne.access.DataRowStore;
-import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.di.DIRuntimeException;
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.di.Provider;
-
-import java.util.Collections;
-import java.util.Map;
-
-public class XMPPBridgeProvider implements Provider<EventBridge> {
-
-    @Inject
-    protected DataDomain dataDomain;
-
-    @Inject(Constants.XMPP_BRIDGE_PROPERTIES_MAP)
-    Map<String, String> properties;
-
-    @Override
-    public EventBridge get() throws DIRuntimeException {
-        EventSubject snapshotEventSubject = EventSubject.getSubject(DataRowStore.class.getClass(), dataDomain.getName());
-
-        return new XMPPBridge(
-                Collections.singleton(snapshotEventSubject),
-                EventBridge.convertToExternalSubject(snapshotEventSubject),
-                properties);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/test/java/org/apache/cayenne/event/JMSBridgeFactoryTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/event/JMSBridgeFactoryTest.java b/cayenne-server/src/test/java/org/apache/cayenne/event/JMSBridgeFactoryTest.java
deleted file mode 100644
index e098d4e..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/event/JMSBridgeFactoryTest.java
+++ /dev/null
@@ -1,77 +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.event;
-
-import org.junit.Test;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-public class JMSBridgeFactoryTest {
-
-    protected Collection<EventSubject> subjects = Collections.singleton(new EventSubject("test"));
-    protected String externalSubject = "subject";
-
-    @Test
-    public void testCreateEventBridge() throws Exception {
-        EventBridge bridge = new JMSBridgeFactory().createEventBridge(
-                subjects,
-                externalSubject,
-                Collections.EMPTY_MAP);
-
-        assertNotNull(bridge);
-        assertTrue(bridge instanceof JMSBridge);
-        assertEquals(subjects, bridge.getLocalSubjects());
-        assertEquals(externalSubject, bridge.getExternalSubject());
-    }
-
-    @Test
-    public void testUseProperties() throws Exception {
-        JMSBridgeFactory bridgeFactory = new JMSBridgeFactory();
-
-        Map<String, String> properties = new HashMap<String, String>();
-        properties.put(JMSBridge.TOPIC_CONNECTION_FACTORY_PROPERTY, JMSBridgeProviderTest.TOPIC_CONNECTION_FACTORY_TEST);
-
-        JMSBridge bridge = (JMSBridge) bridgeFactory.createEventBridge(
-                subjects,
-                externalSubject,
-                properties);
-
-        assertEquals(bridge.getTopicConnectionFactoryName(), JMSBridgeProviderTest.TOPIC_CONNECTION_FACTORY_TEST);
-    }
-
-    @Test
-    public void testUseDefaultProperties() throws Exception {
-        JMSBridgeFactory bridgeFactory = new JMSBridgeFactory();
-        JMSBridge bridge = (JMSBridge) bridgeFactory.createEventBridge(
-                subjects,
-                externalSubject,
-                Collections.EMPTY_MAP);
-
-        assertEquals(bridge.getTopicConnectionFactoryName(), JMSBridge.TOPIC_CONNECTION_FACTORY_DEFAULT);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java b/cayenne-server/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java
deleted file mode 100644
index c683666..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java
+++ /dev/null
@@ -1,119 +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.event;
-
-import org.apache.cayenne.access.DataDomain;
-import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.configuration.DefaultRuntimeProperties;
-import org.apache.cayenne.configuration.RuntimeProperties;
-import org.apache.cayenne.di.Binder;
-import org.apache.cayenne.di.DIBootstrap;
-import org.apache.cayenne.di.Injector;
-import org.apache.cayenne.di.Module;
-import org.apache.cayenne.log.CommonsJdbcEventLogger;
-import org.apache.cayenne.log.JdbcEventLogger;
-import org.apache.cayenne.tx.DefaultTransactionFactory;
-import org.apache.cayenne.tx.DefaultTransactionManager;
-import org.apache.cayenne.tx.TransactionFactory;
-import org.apache.cayenne.tx.TransactionManager;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-public class JMSBridgeProviderTest {
-
-    private final DataDomain DOMAIN = new DataDomain("test");
-    private final EventManager EVENT_MANAGER = new DefaultEventManager();
-    protected static final String TOPIC_CONNECTION_FACTORY_TEST = "SomeTopicConnectionFactory";
-
-    @Test
-    public void testGetJMSBridge() throws Exception {
-        Module module = new Module() {
-
-            public void configure(Binder binder) {
-                binder.bindMap(Constants.PROPERTIES_MAP);
-                binder.bind(DataDomain.class).toInstance(DOMAIN);
-                binder.bind(EventManager.class).toInstance(EVENT_MANAGER);
-                binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
-                binder.bind(TransactionFactory.class).to(DefaultTransactionFactory.class);
-                binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
-                binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
-                binder.bind(EventBridge.class).toProvider(JMSBridgeProvider.class);
-                binder.bindMap(Constants.JMS_BRIDGE_PROPERTIES_MAP);
-            }
-        };
-
-        Injector injector = DIBootstrap.createInjector(module);
-        EventBridge bridge = injector.getInstance(EventBridge.class);
-
-        assertNotNull(bridge);
-        assertTrue(bridge instanceof JMSBridge);
-    }
-
-    @Test
-    public void testUseProperties() throws Exception {
-        Module module = new Module() {
-
-            public void configure(Binder binder) {
-                binder.bindMap(Constants.PROPERTIES_MAP);
-                binder.bind(DataDomain.class).toInstance(DOMAIN);
-                binder.bind(EventManager.class).toInstance(EVENT_MANAGER);
-                binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
-                binder.bind(TransactionFactory.class).to(DefaultTransactionFactory.class);
-                binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
-                binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
-                binder.bind(EventBridge.class).toProvider(JMSBridgeProvider.class);
-                binder.bindMap(Constants.JMS_BRIDGE_PROPERTIES_MAP)
-                        .put(JMSBridge.TOPIC_CONNECTION_FACTORY_PROPERTY, TOPIC_CONNECTION_FACTORY_TEST);
-            }
-        };
-
-        Injector injector = DIBootstrap.createInjector(module);
-        JMSBridge bridge = (JMSBridge) injector.getInstance(EventBridge.class);
-
-        assertEquals(JMSBridgeProviderTest.TOPIC_CONNECTION_FACTORY_TEST, bridge.getTopicConnectionFactoryName());
-    }
-
-    @Test
-    public void testUseDefaultProperties() throws Exception {
-        Module module = new Module() {
-
-            public void configure(Binder binder) {
-                binder.bindMap(Constants.PROPERTIES_MAP);
-                binder.bind(DataDomain.class).toInstance(DOMAIN);
-                binder.bind(EventManager.class).toInstance(EVENT_MANAGER);
-                binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
-                binder.bind(TransactionFactory.class).to(DefaultTransactionFactory.class);
-                binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
-                binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
-                binder.bind(EventBridge.class).toProvider(JMSBridgeProvider.class);
-                binder.bindMap(Constants.JMS_BRIDGE_PROPERTIES_MAP);
-            }
-        };
-
-        Injector injector = DIBootstrap.createInjector(module);
-        JMSBridge bridge = (JMSBridge) injector.getInstance(EventBridge.class);
-
-        assertEquals(bridge.getTopicConnectionFactoryName(), JMSBridge.TOPIC_CONNECTION_FACTORY_DEFAULT);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeFactoryTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeFactoryTest.java b/cayenne-server/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeFactoryTest.java
deleted file mode 100644
index 3497888..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeFactoryTest.java
+++ /dev/null
@@ -1,84 +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.event;
-
-import org.junit.Test;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-/**
- */
-public class JavaGroupsBridgeFactoryTest {
-
-    protected Collection<EventSubject> subjects = Collections.singleton(new EventSubject("test"));
-    protected String externalSubject = "subject";
-
-    @Test
-    public void testCreateEventBridge() throws Exception {
-        EventBridge bridge = new JavaGroupsBridgeFactory().createEventBridge(
-                subjects,
-                externalSubject,
-                Collections.EMPTY_MAP);
-
-        assertNotNull(bridge);
-        assertTrue(bridge instanceof JavaGroupsBridge);
-        assertEquals(subjects, bridge.getLocalSubjects());
-        assertEquals(externalSubject, bridge.getExternalSubject());
-    }
-
-    @Test
-    public void testUseProperties() throws Exception {
-        JavaGroupsBridgeFactory bridgeFactory = new JavaGroupsBridgeFactory();
-
-        Map<String, String> properties = new HashMap<String, String>();
-        properties.put(JavaGroupsBridge.MCAST_ADDRESS_PROPERTY, JavaGroupsBridgeProviderTest.MCAST_ADDRESS_TEST);
-        properties.put(JavaGroupsBridge.MCAST_PORT_PROPERTY, JavaGroupsBridgeProviderTest.MCAST_PORT_TEST);
-        properties.put(JavaGroupsBridge.JGROUPS_CONFIG_URL_PROPERTY, JavaGroupsBridgeProviderTest.CONFIG_URL_TEST);
-
-        JavaGroupsBridge bridge = (JavaGroupsBridge) bridgeFactory.createEventBridge(
-                subjects,
-                externalSubject,
-                properties);
-
-        assertEquals(bridge.getMulticastAddress(), JavaGroupsBridgeProviderTest.MCAST_ADDRESS_TEST);
-        assertEquals(bridge.getMulticastPort(), JavaGroupsBridgeProviderTest.MCAST_PORT_TEST);
-        assertEquals(bridge.getConfigURL(), JavaGroupsBridgeProviderTest.CONFIG_URL_TEST);
-    }
-
-    @Test
-    public void testUseDefaultProperties() throws Exception {
-        JavaGroupsBridgeFactory bridgeFactory = new JavaGroupsBridgeFactory();
-        JavaGroupsBridge bridge = (JavaGroupsBridge) bridgeFactory.createEventBridge(
-                subjects,
-                externalSubject,
-                Collections.EMPTY_MAP);
-
-        assertEquals(bridge.getMulticastAddress(), JavaGroupsBridge.MCAST_ADDRESS_DEFAULT);
-        assertEquals(bridge.getMulticastPort(), JavaGroupsBridge.MCAST_PORT_DEFAULT);
-        assertEquals(bridge.getConfigURL(), null);
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeProviderTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeProviderTest.java b/cayenne-server/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeProviderTest.java
deleted file mode 100644
index 6e82cd5..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeProviderTest.java
+++ /dev/null
@@ -1,123 +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.event;
-
-import org.apache.cayenne.access.DataDomain;
-import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.configuration.DefaultRuntimeProperties;
-import org.apache.cayenne.configuration.RuntimeProperties;
-import org.apache.cayenne.di.Binder;
-import org.apache.cayenne.di.DIBootstrap;
-import org.apache.cayenne.di.Injector;
-import org.apache.cayenne.di.Module;
-import org.apache.cayenne.log.CommonsJdbcEventLogger;
-import org.apache.cayenne.log.JdbcEventLogger;
-import org.apache.cayenne.tx.DefaultTransactionFactory;
-import org.apache.cayenne.tx.DefaultTransactionManager;
-import org.apache.cayenne.tx.TransactionFactory;
-import org.apache.cayenne.tx.TransactionManager;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-public class JavaGroupsBridgeProviderTest {
-
-    private final DataDomain DOMAIN = new DataDomain("test");
-    private final EventManager EVENT_MANAGER = new DefaultEventManager();
-    protected static final String MCAST_ADDRESS_TEST = "192.168.0.0";
-    protected static final String MCAST_PORT_TEST = "1521";
-    protected static final String CONFIG_URL_TEST = "somehost.com";
-
-    @Test
-    public void testGetJavaGroupsBridge() throws Exception {
-        Module module = new Module() {
-            public void configure(Binder binder) {
-                binder.bindMap(Constants.PROPERTIES_MAP);
-                binder.bind(DataDomain.class).toInstance(DOMAIN);
-                binder.bind(EventManager.class).toInstance(EVENT_MANAGER);
-                binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
-                binder.bind(TransactionFactory.class).to(DefaultTransactionFactory.class);
-                binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
-                binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
-                binder.bind(EventBridge.class).toProvider(JavaGroupsBridgeProvider.class);
-                binder.bindMap(Constants.JAVA_GROUPS_BRIDGE_PROPERTIES_MAP);
-            }
-        };
-
-        Injector injector = DIBootstrap.createInjector(module);
-        EventBridge bridge = injector.getInstance(EventBridge.class);
-
-        assertNotNull(bridge);
-        assertTrue(bridge instanceof JavaGroupsBridge);
-    }
-
-    @Test
-    public void testUseProperties() throws Exception {
-        Module module = new Module() {
-            public void configure(Binder binder) {
-                binder.bindMap(Constants.PROPERTIES_MAP);
-                binder.bind(DataDomain.class).toInstance(DOMAIN);
-                binder.bind(EventManager.class).toInstance(EVENT_MANAGER);
-                binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
-                binder.bind(TransactionFactory.class).to(DefaultTransactionFactory.class);
-                binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
-                binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
-                binder.bind(EventBridge.class).toProvider(JavaGroupsBridgeProvider.class);
-                binder.bindMap(Constants.JAVA_GROUPS_BRIDGE_PROPERTIES_MAP)
-                        .put(JavaGroupsBridge.MCAST_ADDRESS_PROPERTY, MCAST_ADDRESS_TEST)
-                        .put(JavaGroupsBridge.MCAST_PORT_PROPERTY, MCAST_PORT_TEST)
-                        .put(JavaGroupsBridge.JGROUPS_CONFIG_URL_PROPERTY, CONFIG_URL_TEST);
-            }
-        };
-
-        Injector injector = DIBootstrap.createInjector(module);
-        JavaGroupsBridge bridge = (JavaGroupsBridge) injector.getInstance(EventBridge.class);
-
-        assertEquals(MCAST_ADDRESS_TEST, bridge.getMulticastAddress());
-        assertEquals(MCAST_PORT_TEST, bridge.getMulticastPort());
-        assertEquals(CONFIG_URL_TEST, bridge.getConfigURL());
-    }
-
-    @Test
-    public void testUseDefaultProperties() throws Exception {
-        Module module = new Module() {
-            public void configure(Binder binder) {
-                binder.bindMap(Constants.PROPERTIES_MAP);
-                binder.bind(DataDomain.class).toInstance(DOMAIN);
-                binder.bind(EventManager.class).toInstance(EVENT_MANAGER);
-                binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
-                binder.bind(TransactionFactory.class).to(DefaultTransactionFactory.class);
-                binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
-                binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
-                binder.bind(EventBridge.class).toProvider(JavaGroupsBridgeProvider.class);
-                binder.bindMap(Constants.JAVA_GROUPS_BRIDGE_PROPERTIES_MAP);
-            }
-        };
-
-        Injector injector = DIBootstrap.createInjector(module);
-        JavaGroupsBridge bridge = (JavaGroupsBridge) injector.getInstance(EventBridge.class);
-
-        assertEquals(bridge.getMulticastAddress(), JavaGroupsBridge.MCAST_ADDRESS_DEFAULT);
-        assertEquals(bridge.getMulticastPort(), JavaGroupsBridge.MCAST_PORT_DEFAULT);
-        assertEquals(bridge.getConfigURL(), null);
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/test/java/org/apache/cayenne/event/XMPPBridgeFactoryTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/event/XMPPBridgeFactoryTest.java b/cayenne-server/src/test/java/org/apache/cayenne/event/XMPPBridgeFactoryTest.java
deleted file mode 100644
index fb54ede..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/event/XMPPBridgeFactoryTest.java
+++ /dev/null
@@ -1,72 +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.event;
-
-import org.junit.Test;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-public class XMPPBridgeFactoryTest {
-
-    protected Collection<EventSubject> subjects = Collections.singleton(new EventSubject("test"));
-    protected String externalSubject = "subject";
-
-    @Test
-    public void testCreateEventBridge() {
-        EventBridge bridge = new XMPPBridgeFactory().createEventBridge(
-                subjects,
-                externalSubject,
-                Collections.EMPTY_MAP);
-
-        assertTrue(bridge instanceof XMPPBridge);
-        assertEquals(subjects, bridge.getLocalSubjects());
-        assertEquals(externalSubject, bridge.getExternalSubject());
-    }
-
-    @Test
-    public void testUseMapPropertiesSetter() throws Exception {
-        XMPPBridgeFactory bridgeFactory = new XMPPBridgeFactory();
-        Map<String, String> properties = new HashMap<String, String>();
-
-        properties.put(XMPPBridge.XMPP_HOST_PROPERTY, XMPPBridgeProviderTest.HOST_TEST);
-        properties.put(XMPPBridge.XMPP_CHAT_SERVICE_PROPERTY, XMPPBridgeProviderTest.CHAT_SERVICE_TEST);
-        properties.put(XMPPBridge.XMPP_LOGIN_PROPERTY, XMPPBridgeProviderTest.LOGIN_TEST);
-        properties.put(XMPPBridge.XMPP_PASSWORD_PROPERTY, XMPPBridgeProviderTest.PASSWORD_TEST);
-        properties.put(XMPPBridge.XMPP_SECURE_CONNECTION_PROPERTY, String.valueOf(XMPPBridgeProviderTest.SECURE_CONNECTION_TEST));
-        properties.put(XMPPBridge.XMPP_PORT_PROPERTY, String.valueOf(XMPPBridgeProviderTest.PORT_TEST));
-
-        XMPPBridge bridge = (XMPPBridge) bridgeFactory.createEventBridge(subjects,
-                externalSubject,
-                properties);
-
-        assertEquals(bridge.getXmppHost(), XMPPBridgeProviderTest.HOST_TEST);
-        assertEquals(bridge.getChatService(), XMPPBridgeProviderTest.CHAT_SERVICE_TEST);
-        assertEquals(bridge.getLoginId(), XMPPBridgeProviderTest.LOGIN_TEST);
-        assertEquals(bridge.getPassword(), XMPPBridgeProviderTest.PASSWORD_TEST);
-        assertEquals(bridge.getXmppPort(), XMPPBridgeProviderTest.PORT_TEST);
-        assertEquals(bridge.isSecureConnection(), XMPPBridgeProviderTest.SECURE_CONNECTION_TEST);
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/test/java/org/apache/cayenne/event/XMPPBridgeProviderTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/event/XMPPBridgeProviderTest.java b/cayenne-server/src/test/java/org/apache/cayenne/event/XMPPBridgeProviderTest.java
deleted file mode 100644
index 7d225a5..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/event/XMPPBridgeProviderTest.java
+++ /dev/null
@@ -1,130 +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.event;
-
-import org.apache.cayenne.access.DataDomain;
-import org.apache.cayenne.configuration.Constants;
-import org.apache.cayenne.configuration.DefaultRuntimeProperties;
-import org.apache.cayenne.configuration.RuntimeProperties;
-import org.apache.cayenne.di.Binder;
-import org.apache.cayenne.di.DIBootstrap;
-import org.apache.cayenne.di.Injector;
-import org.apache.cayenne.di.Module;
-import org.apache.cayenne.log.CommonsJdbcEventLogger;
-import org.apache.cayenne.log.JdbcEventLogger;
-import org.apache.cayenne.tx.DefaultTransactionFactory;
-import org.apache.cayenne.tx.DefaultTransactionManager;
-import org.apache.cayenne.tx.TransactionFactory;
-import org.apache.cayenne.tx.TransactionManager;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-public class XMPPBridgeProviderTest {
-
-    private final DataDomain DOMAIN = new DataDomain("test");
-    private final EventManager EVENT_MANAGER = new DefaultEventManager();
-    protected static final String HOST_TEST = "somehost.com";
-    protected static final String CHAT_SERVICE_TEST = "conference";
-    protected static final String LOGIN_TEST = "login";
-    protected static final String PASSWORD_TEST = "password";
-    protected static final boolean SECURE_CONNECTION_TEST = true;
-    protected static final int PORT_TEST = 12345;
-
-    @Test
-    public void testGetXMPPBridge() throws Exception {
-        Module module = new Module() {
-            public void configure(Binder binder) {
-                binder.bindMap(Constants.PROPERTIES_MAP);
-                binder.bind(DataDomain.class).toInstance(DOMAIN);
-                binder.bind(EventManager.class).toInstance(EVENT_MANAGER);
-                binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
-                binder.bind(TransactionFactory.class).to(DefaultTransactionFactory.class);
-                binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
-                binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
-                binder.bind(EventBridge.class).toProvider(XMPPBridgeProvider.class);
-                binder.bindMap(Constants.XMPP_BRIDGE_PROPERTIES_MAP);
-            }
-        };
-
-        Injector injector = DIBootstrap.createInjector(module);
-        EventBridge bridge = injector.getInstance(EventBridge.class);
-
-        assertNotNull(bridge);
-        assertTrue(bridge instanceof XMPPBridge);
-    }
-
-    @Test
-    public void testUseProperties() throws Exception {
-        Module module = new Module() {
-            public void configure(Binder binder) {
-                binder.bindMap(Constants.PROPERTIES_MAP);
-                binder.bind(DataDomain.class).toInstance(DOMAIN);
-                binder.bind(EventManager.class).toInstance(EVENT_MANAGER);
-                binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
-                binder.bind(TransactionFactory.class).to(DefaultTransactionFactory.class);
-                binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
-                binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
-                binder.bind(EventBridge.class).toProvider(XMPPBridgeProvider.class);
-                binder.bindMap(Constants.XMPP_BRIDGE_PROPERTIES_MAP)
-                        .put(XMPPBridge.XMPP_HOST_PROPERTY, HOST_TEST)
-                        .put(XMPPBridge.XMPP_CHAT_SERVICE_PROPERTY, CHAT_SERVICE_TEST)
-                        .put(XMPPBridge.XMPP_LOGIN_PROPERTY, LOGIN_TEST)
-                        .put(XMPPBridge.XMPP_PASSWORD_PROPERTY, PASSWORD_TEST)
-                        .put(XMPPBridge.XMPP_SECURE_CONNECTION_PROPERTY, String.valueOf(SECURE_CONNECTION_TEST))
-                        .put(XMPPBridge.XMPP_PORT_PROPERTY, String.valueOf(PORT_TEST));
-            }
-        };
-
-        Injector injector = DIBootstrap.createInjector(module);
-        XMPPBridge bridge = (XMPPBridge) injector.getInstance(EventBridge.class);
-
-        assertEquals(HOST_TEST, bridge.getXmppHost());
-        assertEquals(CHAT_SERVICE_TEST, bridge.getChatService());
-        assertEquals(LOGIN_TEST, bridge.getLoginId());
-        assertEquals(PASSWORD_TEST, bridge.getPassword());
-        assertEquals(SECURE_CONNECTION_TEST, bridge.isSecureConnection());
-        assertEquals(PORT_TEST, bridge.getXmppPort());
-    }
-
-    public void testUseDefaultProperties() throws Exception {
-        Module module = new Module() {
-            public void configure(Binder binder) {
-                binder.bindMap(Constants.PROPERTIES_MAP);
-                binder.bind(DataDomain.class).toInstance(DOMAIN);
-                binder.bind(EventManager.class).toInstance(EVENT_MANAGER);
-                binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
-                binder.bind(TransactionFactory.class).to(DefaultTransactionFactory.class);
-                binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
-                binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
-                binder.bind(EventBridge.class).toProvider(XMPPBridgeProvider.class);
-                binder.bindMap(Constants.XMPP_BRIDGE_PROPERTIES_MAP);
-            }
-        };
-
-        Injector injector = DIBootstrap.createInjector(module);
-        XMPPBridge bridge = (XMPPBridge) injector.getInstance(EventBridge.class);
-
-        assertEquals(bridge.getChatService(), XMPPBridge.DEFAULT_CHAT_SERVICE);
-        assertEquals(bridge.getXmppPort(), XMPPBridge.DEFAULT_XMPP_PORT);
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/cayenne-server/src/test/java/org/apache/cayenne/event/XMPPBridgeTest.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/event/XMPPBridgeTest.java b/cayenne-server/src/test/java/org/apache/cayenne/event/XMPPBridgeTest.java
deleted file mode 100644
index d3d0a80..0000000
--- a/cayenne-server/src/test/java/org/apache/cayenne/event/XMPPBridgeTest.java
+++ /dev/null
@@ -1,54 +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.event;
-
-import org.junit.Test;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-/**
- */
-public class XMPPBridgeTest {
-
-    @Test
-    public void testEventSerialization() throws Exception {
-        Map info = new HashMap();
-        info.put("a", "b");
-        CayenneEvent e = new CayenneEvent(this, this, info);
-
-        String string = XMPPBridge.serializeToString(e);
-        assertNotNull(string);
-
-        Object copy = XMPPBridge.deserializeFromString(string);
-        assertNotNull(copy);
-        assertTrue(copy instanceof CayenneEvent);
-
-        CayenneEvent e2 = (CayenneEvent) copy;
-        assertEquals(info, e2.getInfo());
-        assertNull(e2.getPostedBy());
-        assertNull(e2.getSource());
-    }
-}


[2/3] cayenne git commit: CAY-2266 Move EventBridge implementations into autoloadable modules

Posted by nt...@apache.org.
http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/docs/doc/pom.xml
----------------------------------------------------------------------
diff --git a/docs/doc/pom.xml b/docs/doc/pom.xml
index 011bda6..00e84d4 100644
--- a/docs/doc/pom.xml
+++ b/docs/doc/pom.xml
@@ -83,11 +83,6 @@
 		</dependency>
 
 		<dependency>
-			<groupId>org.apache.geronimo.specs</groupId>
-			<artifactId>geronimo-jms_1.1_spec</artifactId>
-		</dependency>
-
-		<dependency>
 			<groupId>ognl</groupId>
 			<artifactId>ognl</artifactId>
 		</dependency>
@@ -98,11 +93,6 @@
 		</dependency>
 
 		<dependency>
-			<groupId>jivesoftware</groupId>
-			<artifactId>smack</artifactId>
-		</dependency>
-
-		<dependency>
 			<groupId>opensymphony</groupId>
 			<artifactId>oscache</artifactId>
 		</dependency>
@@ -178,11 +168,6 @@
 					<stylesheet>java</stylesheet>
                     <additionalDependencies>
                         <additionalDependency>
-                            <groupId>jgroups</groupId>
-                            <artifactId>jgroups-all</artifactId>
-                            <version>2.2.7</version>
-                        </additionalDependency>
-                        <additionalDependency>
                             <groupId>net.sf.ehcache</groupId>
                             <artifactId>ehcache-core</artifactId>
                             <version>2.4.3</version>

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index 57547c1..5c8be17 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -13,7 +13,8 @@ Date:
 ----------------------------------
 Changes/New Features:
 
-
+CAY-1873 Move DataDomain cache configuration from the Modeler and into DI
+CAY-2266 Move EventBridge implementations into autoloadable modules
 
 Bug Fixes:
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/docs/doc/src/main/resources/UPGRADE.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/UPGRADE.txt b/docs/doc/src/main/resources/UPGRADE.txt
index 43cbc20..f90a816 100644
--- a/docs/doc/src/main/resources/UPGRADE.txt
+++ b/docs/doc/src/main/resources/UPGRADE.txt
@@ -7,6 +7,34 @@ IMPORTANT: be sure to read all notes for the intermediate releases between your
 
 UPGRADING TO 4.0.M6
 
+* Per CAY-1873 and CAY-2266 Cache and remote notification configuration was moved
+  from Modeler into runtime DI settings. To set custom cache size and expiration time,
+  you should use custom module like this:
+        Module module = binder -> {
+            binder.bindMap(Constants.DATA_ROW_STORE_PROPERTIES_MAP)
+                .put(DataRowStore.SNAPSHOT_EXPIRATION_PROPERTY, "10800")
+                .put(DataRowStore.SNAPSHOT_CACHE_SIZE_PROPERTY, "20000");
+        };
+
+  If you have used remote notifications, you should include one of the following modules into your project:
+        - cayenne-jgroups
+        - cayenne-jms
+        - cayenne-xmpp
+  For maven users this can be easily done by adding dependency to pom.xml:
+        <dependency>
+            <groupId>org.apache.cayenne</groupId>
+            <artifactId>cayenne-jgroups</artifactId>
+            <version>4.0.M6</version>
+        </dependency>
+
+  Module will be autoloaded and remote notifications enabled, so only thing you need is to provide configuration.
+  Custom DI module should be used for that, e.g. for JGroups:
+        Module module = binder -> {
+            JGroupsModule.contributeMulticastAddress(binder, MCAST_ADDRESS);
+            JGroupsModule.contributeMulticastPort(binder, MCAST_PORT));
+        };
+
+
 * Per CAY-2256 Fix for CAY-2146 was reverted, as it appears that we can't reliably deduce whether
   relationship is optional or not. So in case of mandatory relationships in vertical inheritance
   you should perform manual validation before insert by using "prePersist" callback in your

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jgroups/pom.xml
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jgroups/pom.xml b/eventbridges/cayenne-jgroups/pom.xml
new file mode 100644
index 0000000..8ba3fcb
--- /dev/null
+++ b/eventbridges/cayenne-jgroups/pom.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~   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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<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">
+    <parent>
+        <artifactId>cayenne-eventbridges-parent</artifactId>
+        <groupId>org.apache.cayenne</groupId>
+        <version>4.0.M6-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>cayenne-jgroups</artifactId>
+    <name>cayenne-jgroups: Cayenne JGroups Event bridge</name>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>jgroups</groupId>
+            <artifactId>jgroups-all</artifactId>
+            <version>2.2.7</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsModule.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsModule.java b/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsModule.java
new file mode 100644
index 0000000..8dc1346
--- /dev/null
+++ b/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsModule.java
@@ -0,0 +1,63 @@
+/*****************************************************************
+ *   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.event;
+
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.MapBuilder;
+import org.apache.cayenne.di.Module;
+
+/**
+ * @since 4.0
+ */
+public class JGroupsModule implements Module {
+
+    /**
+     * A DI container key for the Map&lt;String, String&gt; storing
+     * {@link org.apache.cayenne.event.JavaGroupsBridge} properties
+     *
+     * @since 4.0
+     */
+    public static final String JAVA_GROUPS_BRIDGE_PROPERTIES_MAP = "cayenne.server.java_group_bridge";
+
+    public static void contributeMulticastAddress(Binder binder, String address) {
+        contributeProperties(binder).put(JavaGroupsBridge.MCAST_ADDRESS_PROPERTY, address);
+    }
+
+    public static void contributeMulticastPort(Binder binder, int port) {
+        contributeProperties(binder).put(JavaGroupsBridge.MCAST_PORT_PROPERTY, Integer.toString(port));
+    }
+
+    public static void contributeConfigUrl(Binder binder, String config) {
+        contributeProperties(binder).put(JavaGroupsBridge.JGROUPS_CONFIG_URL_PROPERTY, config);
+    }
+
+    private static MapBuilder<String> contributeProperties(Binder binder) {
+        return binder.bindMap(JAVA_GROUPS_BRIDGE_PROPERTIES_MAP);
+    }
+
+    @Override
+    public void configure(Binder binder) {
+        // init properties' defaults
+        contributeMulticastAddress(binder, JavaGroupsBridge.MCAST_ADDRESS_DEFAULT);
+        contributeMulticastPort(binder, JavaGroupsBridge.MCAST_PORT_DEFAULT_INT);
+
+        binder.bind(EventBridge.class).toProvider(JavaGroupsBridgeProvider.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsModuleProvider.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsModuleProvider.java b/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsModuleProvider.java
new file mode 100644
index 0000000..00e9555
--- /dev/null
+++ b/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsModuleProvider.java
@@ -0,0 +1,50 @@
+/*****************************************************************
+ *   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.event;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.cayenne.configuration.server.ServerModule;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.di.spi.ModuleProvider;
+
+/**
+ * @since 4.0
+ */
+public class JGroupsModuleProvider implements ModuleProvider {
+
+    @Override
+    public Module module() {
+        return new JGroupsModule();
+    }
+
+    @Override
+    public Class<? extends Module> moduleType() {
+        return JGroupsModule.class;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Collection<Class<? extends Module>> overrides() {
+        Collection modules = Collections.singletonList(ServerModule.class);
+        return modules;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridge.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridge.java b/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridge.java
new file mode 100644
index 0000000..cd7bae2
--- /dev/null
+++ b/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridge.java
@@ -0,0 +1,231 @@
+/*****************************************************************
+ *   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.event;
+
+import org.jgroups.Channel;
+import org.jgroups.JChannel;
+import org.jgroups.Message;
+import org.jgroups.MessageListener;
+import org.jgroups.blocks.PullPushAdapter;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Implementation of EventBridge that passes and receives events via JavaGroups
+ * communication software.
+ * 
+ * @since 1.1
+ */
+public class JavaGroupsBridge extends EventBridge implements MessageListener {
+
+    public static final String MCAST_ADDRESS_DEFAULT = "228.0.0.5";
+    public static final int MCAST_PORT_DEFAULT_INT = 22222;
+    public static final String MCAST_PORT_DEFAULT = Integer.toString(MCAST_PORT_DEFAULT_INT);
+
+    public static final String MCAST_ADDRESS_PROPERTY = "cayenne.JavaGroupsBridge.mcast.address";
+    public static final String MCAST_PORT_PROPERTY = "cayenne.JavaGroupsBridge.mcast.port";
+
+    /**
+     * Defines a property for JavaGroups XML configuration file.
+     */
+    public static final String JGROUPS_CONFIG_URL_PROPERTY = "javagroupsbridge.config.url";
+
+    // TODO: Meaning of "state" in JGroups is not yet clear to me
+    protected byte[] state;
+
+    protected Channel channel;
+    protected PullPushAdapter adapter;
+    protected String multicastAddress;
+    protected String multicastPort;
+    protected String configURL;
+
+    /**
+     * Creates new instance of JavaGroupsBridge.
+     */
+    public JavaGroupsBridge(EventSubject localSubject, String externalSubject) {
+        super(localSubject, externalSubject);
+    }
+
+    /**
+     * @since 1.2
+     */
+    public JavaGroupsBridge(Collection<EventSubject> localSubjects, String externalSubject) {
+        super(localSubjects, externalSubject);
+    }
+
+    /**
+     * @since 4.0
+     */
+    public JavaGroupsBridge(Collection<EventSubject> localSubjects, String externalSubject, Map<String, String> properties) {
+        super(localSubjects, externalSubject);
+
+        // configure properties
+        String multicastAddress = properties.get(MCAST_ADDRESS_PROPERTY);
+        String multicastPort = properties.get(MCAST_PORT_PROPERTY);
+
+        this.configURL = properties.get(JGROUPS_CONFIG_URL_PROPERTY);
+        this.multicastAddress = (multicastAddress != null) ? multicastAddress : MCAST_ADDRESS_DEFAULT;
+        this.multicastPort = (multicastPort != null) ? multicastPort : MCAST_PORT_DEFAULT;
+    }
+
+    public String getConfigURL() {
+        return configURL;
+    }
+
+    public void setConfigURL(String configURL) {
+        this.configURL = configURL;
+    }
+
+    public String getMulticastAddress() {
+        return multicastAddress;
+    }
+
+    public void setMulticastAddress(String multicastAddress) {
+        this.multicastAddress = multicastAddress;
+    }
+
+    public String getMulticastPort() {
+        return multicastPort;
+    }
+
+    public void setMulticastPort(String multicastPort) {
+        this.multicastPort = multicastPort;
+    }
+
+    public byte[] getState() {
+        return state;
+    }
+
+    public void setState(byte[] state) {
+        this.state = state;
+    }
+
+    /**
+     * Implementation of org.javagroups.MessageListener - a callback method to process
+     * incoming messages.
+     */
+    public void receive(Message message) {
+        try {
+            CayenneEvent event = messageObjectToEvent((Serializable) message.getObject());
+            if (event != null) {
+
+                onExternalEvent(event);
+            }
+        }
+        catch (Exception ex) {
+            // TODO: Andrus, 2/8/2006 logging... Log4J was removed to make this usable on
+            // the client
+        }
+    }
+
+    @Override
+    protected void startupExternal() throws Exception {
+        // TODO: need to do more research to figure out the best default transport
+        // settings
+        // to avoid fragmentation, etc.
+
+        // if config file is set, use it, otherwise use a default
+        // set of properties, trying to configure multicast address and port
+        if (configURL != null) {
+            channel = new JChannel(configURL);
+        }
+        else {
+            String configString = buildConfigString();
+            channel = new JChannel(configString);
+        }
+
+        // Important - discard messages from self
+        channel.setOpt(Channel.LOCAL, Boolean.FALSE);
+        channel.connect(externalSubject);
+
+        if (receivesExternalEvents()) {
+            adapter = new PullPushAdapter(channel, this);
+        }
+    }
+
+    /**
+     * Creates JavaGroups configuration String, using preconfigured multicast port and
+     * address.
+     */
+    protected String buildConfigString() {
+        if (multicastAddress == null) {
+            throw new IllegalStateException("'multcastAddress' is not set");
+        }
+
+        if (multicastPort == null) {
+            throw new IllegalStateException("'multcastPort' is not set");
+        }
+
+        return "UDP(mcast_addr="
+                + multicastAddress
+                + ";mcast_port="
+                + multicastPort
+                + ";ip_ttl=32):"
+                + "PING(timeout=3000;num_initial_members=6):"
+                + "FD(timeout=3000):"
+                + "VERIFY_SUSPECT(timeout=1500):"
+                + "pbcast.NAKACK(gc_lag=10;retransmit_timeout=600,1200,2400,4800):"
+                + "pbcast.STABLE(desired_avg_gossip=10000):"
+                + "FRAG:"
+                + "pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;"
+                + "shun=true;print_local_addr=false)";
+    }
+
+    @Override
+    protected void shutdownExternal() throws Exception {
+        try {
+            if (adapter != null) {
+                adapter.stop();
+            }
+
+            channel.close();
+        }
+        finally {
+            adapter = null;
+            channel = null;
+        }
+    }
+
+    @Override
+    protected void sendExternalEvent(CayenneEvent localEvent) throws Exception {
+        Message message = new Message(null, null, eventToMessageObject(localEvent));
+        channel.send(message);
+    }
+
+    /**
+     * Converts CayenneEvent to a serializable object that will be sent via JMS. Default
+     * implementation simply returns the event, but subclasses can customize this
+     * behavior.
+     */
+    protected Serializable eventToMessageObject(CayenneEvent event) throws Exception {
+        return event;
+    }
+
+    /**
+     * Converts a Serializable instance to CayenneEvent. Returns null if the object is not
+     * supported. Default implementation simply tries to cast the object to CayenneEvent,
+     * but subclasses can customize this behavior.
+     */
+    protected CayenneEvent messageObjectToEvent(Serializable object) throws Exception {
+        return (object instanceof CayenneEvent) ? (CayenneEvent) object : null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeFactory.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeFactory.java b/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeFactory.java
new file mode 100644
index 0000000..302e5ca
--- /dev/null
+++ b/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeFactory.java
@@ -0,0 +1,49 @@
+/*****************************************************************
+ *   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.event;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Factory to create JavaGroupsBridge instances. If JavaGroups library is not installed
+ * this factory will return a noop EventBridge as a failover mechanism.
+ * <p/>
+ * For further information about JavaGroups consult the <a href="http://www.jgroups.org/">documentation</a>.
+ *
+ * @since 1.1
+ */
+public class JavaGroupsBridgeFactory implements EventBridgeFactory {
+
+    /**
+     * Creates a JavaGroupsBridge instance. Since JavaGroups is not shipped with Cayenne
+     * and should be installed separately, a common misconfiguration problem may be the
+     * absence of JavaGroups jar file. This factory returns a dummy noop EventBridge, if
+     * this is the case. This would allow the application to continue to run, but without
+     * remote notifications.
+     */
+    public EventBridge createEventBridge(
+            Collection<EventSubject> localSubjects,
+            String externalSubject,
+            Map<String, String> properties) {
+        return new JavaGroupsBridge(localSubjects, externalSubject, properties);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeProvider.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeProvider.java b/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeProvider.java
new file mode 100644
index 0000000..983aa7f
--- /dev/null
+++ b/eventbridges/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeProvider.java
@@ -0,0 +1,50 @@
+/*****************************************************************
+ *   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.event;
+
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.access.DataRowStore;
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.di.DIRuntimeException;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.di.Provider;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class JavaGroupsBridgeProvider implements Provider<EventBridge> {
+
+    @Inject
+    protected DataDomain dataDomain;
+
+    @Inject(JGroupsModule.JAVA_GROUPS_BRIDGE_PROPERTIES_MAP)
+    Map<String, String> properties;
+
+    @Override
+    public EventBridge get() throws DIRuntimeException {
+        EventSubject snapshotEventSubject = EventSubject.getSubject(DataRowStore.class, dataDomain.getName());
+
+        return new JavaGroupsBridge(
+                Collections.singleton(snapshotEventSubject),
+                EventBridge.convertToExternalSubject(snapshotEventSubject),
+                properties);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jgroups/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jgroups/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider b/eventbridges/cayenne-jgroups/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
new file mode 100644
index 0000000..1153bdb
--- /dev/null
+++ b/eventbridges/cayenne-jgroups/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
@@ -0,0 +1,20 @@
+##################################################################
+#   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.
+##################################################################
+
+org.apache.cayenne.event.JGroupsModuleProvider
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jgroups/src/test/java/org/apache/cayenne/event/CayenneJGroupsModuleProviderTest.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jgroups/src/test/java/org/apache/cayenne/event/CayenneJGroupsModuleProviderTest.java b/eventbridges/cayenne-jgroups/src/test/java/org/apache/cayenne/event/CayenneJGroupsModuleProviderTest.java
new file mode 100644
index 0000000..c639fcb
--- /dev/null
+++ b/eventbridges/cayenne-jgroups/src/test/java/org/apache/cayenne/event/CayenneJGroupsModuleProviderTest.java
@@ -0,0 +1,35 @@
+/*****************************************************************
+ *   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.event;
+
+import org.apache.cayenne.unit.util.ModuleProviderChecker;
+import org.junit.Test;
+
+/**
+ * @since 4.0
+ */
+public class CayenneJGroupsModuleProviderTest {
+
+    @Test
+    public void testAutoLoadable() {
+        ModuleProviderChecker.testProviderPresent(JGroupsModuleProvider.class);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeFactoryTest.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeFactoryTest.java b/eventbridges/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeFactoryTest.java
new file mode 100644
index 0000000..694dc24
--- /dev/null
+++ b/eventbridges/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeFactoryTest.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.event;
+
+import org.junit.Test;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ */
+public class JavaGroupsBridgeFactoryTest {
+
+    protected Collection<EventSubject> subjects = Collections.singleton(new EventSubject("test"));
+    protected String externalSubject = "subject";
+
+    @Test
+    public void testCreateEventBridge() throws Exception {
+        EventBridge bridge = new JavaGroupsBridgeFactory().createEventBridge(
+                subjects,
+                externalSubject,
+                Collections.<String, String>emptyMap());
+
+        assertNotNull(bridge);
+        assertTrue(bridge instanceof JavaGroupsBridge);
+        assertEquals(subjects, bridge.getLocalSubjects());
+        assertEquals(externalSubject, bridge.getExternalSubject());
+    }
+
+    @Test
+    public void testUseProperties() throws Exception {
+        JavaGroupsBridgeFactory bridgeFactory = new JavaGroupsBridgeFactory();
+
+        Map<String, String> properties = new HashMap<>();
+        properties.put(JavaGroupsBridge.MCAST_ADDRESS_PROPERTY, JavaGroupsBridgeProviderTest.MCAST_ADDRESS_TEST);
+        properties.put(JavaGroupsBridge.MCAST_PORT_PROPERTY, JavaGroupsBridgeProviderTest.MCAST_PORT_TEST);
+        properties.put(JavaGroupsBridge.JGROUPS_CONFIG_URL_PROPERTY, JavaGroupsBridgeProviderTest.CONFIG_URL_TEST);
+
+        JavaGroupsBridge bridge = (JavaGroupsBridge) bridgeFactory.createEventBridge(
+                subjects,
+                externalSubject,
+                properties);
+
+        assertEquals(bridge.getMulticastAddress(), JavaGroupsBridgeProviderTest.MCAST_ADDRESS_TEST);
+        assertEquals(bridge.getMulticastPort(), JavaGroupsBridgeProviderTest.MCAST_PORT_TEST);
+        assertEquals(bridge.getConfigURL(), JavaGroupsBridgeProviderTest.CONFIG_URL_TEST);
+    }
+
+    @Test
+    public void testUseDefaultProperties() throws Exception {
+        JavaGroupsBridgeFactory bridgeFactory = new JavaGroupsBridgeFactory();
+        JavaGroupsBridge bridge = (JavaGroupsBridge) bridgeFactory.createEventBridge(
+                subjects,
+                externalSubject,
+                Collections.<String, String>emptyMap());
+
+        assertEquals(bridge.getMulticastAddress(), JavaGroupsBridge.MCAST_ADDRESS_DEFAULT);
+        assertEquals(bridge.getMulticastPort(), JavaGroupsBridge.MCAST_PORT_DEFAULT);
+        assertEquals(bridge.getConfigURL(), null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeProviderTest.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeProviderTest.java b/eventbridges/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeProviderTest.java
new file mode 100644
index 0000000..41584b7
--- /dev/null
+++ b/eventbridges/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeProviderTest.java
@@ -0,0 +1,99 @@
+/*****************************************************************
+ *   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.event;
+
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.configuration.DefaultRuntimeProperties;
+import org.apache.cayenne.configuration.RuntimeProperties;
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.DIBootstrap;
+import org.apache.cayenne.di.Injector;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.log.CommonsJdbcEventLogger;
+import org.apache.cayenne.log.JdbcEventLogger;
+import org.apache.cayenne.tx.DefaultTransactionFactory;
+import org.apache.cayenne.tx.DefaultTransactionManager;
+import org.apache.cayenne.tx.TransactionFactory;
+import org.apache.cayenne.tx.TransactionManager;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class JavaGroupsBridgeProviderTest {
+
+    private final DataDomain DOMAIN = new DataDomain("test");
+    private final EventManager EVENT_MANAGER = new DefaultEventManager();
+    protected static final String MCAST_ADDRESS_TEST = "192.168.0.0";
+    protected static final String MCAST_PORT_TEST = "1521";
+    protected static final String CONFIG_URL_TEST = "somehost.com";
+
+    @Test
+    public void testGetJavaGroupsBridge() throws Exception {
+        Injector injector = DIBootstrap.createInjector(new DefaultBindings(), new JGroupsModule());
+        EventBridge bridge = injector.getInstance(EventBridge.class);
+
+        assertNotNull(bridge);
+        assertTrue(bridge instanceof JavaGroupsBridge);
+    }
+
+    @Test
+    public void testUseProperties() throws Exception {
+        Module module = new Module() {
+            public void configure(Binder binder) {
+                JGroupsModule.contributeMulticastAddress(binder, MCAST_ADDRESS_TEST);
+                JGroupsModule.contributeMulticastPort(binder, Integer.parseInt(MCAST_PORT_TEST));
+                JGroupsModule.contributeConfigUrl(binder, CONFIG_URL_TEST);
+            }
+        };
+
+        Injector injector = DIBootstrap.createInjector(new DefaultBindings(), new JGroupsModule(), module);
+        JavaGroupsBridge bridge = (JavaGroupsBridge) injector.getInstance(EventBridge.class);
+
+        assertEquals(MCAST_ADDRESS_TEST, bridge.getMulticastAddress());
+        assertEquals(MCAST_PORT_TEST, bridge.getMulticastPort());
+        assertEquals(CONFIG_URL_TEST, bridge.getConfigURL());
+    }
+
+    @Test
+    public void testUseDefaultProperties() throws Exception {
+        Injector injector = DIBootstrap.createInjector(new DefaultBindings(), new JGroupsModule());
+        JavaGroupsBridge bridge = (JavaGroupsBridge) injector.getInstance(EventBridge.class);
+
+        assertEquals(JavaGroupsBridge.MCAST_ADDRESS_DEFAULT, bridge.getMulticastAddress());
+        assertEquals(JavaGroupsBridge.MCAST_PORT_DEFAULT, bridge.getMulticastPort());
+        assertEquals(null, bridge.getConfigURL());
+    }
+
+    class DefaultBindings implements Module {
+        @Override
+        public void configure(Binder binder) {
+            binder.bindMap(Constants.PROPERTIES_MAP);
+            binder.bind(DataDomain.class).toInstance(DOMAIN);
+            binder.bind(EventManager.class).toInstance(EVENT_MANAGER);
+            binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
+            binder.bind(TransactionFactory.class).to(DefaultTransactionFactory.class);
+            binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
+            binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jms/pom.xml
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jms/pom.xml b/eventbridges/cayenne-jms/pom.xml
new file mode 100644
index 0000000..7b275fa
--- /dev/null
+++ b/eventbridges/cayenne-jms/pom.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~   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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<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">
+    <parent>
+        <artifactId>cayenne-eventbridges-parent</artifactId>
+        <groupId>org.apache.cayenne</groupId>
+        <version>4.0.M6-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>cayenne-jms</artifactId>
+    <name>cayenne-jms: Cayenne JMS Event bridge</name>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-jms_1.1_spec</artifactId>
+            <version>1.1</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridge.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridge.java b/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridge.java
new file mode 100644
index 0000000..0b746fc
--- /dev/null
+++ b/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridge.java
@@ -0,0 +1,280 @@
+/*****************************************************************
+ *   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.event;
+
+import org.apache.cayenne.util.IDUtil;
+
+import javax.jms.Message;
+import javax.jms.MessageFormatException;
+import javax.jms.MessageListener;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicConnectionFactory;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.jms.TopicSubscriber;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Implementation of EventBridge that passes and receives events via JMS (Java Messaging
+ * Service). JMSBridge uses "publish/subscribe" model for communication with external
+ * agents.
+ * 
+ * @since 1.1
+ */
+public class JMSBridge extends EventBridge implements MessageListener {
+
+    // this is an OpenJMS default for the factory name. Likely it won't work with
+    // anything else
+    public static final String TOPIC_CONNECTION_FACTORY_DEFAULT = "JmsTopicConnectionFactory";
+
+    public static final String TOPIC_CONNECTION_FACTORY_PROPERTY = "cayenne.JMSBridge.topic.connection.factory";
+
+    static final String VM_ID = new String(IDUtil.pseudoUniqueByteSequence16());
+    static final String VM_ID_PROPERTY = "VM_ID";
+
+    protected String topicConnectionFactoryName;
+
+    protected TopicConnection sendConnection;
+    protected TopicSession sendSession;
+    protected TopicConnection receivedConnection;
+    protected TopicPublisher publisher;
+    protected TopicSubscriber subscriber;
+
+    public JMSBridge(EventSubject localSubject, String externalSubject) {
+        super(localSubject, externalSubject);
+    }
+
+    /**
+     * @since 1.2
+     */
+    public JMSBridge(Collection<EventSubject> localSubjects, String externalSubject) {
+        super(localSubjects, externalSubject);
+    }
+
+    /**
+     * @since 4.0
+     */
+    public JMSBridge(Collection<EventSubject> localSubjects, String externalSubject, Map<String, String> properties) {
+        super(localSubjects, externalSubject);
+
+        // configure properties
+        String topicConnectionFactory = properties
+                .get(TOPIC_CONNECTION_FACTORY_PROPERTY);
+
+        this.topicConnectionFactoryName = (topicConnectionFactory != null)
+                ? topicConnectionFactory
+                : TOPIC_CONNECTION_FACTORY_DEFAULT;
+    }
+
+    /**
+     * JMS MessageListener implementation. Injects received events to the EventManager
+     * local event queue.
+     */
+    public void onMessage(Message message) {
+
+        try {
+            Object vmID = message.getObjectProperty(JMSBridge.VM_ID_PROPERTY);
+            if (JMSBridge.VM_ID.equals(vmID)) {
+                return;
+            }
+
+            if (!(message instanceof ObjectMessage)) {
+                return;
+            }
+
+            ObjectMessage objectMessage = (ObjectMessage) message;
+            CayenneEvent event = messageObjectToEvent(objectMessage.getObject());
+            if (event != null) {
+                onExternalEvent(event);
+            }
+
+        }
+        catch (MessageFormatException mfex) {
+            // TODO: Andrus, 2/8/2006 logging... Log4J was removed to make this usable on
+            // the client
+        }
+        catch (Exception ex) {
+            // TODO: Andrus, 2/8/2006 logging... Log4J was removed to make this usable on
+            // the client
+        }
+    }
+
+    /**
+     * @return Name of javax.jms.TopicConnectionFactory accessible via JNDI.
+     */
+    public String getTopicConnectionFactoryName() {
+        return topicConnectionFactoryName;
+    }
+
+    public void setTopicConnectionFactoryName(String name) {
+        this.topicConnectionFactoryName = name;
+    }
+
+    /**
+     * Starts up JMS machinery for "publish/subscribe" model.
+     */
+    @Override
+    protected void startupExternal() throws Exception {
+        Context jndiContext = new InitialContext();
+        TopicConnectionFactory connectionFactory = (TopicConnectionFactory) jndiContext
+                .lookup(topicConnectionFactoryName);
+
+        Topic topic = null;
+
+        try {
+            topic = (Topic) jndiContext.lookup(externalSubject);
+        }
+        catch (NameNotFoundException ex) {
+            // can't find topic, try to create it
+            topic = topicNotFound(jndiContext, ex);
+
+            if (topic == null) {
+                throw ex;
+            }
+        }
+
+        // config publisher
+        if (receivesLocalEvents()) {
+            this.sendConnection = connectionFactory.createTopicConnection();
+            this.sendSession = sendConnection.createTopicSession(
+                    false,
+                    Session.AUTO_ACKNOWLEDGE);
+            this.publisher = sendSession.createPublisher(topic);
+        }
+
+        // config subscriber
+        if (receivesExternalEvents()) {
+            this.receivedConnection = connectionFactory.createTopicConnection();
+            this.subscriber = receivedConnection.createTopicSession(
+                    false,
+                    Session.AUTO_ACKNOWLEDGE).createSubscriber(topic);
+            this.subscriber.setMessageListener(this);
+            this.receivedConnection.start();
+        }
+    }
+
+    /**
+     * Attempts to create missing Topic. Since Topic creation is JMS-implementation
+     * specific, this task is left to subclasses. Current implementation simply rethrows
+     * the exception.
+     */
+    protected Topic topicNotFound(Context jndiContext, NamingException ex)
+            throws Exception {
+        throw ex;
+    }
+
+    /**
+     * Closes all resources used to communicate via JMS.
+     */
+    @Override
+    protected void shutdownExternal() throws Exception {
+        Exception lastException = null;
+
+        if (publisher != null) {
+            try {
+                publisher.close();
+            }
+            catch (Exception ex) {
+                lastException = ex;
+            }
+        }
+
+        if (subscriber != null) {
+            try {
+                subscriber.close();
+            }
+            catch (Exception ex) {
+                lastException = ex;
+            }
+        }
+
+        if (receivedConnection != null) {
+            try {
+                receivedConnection.close();
+            }
+            catch (Exception ex) {
+                lastException = ex;
+            }
+        }
+
+        if (sendSession != null) {
+            try {
+                sendSession.close();
+            }
+            catch (Exception ex) {
+                lastException = ex;
+            }
+        }
+
+        if (sendConnection != null) {
+            try {
+                sendConnection.close();
+            }
+            catch (Exception ex) {
+                lastException = ex;
+            }
+        }
+
+        publisher = null;
+        subscriber = null;
+        receivedConnection = null;
+        sendConnection = null;
+        sendSession = null;
+
+        if (lastException != null) {
+            throw lastException;
+        }
+    }
+
+    @Override
+    protected void sendExternalEvent(CayenneEvent localEvent) throws Exception {
+        ObjectMessage message = sendSession
+                .createObjectMessage(eventToMessageObject(localEvent));
+        message.setObjectProperty(JMSBridge.VM_ID_PROPERTY, JMSBridge.VM_ID);
+        publisher.publish(message);
+    }
+
+    /**
+     * Converts CayenneEvent to a serializable object that will be sent via JMS. Default
+     * implementation simply returns the event, but subclasses can customize this
+     * behavior.
+     */
+    protected Serializable eventToMessageObject(CayenneEvent event) throws Exception {
+        return event;
+    }
+
+    /**
+     * Converts a Serializable instance to CayenneEvent. Returns null if the object is not
+     * supported. Default implementation simply tries to cast the object to CayenneEvent,
+     * but subclasses can customize this behavior.
+     */
+    protected CayenneEvent messageObjectToEvent(Serializable object) throws Exception {
+        return (object instanceof CayenneEvent) ? (CayenneEvent) object : null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridgeFactory.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridgeFactory.java b/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridgeFactory.java
new file mode 100644
index 0000000..b7772d8
--- /dev/null
+++ b/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridgeFactory.java
@@ -0,0 +1,39 @@
+/*****************************************************************
+ *   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.event;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Factory to create JMSBridge instances.
+ * 
+ * @since 1.1
+ */
+public class JMSBridgeFactory implements EventBridgeFactory {
+
+    /**
+     * @since 1.2
+     */
+    public EventBridge createEventBridge(Collection<EventSubject> localSubjects, String externalSubject, Map<String, String> properties) {
+        return new JMSBridge(localSubjects, externalSubject, properties);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridgeProvider.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridgeProvider.java b/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridgeProvider.java
new file mode 100644
index 0000000..6a06faa
--- /dev/null
+++ b/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridgeProvider.java
@@ -0,0 +1,50 @@
+/*****************************************************************
+ *   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.event;
+
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.access.DataRowStore;
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.di.DIRuntimeException;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.di.Provider;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class JMSBridgeProvider implements Provider<EventBridge> {
+
+    @Inject
+    protected DataDomain dataDomain;
+
+    @Inject(JMSModule.JMS_BRIDGE_PROPERTIES_MAP)
+    Map<String, String> properties;
+
+    @Override
+    public EventBridge get() throws DIRuntimeException {
+        EventSubject snapshotEventSubject = EventSubject.getSubject(DataRowStore.class, dataDomain.getName());
+
+        return new JMSBridge(
+                Collections.singleton(snapshotEventSubject),
+                EventBridge.convertToExternalSubject(snapshotEventSubject),
+                properties);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSModule.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSModule.java b/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSModule.java
new file mode 100644
index 0000000..ad9494c
--- /dev/null
+++ b/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSModule.java
@@ -0,0 +1,54 @@
+/*****************************************************************
+ *   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.event;
+
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.MapBuilder;
+import org.apache.cayenne.di.Module;
+
+/**
+ * @since 4.0
+ */
+public class JMSModule implements Module {
+
+    /**
+     * A DI container key for the Map&lt;String, String&gt; storing
+     * {@link org.apache.cayenne.event.JMSBridge} properties
+     *
+     * @since 4.0
+     */
+    public static final String JMS_BRIDGE_PROPERTIES_MAP = "cayenne.server.jms_bridge";
+
+    public static void contributeTopicConnectionFactory(Binder binder, String factory) {
+        contributeProperties(binder).put(JMSBridge.TOPIC_CONNECTION_FACTORY_PROPERTY, factory);
+    }
+
+    private static MapBuilder<String> contributeProperties(Binder binder) {
+        return binder.bindMap(JMS_BRIDGE_PROPERTIES_MAP);
+    }
+
+    @Override
+    public void configure(Binder binder) {
+        // init properties' defaults
+        contributeTopicConnectionFactory(binder, JMSBridge.TOPIC_CONNECTION_FACTORY_DEFAULT);
+
+        binder.bind(EventBridge.class).toProvider(JMSBridgeProvider.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSModuleProvider.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSModuleProvider.java b/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSModuleProvider.java
new file mode 100644
index 0000000..58d5442
--- /dev/null
+++ b/eventbridges/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSModuleProvider.java
@@ -0,0 +1,50 @@
+/*****************************************************************
+ *   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.event;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.cayenne.configuration.server.ServerModule;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.di.spi.ModuleProvider;
+
+/**
+ * @since 4.0
+ */
+public class JMSModuleProvider implements ModuleProvider {
+
+    @Override
+    public Module module() {
+        return new JMSModule();
+    }
+
+    @Override
+    public Class<? extends Module> moduleType() {
+        return JMSModule.class;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Collection<Class<? extends Module>> overrides() {
+        Collection modules = Collections.singletonList(ServerModule.class);
+        return modules;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jms/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jms/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider b/eventbridges/cayenne-jms/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
new file mode 100644
index 0000000..d25aa6a
--- /dev/null
+++ b/eventbridges/cayenne-jms/src/main/resources/META-INF/services/org.apache.cayenne.di.spi.ModuleProvider
@@ -0,0 +1,20 @@
+##################################################################
+#   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.
+##################################################################
+
+org.apache.cayenne.event.JMSModuleProvider
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jms/src/test/java/org/apache/cayenne/event/CayenneJMSModuleProviderTest.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jms/src/test/java/org/apache/cayenne/event/CayenneJMSModuleProviderTest.java b/eventbridges/cayenne-jms/src/test/java/org/apache/cayenne/event/CayenneJMSModuleProviderTest.java
new file mode 100644
index 0000000..d690e9a
--- /dev/null
+++ b/eventbridges/cayenne-jms/src/test/java/org/apache/cayenne/event/CayenneJMSModuleProviderTest.java
@@ -0,0 +1,35 @@
+/*****************************************************************
+ *   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.event;
+
+import org.apache.cayenne.unit.util.ModuleProviderChecker;
+import org.junit.Test;
+
+/**
+ * @since 4.0
+ */
+public class CayenneJMSModuleProviderTest {
+
+    @Test
+    public void testAutoLoadable() {
+        ModuleProviderChecker.testProviderPresent(JMSModuleProvider.class);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeFactoryTest.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeFactoryTest.java b/eventbridges/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeFactoryTest.java
new file mode 100644
index 0000000..e098d4e
--- /dev/null
+++ b/eventbridges/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeFactoryTest.java
@@ -0,0 +1,77 @@
+/*****************************************************************
+ *   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.event;
+
+import org.junit.Test;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class JMSBridgeFactoryTest {
+
+    protected Collection<EventSubject> subjects = Collections.singleton(new EventSubject("test"));
+    protected String externalSubject = "subject";
+
+    @Test
+    public void testCreateEventBridge() throws Exception {
+        EventBridge bridge = new JMSBridgeFactory().createEventBridge(
+                subjects,
+                externalSubject,
+                Collections.EMPTY_MAP);
+
+        assertNotNull(bridge);
+        assertTrue(bridge instanceof JMSBridge);
+        assertEquals(subjects, bridge.getLocalSubjects());
+        assertEquals(externalSubject, bridge.getExternalSubject());
+    }
+
+    @Test
+    public void testUseProperties() throws Exception {
+        JMSBridgeFactory bridgeFactory = new JMSBridgeFactory();
+
+        Map<String, String> properties = new HashMap<String, String>();
+        properties.put(JMSBridge.TOPIC_CONNECTION_FACTORY_PROPERTY, JMSBridgeProviderTest.TOPIC_CONNECTION_FACTORY_TEST);
+
+        JMSBridge bridge = (JMSBridge) bridgeFactory.createEventBridge(
+                subjects,
+                externalSubject,
+                properties);
+
+        assertEquals(bridge.getTopicConnectionFactoryName(), JMSBridgeProviderTest.TOPIC_CONNECTION_FACTORY_TEST);
+    }
+
+    @Test
+    public void testUseDefaultProperties() throws Exception {
+        JMSBridgeFactory bridgeFactory = new JMSBridgeFactory();
+        JMSBridge bridge = (JMSBridge) bridgeFactory.createEventBridge(
+                subjects,
+                externalSubject,
+                Collections.EMPTY_MAP);
+
+        assertEquals(bridge.getTopicConnectionFactoryName(), JMSBridge.TOPIC_CONNECTION_FACTORY_DEFAULT);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java b/eventbridges/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java
new file mode 100644
index 0000000..06e3a5c
--- /dev/null
+++ b/eventbridges/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java
@@ -0,0 +1,92 @@
+/*****************************************************************
+ *   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.event;
+
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.configuration.DefaultRuntimeProperties;
+import org.apache.cayenne.configuration.RuntimeProperties;
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.DIBootstrap;
+import org.apache.cayenne.di.Injector;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.log.CommonsJdbcEventLogger;
+import org.apache.cayenne.log.JdbcEventLogger;
+import org.apache.cayenne.tx.DefaultTransactionFactory;
+import org.apache.cayenne.tx.DefaultTransactionManager;
+import org.apache.cayenne.tx.TransactionFactory;
+import org.apache.cayenne.tx.TransactionManager;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class JMSBridgeProviderTest {
+
+    private final DataDomain DOMAIN = new DataDomain("test");
+    private final EventManager EVENT_MANAGER = new DefaultEventManager();
+    protected static final String TOPIC_CONNECTION_FACTORY_TEST = "SomeTopicConnectionFactory";
+
+    @Test
+    public void testGetJMSBridge() throws Exception {
+        Injector injector = DIBootstrap.createInjector(new DefaultBindings(), new JMSModule());
+        EventBridge bridge = injector.getInstance(EventBridge.class);
+
+        assertNotNull(bridge);
+        assertTrue(bridge instanceof JMSBridge);
+    }
+
+    @Test
+    public void testUseProperties() throws Exception {
+        Module module = new Module() {
+
+            public void configure(Binder binder) {
+                JMSModule.contributeTopicConnectionFactory(binder, TOPIC_CONNECTION_FACTORY_TEST);
+            }
+        };
+
+        Injector injector = DIBootstrap.createInjector(new DefaultBindings(), new JMSModule(), module);
+        JMSBridge bridge = (JMSBridge) injector.getInstance(EventBridge.class);
+
+        assertEquals(TOPIC_CONNECTION_FACTORY_TEST, bridge.getTopicConnectionFactoryName());
+    }
+
+    @Test
+    public void testUseDefaultProperties() throws Exception {
+        Injector injector = DIBootstrap.createInjector(new DefaultBindings(), new JMSModule());
+        JMSBridge bridge = (JMSBridge) injector.getInstance(EventBridge.class);
+
+        assertEquals(JMSBridge.TOPIC_CONNECTION_FACTORY_DEFAULT, bridge.getTopicConnectionFactoryName());
+    }
+
+    class DefaultBindings implements Module {
+        @Override
+        public void configure(Binder binder) {
+            binder.bindMap(Constants.PROPERTIES_MAP);
+            binder.bind(DataDomain.class).toInstance(DOMAIN);
+            binder.bind(EventManager.class).toInstance(EVENT_MANAGER);
+            binder.bind(TransactionManager.class).to(DefaultTransactionManager.class);
+            binder.bind(TransactionFactory.class).to(DefaultTransactionFactory.class);
+            binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
+            binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-xmpp/pom.xml
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-xmpp/pom.xml b/eventbridges/cayenne-xmpp/pom.xml
new file mode 100644
index 0000000..c78e981
--- /dev/null
+++ b/eventbridges/cayenne-xmpp/pom.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~   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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<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">
+    <parent>
+        <artifactId>cayenne-eventbridges-parent</artifactId>
+        <groupId>org.apache.cayenne</groupId>
+        <version>4.0.M6-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>cayenne-xmpp</artifactId>
+    <name>cayenne-xmpp: Cayenne XMPP Event bridge</name>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>jivesoftware</groupId>
+            <artifactId>smack</artifactId>
+            <version>2.2.1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jivesoftware</groupId>
+            <artifactId>smackx</artifactId>
+            <version>2.2.1</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPBridge.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPBridge.java b/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPBridge.java
new file mode 100644
index 0000000..5a2b339
--- /dev/null
+++ b/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPBridge.java
@@ -0,0 +1,330 @@
+/*****************************************************************
+ *   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.event;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.util.Base64Codec;
+import org.apache.cayenne.util.Util;
+import org.jivesoftware.smack.GroupChat;
+import org.jivesoftware.smack.PacketListener;
+import org.jivesoftware.smack.SSLXMPPConnection;
+import org.jivesoftware.smack.XMPPConnection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smack.packet.Packet;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * An EventBridge implementation based on XMPP protocol and Smack XMPP client library.
+ * What's good about XMPP (Extensible Messaging and Presence Protocol, an IETF standard
+ * protocol that grew up from Jabber IM) is that generally it has fewer or no deployment
+ * limitations (unlike JMS and JGroups that are generally a good solution for local
+ * controlled networks). Also it provides lots of additional information for free, such as
+ * presence, and much more.
+ * <p>
+ * This implementation is based on Smack XMPP client library from JiveSoftware.
+ * </p>
+ * 
+ * @since 1.2
+ */
+public class XMPPBridge extends EventBridge {
+
+    public static final String XMPP_HOST_PROPERTY = "cayenne.XMPPBridge.xmppHost";
+
+    /**
+     * An optional property, port 5222 is used as default XMPP port.
+     */
+    public static final String XMPP_PORT_PROPERTY = "cayenne.XMPPBridge.xmppPort";
+
+    /**
+     * An optional property, "conference" is used as default chat service.
+     */
+    public static final String XMPP_CHAT_SERVICE_PROPERTY = "cayenne.XMPPBridge.xmppChatService";
+
+    public static final String XMPP_SECURE_CONNECTION_PROPERTY = "cayenne.XMPPBridge.xmppSecure";
+    public static final String XMPP_LOGIN_PROPERTY = "cayenne.XMPPBridge.xmppLogin";
+    public static final String XMPP_PASSWORD_PROPERTY = "cayenne.XMPPBridge.xmppPassword";
+
+    static final String DEFAULT_CHAT_SERVICE = "conference";
+    static final int DEFAULT_XMPP_PORT = 5222;
+    static final int DEFAULT_XMPP_SECURE_PORT = 5223;
+
+    protected boolean secureConnection;
+    protected String loginId;
+    protected String password;
+    protected String xmppHost;
+    protected int xmppPort;
+    protected String chatService;
+    protected String sessionHandle;
+
+    protected XMPPConnection connection;
+    protected GroupChat groupChat;
+    protected boolean connected;
+
+    /**
+     * Creates an XMPPBridge. External subject will be used as the chat group name.
+     */
+    public XMPPBridge(EventSubject localSubject, String externalSubject) {
+        this(Collections.singleton(localSubject), externalSubject);
+    }
+
+    /**
+     * Creates an XMPPBridge. External subject will be used as the chat group name.
+     */
+    public XMPPBridge(Collection<EventSubject> localSubjects, String externalSubject) {
+        super(localSubjects, externalSubject);
+
+        // generate a unique session handle... users can override it to use a specific
+        // handle...
+        this.sessionHandle = "cayenne-xmpp-" + System.currentTimeMillis();
+    }
+
+    public XMPPBridge(Collection<EventSubject> localSubjects, String externalSubject, Map<String, String> properties) {
+        this(localSubjects, externalSubject);
+
+        this.chatService = properties.get(XMPP_CHAT_SERVICE_PROPERTY);
+        this.xmppHost = properties.get(XMPP_HOST_PROPERTY);
+
+        this.loginId = properties.get(XMPP_LOGIN_PROPERTY);
+        this.password = properties.get(XMPP_PASSWORD_PROPERTY);
+
+        String secureConnectionString = properties.get(XMPP_SECURE_CONNECTION_PROPERTY);
+        secureConnection = "true".equalsIgnoreCase(secureConnectionString);
+
+        String portString = properties.get(XMPP_PORT_PROPERTY);
+        if (portString != null) {
+
+            try {
+                this.xmppPort = Integer.parseInt(portString);
+            }
+            catch (NumberFormatException e) {
+                throw new CayenneRuntimeException("Invalid port: " + portString);
+            }
+        }
+    }
+
+    public String getXmppHost() {
+        return xmppHost;
+    }
+
+    public void setXmppHost(String xmppHost) {
+        this.xmppHost = xmppHost;
+    }
+
+    public int getXmppPort() {
+        return xmppPort;
+    }
+
+    public void setXmppPort(int xmppPort) {
+        this.xmppPort = xmppPort;
+    }
+
+    public String getLoginId() {
+        return loginId;
+    }
+
+    public void setLoginId(String loginId) {
+        this.loginId = loginId;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public boolean isSecureConnection() {
+        return secureConnection;
+    }
+
+    public void setSecureConnection(boolean secureConnection) {
+        this.secureConnection = secureConnection;
+    }
+
+    public String getChatService() {
+        return chatService;
+    }
+
+    public void setChatService(String chatService) {
+        this.chatService = chatService;
+    }
+
+    public String getSessionHandle() {
+        return sessionHandle;
+    }
+
+    public void setSessionHandle(String sessionHandle) {
+        this.sessionHandle = sessionHandle;
+    }
+
+    @Override
+    protected void startupExternal() throws Exception {
+
+        // validate settings
+        if (xmppHost == null) {
+            throw new CayenneRuntimeException("Null 'xmppHost', can't start XMPPBridge");
+        }
+
+        // shutdown old bridge
+        if (connected) {
+            shutdownExternal();
+        }
+
+        try {
+            // connect and log in to chat
+            if (secureConnection) {
+                int port = xmppPort > 0 ? xmppPort : DEFAULT_XMPP_SECURE_PORT;
+                this.connection = new SSLXMPPConnection(xmppHost, port);
+            }
+            else {
+                int port = xmppPort > 0 ? xmppPort : DEFAULT_XMPP_PORT;
+                this.connection = new XMPPConnection(xmppHost, port);
+            }
+
+            if (loginId != null) {
+                // it is important to provide a (pseudo) globally unique string as the
+                // third argument ("sessionHandle" is such string); without it same
+                // loginId can not be reused from the same machine.
+                connection.login(loginId, password, sessionHandle);
+            }
+            else {
+                connection.loginAnonymously();
+            }
+        }
+        catch (XMPPException e) {
+            throw new CayenneRuntimeException("Error connecting to XMPP Server"
+                    + e.getLocalizedMessage());
+        }
+
+        String service = this.chatService != null
+                ? this.chatService
+                : DEFAULT_CHAT_SERVICE;
+
+        try {
+            this.groupChat = connection.createGroupChat(externalSubject
+                    + '@'
+                    + service
+                    + "."
+                    + connection.getHost());
+
+            groupChat.join(sessionHandle);
+            groupChat.addMessageListener(new XMPPListener());
+        }
+        catch (XMPPException e) {
+            throw new CayenneRuntimeException("Error setting up a group chat: "
+                    + e.getLocalizedMessage());
+        }
+
+        this.connected = true;
+    }
+
+    @Override
+    protected void shutdownExternal() throws Exception {
+        if (groupChat != null) {
+            groupChat.leave();
+            groupChat = null;
+        }
+
+        if (connection != null) {
+            connection.close();
+            connection = null;
+        }
+
+        connected = false;
+    }
+
+    @Override
+    protected void sendExternalEvent(CayenneEvent localEvent) throws Exception {
+
+        Message message = groupChat.createMessage();
+        message.setBody(serializeToString(localEvent));
+
+        // set thread to our session handle to be able to discard messages from self
+        message.setThread(sessionHandle);
+
+        groupChat.sendMessage(message);
+    }
+
+    class XMPPListener implements PacketListener {
+
+        public void processPacket(Packet packet) {
+
+            if (packet instanceof Message) {
+                Message message = (Message) packet;
+
+                // filter our own messages
+                if (sessionHandle.equals(message.getThread())) {
+                    // discarding
+                }
+                else {
+
+                    String payload = message.getBody();
+
+                    try {
+                        Object event = deserializeFromString(payload);
+                        if (event instanceof CayenneEvent) {
+                            onExternalEvent((CayenneEvent) event);
+                        }
+                    }
+                    catch (Exception ex) {
+                        // ignore for now... need to add logging.
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Decodes the String (assuming it is using Base64 encoding), and then deserializes
+     * object from the byte array.
+     */
+    static Object deserializeFromString(String string) throws Exception {
+        if (Util.isEmptyString(string)) {
+            return null;
+        }
+
+        byte[] bytes = Base64Codec.decodeBase64(string.getBytes());
+        ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
+        Object object = in.readObject();
+        in.close();
+        return object;
+    }
+
+    /**
+     * Serializes object and then encodes it using Base64 encoding.
+     */
+    static String serializeToString(Object object) throws Exception {
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        ObjectOutputStream out = new ObjectOutputStream(bytes);
+        out.writeObject(object);
+        out.close();
+
+        return new String(Base64Codec.encodeBase64(bytes.toByteArray()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ba3c7ead/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPBridgeFactory.java
----------------------------------------------------------------------
diff --git a/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPBridgeFactory.java b/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPBridgeFactory.java
new file mode 100644
index 0000000..7934752
--- /dev/null
+++ b/eventbridges/cayenne-xmpp/src/main/java/org/apache/cayenne/event/XMPPBridgeFactory.java
@@ -0,0 +1,41 @@
+/*****************************************************************
+ *   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.event;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * A factory of XMPPBridge. Note that to deploy an XMPPBridge, you need to have
+ * <em>smack.jar</em> library in the runtime.
+ * 
+ * @since 1.2
+ */
+public class XMPPBridgeFactory implements EventBridgeFactory {
+
+    @Override
+    public EventBridge createEventBridge(
+            Collection<EventSubject> localSubjects,
+            String externalSubject,
+            Map<String, String> properties) {
+        return new XMPPBridge(localSubjects, externalSubject, properties);
+    }
+
+}