CAY-2403
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/ab1fd0bf Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/ab1fd0bf Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/ab1fd0bf Branch: refs/heads/master Commit: ab1fd0bf471954b94aba1aeda0c149175de50929 Parents: 023547e Author: Arseni Bulatski <ancars...@gmail.com> Authored: Fri Feb 2 15:24:03 2018 +0300 Committer: Arseni Bulatski <ancars...@gmail.com> Committed: Fri Feb 2 15:24:03 2018 +0300 ---------------------------------------------------------------------- RELEASE-NOTES.txt | 1 + cayenne-jgroups/pom.xml | 82 +++++ .../org/apache/cayenne/event/JGroupsModule.java | 63 ++++ .../event/JGroupsServerModuleProvider.java | 50 +++ .../apache/cayenne/event/JavaGroupsBridge.java | 231 ++++++++++++++ .../cayenne/event/JavaGroupsBridgeFactory.java | 49 +++ .../cayenne/event/JavaGroupsBridgeProvider.java | 50 +++ ...iguration.server.CayenneServerModuleProvider | 20 ++ .../event/CayenneJGroupsModuleProviderTest.java | 36 +++ .../event/JavaGroupsBridgeFactoryTest.java | 84 +++++ .../event/JavaGroupsBridgeProviderTest.java | 97 ++++++ cayenne-jms/pom.xml | 81 +++++ .../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 ++++ .../cayenne/event/JMSServerModuleProvider.java | 50 +++ ...iguration.server.CayenneServerModuleProvider | 20 ++ .../event/CayenneJMSModuleProviderTest.java | 36 +++ .../cayenne/event/JMSBridgeFactoryTest.java | 77 +++++ .../cayenne/event/JMSBridgeProviderTest.java | 87 ++++++ cayenne-xmpp/pom.xml | 86 ++++++ .../org/apache/cayenne/event/XMPPBridge.java | 308 +++++++++++++++++++ .../apache/cayenne/event/XMPPBridgeFactory.java | 41 +++ .../cayenne/event/XMPPBridgeProvider.java | 50 +++ .../org/apache/cayenne/event/XMPPModule.java | 71 +++++ .../cayenne/event/XMPPServerModuleProvider.java | 50 +++ ...iguration.server.CayenneServerModuleProvider | 20 ++ .../event/CayenneXMPPModuleProviderTest.java | 36 +++ .../cayenne/event/XMPPBridgeFactoryTest.java | 72 +++++ .../cayenne/event/XMPPBridgeProviderTest.java | 105 +++++++ .../apache/cayenne/event/XMPPBridgeTest.java | 54 ++++ eventbridges/cayenne-jgroups/pom.xml | 41 --- .../org/apache/cayenne/event/JGroupsModule.java | 63 ---- .../event/JGroupsServerModuleProvider.java | 50 --- .../apache/cayenne/event/JavaGroupsBridge.java | 231 -------------- .../cayenne/event/JavaGroupsBridgeFactory.java | 49 --- .../cayenne/event/JavaGroupsBridgeProvider.java | 50 --- ...iguration.server.CayenneServerModuleProvider | 20 -- .../event/CayenneJGroupsModuleProviderTest.java | 36 --- .../event/JavaGroupsBridgeFactoryTest.java | 84 ----- .../event/JavaGroupsBridgeProviderTest.java | 97 ------ eventbridges/cayenne-jms/pom.xml | 41 --- .../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 ---- .../cayenne/event/JMSServerModuleProvider.java | 50 --- ...iguration.server.CayenneServerModuleProvider | 20 -- .../event/CayenneJMSModuleProviderTest.java | 36 --- .../cayenne/event/JMSBridgeFactoryTest.java | 77 ----- .../cayenne/event/JMSBridgeProviderTest.java | 87 ------ eventbridges/cayenne-xmpp/pom.xml | 46 --- .../org/apache/cayenne/event/XMPPBridge.java | 308 ------------------- .../apache/cayenne/event/XMPPBridgeFactory.java | 41 --- .../cayenne/event/XMPPBridgeProvider.java | 50 --- .../org/apache/cayenne/event/XMPPModule.java | 71 ----- .../cayenne/event/XMPPServerModuleProvider.java | 50 --- ...iguration.server.CayenneServerModuleProvider | 20 -- .../event/CayenneXMPPModuleProviderTest.java | 36 --- .../cayenne/event/XMPPBridgeFactoryTest.java | 72 ----- .../cayenne/event/XMPPBridgeProviderTest.java | 105 ------- .../apache/cayenne/event/XMPPBridgeTest.java | 54 ---- eventbridges/pom.xml | 82 ----- pom.xml | 4 +- 65 files changed, 2433 insertions(+), 2391 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/ab1fd0bf/RELEASE-NOTES.txt ---------------------------------------------------------------------- diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 746012c..dbf604a 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -21,6 +21,7 @@ CAY-2393 Add sqlserver-docker profile to automate tests on SQLServer CAY-2394 Upgrade to Apache Velocity 2.0 CAY-2395 cdbimport: add option to create project file CAY-2396 Upgrade maven-assembly-plugin to 3.1.0 +CAY-2403 Extract eventbridges to top level Bug Fixes: http://git-wip-us.apache.org/repos/asf/cayenne/blob/ab1fd0bf/cayenne-jgroups/pom.xml ---------------------------------------------------------------------- diff --git a/cayenne-jgroups/pom.xml b/cayenne-jgroups/pom.xml new file mode 100644 index 0000000..9698a38 --- /dev/null +++ b/cayenne-jgroups/pom.xml @@ -0,0 +1,82 @@ +<?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.1.M2-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> + <scope>provided</scope> + </dependency> + + <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> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</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/ab1fd0bf/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsModule.java ---------------------------------------------------------------------- diff --git a/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsModule.java b/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsModule.java new file mode 100644 index 0000000..77738c3 --- /dev/null +++ b/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<String, String> 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(String.class, 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/ab1fd0bf/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsServerModuleProvider.java ---------------------------------------------------------------------- diff --git a/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsServerModuleProvider.java b/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsServerModuleProvider.java new file mode 100644 index 0000000..60c5bcd --- /dev/null +++ b/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JGroupsServerModuleProvider.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.CayenneServerModuleProvider; +import org.apache.cayenne.configuration.server.ServerModule; +import org.apache.cayenne.di.Module; + +/** + * @since 4.0 + */ +public class JGroupsServerModuleProvider implements CayenneServerModuleProvider { + + @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/ab1fd0bf/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridge.java ---------------------------------------------------------------------- diff --git a/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridge.java b/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridge.java new file mode 100644 index 0000000..cd7bae2 --- /dev/null +++ b/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/ab1fd0bf/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeFactory.java ---------------------------------------------------------------------- diff --git a/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeFactory.java b/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeFactory.java new file mode 100644 index 0000000..302e5ca --- /dev/null +++ b/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/ab1fd0bf/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeProvider.java ---------------------------------------------------------------------- diff --git a/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeProvider.java b/cayenne-jgroups/src/main/java/org/apache/cayenne/event/JavaGroupsBridgeProvider.java new file mode 100644 index 0000000..983aa7f --- /dev/null +++ b/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/ab1fd0bf/cayenne-jgroups/src/main/resources/META-INF/services/org.apache.cayenne.configuration.server.CayenneServerModuleProvider ---------------------------------------------------------------------- diff --git a/cayenne-jgroups/src/main/resources/META-INF/services/org.apache.cayenne.configuration.server.CayenneServerModuleProvider b/cayenne-jgroups/src/main/resources/META-INF/services/org.apache.cayenne.configuration.server.CayenneServerModuleProvider new file mode 100644 index 0000000..b6c6632 --- /dev/null +++ b/cayenne-jgroups/src/main/resources/META-INF/services/org.apache.cayenne.configuration.server.CayenneServerModuleProvider @@ -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.JGroupsServerModuleProvider \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/ab1fd0bf/cayenne-jgroups/src/test/java/org/apache/cayenne/event/CayenneJGroupsModuleProviderTest.java ---------------------------------------------------------------------- diff --git a/cayenne-jgroups/src/test/java/org/apache/cayenne/event/CayenneJGroupsModuleProviderTest.java b/cayenne-jgroups/src/test/java/org/apache/cayenne/event/CayenneJGroupsModuleProviderTest.java new file mode 100644 index 0000000..320d490 --- /dev/null +++ b/cayenne-jgroups/src/test/java/org/apache/cayenne/event/CayenneJGroupsModuleProviderTest.java @@ -0,0 +1,36 @@ +/***************************************************************** + * 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.configuration.server.CayenneServerModuleProvider; +import org.apache.cayenne.unit.util.ModuleProviderChecker; +import org.junit.Test; + +/** + * @since 4.0 + */ +public class CayenneJGroupsModuleProviderTest { + + @Test + public void testAutoLoadable() { + ModuleProviderChecker.testProviderPresent(JGroupsServerModuleProvider.class, CayenneServerModuleProvider.class); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/ab1fd0bf/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeFactoryTest.java ---------------------------------------------------------------------- diff --git a/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeFactoryTest.java b/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeFactoryTest.java new file mode 100644 index 0000000..694dc24 --- /dev/null +++ b/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/ab1fd0bf/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeProviderTest.java ---------------------------------------------------------------------- diff --git a/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeProviderTest.java b/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeProviderTest.java new file mode 100644 index 0000000..00232d4 --- /dev/null +++ b/cayenne-jgroups/src/test/java/org/apache/cayenne/event/JavaGroupsBridgeProviderTest.java @@ -0,0 +1,97 @@ +/***************************************************************** + * 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.Slf4jJdbcEventLogger; +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 = 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(String.class, 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(Slf4jJdbcEventLogger.class); + binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class); + } + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/ab1fd0bf/cayenne-jms/pom.xml ---------------------------------------------------------------------- diff --git a/cayenne-jms/pom.xml b/cayenne-jms/pom.xml new file mode 100644 index 0000000..f4da57b --- /dev/null +++ b/cayenne-jms/pom.xml @@ -0,0 +1,81 @@ +<?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.1.M2-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> + <scope>provided</scope> + </dependency> + + <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> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</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/ab1fd0bf/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridge.java ---------------------------------------------------------------------- diff --git a/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridge.java b/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridge.java new file mode 100644 index 0000000..0b746fc --- /dev/null +++ b/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/ab1fd0bf/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridgeFactory.java ---------------------------------------------------------------------- diff --git a/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridgeFactory.java b/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridgeFactory.java new file mode 100644 index 0000000..b7772d8 --- /dev/null +++ b/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/ab1fd0bf/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridgeProvider.java ---------------------------------------------------------------------- diff --git a/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridgeProvider.java b/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSBridgeProvider.java new file mode 100644 index 0000000..6a06faa --- /dev/null +++ b/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/ab1fd0bf/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSModule.java ---------------------------------------------------------------------- diff --git a/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSModule.java b/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSModule.java new file mode 100644 index 0000000..b532bc5 --- /dev/null +++ b/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<String, String> 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(String.class, 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/ab1fd0bf/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSServerModuleProvider.java ---------------------------------------------------------------------- diff --git a/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSServerModuleProvider.java b/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSServerModuleProvider.java new file mode 100644 index 0000000..6979d21 --- /dev/null +++ b/cayenne-jms/src/main/java/org/apache/cayenne/event/JMSServerModuleProvider.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.CayenneServerModuleProvider; +import org.apache.cayenne.configuration.server.ServerModule; +import org.apache.cayenne.di.Module; + +/** + * @since 4.0 + */ +public class JMSServerModuleProvider implements CayenneServerModuleProvider { + + @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/ab1fd0bf/cayenne-jms/src/main/resources/META-INF/services/org.apache.cayenne.configuration.server.CayenneServerModuleProvider ---------------------------------------------------------------------- diff --git a/cayenne-jms/src/main/resources/META-INF/services/org.apache.cayenne.configuration.server.CayenneServerModuleProvider b/cayenne-jms/src/main/resources/META-INF/services/org.apache.cayenne.configuration.server.CayenneServerModuleProvider new file mode 100644 index 0000000..7c54e3f --- /dev/null +++ b/cayenne-jms/src/main/resources/META-INF/services/org.apache.cayenne.configuration.server.CayenneServerModuleProvider @@ -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.JMSServerModuleProvider \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/ab1fd0bf/cayenne-jms/src/test/java/org/apache/cayenne/event/CayenneJMSModuleProviderTest.java ---------------------------------------------------------------------- diff --git a/cayenne-jms/src/test/java/org/apache/cayenne/event/CayenneJMSModuleProviderTest.java b/cayenne-jms/src/test/java/org/apache/cayenne/event/CayenneJMSModuleProviderTest.java new file mode 100644 index 0000000..3b3e87a --- /dev/null +++ b/cayenne-jms/src/test/java/org/apache/cayenne/event/CayenneJMSModuleProviderTest.java @@ -0,0 +1,36 @@ +/***************************************************************** + * 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.configuration.server.CayenneServerModuleProvider; +import org.apache.cayenne.unit.util.ModuleProviderChecker; +import org.junit.Test; + +/** + * @since 4.0 + */ +public class CayenneJMSModuleProviderTest { + + @Test + public void testAutoLoadable() { + ModuleProviderChecker.testProviderPresent(JMSServerModuleProvider.class, CayenneServerModuleProvider.class); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/ab1fd0bf/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeFactoryTest.java ---------------------------------------------------------------------- diff --git a/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeFactoryTest.java b/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeFactoryTest.java new file mode 100644 index 0000000..1e50145 --- /dev/null +++ b/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.<String, String>emptyMap()); + + 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<>(); + 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.<String, String>emptyMap()); + + assertEquals(bridge.getTopicConnectionFactoryName(), JMSBridge.TOPIC_CONNECTION_FACTORY_DEFAULT); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/ab1fd0bf/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java ---------------------------------------------------------------------- diff --git a/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java b/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java new file mode 100644 index 0000000..83404eb --- /dev/null +++ b/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java @@ -0,0 +1,87 @@ +/***************************************************************** + * 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.Slf4jJdbcEventLogger; +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() { + Module module = 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(String.class, 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(Slf4jJdbcEventLogger.class); + binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class); + } + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/ab1fd0bf/cayenne-xmpp/pom.xml ---------------------------------------------------------------------- diff --git a/cayenne-xmpp/pom.xml b/cayenne-xmpp/pom.xml new file mode 100644 index 0000000..cf3f849 --- /dev/null +++ b/cayenne-xmpp/pom.xml @@ -0,0 +1,86 @@ +<?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.1.M2-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> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>jivesoftware</groupId> + <artifactId>smackx</artifactId> + <scope>provided</scope> + </dependency> + + <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> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</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