commit 40a7d3b6ac35d2ecb34e85fc3403d2e48e33874e Author: James Strachan Date: Mon Dec 12 17:53:59 2005 +0000 Moved the trunk code into the trunk sub directory git-svn-id: https://svn.apache.org/repos/asf/incubator/activemq/trunk@356304 13f79535-47bb-0310-9956-ffa450edef68 diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100755 index 0000000000..e5582f22e4 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,20 @@ +/** + * + * Copyright 2004 Protique Ltd + * Copyright 2004 Hiram Chirino + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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. + * + **/ + diff --git a/README.txt b/README.txt new file mode 100755 index 0000000000..43d419195c --- /dev/null +++ b/README.txt @@ -0,0 +1,29 @@ +Welcome to ActiveMQ +=================== + +ActiveMQ is a high performance Apache 2.0 licenced Message Broker and JMS 1.1 implementation. + +To help you get started, try the following links:- + +Getting Started +http://activemq.org/Getting+Started + +Building +http://activemq.org/Building + +Examples +http://activemq.org/Examples + +We welcome contributions of all kinds, for details of how you can help +http://activemq.codehaus.org/Contributing + +Please refer to the website for details of finding the issue tracker, email lists, wiki or IRC channel +http://activemq.org/ + + +Please help us make ActiveMQ better - we appreciate any feedback you may have. + +Enjoy! + +----------------- +The ActiveMQ team diff --git a/activecluster/.classpath b/activecluster/.classpath new file mode 100644 index 0000000000..e509d5f6fa --- /dev/null +++ b/activecluster/.classpath @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/activecluster/.cvsignore b/activecluster/.cvsignore new file mode 100644 index 0000000000..1689e072ef --- /dev/null +++ b/activecluster/.cvsignore @@ -0,0 +1,10 @@ +target +bin +*.log +junit*.properties +activecluster.iml +activecluster.ipr +activecluster.iws +ActiveMQ +classes +test-classes diff --git a/activecluster/.project b/activecluster/.project new file mode 100644 index 0000000000..9b048b4926 --- /dev/null +++ b/activecluster/.project @@ -0,0 +1,18 @@ + + + + activecluster + ActiveCluster is a framework for building cluster-aware software + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + \ No newline at end of file diff --git a/activecluster/README.txt b/activecluster/README.txt new file mode 100644 index 0000000000..bcec715f5b --- /dev/null +++ b/activecluster/README.txt @@ -0,0 +1,23 @@ +Welcome to ActiveCluster! +========================= + +ActiveCluster is an Apache 2.0 licenced cluster communication toolkit. + +To help you get started, try surfing the following links... + +Building +http://activecluster.codehaus.org/Building + +Examples +http://activecluster.codehaus.org/Examples + +Refer to the website for details of finding the issue tracker, email lists, wiki or IRC channel +http://activecluster.codehaus.org/ + +Please help us make ActiveCluster better - we appreciate any feedback you may have. + +Enjoy! + + +---------------------- +The ActiveCluster team diff --git a/activecluster/maven.xml b/activecluster/maven.xml new file mode 100644 index 0000000000..b189f6408f --- /dev/null +++ b/activecluster/maven.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Running the ActiveCluster demo... + + + + + + + + + Running the ActiveCluster demo... + + + + + + + + + + diff --git a/activecluster/project.properties b/activecluster/project.properties new file mode 100644 index 0000000000..b9bb4bd2cf --- /dev/null +++ b/activecluster/project.properties @@ -0,0 +1,40 @@ +maven.repo.remote=http://www.ibiblio.org/maven,http://dist.codehaus.org,http://cvs.apache.org/repository + +maven.compile.source=1.4 +maven.compile.target=1.4 +maven.test.source=1.4 +maven.compile.deprecation=true +maven.compile.debug=true +maven.compile.optimize=true + +maven.javadoc.links=http://java.sun.com/j2se/1.4.1/docs/api/,http://java.sun.com/j2ee/1.4/docs/api/ +maven.javadoc.source=1.4 + +maven.junit.fork = true + +# use Sun coding standards + +checkstyle.lcurly.type = eol +checkstyle.lcurly.method = eol +checkstyle.lcurly.other = eol +checkstyle.header.ignore.line = 1,2,3,4,5,6 +checkstyle.const.pattern = ^[a-z][a-zA-Z0-9]*$ + +# disable these non-critical errors to highlight +# more important ones line missing javadoc + +checkstyle.maxlinelen = 132 +checkstyle.ignore.whitespace = true +#maven.checkstyle.ignore.public.in.interface = true +checkstyle.paren.pad = ignore + +##################################################### +# codehaus theme +##################################################### +maven.xdoc.theme.url=http://codehaus.org/codehaus-style.css + +maven.repo.central = dist.codehaus.org +maven.repo.central.directory = /dist + +maven.remote.group = activecluster + diff --git a/activecluster/project.xml b/activecluster/project.xml new file mode 100644 index 0000000000..9a0bcd3dad --- /dev/null +++ b/activecluster/project.xml @@ -0,0 +1,225 @@ + + + + 3 + ActiveCluster + activecluster + 1.2 + + + LogicBlaze, Inc. + http://logicblaze.com + http://logicblaze.com/images/logo.jpg + + + + 2004 + + org.activecluster + + + Core ActiveCluster API + org.activecluster + + + Group organisation protocols + org.activecluster.group + + + Election protocols + org.activecluster.election + + + ActiveMQ specific implementation classes + org.activecluster.activemq + + + Implementation classes + org.activecluster.impl:org.activecluster.election.impl + + + + ActiveCluster is a framework for building cluster-aware software + + activecluster + + + ActiveCluster is a framework for building cluster-aware software + + + http://activecluster.codehaus.org/ + http://jira.codehaus.org/browse/ACL + + beaver.codehaus.org + /home/projects/activecluster/public_html/maven + /home/projects/activecluster/dist + + + scm:cvs:pserver:anonymous@cvs.activecluster.codehaus.org:/home/projects/activecluster/scm:activecluster + scm:cvs:ext:${maven.username}@cvs.activecluster.codehaus.org:/home/projects/activecluster/scm:activecluster + http://cvs.activecluster.codehaus.org/ + + + + + ActiveCluster Developer List + dev-subscribe@activecluster.codehaus.org + dev-unsubscribe@activecluster.codehaus.org + http://archive.activecluster.codehaus.org/dev/ + + + ActiveCluster User List + user-subscribe@activecluster.codehaus.org + user-unsubscribe@activecluster.codehaus.org + http://archive.activecluster.codehaus.org/user/ + + + ActiveCluster SCM List + scm-subscribe@activecluster.codehaus.org + scm-unsubscribe@activecluster.codehaus.org + http://archive.activecluster.codehaus.org/scm/ + + + + + + + 1.0 + 1.0 + ACTIVECLUSTER_1_0 + + + 1.2 + 1.2 + ACTIVECLUSTER_1_2 + + + + + + + James Strachan + jstrachan + jstrachan@logicblaze.com + LogicBlaze, Inc. + + + Hiram Chirino + chirino + hiram@logicblaze.com + LogicBlaze, Inc. + + + Rob Davies + Rob + rajdavies@gmail.com + RAJD Consultancy Ltd + + + + Jules Gosnell + jules + jules@coredevelopers.net + Core Developers Network + + + + + + + + + + commons-logging + commons-logging + 1.0.4 + http://jakarta.apache.org/commons/logging/ + + + + + geronimo-spec+jms + 1.1-rc4 + + true + + + + geronimo-spec+jta + 1.0.1B-rc4 + + true + + + + geronimo-spec+j2ee-management + 1.0-rc4 + + true + + + + + + activemq + activemq-core + 4.0-SNAPSHOT + + + + activeio + activeio + 2.1-SNAPSHOT + + + + + junit + 3.8.1 + + + + + backport-util-concurrent + 2.0_01_pd + + + + + + + + scm@activecluster.codehaus.org + src/java + src/test + + + + + + + + src/java + + **/*.properties + + + + + **/*Test.* + + + + + + + + diff --git a/activecluster/src/java/org/activecluster/Cluster.java b/activecluster/src/java/org/activecluster/Cluster.java new file mode 100644 index 0000000000..468ce7adfe --- /dev/null +++ b/activecluster/src/java/org/activecluster/Cluster.java @@ -0,0 +1,234 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; + +import java.io.Serializable; +import java.util.Map; +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.ObjectMessage; +import javax.jms.StreamMessage; +import javax.jms.TextMessage; +import org.activecluster.election.ElectionStrategy; + +/** + * Represents a logical connection to a cluster. From this object you can + * obtain the destination to send messages to, view the members of the cluster, + * watch cluster events (nodes joining, leaving, updating their state) as well + * as viewing each members state. + *

+ * You may also update the local node's state. + * + * @version $Revision: 1.5 $ + */ +public interface Cluster extends Service { + + /** + * Returns the destination used to send a message to all members of the cluster + * + * @return the destination to send messages to all members of the cluster + */ + public String getDestination(); + + /** + * A snapshot of the nodes in the cluster indexed by the Destination + * @return a Map containing all the nodes in the cluster, where key=node destination,value=node + */ + public Map getNodes(); + + /** + * Adds a new listener to cluster events + * + * @param listener + */ + public void addClusterListener(ClusterListener listener); + + /** + * Removes a listener to cluster events + * + * @param listener + */ + public void removeClusterListener(ClusterListener listener); + + /** + * The local Node which allows you to mutate the state or subscribe to the + * nodes temporary queue for inbound messages direct to the Node + * @return the Node representing this peer in the cluster + */ + public LocalNode getLocalNode(); + + /** + * Allows overriding of the default election strategy with a custom + * implementation. + * @param strategy + */ + public void setElectionStrategy(ElectionStrategy strategy); + + + // Messaging helper methods + //------------------------------------------------------------------------- + + /** + * Sends a message to a destination, which could be to the entire group + * or could be a single Node's destination + * + * @param destination is either the group topic or a node's destination + * @param message the message to be sent + * @throws JMSException + */ + public void send(String destination, Message message) throws JMSException; + + + /** + * Utility method for sending back replies in message exchanges + * + * @param replyTo the replyTo JMS Destination on a Message + * @param message the message to be sent + * @throws JMSException + */ + public void send(Destination replyTo, Message message) throws JMSException; + /** + * Creates a consumer of all the messags sent to the given destination, + * including messages sent via the send() messages + * + * @param destination + * @return a newly created message consumer + * @throws JMSException + */ + public MessageConsumer createConsumer(String destination) throws JMSException; + + /** + * Creates a consumer of all message sent to the given destination, + * including messages sent via the send() message with an optional SQL 92 based selector to filter + * messages + * + * @param destination + * @param selector + * @return a newly created message consumer + * @throws JMSException + */ + public MessageConsumer createConsumer(String destination, String selector) throws JMSException; + + /** + * Creates a consumer of all message sent to the given destination, + * including messages sent via the send() message with an optional SQL 92 based selector to filter + * messages along with optionally ignoring local traffic - messages sent via the send() + * method on this object. + * + * @param destination the destination to consume from + * @param selector an optional SQL 92 filter of messages which could be null + * @param noLocal which if true messages sent via send() on this object will not be delivered to the consumer + * @return a newly created message consumer + * @throws JMSException + */ + public MessageConsumer createConsumer(String destination, String selector, boolean noLocal) throws JMSException; + + + // Message factory methods + //------------------------------------------------------------------------- + + /** + * Creates a new message without a body + * @return the create Message + * + * @throws JMSException + */ + public Message createMessage() throws JMSException; + + /** + * Creates a new bytes message + * @return the create BytesMessage + * + * @throws JMSException + */ + public BytesMessage createBytesMessage() throws JMSException; + + /** + * Creates a new {@link MapMessage} + * @return the created MapMessage + * + * @throws JMSException + */ + public MapMessage createMapMessage() throws JMSException; + + /** + * Creates a new {@link ObjectMessage} + * @return the created ObjectMessage + * + * @throws JMSException + */ + public ObjectMessage createObjectMessage() throws JMSException; + + /** + * Creates a new {@link ObjectMessage} + * + * @param object + * @return the createdObjectMessage + * @throws JMSException + */ + public ObjectMessage createObjectMessage(Serializable object) throws JMSException; + + /** + * Creates a new {@link StreamMessage} + * @return the create StreamMessage + * + * @throws JMSException + */ + public StreamMessage createStreamMessage() throws JMSException; + + /** + * Creates a new {@link TextMessage} + * @return the create TextMessage + * + * @throws JMSException + */ + public TextMessage createTextMessage() throws JMSException; + + /** + * Creates a new {@link TextMessage} + * + * @param text + * @return the create TextMessage + * @throws JMSException + */ + public TextMessage createTextMessage(String text) throws JMSException; + + /** + * Create a named Destination + * @param name + * @return the Destinatiion + * @throws JMSException + */ + public Destination createDestination(String name) throws JMSException; + + /** + * wait until a the cardimality of the cluster is reaches the expected count. This method will return false if the + * cluster isn't started or stopped while waiting + * + * @param expectedCount the number of expected members of a cluster + * @param timeout timeout in milliseconds + * @return true if the cluster is fully connected + * @throws InterruptedException + */ + boolean waitForClusterToComplete(int expectedCount, long timeout) throws InterruptedException; +} diff --git a/activecluster/src/java/org/activecluster/ClusterEvent.java b/activecluster/src/java/org/activecluster/ClusterEvent.java new file mode 100644 index 0000000000..afb0194a67 --- /dev/null +++ b/activecluster/src/java/org/activecluster/ClusterEvent.java @@ -0,0 +1,150 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +/** + * A cluster event + * + * @version $Revision: 1.3 $ + */ +public class ClusterEvent implements Externalizable { + + private static final long serialVersionUID=-4103732679231950873L; + /** + * A node has joined the cluster + */ + public static final int ADD_NODE = 1; + /** + * existing node has updated it's state + */ + public static final int UPDATE_NODE = 2; + /** + * A node has left the Cluster + */ + public static final int REMOVE_NODE = 3; + /** + * A node has failed due to a system/network error + */ + public static final int FAILED_NODE = 4; + + /** + * this node has been elected Coordinator + */ + public static final int ELECTED_COORDINATOR = 5; + + private transient Cluster cluster; + private Node node; + private int type; + + /** + * empty constructor + */ + public ClusterEvent() { + } + + /** + * @param source + * @param node + * @param type + */ + public ClusterEvent(Cluster source, Node node, int type) { + this.cluster = source; + this.node = node; + this.type = type; + } + + /** + * @return the Cluster + */ + public Cluster getCluster() { + return cluster; + } + + /** + * set the cluster + * @param source + */ + public void setCluster(Cluster source){ + this.cluster = source; + } + /** + * @return the node + */ + public Node getNode() { + return node; + } + + /** + * @return the type of event + */ + public int getType() { + return type; + } + + /** + * @return pretty type + */ + public String toString() { + return "ClusterEvent[" + getTypeAsString() + " : " + node + "]"; + } + + /** + * dump on to a stream + * + * @param out + * @throws IOException + */ + public void writeExternal(ObjectOutput out) throws IOException { + out.writeByte(type); + out.writeObject(node); + } + + /** + * read from stream + * + * @param in + * @throws IOException + * @throws ClassNotFoundException + */ + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type = in.readByte(); + node = (Node) in.readObject(); + } + + private String getTypeAsString() { + String result = "unknown type"; + if (type == ADD_NODE) { + result = "ADD_NODE"; + } + else if (type == REMOVE_NODE) { + result = "REMOVE_NODE"; + } + else if (type == UPDATE_NODE) { + result = "UPDATE_NODE"; + } + else if (type == FAILED_NODE) { + result = "FAILED_NODE"; + } + return result; + } +} \ No newline at end of file diff --git a/activecluster/src/java/org/activecluster/ClusterException.java b/activecluster/src/java/org/activecluster/ClusterException.java new file mode 100644 index 0000000000..f8d3ce9b6a --- /dev/null +++ b/activecluster/src/java/org/activecluster/ClusterException.java @@ -0,0 +1,28 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; + + +/** + * Represents a Cluster related exception + * + * @version $Revision: 1.2 $ + */ +public class ClusterException extends Exception { +} diff --git a/activecluster/src/java/org/activecluster/ClusterFactory.java b/activecluster/src/java/org/activecluster/ClusterFactory.java new file mode 100644 index 0000000000..ca753eb77e --- /dev/null +++ b/activecluster/src/java/org/activecluster/ClusterFactory.java @@ -0,0 +1,49 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; + +import javax.jms.JMSException; + + +/** + * A Factory of Cluster instances + * + * @version $Revision: 1.3 $ + */ +public interface ClusterFactory { + + /** + * Creates a new cluster connection using the given local name and destination name + * @param localName + * @param destination + * + * @return Cluster + * @throws JMSException + */ + public Cluster createCluster(String localName,String destination) throws JMSException; + + + /** + * Creates a new cluster connection - generating the localName automatically + * @param destination + * @return + * @throws JMSException + */ + public Cluster createCluster(String destination) throws JMSException; +} diff --git a/activecluster/src/java/org/activecluster/ClusterListener.java b/activecluster/src/java/org/activecluster/ClusterListener.java new file mode 100644 index 0000000000..ff224dfc30 --- /dev/null +++ b/activecluster/src/java/org/activecluster/ClusterListener.java @@ -0,0 +1,64 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; + +import java.util.EventListener; + + +/** + * Listener to events occuring on the cluster + * + * @version $Revision: 1.2 $ + */ +public interface ClusterListener extends EventListener { + + /** + * A new node has been added + * + * @param event + */ + public void onNodeAdd(ClusterEvent event); + + /** + * A node has updated its state + * + * @param event + */ + public void onNodeUpdate(ClusterEvent event); + + /** + * A node has been removed (a clean shutdown) + * + * @param event + */ + public void onNodeRemoved(ClusterEvent event); + + /** + * A node has failed due to process or network failure + * + * @param event + */ + public void onNodeFailed(ClusterEvent event); + + /** + * An election has occurred and a new coordinator has been selected + * @param event + */ + public void onCoordinatorChanged(ClusterEvent event); +} diff --git a/activecluster/src/java/org/activecluster/LocalNode.java b/activecluster/src/java/org/activecluster/LocalNode.java new file mode 100644 index 0000000000..eb829f4355 --- /dev/null +++ b/activecluster/src/java/org/activecluster/LocalNode.java @@ -0,0 +1,38 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; + +import javax.jms.JMSException; +import java.util.Map; + +/** + * Represents the local (in process) node + * + * @version $Revision: 1.2 $ + */ +public interface LocalNode extends Node { + + /** + * Allows the local state to be modified, which will + * be replicated asynchronously around the cluster + * @param state + * @throws JMSException + */ + public void setState(Map state) throws JMSException; + +} diff --git a/activecluster/src/java/org/activecluster/Node.java b/activecluster/src/java/org/activecluster/Node.java new file mode 100644 index 0000000000..6889ba2a0a --- /dev/null +++ b/activecluster/src/java/org/activecluster/Node.java @@ -0,0 +1,58 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; + +import java.io.Serializable; +import java.util.Map; + + +/** + * Represents a node member in a cluster + * + * @version $Revision: 1.3 $ + */ +public interface Node extends Serializable { + + /** + * Access to the queue to send messages direct to this node. + * + * @return the destination to send messages to this node while its available + */ + public String getDestination(); + + /** + * @return an immutable map of the nodes state + */ + public Map getState(); + + /** + * @return the name of the node + */ + public String getName(); + + /** + * @return true if this node has been elected as coordinator + */ + public boolean isCoordinator(); + + /** + * Returns the Zone of this node - typically the DMZ zone or the subnet on which the + * node is on + */ + public Object getZone(); +} diff --git a/activecluster/src/java/org/activecluster/Service.java b/activecluster/src/java/org/activecluster/Service.java new file mode 100644 index 0000000000..aade1e2a91 --- /dev/null +++ b/activecluster/src/java/org/activecluster/Service.java @@ -0,0 +1,42 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; + +import javax.jms.JMSException; + +/** + *

Service represents some service of some kind with a simple start/stop lifecycle.

+ * + * @version $Revision: 1.2 $ + */ +public interface Service { + + /** + * Called to start the service + * @throws JMSException + */ + public void start() throws JMSException; + + /** + * Called to shutdown the service + * @throws JMSException + */ + public void stop() throws JMSException; + +} diff --git a/activecluster/src/java/org/activecluster/election/ElectionStrategy.java b/activecluster/src/java/org/activecluster/election/ElectionStrategy.java new file mode 100644 index 0000000000..f5bf561118 --- /dev/null +++ b/activecluster/src/java/org/activecluster/election/ElectionStrategy.java @@ -0,0 +1,40 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.election; + +import javax.jms.JMSException; +import org.activecluster.Cluster; +import org.activecluster.Node; + +/** + *

Service Used by the Cluster to elect a coordinator.

+ * + * @version $Revision: 1.2 $ + */ +public interface ElectionStrategy { + + /** + * Elect a coordinator. + * @param cluster + * @return the elected Node + * @throws JMSException + */ + public Node doElection(Cluster cluster) throws JMSException; + +} diff --git a/activecluster/src/java/org/activecluster/election/impl/BullyElectionStrategy.java b/activecluster/src/java/org/activecluster/election/impl/BullyElectionStrategy.java new file mode 100644 index 0000000000..05ef946b1e --- /dev/null +++ b/activecluster/src/java/org/activecluster/election/impl/BullyElectionStrategy.java @@ -0,0 +1,58 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.election.impl; + +import org.activecluster.Cluster; +import org.activecluster.Node; +import org.activecluster.election.ElectionStrategy; + +import javax.jms.JMSException; +import java.util.Iterator; +import java.util.Map; + +/** + *

BullyElectionStrategy Use a simple bully algorithm to elect a coordinator. + * the member with the lowest lexicographical name is choosen

+ * + * @version $Revision: 1.2 $ + */ +public class BullyElectionStrategy implements ElectionStrategy { + + /** + * Elect a coordinator. + * + * @param cluster + * @return the elected Node + * @throws JMSException + */ + public Node doElection(Cluster cluster) throws JMSException { + Node elect = cluster.getLocalNode(); + + Map nodes = cluster.getNodes(); + for (Iterator i = nodes.values().iterator(); i.hasNext();) { + Node node = (Node) i.next(); + if (elect.getName().compareTo(node.getName()) < 0) { + elect = node; + } + } + + return elect; + } + +} diff --git a/activecluster/src/java/org/activecluster/group/BuddyGroupModel.java b/activecluster/src/java/org/activecluster/group/BuddyGroupModel.java new file mode 100644 index 0000000000..5bd3c73bc0 --- /dev/null +++ b/activecluster/src/java/org/activecluster/group/BuddyGroupModel.java @@ -0,0 +1,46 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.group; + +import org.activecluster.Node; + +/** + * A kind of {@link GroupModel} in which every {@link Node} has its + * own {@link Group} and other nodes in the cluster act as buddies (slaves) + * + * @version $Revision: 1.2 $ + */ +public class BuddyGroupModel extends GroupModel { + + public synchronized void addNode(Node node) { + Group group = makeNewGroup(node); + if (group == null) { + if (!addToExistingGroup(node)) { + addToUnusedNodes(node); + } + } + else { + // now lets try choose some existing nodes to add as buddy's + tryToFillGroupWithBuddies(group); + + // now that the group may well be filled, add it to the collections + addGroup(group); + } + } + +} diff --git a/activecluster/src/java/org/activecluster/group/Group.java b/activecluster/src/java/org/activecluster/group/Group.java new file mode 100644 index 0000000000..97795aa55f --- /dev/null +++ b/activecluster/src/java/org/activecluster/group/Group.java @@ -0,0 +1,114 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.group; + +import org.activecluster.Node; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a logical group of nodes in a cluster, + * such as a Master and a number of Slaves which operate as a + * logical unit. + *

+ * A cluster can be divided into a single group, or many groups + * depending on the policy required. + *

+ * The number of groups could be application defined; created on demand + * or there could even be one group for each node, with other nodes acting + * as buddy nodes in each nodes' group (i.e. each node is a master with N + * buddies/slaves) + * + * @version $Revision: 1.2 $ + */ +public class Group { + private int minimumMemberCount; + private int maximumMemberCount; + private List members = new ArrayList(); + private int memberCount; + + public Group() { + } + + public Group(int minimumMemberCount, int maximumMemberCount) { + this.minimumMemberCount = minimumMemberCount; + this.maximumMemberCount = maximumMemberCount; + } + + public synchronized List getMembers() { + return new ArrayList(members); + } + + /** + * Adds a node to the given group + * + * @return the index of the node in the group (0 = master, 1..N = slave) + */ + public synchronized int addMember(Node node) { + int index = members.indexOf(node); + if (index >= 0) { + return index; + } + members.add(node); + return memberCount++; + } + + public synchronized boolean removeMember(Node node) { + boolean answer = members.remove(node); + if (answer) { + memberCount--; + } + return answer; + } + + + /** + * Returns true if the group is usable, that it has enough members to be used. + */ + public boolean isUsable() { + return memberCount >= minimumMemberCount; + } + + /** + * Returns true if the group cannot accept any more new members + */ + public boolean isFull() { + return memberCount >= maximumMemberCount; + } + + public int getMemberCount() { + return memberCount; + } + + public int getMaximumMemberCount() { + return maximumMemberCount; + } + + public void setMaximumMemberCount(int maximumMemberCount) { + this.maximumMemberCount = maximumMemberCount; + } + + public int getMinimumMemberCount() { + return minimumMemberCount; + } + + public void setMinimumMemberCount(int minimumMemberCount) { + this.minimumMemberCount = minimumMemberCount; + } +} diff --git a/activecluster/src/java/org/activecluster/group/GroupClusterListener.java b/activecluster/src/java/org/activecluster/group/GroupClusterListener.java new file mode 100644 index 0000000000..f973a47d37 --- /dev/null +++ b/activecluster/src/java/org/activecluster/group/GroupClusterListener.java @@ -0,0 +1,60 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.group; + +import org.activecluster.ClusterEvent; +import org.activecluster.ClusterListener; + +/** + * A {@link ClusterListener} which maintains a {@link GroupModel} implementation + * + * @version $Revision: 1.2 $ + */ +public class GroupClusterListener implements ClusterListener { + private GroupModel model; + + public GroupClusterListener(GroupModel model) { + this.model = model; + } + + // Properties + //------------------------------------------------------------------------- + public GroupModel getModel() { + return model; + } + + // ClusterListener interface + //------------------------------------------------------------------------- + public void onNodeAdd(ClusterEvent event) { + model.addNode(event.getNode()); + } + + public void onNodeUpdate(ClusterEvent event) { + } + + public void onNodeRemoved(ClusterEvent event) { + model.removeNode(event.getNode()); + } + + public void onNodeFailed(ClusterEvent event) { + model.removeNode(event.getNode()); + } + + public void onCoordinatorChanged(ClusterEvent event) { + } +} diff --git a/activecluster/src/java/org/activecluster/group/GroupModel.java b/activecluster/src/java/org/activecluster/group/GroupModel.java new file mode 100644 index 0000000000..ae3f238cfe --- /dev/null +++ b/activecluster/src/java/org/activecluster/group/GroupModel.java @@ -0,0 +1,330 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.group; + +import org.activecluster.Node; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Represents a collection of zero or more groups in a cluster. + * The default implementation will create groups as nodes are added to the cluster; filling + * the groups with its required number of buddies / slaves until a new group can be created. + *

+ * Nodes which are not allowed to be master nodes will be kept around in a pool ready to be added + * as slaves when a new master arrives and forces the creation of a group. + * + * @version $Revision: 1.2 $ + * @see Group + */ +public class GroupModel { + private int maximumGroups = -1; + private int minimumMemberCount = 2; + private int maximumMemberCount = 3; + private List groups = new ArrayList(); + private LinkedList incompleteGroups = new LinkedList(); + private LinkedList completeGroups = new LinkedList(); + private LinkedList fullGroups = new LinkedList(); + private LinkedList unusedNodes = new LinkedList(); + private NodeFilter masterFilter; + private Map nodeMemberships = new HashMap(); + + // allow a node to be a master and 2 buddies + private int maximumWeighting = 10; + + /** + * Adds the new node to this group model; we assume the node has not been added before. + * + * @param node + */ + public synchronized void addNode(Node node) { + if (!addToExistingGroup(node)) { + Group group = makeNewGroup(node); + if (group == null) { + addToUnusedNodes(node); + } + else { + addGroup(group); + } + } + } + + /** + * Removes the node from the group model + * + * @param node + */ + public synchronized void removeNode(Node node) { + unusedNodes.remove(node); + + // lets remove the node from each group + for (Iterator iter = groups.iterator(); iter.hasNext();) { + Group group = (Group) iter.next(); + boolean wasFull = group.isFull(); + boolean wasUsable = group.isUsable(); + + if (removeNodeFromGroup(group, node)) { + updateGroupCollections(group, wasFull, wasUsable); + } + } + } + + // Properties + //------------------------------------------------------------------------- + + /** + * Returns a snapshot of the groups currently available + */ + public synchronized List getGroups() { + return new ArrayList(groups); + } + + public NodeFilter getMasterFilter() { + return masterFilter; + } + + public void setMasterFilter(NodeFilter masterFilter) { + this.masterFilter = masterFilter; + } + + public int getMaximumGroups() { + return maximumGroups; + } + + public void setMaximumGroups(int maximumGroups) { + this.maximumGroups = maximumGroups; + } + + public int getMaximumMemberCount() { + return maximumMemberCount; + } + + public void setMaximumMemberCount(int maximumMemberCount) { + this.maximumMemberCount = maximumMemberCount; + } + + public int getMinimumMemberCount() { + return minimumMemberCount; + } + + public void setMinimumMemberCount(int minimumMemberCount) { + this.minimumMemberCount = minimumMemberCount; + } + + public int getMaximumWeighting() { + return maximumWeighting; + } + + public void setMaximumWeighting(int maximumWeighting) { + this.maximumWeighting = maximumWeighting; + } + + + // Implementation methods + //------------------------------------------------------------------------- + + + /** + * Attempt to make a new group with the current node as the master + * or if the node cannot be a master node + * + * @return the newly created group or false if none was created. + */ + protected Group makeNewGroup(Node node) { + // no pending groups available so lets try and create a new group + if (canCreateGroup(node)) { + Group group = createGroup(node); + addNodeToGroup(group, node); + return group; + } + else { + return null; + } + } + + protected void tryToFillGroupWithBuddies(Group group) { + boolean continueFillingGroups = true; + while (!group.isUsable() && continueFillingGroups) { + continueFillingGroups = tryToAddBuddy(group); + } + + if (continueFillingGroups) { + // lets try fill more unfilled nodes + for (Iterator iter = new ArrayList(incompleteGroups).iterator(); iter.hasNext() && continueFillingGroups;) { + group = (Group) iter.next(); + + boolean wasFull = group.isFull(); + boolean wasUsable = group.isUsable(); + + while (!group.isUsable() && continueFillingGroups) { + continueFillingGroups = tryToAddBuddy(group); + } + + if (group.isUsable()) { + updateGroupCollections(group, wasFull, wasUsable); + } + } + } + } + + protected boolean tryToAddBuddy(Group group) { + boolean continueFillingGroups = true; + // TODO we could make this much faster using a weighting-sorted collection + NodeMemberships lowest = null; + int lowestWeight = 0; + for (Iterator iter = nodeMemberships.values().iterator(); iter.hasNext();) { + NodeMemberships memberships = (NodeMemberships) iter.next(); + if (!memberships.isMember(group)) { + int weighting = memberships.getWeighting(); + if ((lowest == null || weighting < lowestWeight) && weighting < maximumWeighting) { + lowest = memberships; + lowestWeight = weighting; + } + } + } + if (lowest == null) { + continueFillingGroups = false; + } + else { + addNodeToGroup(group, lowest.getNode()); + } + return continueFillingGroups; + } + + /** + * Lets move the group from its current state collection to the new collection if its + * state has changed + */ + protected void updateGroupCollections(Group group, boolean wasFull, boolean wasUsable) { + boolean full = group.isFull(); + if (wasFull && !full) { + fullGroups.remove(group); + } + boolean usable = group.isUsable(); + if (wasUsable && !usable) { + completeGroups.remove(group); + } + if ((!usable || !full) && (wasFull || wasUsable)) { + incompleteGroups.add(group); + } + } + + protected void addToUnusedNodes(Node node) { + // lets add the node to the pool ready to be used if a node fails + unusedNodes.add(node); + } + + /** + * Attempts to add the node to an incomplete group, or + * a not-full group and returns true if its possible - else returns false + * + * @return true if the node has been added to a groupu + */ + protected boolean addToExistingGroup(Node node) { + if (!addToIncompleteGroup(node)) { + if (!addToNotFullGroup(node)) { + return false; + } + } + return true; + } + + protected boolean addToNotFullGroup(Node node) { + return addToPendingGroup(completeGroups, node); + } + + protected boolean addToIncompleteGroup(Node node) { + return addToPendingGroup(incompleteGroups, node); + } + + /** + * Adds the given node to the first pending group if possible + * + * @return true if the node was added to the first available group + */ + protected boolean addToPendingGroup(LinkedList list, Node node) { + if (!list.isEmpty()) { + Group group = (Group) list.getFirst(); + addNodeToGroup(group, node); + if (group.isFull()) { + list.removeFirst(); + fullGroups.add(group); + } + else if (group.isUsable()) { + list.removeFirst(); + completeGroups.add(group); + } + return true; + } + return false; + } + + protected void addNodeToGroup(Group group, Node node) { + NodeMemberships memberships = (NodeMemberships) nodeMemberships.get(node); + if (memberships == null) { + memberships = new NodeMemberships(node); + nodeMemberships.put(node, memberships); + } + memberships.addToGroup(group); + } + + protected boolean removeNodeFromGroup(Group group, Node node) { + NodeMemberships memberships = (NodeMemberships) nodeMemberships.get(node); + if (memberships != null) { + return memberships.removeFromGroup(group); + } + return false; + } + + + protected void addGroup(Group group) { + groups.add(group); + if (group.isFull()) { + fullGroups.add(group); + } + else if (group.isUsable()) { + completeGroups.add(group); + } + else { + incompleteGroups.add(group); + } + } + + protected Group createGroup(Node node) { + return new Group(minimumMemberCount, maximumMemberCount); + } + + /** + * Returns true if we can add a new group to the cluster + */ + protected boolean canCreateGroup(Node node) { + return (maximumGroups < 0 || groups.size() < maximumGroups) && canBeMaster(node); + } + + /** + * Returns true if the given node can be a master + */ + protected boolean canBeMaster(Node node) { + return masterFilter == null || masterFilter.evaluate(node); + } +} diff --git a/activecluster/src/java/org/activecluster/group/MasterZoneFilter.java b/activecluster/src/java/org/activecluster/group/MasterZoneFilter.java new file mode 100644 index 0000000000..369b74c284 --- /dev/null +++ b/activecluster/src/java/org/activecluster/group/MasterZoneFilter.java @@ -0,0 +1,41 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.group; + +import org.activecluster.Node; + +import java.util.List; + +/** + * A filter configured with a list of DMZ zones on which to restrict which nodes + * are allowed to be master nodes. + * + * @version $Revision: 1.2 $ + */ +public class MasterZoneFilter implements NodeFilter { + private List zones; + + public MasterZoneFilter(List zones) { + this.zones = zones; + } + + public boolean evaluate(Node node) { + Object zone = node.getZone(); + return zones.contains(zone); + } +} diff --git a/activecluster/src/java/org/activecluster/group/Membership.java b/activecluster/src/java/org/activecluster/group/Membership.java new file mode 100644 index 0000000000..95d52a67a1 --- /dev/null +++ b/activecluster/src/java/org/activecluster/group/Membership.java @@ -0,0 +1,64 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.group; + +/** + * Represents the membership of a Group for a Node + * + * @version $Revision: 1.2 $ + */ +public class Membership { + public static final int STATUS_REQUESTED = 1; + public static final int STATUS_SYNCHONIZING = 2; + public static final int STATUS_FAILED = 3; + public static final int STATUS_OK = 4; + + private Group group; + private int index; + private int status = STATUS_REQUESTED; + + public Membership(Group group, int index) { + this.group = group; + this.index = index; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + /** + * @return the weighting of this membership + */ + public int getWeighting() { + // lets make master heavy and the further from the end of the + // list of slaves, the lighter we become + return group.getMaximumMemberCount() - getIndex(); + } +} diff --git a/activecluster/src/java/org/activecluster/group/NodeFilter.java b/activecluster/src/java/org/activecluster/group/NodeFilter.java new file mode 100644 index 0000000000..454cde1661 --- /dev/null +++ b/activecluster/src/java/org/activecluster/group/NodeFilter.java @@ -0,0 +1,34 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.group; + +import org.activecluster.Node; + +/** + * Represents a filter on a Node to allow a pluggable + * Strategy Pattern to decide which nodes can be master nodes etc. + * + * @version $Revision: 1.2 $ + */ +public interface NodeFilter { + + /** + * Returns true if the given node matches the filter + */ + public boolean evaluate(Node node); +} diff --git a/activecluster/src/java/org/activecluster/group/NodeMemberships.java b/activecluster/src/java/org/activecluster/group/NodeMemberships.java new file mode 100644 index 0000000000..be67b7d121 --- /dev/null +++ b/activecluster/src/java/org/activecluster/group/NodeMemberships.java @@ -0,0 +1,77 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.group; + +import org.activecluster.Node; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents all of the memberhips of a node and can be used to act + * as a weighting to decide which is the least heavily loaded Node + * to be assigned to a buddy group. + * + * @version $Revision: 1.2 $ + */ +public class NodeMemberships { + private Node node; + private Map memberships = new HashMap(); + private int weighting; + + public NodeMemberships(Node node) { + this.node = node; + } + + public void addToGroup(Group group) { + if (!isMember(group)) { + int index = group.addMember(node); + Membership membership = new Membership(group, index); + memberships.put(group, membership); + weighting += membership.getWeighting(); + } + } + + public boolean removeFromGroup(Group group) { + // TODO when we remove a node from a group, we need to reweight the + // other nodes in the group + + memberships.remove(group); + return group.removeMember(node); + } + + public Node getNode() { + return node; + } + + /** + * Returns the weighting of how heavily loaded the node is + * so that a decision can be made on which node to buddy group + * with + */ + public int getWeighting() { + return weighting; + } + + /** + * Returns true if this node is a member of the given group + */ + public boolean isMember(Group group) { + return memberships.containsKey(group); + } +} diff --git a/activecluster/src/java/org/activecluster/group/package.html b/activecluster/src/java/org/activecluster/group/package.html new file mode 100644 index 0000000000..09233ed846 --- /dev/null +++ b/activecluster/src/java/org/activecluster/group/package.html @@ -0,0 +1,10 @@ + + + + + +Contains Group Organsisation models and policies for arranging {@link org.activecluster.Node} instances into +groups, such as buddy-groups (failover nodes) or master/slave groups for High Availability (HA) protocols. + + + diff --git a/activecluster/src/java/org/activecluster/impl/ActiveMQClusterFactory.java b/activecluster/src/java/org/activecluster/impl/ActiveMQClusterFactory.java new file mode 100644 index 0000000000..01ea56dbf8 --- /dev/null +++ b/activecluster/src/java/org/activecluster/impl/ActiveMQClusterFactory.java @@ -0,0 +1,61 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.impl; + +import org.activecluster.impl.DefaultClusterFactory; +import org.activemq.ActiveMQConnectionFactory; + +/** + * An implementation of {@link org.activecluster.ClusterFactory} using + * ActiveMQ + * + * @version $Revision: 1.4 $ + */ +public class ActiveMQClusterFactory extends DefaultClusterFactory { + + public static String DEFAULT_CLUSTER_URL = "peer://org.activecluster?persistent=false"; + + public ActiveMQClusterFactory() { + super(new ActiveMQConnectionFactory(DEFAULT_CLUSTER_URL)); + } + + public ActiveMQClusterFactory(String brokerURL) { + super(new ActiveMQConnectionFactory(brokerURL)); + } + + public ActiveMQClusterFactory(ActiveMQConnectionFactory connectionFactory) { + super(connectionFactory); + } + + public ActiveMQClusterFactory(boolean transacted, int acknowledgeMode, String dataTopicPrefix, long inactiveTime) { + super(new ActiveMQConnectionFactory(DEFAULT_CLUSTER_URL), transacted, acknowledgeMode, dataTopicPrefix, inactiveTime); + } + + public ActiveMQClusterFactory(ActiveMQConnectionFactory connectionFactory, boolean transacted, int acknowledgeMode, String dataTopicPrefix, long inactiveTime) { + super(connectionFactory, transacted, acknowledgeMode, dataTopicPrefix, inactiveTime); + } + + public ActiveMQConnectionFactory getActiveMQConnectionFactory() { + return (ActiveMQConnectionFactory) getConnectionFactory(); + } + + public void setActiveMQConnectionFactory(ActiveMQConnectionFactory connectionFactory) { + setConnectionFactory(connectionFactory); + } + +} diff --git a/activecluster/src/java/org/activecluster/impl/DefaultCluster.java b/activecluster/src/java/org/activecluster/impl/DefaultCluster.java new file mode 100644 index 0000000000..71b510cf7b --- /dev/null +++ b/activecluster/src/java/org/activecluster/impl/DefaultCluster.java @@ -0,0 +1,221 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.impl; + +import java.io.Serializable; +import java.util.Map; +import java.util.Timer; +import javax.jms.BytesMessage; +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Session; +import javax.jms.StreamMessage; +import javax.jms.TextMessage; +import org.activecluster.Cluster; +import org.activecluster.ClusterListener; +import org.activecluster.LocalNode; +import org.activecluster.Service; +import org.activecluster.election.ElectionStrategy; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.*; +/** + * A default implementation of ActiveCluster which uses standard JMS operations + * + * @version $Revision: 1.6 $ + */ +public class DefaultCluster implements Cluster { + + private final static Log log = LogFactory.getLog(DefaultCluster.class); + + private StateServiceImpl stateService; + private LocalNode localNode; + private String destination; + private Connection connection; + private Session session; + private MessageProducer producer; + private MessageConsumer consumer; + private Timer timer; + private AtomicBoolean started = new AtomicBoolean(false); + private Object clusterLock = new Object(); + + public DefaultCluster(final LocalNode localNode,String dataDestination, String destination, Connection connection, Session session, + MessageProducer producer, Timer timer, long inactiveTime) throws JMSException { + this.localNode = localNode; + this.destination = destination; + this.connection = connection; + this.session = session; + this.producer = producer; + this.timer = timer; + + if (producer == null) { + throw new IllegalArgumentException("No producer specified!"); + } + + // now lets subscribe the service to the updates from the data topic + consumer = session.createConsumer(createDestination(dataDestination), null, true); + + log.info("Creating data consumer on topic: " + dataDestination); + + this.stateService = new StateServiceImpl(this, clusterLock, new Runnable() { + public void run() { + if (localNode instanceof ReplicatedLocalNode) { + ((ReplicatedLocalNode) localNode).pingRemoteNodes(); + } + } + }, timer, inactiveTime); + consumer.setMessageListener(new StateConsumer(stateService)); + } + + public void addClusterListener(ClusterListener listener) { + stateService.addClusterListener(listener); + } + + public void removeClusterListener(ClusterListener listener) { + stateService.removeClusterListener(listener); + } + + public String getDestination() { + return destination; + } + + public LocalNode getLocalNode() { + return localNode; + } + + public Map getNodes() { + return stateService.getNodes(); + } + + public void setElectionStrategy(ElectionStrategy strategy) { + stateService.setElectionStrategy(strategy); + } + + public void send(String destination,Message message) throws JMSException { + producer.send(createDestination(destination), message); + } + + public void send(Destination replyTo, Message message) throws JMSException{ + producer.send(replyTo,message); + } + + public MessageConsumer createConsumer(String destination) throws JMSException { + return getSession().createConsumer(createDestination(destination)); + } + + public MessageConsumer createConsumer(String destination, String selector) throws JMSException { + return getSession().createConsumer(createDestination(destination), selector); + } + + public MessageConsumer createConsumer(String destination, String selector, boolean noLocal) throws JMSException { + return getSession().createConsumer(createDestination(destination), selector, noLocal); + } + + public Message createMessage() throws JMSException { + return getSession().createMessage(); + } + + public BytesMessage createBytesMessage() throws JMSException { + return getSession().createBytesMessage(); + } + + public MapMessage createMapMessage() throws JMSException { + return getSession().createMapMessage(); + } + + public ObjectMessage createObjectMessage() throws JMSException { + return getSession().createObjectMessage(); + } + + public ObjectMessage createObjectMessage(Serializable object) throws JMSException { + return getSession().createObjectMessage(object); + } + + public StreamMessage createStreamMessage() throws JMSException { + return getSession().createStreamMessage(); + } + + public TextMessage createTextMessage() throws JMSException { + return getSession().createTextMessage(); + } + + public TextMessage createTextMessage(String text) throws JMSException { + return getSession().createTextMessage(text); + } + + public void start() throws JMSException { + if (started.compareAndSet(false, true)) { + connection.start(); + } + } + + public void stop() throws JMSException { + try { + if (localNode instanceof Service) { + ((Service) localNode).stop(); + } + timer.cancel(); + session.close(); + connection.stop(); + connection.close(); + } + finally { + connection = null; + session = null; + } + } + + public boolean waitForClusterToComplete(int expectedCount, long timeout) throws InterruptedException { + timeout = timeout > 0 ? timeout : Long.MAX_VALUE; + long increment = 500; + increment = increment < timeout ? increment : timeout; + long waitTime = timeout; + long start = System.currentTimeMillis(); + synchronized (clusterLock) { + while (stateService.getNodes().size() < expectedCount && started.get() && waitTime > 0) { + clusterLock.wait(increment); + waitTime = timeout - (System.currentTimeMillis() - start); + } + } + return stateService.getNodes().size() >= expectedCount; + } + + protected Session getSession() throws JMSException { + if (session == null) { + throw new JMSException("Cannot perform operation, this cluster connection is now closed"); + } + return session; + } + + /** + * Create a named Destination + * @param name + * @return the Destinatiion + * @throws JMSException + */ + public Destination createDestination(String name) throws JMSException{ + Destination result = getSession().createTopic(name); + return result; + } +} diff --git a/activecluster/src/java/org/activecluster/impl/DefaultClusterFactory.java b/activecluster/src/java/org/activecluster/impl/DefaultClusterFactory.java new file mode 100644 index 0000000000..71fb08e0b7 --- /dev/null +++ b/activecluster/src/java/org/activecluster/impl/DefaultClusterFactory.java @@ -0,0 +1,175 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.impl; + +import java.util.Timer; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.Topic; +import org.activecluster.Cluster; +import org.activecluster.ClusterException; +import org.activecluster.ClusterFactory; +import org.activemq.util.IdGenerator; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A Factory of DefaultCluster instances + * + * @version $Revision: 1.4 $ + */ +public class DefaultClusterFactory implements ClusterFactory { + + private final static Log log = LogFactory.getLog(DefaultClusterFactory.class); + + private ConnectionFactory connectionFactory; + private boolean transacted; + private int acknowledgeMode; + private String dataTopicPrefix; + private long inactiveTime; + private boolean useQueueForInbox = false; + private int deliveryMode = DeliveryMode.NON_PERSISTENT; + private IdGenerator idGenerator = new IdGenerator(); + + public DefaultClusterFactory(ConnectionFactory connectionFactory, boolean transacted, int acknowledgeMode, String dataTopicPrefix, long inactiveTime) { + this.connectionFactory = connectionFactory; + this.transacted = transacted; + this.acknowledgeMode = acknowledgeMode; + this.dataTopicPrefix = dataTopicPrefix; + this.inactiveTime = inactiveTime; + } + + public DefaultClusterFactory(ConnectionFactory connectionFactory) { + this(connectionFactory, false, Session.AUTO_ACKNOWLEDGE, "ACTIVECLUSTER.DATA.", 6000L); + } + + public Cluster createCluster(String groupDestination) throws JMSException { + return createCluster(idGenerator.generateId(), groupDestination); + } + + public Cluster createCluster(String name,String groupDestination) throws JMSException { + Connection connection = getConnectionFactory().createConnection(); + Session session = createSession(connection); + return createCluster(connection, session, name,groupDestination); + } + + // Properties + //------------------------------------------------------------------------- + public String getDataTopicPrefix() { + return dataTopicPrefix; + } + + public void setDataTopicPrefix(String dataTopicPrefix) { + this.dataTopicPrefix = dataTopicPrefix; + } + + public int getAcknowledgeMode() { + return acknowledgeMode; + } + + public void setAcknowledgeMode(int acknowledgeMode) { + this.acknowledgeMode = acknowledgeMode; + } + + public long getInactiveTime() { + return inactiveTime; + } + + public void setInactiveTime(long inactiveTime) { + this.inactiveTime = inactiveTime; + } + + public boolean isTransacted() { + return transacted; + } + + public void setTransacted(boolean transacted) { + this.transacted = transacted; + } + + public boolean isUseQueueForInbox() { + return useQueueForInbox; + } + + public void setUseQueueForInbox(boolean useQueueForInbox) { + this.useQueueForInbox = useQueueForInbox; + } + + public ConnectionFactory getConnectionFactory() { + return connectionFactory; + } + + public void setConnectionFactory(ConnectionFactory connectionFactory) { + this.connectionFactory = connectionFactory; + } + + public int getDeliveryMode() { + return deliveryMode; + } + + /** + * Sets the delivery mode of the group based producer + */ + public void setDeliveryMode(int deliveryMode) { + this.deliveryMode = deliveryMode; + } + + // Implementation methods + //------------------------------------------------------------------------- + protected Cluster createCluster(Connection connection, Session session, String name,String groupDestination) throws JMSException { + String dataDestination = dataTopicPrefix + groupDestination; + + log.info("Creating cluster group producer on topic: " + groupDestination); + + MessageProducer producer = createProducer(session, null); + producer.setDeliveryMode(deliveryMode); + + log.info("Creating cluster data producer on data destination: " + dataDestination); + + Topic dataTopic = session.createTopic(dataDestination); + MessageProducer keepAliveProducer = session.createProducer(dataTopic); + keepAliveProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + StateService serviceStub = new StateServiceStub(session, keepAliveProducer); + + String localInboxDestination = dataDestination + "." + name; + + ReplicatedLocalNode localNode = new ReplicatedLocalNode(name,localInboxDestination, serviceStub); + Timer timer = new Timer(); + DefaultCluster answer = new DefaultCluster(localNode, dataDestination, groupDestination, connection, session, producer, timer, inactiveTime); + return answer; + } + + /* + protected Cluster createInternalCluster(Session session, Topic dataDestination) { + MessageProducer producer = createProducer(session); + return new DefaultCluster(new NonReplicatedLocalNode(), dataDestination, connection, session, producer); + } + */ + + protected MessageProducer createProducer(Session session, Topic groupDestination) throws JMSException { + return session.createProducer(groupDestination); + } + + protected Session createSession(Connection connection) throws JMSException { + return connection.createSession(transacted, acknowledgeMode); + } +} \ No newline at end of file diff --git a/activecluster/src/java/org/activecluster/impl/NodeImpl.java b/activecluster/src/java/org/activecluster/impl/NodeImpl.java new file mode 100644 index 0000000000..64f0cace2a --- /dev/null +++ b/activecluster/src/java/org/activecluster/impl/NodeImpl.java @@ -0,0 +1,122 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.impl; + +import java.util.HashMap; +import java.util.Map; +import org.activecluster.Node; + + +/** + * Default implementation of a remote Node + * + * @version $Revision: 1.3 $ + */ +public class NodeImpl implements Node { + private static final long serialVersionUID=-3909792803360045064L; + private String name; + private String destination; + protected Map state; + protected boolean coordinator; + + /** + * Allow a node to be copied for sending it as a message + * + * @param node + */ + public NodeImpl(Node node) { + this(node.getName(),node.getDestination(), node.getState()); + } + + /** + * Create a Node + * @param name + * @param destination + */ + public NodeImpl(String name,String destination) { + this(name,destination, new HashMap()); + } + + /** + * Create A Node + * @param name + * @param destination + * @param state + */ + public NodeImpl(String name,String destination, Map state) { + this.name = name; + this.destination = destination; + this.state = state; + } + + /** + * @return the name of the node + */ + public String getName() { + return name; + } + + /** + * @return pretty print of the node + */ + public String toString() { + return "Node[<" + name + ">destination: " + destination + " state: " + state + "]"; + } + + /** + * @return the destination of the node + */ + public String getDestination() { + return destination; + } + + /** + * Get the State + * @return the State of the Node + */ + public synchronized Map getState() { + return new HashMap(state); + } + + + /** + * @return true if this node has been elected as coordinator + */ + public boolean isCoordinator() { + return coordinator; + } + + /** + * Get the zone + * @return the Zone + */ + public Object getZone() { + return state.get("zone"); + } + + // Implementation methods + //------------------------------------------------------------------------- + + protected synchronized void setState(Map state) { + this.state = state; + } + + protected void setCoordinator(boolean value) { + coordinator = value; + } +} diff --git a/activecluster/src/java/org/activecluster/impl/NonReplicatedLocalNode.java b/activecluster/src/java/org/activecluster/impl/NonReplicatedLocalNode.java new file mode 100644 index 0000000000..8ed2a58d23 --- /dev/null +++ b/activecluster/src/java/org/activecluster/impl/NonReplicatedLocalNode.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.impl; + +import java.util.Map; +import org.activecluster.LocalNode; + +/** + * Default implementation of a local Node which doesn't + * have its state replicated + * + * @version $Revision: 1.4 $ + */ +public class NonReplicatedLocalNode extends NodeImpl implements LocalNode { + private static final long serialVersionUID=2525565639637967143L; + + /** + * Create a Non-replicated local node + * @param name + * @param destination + */ + public NonReplicatedLocalNode(String name, String destination) { + super(name,destination); + } + + /** + * Set the local state + * @param state + */ + public void setState(Map state) { + super.setState(state); + } + + /** + * Shouldn't be called for non-replicated local nodes + */ + public void pingRemoteNodes() { + throw new RuntimeException("Non-Replicated Local Node should not distribute it's state!"); + } +} \ No newline at end of file diff --git a/activecluster/src/java/org/activecluster/impl/ReplicatedLocalNode.java b/activecluster/src/java/org/activecluster/impl/ReplicatedLocalNode.java new file mode 100644 index 0000000000..eb58ae3710 --- /dev/null +++ b/activecluster/src/java/org/activecluster/impl/ReplicatedLocalNode.java @@ -0,0 +1,81 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.impl; + +import java.util.Map; +import javax.jms.JMSException; +import org.activecluster.LocalNode; +import org.activecluster.Service; + + +/** + * Default implementation of a local Node which has its + * state replicated across the cluster + * + * @version $Revision: 1.3 $ + */ +public class ReplicatedLocalNode extends NodeImpl implements LocalNode, Service { + + /** + * + */ + private static final long serialVersionUID=4626381612145333540L; + private StateService serviceStub; + + /** + * Create ReplicatedLocalNode + * @param name + * @param destination + * @param serviceStub + */ + public ReplicatedLocalNode(String name,String destination, StateService serviceStub) { + super(name,destination); + this.serviceStub = serviceStub; + } + + /** + * Set the State of the local node + * @param state + */ + public void setState(Map state) { + super.setState(state); + serviceStub.keepAlive(this); + } + + /** + * ping remote nodes + * + */ + public void pingRemoteNodes() { + serviceStub.keepAlive(this); + } + + /** + * start (lifecycle) + * @throws JMSException + */ + public void start() throws JMSException { + } + + /** + * stop (lifecycle) + * @throws JMSException + */ + public void stop() throws JMSException { + } +} diff --git a/activecluster/src/java/org/activecluster/impl/StateConsumer.java b/activecluster/src/java/org/activecluster/impl/StateConsumer.java new file mode 100644 index 0000000000..aaaaf6e0a1 --- /dev/null +++ b/activecluster/src/java/org/activecluster/impl/StateConsumer.java @@ -0,0 +1,73 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.activecluster.Node; + +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.ObjectMessage; + + +/** + * A JMS MessageListener which processes inbound messages and + * applies them to a StateService + * + * @version $Revision: 1.2 $ + */ +public class StateConsumer implements MessageListener { + + private final static Log log = LogFactory.getLog(StateConsumer.class); + + private StateService stateService; + + public StateConsumer(StateService stateService) { + if (stateService == null) { + throw new IllegalArgumentException("Must specify a valid StateService implementation"); + } + this.stateService = stateService; + } + + public void onMessage(Message message) { + if (log.isDebugEnabled()) { + log.debug("Received cluster data message!: " + message); + } + + if (message instanceof ObjectMessage) { + ObjectMessage objectMessage = (ObjectMessage) message; + try { + Node node = (Node) objectMessage.getObject(); + String type = objectMessage.getJMSType(); + if (type != null && type.equals("shutdown")) { + stateService.shutdown(node); + } + else { + stateService.keepAlive(node); + } + } + catch (Exception e) { + log.error("Could not extract node from message: " + e + ". Message: " + message, e); + } + } + else { + log.warn("Ignoring message: " + message); + } + } +} diff --git a/activecluster/src/java/org/activecluster/impl/StateService.java b/activecluster/src/java/org/activecluster/impl/StateService.java new file mode 100644 index 0000000000..f6f5f02639 --- /dev/null +++ b/activecluster/src/java/org/activecluster/impl/StateService.java @@ -0,0 +1,42 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.impl; + +import org.activecluster.Node; + + +/** + * A client side proxy to the remove cluster + * + * @version $Revision: 1.3 $ + */ +public interface StateService { + + /** + * Sends a keep alive to the cluster + * + * @param node + */ + public void keepAlive(Node node); + + /** + * Sends a shutdown message to the cluster + */ + public void shutdown(Node node); + +} diff --git a/activecluster/src/java/org/activecluster/impl/StateServiceImpl.java b/activecluster/src/java/org/activecluster/impl/StateServiceImpl.java new file mode 100644 index 0000000000..6830e85d81 --- /dev/null +++ b/activecluster/src/java/org/activecluster/impl/StateServiceImpl.java @@ -0,0 +1,299 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.impl; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.Map.Entry; +import javax.jms.JMSException; +import org.activecluster.Cluster; +import org.activecluster.ClusterEvent; +import org.activecluster.ClusterListener; +import org.activecluster.Node; +import org.activecluster.election.ElectionStrategy; +import org.activecluster.election.impl.BullyElectionStrategy; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + + +/** + * Represents a node list + * + * @version $Revision: 1.4 $ + */ +public class StateServiceImpl implements StateService { + + private final static Log log = LogFactory.getLog(StateServiceImpl.class); + private Cluster cluster; + private Object clusterLock; + private Map nodes = new ConcurrentHashMap(); + private long inactiveTime; + private List listeners = new CopyOnWriteArrayList(); + private String localDestination; + private Runnable localNodePing; + private NodeImpl coordinator; + private ElectionStrategy electionStrategy; + + /** + * @param cluster + * @param clusterLock + * @param localNodePing + * @param timer + * @param inactiveTime + */ + /** + * Constructor StateServiceImpl + * @param cluster + * @param clusterLock + * @param localNodePing + * @param timer + * @param inactiveTime + */ + public StateServiceImpl(Cluster cluster, Object clusterLock, Runnable localNodePing, Timer timer, long inactiveTime) { + this.cluster = cluster; + this.clusterLock = clusterLock; + this.localDestination = cluster.getLocalNode().getDestination(); + this.localNodePing = localNodePing; + this.inactiveTime = inactiveTime; + long delay = inactiveTime / 3; + timer.scheduleAtFixedRate(createTimerTask(), delay, delay); + (this.coordinator = (NodeImpl) cluster.getLocalNode()).setCoordinator(true); + this.electionStrategy = new BullyElectionStrategy(); + } + + /** + * @return the current election strategy + */ + public ElectionStrategy getElectionStrategy() { + return electionStrategy; + } + + /** + * set the election strategy + * + * @param electionStrategy + */ + public void setElectionStrategy(ElectionStrategy electionStrategy) { + this.electionStrategy = electionStrategy; + } + + /** + * Get time of since last communication + * @return length of time inactive + */ + public long getInactiveTime() { + return inactiveTime; + } + + /** + * Set the time inactive + * @param inactiveTime + */ + public void setInactiveTime(long inactiveTime) { + this.inactiveTime = inactiveTime; + } + + /** + * Get A Map of nodes - where key = destination, value = node + * @return map of destination/nodes + */ + public Map getNodes() { + HashMap answer = new HashMap(nodes.size()); + for (Iterator iter = nodes.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry = (Map.Entry) iter.next(); + String key = entry.getKey().toString(); + NodeEntry nodeEntry = (NodeEntry) entry.getValue(); + answer.put(key, nodeEntry.node); + } + return answer; + } + + /** + * Got a keepalive + * @param node + */ + public void keepAlive(Node node) { + String key = node.getDestination(); + if (key != null && !localDestination.equals(key)) { + NodeEntry entry = (NodeEntry) nodes.get(key); + if (entry == null) { + entry = new NodeEntry(); + entry.node = node; + nodes.put(key, entry); + nodeAdded(node); + synchronized (clusterLock) { + clusterLock.notifyAll(); + } + } + else { + // has the data changed + if (stateHasChanged(entry.node, node)) { + entry.node = node; + nodeUpdated(node); + } + } + + // lets update the timer at which the node will be considered + // to be dead + entry.lastKeepAlive = getTimeMillis(); + } + } + + /** + * shutdown the node + */ + public void shutdown(Node node){ + String key=node.getDestination(); + if(key!=null){ + nodes.remove(key); + ClusterEvent event=new ClusterEvent(cluster,node,ClusterEvent.ADD_NODE); + for (Iterator i = listeners.iterator(); i.hasNext();){ + ClusterListener listener=(ClusterListener) i.next(); + listener.onNodeRemoved(event); + } + } + } + + /** + * check nodes are alive + * + */ + + public void checkForTimeouts() { + localNodePing.run(); + long time = getTimeMillis(); + for (Iterator iter = nodes.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry = (Entry) iter.next(); + NodeEntry nodeEntry = (NodeEntry) entry.getValue(); + if (nodeEntry.lastKeepAlive + inactiveTime < time) { + iter.remove(); + nodeFailed(nodeEntry.node); + } + } + } + + public TimerTask createTimerTask() { + return new TimerTask() { + public void run() { + checkForTimeouts(); + } + }; + } + + public void addClusterListener(ClusterListener listener) { + listeners.add(listener); + } + + public void removeClusterListener(ClusterListener listener) { + listeners.remove(listener); + } + + protected void nodeAdded(Node node) { + ClusterEvent event = new ClusterEvent(cluster, node, ClusterEvent.ADD_NODE); + // lets take a copy to make contention easier + Object[] array = listeners.toArray(); + for (int i = 0, size = array.length; i < size; i++) { + ClusterListener listener = (ClusterListener) array[i]; + listener.onNodeAdd(event); + } + doElection(); + } + + protected void nodeUpdated(Node node) { + ClusterEvent event = new ClusterEvent(cluster, node, ClusterEvent.UPDATE_NODE); + // lets take a copy to make contention easier + Object[] array = listeners.toArray(); + for (int i = 0, size = array.length; i < size; i++) { + ClusterListener listener = (ClusterListener) array[i]; + listener.onNodeUpdate(event); + } + } + + protected void nodeFailed(Node node) { + ClusterEvent event = new ClusterEvent(cluster, node, ClusterEvent.REMOVE_NODE); + // lets take a copy to make contention easier + Object[] array = listeners.toArray(); + for (int i = 0, size = array.length; i < size; i++) { + ClusterListener listener = (ClusterListener) array[i]; + listener.onNodeFailed(event); + } + doElection(); + } + + protected void coordinatorChanged(Node node) { + ClusterEvent event = new ClusterEvent(cluster, node, ClusterEvent.ELECTED_COORDINATOR); + // lets take a copy to make contention easier + Object[] array = listeners.toArray(); + for (int i = 0, size = array.length; i < size; i++) { + ClusterListener listener = (ClusterListener) array[i]; + listener.onCoordinatorChanged(event); + } + } + + protected void doElection() { + if (electionStrategy != null) { + try { + NodeImpl newElected = (NodeImpl) electionStrategy.doElection(cluster); + if (newElected != null && !newElected.equals(coordinator)) { + coordinator.setCoordinator(false); + coordinator = newElected; + coordinator.setCoordinator(true); + coordinatorChanged(coordinator); + } + } + catch (JMSException jmsEx) { + log.error("do election failed", jmsEx); + } + } + } + + /** + * For performance we may wish to use a less granualar timing mechanism + * only updating the time every x millis since we're only using + * the time as a judge of when a node has not pinged for at least a few + * hundred millis etc. + */ + protected long getTimeMillis() { + return System.currentTimeMillis(); + } + + protected static class NodeEntry { + public Node node; + public long lastKeepAlive; + } + + + /** + * @return true if the node has changed state from the old in memory copy to the + * newly arrived copy + */ + protected boolean stateHasChanged(Node oldNode, Node newNode) { + Map oldState = oldNode.getState(); + Map newState = newNode.getState(); + if (oldState == newState) { + return false; + } + return oldState == null || newState == null || !oldState.equals(newState); + } +} diff --git a/activecluster/src/java/org/activecluster/impl/StateServiceStub.java b/activecluster/src/java/org/activecluster/impl/StateServiceStub.java new file mode 100644 index 0000000000..457221781b --- /dev/null +++ b/activecluster/src/java/org/activecluster/impl/StateServiceStub.java @@ -0,0 +1,76 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.activecluster.Node; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.Session; + + +/** + * A local stub for the state service which sends JMS messages + * to the cluster + * + * @version $Revision: 1.2 $ + */ +public class StateServiceStub implements StateService { + + private final Log log = LogFactory.getLog(getClass()); + + private Session session; + private MessageProducer producer; + + public StateServiceStub(Session session, MessageProducer producer) { + this.session = session; + this.producer = producer; + } + + public void keepAlive(Node node) { + try { + if (log.isDebugEnabled()) { + log.debug("Sending cluster data message: " + node); + } + + Message message = session.createObjectMessage(new NodeImpl(node)); + producer.send(message); + } + catch (JMSException e) { + log.error("Could not send JMS message: " + e, e); + } + } + + public void shutdown(Node node) { + try { + if (log.isDebugEnabled()) { + log.debug("Sending shutdown message: " + node); + } + + Message message = session.createObjectMessage(new NodeImpl(node)); + message.setJMSType("shutdown"); + producer.send(message); + } + catch (JMSException e) { + log.error("Could not send JMS message: " + e, e); + } + } +} diff --git a/activecluster/src/java/org/activecluster/impl/package.html b/activecluster/src/java/org/activecluster/impl/package.html new file mode 100644 index 0000000000..ebf4150fa7 --- /dev/null +++ b/activecluster/src/java/org/activecluster/impl/package.html @@ -0,0 +1,11 @@ + + + + + +

+ Default implementation of ActiveCluster using standard JMS API to build the cluster. +

+ + + diff --git a/activecluster/src/java/org/activecluster/package.html b/activecluster/src/java/org/activecluster/package.html new file mode 100644 index 0000000000..b8a289b644 --- /dev/null +++ b/activecluster/src/java/org/activecluster/package.html @@ -0,0 +1,15 @@ + + + + + +

+ ActiveCluster API for working with a simple cluster abstraction for building cluster algorithms + like buddy systems, voting, master/slave protocols, electing a controller and so forth. +

+ Clusters communicate across a common destination (typically a Topic) but every member (node)of a + cluster has a unique name (the generation of unique names is left to the developer), and a + unique destination (which uses the unique name). + + + diff --git a/activecluster/src/test/org/activecluster/ChatDemo.java b/activecluster/src/test/org/activecluster/ChatDemo.java new file mode 100644 index 0000000000..4a04abd4b5 --- /dev/null +++ b/activecluster/src/test/org/activecluster/ChatDemo.java @@ -0,0 +1,140 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import javax.jms.JMSException; +import org.activecluster.impl.ActiveMQClusterFactory; + +/** + * @version $Revision: 1.2 $ + */ +public class ChatDemo implements ClusterListener { + private Cluster cluster; + private String name = "unknown"; + + public static void main(String[] args) { + try { + ChatDemo test = new ChatDemo(); + test.run(); + } + catch (JMSException e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + Exception c = e.getLinkedException(); + if (c != null) { + System.out.println("Cause: " + c); + c.printStackTrace(); + } + } + catch (Exception e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + + public void run() throws Exception { + cluster = createCluster(); + cluster.addClusterListener(this); + cluster.start(); + + + System.out.println(); + System.out.println(); + System.out.println("Welcome to the ActiveCluster Chat Demo!"); + System.out.println(); + System.out.println("Enter text to talk or type"); + System.out.println(" /quit to terminate the application"); + System.out.println(" /name foo to change your name to be 'foo'"); + + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + boolean running = true; + while (running) { + String line = reader.readLine(); + if (line == null || line.trim().equalsIgnoreCase("quit")) { + break; + } + else { + running = processCommand(line.trim()); + } + } + stop(); + } + + protected boolean processCommand(String text) throws JMSException { + if (text.equals("/quit")) { + return false; + } + else { + if (text.startsWith("/name")) { + name = text.substring(5).trim(); + System.out.println("* name now changed to: " + name); + } + else { + // lets talk + Map map = new HashMap(); + map.put("text", text); + map.put("name", name); + cluster.getLocalNode().setState(map); + } + return true; + } + } + + + public void onNodeAdd(ClusterEvent event) { + System.out.println("* " + getName(event) + " has joined the room"); + } + + public void onNodeUpdate(ClusterEvent event) { + System.out.println(getName(event) + "> " + getText(event)); + } + + public void onNodeRemoved(ClusterEvent event) { + System.out.println("* " + getName(event) + " has left the room"); + } + + public void onNodeFailed(ClusterEvent event) { + System.out.println("* " + getName(event) + " has failed unexpectedly"); + } + + public void onCoordinatorChanged(ClusterEvent event){ + + } + + protected Object getName(ClusterEvent event) { + return event.getNode().getState().get("name"); + } + + protected Object getText(ClusterEvent event) { + return event.getNode().getState().get("text"); + } + + protected void stop() throws JMSException { + cluster.stop(); + } + + protected Cluster createCluster() throws JMSException, ClusterException { + ClusterFactory factory = new ActiveMQClusterFactory(); + return factory.createCluster("ORG.CODEHAUS.ACTIVEMQ.TEST.CLUSTER"); + } + +} diff --git a/activecluster/src/test/org/activecluster/ClusterDemo.java b/activecluster/src/test/org/activecluster/ClusterDemo.java new file mode 100644 index 0000000000..30d303c1e2 --- /dev/null +++ b/activecluster/src/test/org/activecluster/ClusterDemo.java @@ -0,0 +1,109 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; + +import org.activecluster.impl.ActiveMQClusterFactory; +import org.activecluster.impl.DefaultClusterFactory; +import org.activecluster.election.ElectionStrategy; +import org.activecluster.election.impl.BullyElectionStrategy; + +import javax.jms.JMSException; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; + +/** + * @version $Revision: 1.2 $ + */ +public class ClusterDemo { + protected Cluster cluster; + private String name; + private ElectionStrategy electionStrategy; + + public static void main(String[] args) { + try { + ClusterDemo test = new ClusterDemo(); + if (args.length > 0) { + test.name = args[0]; + } + test.demo(); + } + catch (JMSException e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + Exception c = e.getLinkedException(); + if (c != null) { + System.out.println("Cause: " + c); + c.printStackTrace(); + } + } + catch (Exception e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + + public void demo() throws Exception { + start(); + + cluster.addClusterListener(new TestingClusterListener(cluster)); + + System.out.println("Enter 'quit' to terminate"); + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + while (true) { + String line = reader.readLine(); + if (line == null || line.trim().equalsIgnoreCase("quit")) { + break; + } + else { + Map map = new HashMap(); + map.put("text", line); + cluster.getLocalNode().setState(map); + } + } + stop(); + } + + + protected void start() throws JMSException, ClusterException { + cluster = createCluster(); + if (name != null) { + System.out.println("Starting node: " + name); + + // TODO could we do cluster.setName() ? + Map state = new HashMap(); + state.put("name", name); + cluster.getLocalNode().setState(state); + } + cluster.start(); + if (electionStrategy == null) { + electionStrategy = new BullyElectionStrategy(); + } + electionStrategy.doElection(cluster); + } + + protected void stop() throws JMSException { + cluster.stop(); + } + + protected Cluster createCluster() throws JMSException, ClusterException { + ClusterFactory factory = new ActiveMQClusterFactory(); + return factory.createCluster("ORG.CODEHAUS.ACTIVEMQ.TEST.CLUSTER"); + } +} diff --git a/activecluster/src/test/org/activecluster/ClusterFunctionTest.java b/activecluster/src/test/org/activecluster/ClusterFunctionTest.java new file mode 100644 index 0000000000..0489a5ff8f --- /dev/null +++ b/activecluster/src/test/org/activecluster/ClusterFunctionTest.java @@ -0,0 +1,209 @@ +/** + * + * Copyright 2003-2004 The Apache Software Foundation + * + * Licensed 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.activecluster; +import java.util.HashMap; +import java.util.Map; +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.ObjectMessage; +import junit.framework.TestCase; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.activecluster.Cluster; +import org.activecluster.ClusterEvent; +import org.activecluster.ClusterListener; +import org.activecluster.impl.DefaultClusterFactory; +import org.activemq.ActiveMQConnectionFactory; + +/** + * Test ActiveCluster, ActiveMQ, with an eye to putting WADI on top of them. + * + * @author Jules Gosnell + * @version $Revision: 1.4 $ + */ +public class ClusterFunctionTest extends TestCase { + protected Log _log = LogFactory.getLog(ClusterFunctionTest.class); + + public ClusterFunctionTest(String name) { + super(name); + } + protected ActiveMQConnectionFactory _connectionFactory; + protected Connection _connection; + protected DefaultClusterFactory _clusterFactory; + protected Cluster _cluster0; + protected Cluster _cluster1; + + protected void setUp() throws Exception { + testResponsePassed = false; + _connectionFactory = new ActiveMQConnectionFactory("peer://cluster?persistent=false"); + _clusterFactory = new DefaultClusterFactory(_connectionFactory); + _cluster0 = _clusterFactory.createCluster("ORG.CODEHAUS.WADI.TEST.CLUSTER"); + _cluster1 = _clusterFactory.createCluster("ORG.CODEHAUS.WADI.TEST.CLUSTER"); + _cluster0.start(); + _log.info("started node0: " + _cluster0.getLocalNode().getDestination()); + _cluster1.start(); + _log.info("started node1: " + _cluster1.getLocalNode().getDestination()); + } + + protected void tearDown() throws JMSException { + // _cluster1.stop(); + _cluster1 = null; + // _cluster0.stop(); + _cluster0 = null; + _clusterFactory = null; + // _connection.stop(); + _connection = null; + // _connectionFactory.stop(); + } + //---------------------------------------- + class MyClusterListener implements ClusterListener { + public void onNodeAdd(ClusterEvent ce) { + _log.info("node added: " + ce.getNode()); + } + + public void onNodeFailed(ClusterEvent ce) { + _log.info("node failed: " + ce.getNode()); + } + + public void onNodeRemoved(ClusterEvent ce) { + _log.info("node removed: " + ce.getNode()); + } + + public void onNodeUpdate(ClusterEvent ce) { + _log.info("node updated: " + ce.getNode()); + } + + public void onCoordinatorChanged(ClusterEvent ce) { + _log.info("coordinator changed: " + ce.getNode()); + } + } + + public void testCluster() throws Exception { + _cluster0.addClusterListener(new MyClusterListener()); + Map map = new HashMap(); + map.put("text", "testing123"); + _cluster0.getLocalNode().setState(map); + _log.info("nodes: " + _cluster0.getNodes()); + Thread.sleep(10000); + assertTrue(true); + } + /** + * An invokable piece of work. + */ + static interface Invocation extends java.io.Serializable { + public void invoke(Cluster cluster, ObjectMessage om); + } + /** + * Listen for messages, if they contain Invocations, invoke() them. + */ + class InvocationListener implements MessageListener { + protected Cluster _cluster; + + public InvocationListener(Cluster cluster) { + _cluster = cluster; + } + + public void onMessage(Message message) { + _log.info("message received: " + message); + ObjectMessage om = null; + Object tmp = null; + Invocation invocation = null; + try { + if (message instanceof ObjectMessage && (om = (ObjectMessage) message) != null + && (tmp = om.getObject()) != null && tmp instanceof Invocation + && (invocation = (Invocation) tmp) != null) { + _log.info("invoking message on: " + _cluster.getLocalNode()); + invocation.invoke(_cluster, om); + _log.info("message successfully invoked on: " + _cluster.getLocalNode()); + } + else { + _log.warn("bad message: " + message); + } + } + catch (JMSException e) { + _log.warn("unexpected problem", e); + } + } + } + /** + * A request for a piece of work which involves sending a response back to the original requester. + */ + static class Request implements Invocation { + public void invoke(Cluster cluster, ObjectMessage om2) { + try { + System.out.println("request received"); + ObjectMessage om = cluster.createObjectMessage(); + om.setJMSReplyTo(cluster.createDestination(cluster.getLocalNode().getDestination())); + om.setObject(new Response()); + System.out.println("sending response"); + cluster.send(om2.getJMSReplyTo(), om); + System.out.println("request processed"); + } + catch (JMSException e) { + System.err.println("problem sending response"); + e.printStackTrace(); + } + } + } + static boolean testResponsePassed = false; + /** + * A response containing a piece of work. + */ + static class Response implements Invocation { + public void invoke(Cluster cluster, ObjectMessage om) { + try { + System.out.println("response arrived from: " + om.getJMSReplyTo()); + // set a flag to test later + ClusterFunctionTest.testResponsePassed = true; + System.out.println("response processed on: " + cluster.getLocalNode().getDestination()); + } + catch (JMSException e) { + System.err.println("problem processing response"); + } + } + } + + public void testResponse() throws Exception { + MessageListener listener0 = new InvocationListener(_cluster0); + MessageListener listener1 = new InvocationListener(_cluster1); + // 1->(n-1) messages (excludes self) + _cluster0.createConsumer(_cluster0.getDestination(), null, true).setMessageListener(listener0); + // 1->1 messages + _cluster0.createConsumer(_cluster0.getLocalNode().getDestination()).setMessageListener(listener0); + // 1->(n-1) messages (excludes self) + _cluster1.createConsumer(_cluster1.getDestination(), null, true).setMessageListener(listener1); + // 1->1 messages + _cluster1.createConsumer(_cluster1.getLocalNode().getDestination()).setMessageListener(listener1); + ObjectMessage om = _cluster0.createObjectMessage(); + om.setJMSReplyTo(_cluster0.createDestination(_cluster0.getLocalNode().getDestination())); + om.setObject(new Request()); + testResponsePassed = false; + _cluster0.send(_cluster0.getLocalNode().getDestination(), om); + Thread.sleep(3000); + assertTrue(testResponsePassed); + _log.info("request/response between same node OK"); + testResponsePassed = false; + _cluster0.send(_cluster1.getLocalNode().getDestination(), om); + Thread.sleep(3000); + assertTrue(testResponsePassed); + _log.info("request/response between two different nodes OK"); + } +} \ No newline at end of file diff --git a/activecluster/src/test/org/activecluster/ClusterTest.java b/activecluster/src/test/org/activecluster/ClusterTest.java new file mode 100644 index 0000000000..07b52977a9 --- /dev/null +++ b/activecluster/src/test/org/activecluster/ClusterTest.java @@ -0,0 +1,116 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; + +import java.util.List; +import java.util.Map; +import javax.jms.Message; + +/** + * @version $Revision: 1.4 $ + */ +public class ClusterTest extends ClusterTestSupport { + + protected int count = 2; + + public void xtestCluster() throws Exception { + cluster = createCluster(); + + subscribeToCluster(); + + cluster.start(); + + String destination = cluster.getDestination(); + Message message = cluster.createTextMessage("abcdef"); + cluster.send(destination, message); + + //clusterListener.waitForMessageToArrive(); + Thread.sleep(5000); + + List list = clusterListener.flushMessages(); + assertEquals("Should have received a message: " + list, 1, list.size()); + + System.out.println("Received message: " + list.get(0)); + } + + + public void testMembershipCluster() throws Exception { + Cluster[] clusters = new Cluster[count]; + for (int i = 0; i < count; i++) { + Cluster cluster = createCluster("node:" + i); + clusters[i] = cluster; + if (i==0){ + cluster.addClusterListener(new TestingClusterListener(cluster)); + } + cluster.start(); + System.out.println("started " + clusters[i].getLocalNode().getName()); + + } + + System.out.println("waiting to complete ..."); + for (int i = count - 1; i >= 0; i--) { + Cluster cluster = clusters[i]; + String localName = cluster.getLocalNode().getName(); + boolean completed = cluster.waitForClusterToComplete(count - 1, 5000); + assertTrue("Node: " + i + " with contents: " + dumpConnectedNodes(cluster.getNodes()), completed); + + System.out.println(localName + " completed = " + completed + " nodes = " + + dumpConnectedNodes(cluster.getNodes())); + } + + assertClusterMembership(clusters); + + // lets wait for a while to see if things fail + Thread.sleep(10000L); + + assertClusterMembership(clusters); + + Cluster testCluster = clusters[0]; + LocalNode testNode = testCluster.getLocalNode(); + String key = "key"; + String value = "value"; + + Map map = testNode.getState(); + map.put(key, value); + testNode.setState(map); + + Thread.sleep(5000); + for (int i = 1; i < count; i++) { + Node node = (Node) clusters[i].getNodes().get(testNode.getDestination()); + assertTrue("The current test node should be in the cluster: " + i, node != null); + assertTrue(node.getState().get(key).equals(value)); + } + + for (int i = 0; i < count; i++) { + System.out.println(clusters[i].getLocalNode().getName() + " Is coordinator = " + clusters[i].getLocalNode().isCoordinator()); + clusters[i].stop(); + Thread.sleep(250); + + } + } + + protected void assertClusterMembership(Cluster[] clusters) { + for (int i = 0; i < count; i++) { + System.out.println("Cluster: " + i + " = " + clusters[i].getNodes()); + + assertEquals("Size of clusters for cluster: " + i, count - 1, clusters[i].getNodes().size()); + System.out.println(clusters[i].getLocalNode().getName() + " Is coordinator = " + clusters[i].getLocalNode().isCoordinator()); + } + } + +} diff --git a/activecluster/src/test/org/activecluster/ClusterTestSupport.java b/activecluster/src/test/org/activecluster/ClusterTestSupport.java new file mode 100644 index 0000000000..f813ac4525 --- /dev/null +++ b/activecluster/src/test/org/activecluster/ClusterTestSupport.java @@ -0,0 +1,75 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; + +import org.activecluster.impl.ActiveMQClusterFactory; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Topic; + +/** + * @version $Revision: 1.3 $ + */ +public abstract class ClusterTestSupport extends TestSupport { + + protected Cluster cluster; + protected StubMessageListener clusterListener = new StubMessageListener(); + protected StubMessageListener inboxListener = new StubMessageListener(); + private MessageConsumer clusterConsumer; + private MessageConsumer inboxConsumer; + + + protected void sendMessageToNode(Node node, String text) throws Exception { + Message message = cluster.createTextMessage(text); + cluster.send(node.getDestination(), message); + } + + protected void sendMessageToCluster(String text) throws Exception { + Message message = cluster.createTextMessage(text); + cluster.send(cluster.getDestination(), message); + } + + protected void subscribeToCluster() throws Exception { + + // listen to cluster messages + String clusterDestination = cluster.getDestination(); + assertTrue("Local destination must not be null", clusterDestination != null); + clusterConsumer = cluster.createConsumer(clusterDestination); + clusterConsumer.setMessageListener(clusterListener); + + // listen to inbox messages (individual messages) + String localDestination = cluster.getLocalNode().getDestination(); + assertTrue("Local destination must not be null", localDestination != null); + + System.out.println("Consuming from local destination: " + localDestination); + inboxConsumer = cluster.createConsumer(localDestination); + inboxConsumer.setMessageListener(inboxListener); + } + + protected void setUp() throws Exception { + } + + protected void tearDown() throws Exception { + if (cluster != null) { + cluster.stop(); + } + } +} diff --git a/activecluster/src/test/org/activecluster/StubMessageListener.java b/activecluster/src/test/org/activecluster/StubMessageListener.java new file mode 100644 index 0000000000..d7d6bdc2fc --- /dev/null +++ b/activecluster/src/test/org/activecluster/StubMessageListener.java @@ -0,0 +1,77 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; + +import javax.jms.Message; +import javax.jms.MessageListener; +import java.util.ArrayList; +import java.util.List; + +/** + * A mock message listener for testing + * + * @version $Revision: 1.2 $ + */ +public class StubMessageListener implements MessageListener { + private List messages = new ArrayList(); + private Object semaphore; + + public StubMessageListener() { + this(new Object()); + } + + public StubMessageListener(Object semaphore) { + this.semaphore = semaphore; + } + + /** + * @return all the messages on the list so far, clearing the buffer + */ + public synchronized List flushMessages() { + List answer = new ArrayList(messages); + messages.clear(); + return answer; + } + + public synchronized void onMessage(Message message) { + messages.add(message); + synchronized (semaphore) { + semaphore.notifyAll(); + } + } + + public void waitForMessageToArrive() { + System.out.println("Waiting for message to arrive"); + + long start = System.currentTimeMillis(); + + try { + if (messages.isEmpty()) { + synchronized (semaphore) { + semaphore.wait(4000); + } + } + } + catch (InterruptedException e) { + System.out.println("Caught: " + e); + } + long end = System.currentTimeMillis() - start; + + System.out.println("End of wait for " + end + " millis"); + } +} diff --git a/activecluster/src/test/org/activecluster/TestSupport.java b/activecluster/src/test/org/activecluster/TestSupport.java new file mode 100644 index 0000000000..d8e17c9c8e --- /dev/null +++ b/activecluster/src/test/org/activecluster/TestSupport.java @@ -0,0 +1,58 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; + +import junit.framework.TestCase; + +import javax.jms.JMSException; +import java.util.Iterator; +import java.util.Map; + +import org.activecluster.impl.ActiveMQClusterFactory; + +/** + * @version $Revision: 1.2 $ + */ +public class TestSupport extends TestCase { + protected String dumpConnectedNodes(Map nodes) { + String result = ""; + for (Iterator i = nodes.values().iterator(); i.hasNext();) { + + Object value = i.next(); + if (value instanceof Node) { + Node node = (Node) value; + result += node.getName() + ","; + } + else { + System.out.println("Got node of type: " + value.getClass()); + result += value + ","; + } + } + return result; + } + + protected Cluster createCluster() throws JMSException, ClusterException { + ClusterFactory factory = new ActiveMQClusterFactory(); + return factory.createCluster("ORG.CODEHAUS.ACTIVEMQ.TEST.CLUSTER"); + } + + protected Cluster createCluster(String name) throws JMSException, ClusterException { + Cluster cluster = createCluster(); + return cluster; + } +} diff --git a/activecluster/src/test/org/activecluster/TestingClusterListener.java b/activecluster/src/test/org/activecluster/TestingClusterListener.java new file mode 100644 index 0000000000..e28af9e69f --- /dev/null +++ b/activecluster/src/test/org/activecluster/TestingClusterListener.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster; + +import org.activecluster.impl.DefaultCluster; + +/** + * @version $Revision: 1.2 $ + */ +public class TestingClusterListener implements ClusterListener { + private Cluster cluster; + + public TestingClusterListener(Cluster cluster){ + this.cluster = cluster; + } + public void onNodeAdd(ClusterEvent event) { + printEvent("ADDED: ", event); + } + + public void onNodeUpdate(ClusterEvent event) { + printEvent("UPDATED: ", event); + } + + public void onNodeRemoved(ClusterEvent event) { + printEvent("REMOVED: ", event); + } + + public void onNodeFailed(ClusterEvent event) { + printEvent("FAILED: ", event); + } + + public void onCoordinatorChanged(ClusterEvent event) { + printEvent("COORDINATOR: ", event); + } + + protected void printEvent(String text, ClusterEvent event) { + System.out.println(text + event.getNode()); + System.out.println("Current cluster is now: " + cluster.getNodes().keySet()); + } + } diff --git a/activecluster/src/test/org/activecluster/group/BuddyGroupModelTest.java b/activecluster/src/test/org/activecluster/group/BuddyGroupModelTest.java new file mode 100644 index 0000000000..30e86191a2 --- /dev/null +++ b/activecluster/src/test/org/activecluster/group/BuddyGroupModelTest.java @@ -0,0 +1,70 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.group; + +import java.util.List; + +/** + * @version $Revision: 1.4 $ + */ +public class BuddyGroupModelTest extends GroupTestSupport { + + public void testGroups() throws Exception { + addNode("a"); + + // lets check how many groups have been created + List groups = model.getGroups(); + assertEquals("number of groups: " + groups, 1, model.getGroups().size()); + + Group group = (Group) model.getGroups().get(0); + assertIncomplete(group); + + addNode("b"); + assertEquals("number of groups: " + groups, 2, model.getGroups().size()); + + // lets see if the first node is now complete + assertUsable(group); + + group = (Group) model.getGroups().get(1); + assertUsable(group); + + + addNode("c"); + assertEquals("number of groups: " + groups, 3, model.getGroups().size()); + group = (Group) model.getGroups().get(2); + assertUsable(group); + + + addNode("d"); + assertEquals("number of groups: " + groups, 4, model.getGroups().size()); + group = (Group) model.getGroups().get(3); + assertUsable(group); + + } + + public void testRemoveGroups() { + String[] nodeNames = {"a", "b", "c"}; + addNodes(nodeNames); + + // TODO now lets remove the nodes and check group states.. + } + + protected GroupModel createGroupModel() { + return new BuddyGroupModel(); + } +} diff --git a/activecluster/src/test/org/activecluster/group/GroupModelTest.java b/activecluster/src/test/org/activecluster/group/GroupModelTest.java new file mode 100644 index 0000000000..0b408968bf --- /dev/null +++ b/activecluster/src/test/org/activecluster/group/GroupModelTest.java @@ -0,0 +1,59 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.group; + +import java.util.List; + +/** + * @version $Revision: 1.4 $ + */ +public class GroupModelTest extends GroupTestSupport { + + public void testGroups() throws Exception { + addNode("a"); + + // lets check how many groups have been created + List groups = model.getGroups(); + assertEquals("number of groups: " + groups, 1, model.getGroups().size()); + + Group group = (Group) model.getGroups().get(0); + assertIncomplete(group); + + addNode("b"); + assertNotFullButUsable(group); + assertEquals("number of groups: " + groups, 1, model.getGroups().size()); + + addNode("c"); + assertFull(group); + assertEquals("number of groups: " + groups, 1, model.getGroups().size()); + + + addNode("d"); + assertEquals("number of groups: " + groups, 2, model.getGroups().size()); + group = (Group) model.getGroups().get(1); + assertIncomplete(group); + } + + public void testRemoveGroups() { + String[] nodeNames = {"a", "b", "c"}; + addNodes(nodeNames); + + // TODO now lets remove the nodes and check group states.. + } + +} diff --git a/activecluster/src/test/org/activecluster/group/GroupTestSupport.java b/activecluster/src/test/org/activecluster/group/GroupTestSupport.java new file mode 100644 index 0000000000..faa38a9425 --- /dev/null +++ b/activecluster/src/test/org/activecluster/group/GroupTestSupport.java @@ -0,0 +1,78 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. (http://www.logicblaze.com) + * + * Licensed 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.activecluster.group; + +import java.util.HashMap; +import java.util.Map; +import junit.framework.TestCase; +import org.activecluster.Cluster; +import org.activecluster.ClusterEvent; +import org.activecluster.ClusterListener; +import org.activecluster.Node; +import org.activecluster.impl.NodeImpl; + +/** + * A base class for Group model testing + * + * @version $Revision: 1.5 $ + */ +public abstract class GroupTestSupport extends TestCase { + + protected GroupModel model; + private ClusterListener listener; + private Cluster cluster; + private Map nodes = new HashMap(); + + protected void addNodes(String[] nodeNames) { + for (int i = 0; i < nodeNames.length; i++) { + String nodeName = nodeNames[i]; + addNode(nodeName); + } + } + + protected void addNode(String nodeName) { + Node node = new NodeImpl(nodeName,nodeName); + nodes.put(nodeName, node); + listener.onNodeAdd(new ClusterEvent(cluster, node, ClusterEvent.ADD_NODE)); + } + + protected void assertFull(Group group) { + assertTrue("Group is not full and usable. Members: " + group.getMembers(), group.isFull() && group.isUsable()); + } + + protected void assertNotFullButUsable(Group group) { + assertTrue("Group is not not full but usable. Members: " + group.getMembers(), !group.isFull() && group.isUsable()); + } + + protected void assertIncomplete(Group group) { + assertTrue("Group is not not full or usable. Members: " + group.getMembers(), !group.isFull() && !group.isUsable()); + } + + protected void assertUsable(Group group) { + assertTrue("Group is not usable. Members: " + group.getMembers(), group.isUsable()); + } + + protected void setUp() throws Exception { + model = createGroupModel(); + listener = new GroupClusterListener(model); + } + + protected GroupModel createGroupModel() { + return new GroupModel(); + } +} diff --git a/activeio/LICENSE.txt b/activeio/LICENSE.txt new file mode 100644 index 0000000000..43e91eb0b0 --- /dev/null +++ b/activeio/LICENSE.txt @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + diff --git a/activeio/maven.xml b/activeio/maven.xml new file mode 100644 index 0000000000..076110e32e --- /dev/null +++ b/activeio/maven.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Diabling IBM AIO tests since your platform (${context.getVariable('os.name')} ${context.getVariable('os.version')}) is not recognized + + + + + + + + + + + + + + + + + + + + + Running the benchmark client load simulator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Running the benchmark server + + + + + + + + + + + + + + + + + + + + diff --git a/activeio/project.properties b/activeio/project.properties new file mode 100644 index 0000000000..f5df58e030 --- /dev/null +++ b/activeio/project.properties @@ -0,0 +1,68 @@ +maven.repo.remote=http://tranql.codehaus.org/maven,http://www.openejb.org/maven,http://www.ibiblio.org/maven + +maven.eclipse.classpath.include=src/resources + +maven.compile.source=1.4 +maven.compile.target=1.4 + +maven.compile.deprecation=true +maven.compile.debug=true +maven.compile.optimize=true + +#maven.junit.fork=false +maven.junit.fork=true +maven.javadoc.source=1.4 + +maven.javadoc.links=\ +http://java.sun.com/j2se/1.4.1/docs/api/,\ +http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent,\ +http://jakarta.apache.org/commons/logging/apidocs + +maven.xdoc.theme.url=http://codehaus.org/codehaus-style.css + +maven.artifact.legacy=true +maven.repo.central=dist.codehaus.org +maven.repo.central.directory=/dist +maven.remote.group=activeio + +# ------------------------------------------------------------------- +# IDE options +# ------------------------------------------------------------------- +#maven.eclipse.classpath.include=target/test-iiop/classes +#maven.idea.generated.source=test-iiop/classes + +# ------------------------------------------------------------------- +# dependency versions +# ------------------------------------------------------------------- + +# What is used. +concurrent_version=1.3.4 +howl_logger_version=0.1.8 +ibmaio_version=1.0 + +openorb_orb_version=1.4.0-GERONIMO +openorb_tools_version=1.4.0-GERONIMO +openorb_orb_tools_version=1.4.0-GERONIMO +openorb_orb_omg_version=1.4.0-GERONIMO + +avalon_framework_version=4.1.4 +commons_cli_version=1.0 +avalon_logkit_version=1.2.2 + +org.mortbay.jetty_version=5.1.2-SNAPSHOT +p2psockets_core_version=1.1.2 +jxta_version=2.0 + +# For testing +junit_version=3.8.1 +geronimo_spec_j2ee_version=1.4-rc4 +activemq_core_version=2.0 +commons_beanutils_version=1.6.1 +commons_collections_version=2.1 + +commons_logging_version=1.0.3 +log4j_version=1.2.8 +mx4j_version=3.0.1 +geronimo_version=1.0-SNAPSHOT + + diff --git a/activeio/project.xml b/activeio/project.xml new file mode 100644 index 0000000000..10c68097c4 --- /dev/null +++ b/activeio/project.xml @@ -0,0 +1,294 @@ + + + + + + 3 + activeio + ActiveIO + 2.2-SNAPSHOT + + The ActiveIO Project + http://activeio.org + http://activeio.org/activeio-logo.png + + + org.activeio + + + Protocol Implementation Framework + org.activeio.protocol.* + + + + + ActiveIO provides a high performance IO framework for implementing protocols. + /images/activeio-logo.png + + + http://activeio.org/ + + www.activeio.org + /home/projects/activeio/public_html/maven + dist.codehaus.org + /dist + + + scm:svn:https://svn.codehaus.org/activeio/trunk/activeio + scm:svn:svn+ssh://svn.activeio.org/home/projects/activeio/scm/trunk/activeio + http://svn.activeio.org/viewcvs.cgi/activeio/ + + + + + activeio dev + dev-subscribe@activeio.codehaus.org + dev-unsubscribe@activeio.codehaus.org + + + activeio users + user-subscribe@activeio.codehaus.org + user-unsubscribe@activeio.codehaus.org + + + activeio svn messages + scm-subscribe@activeio.codehaus.org + scm-unsubscribe@activeio.codehaus.org + + + + + + Hiram Chirino + chirino + hiram@hiramchirino.com + + + Founder + + + + Alan D. Cabrera + adc + adc@toolazydogs.com + + + Founder + + + + David Blevins + dblevins + dblevins@gluecode.com + + + Founder + + + + + + + + backport-util-concurrent + backport-util-concurrent + 2.0_01_pd + + + + + + + + howl + howl-logger + ${howl_logger_version} + http://forge.objectweb.org/projects/howl + + + + + p2psockets + p2psockets-core + ${p2psockets_core_version} + + + jxta + jxta + ${jxta_version} + + + + ibmaio + ibmaio + ${ibmaio_version} + https://www7b.software.ibm.com/dl/AW-0H8/AW-0H8-p + + + + + + jetty + org.mortbay.jetty + ${org.mortbay.jetty_version} + + + + + openorb + openorb-orb + ${openorb_orb_version} + + + openorb + openorb-tools + ${openorb_tools_version} + + + openorb + openorb-orb-tools + ${openorb_orb_tools_version} + + + openorb + openorb-orb-omg + ${openorb_orb_omg_version} + + + avalon + avalon-framework + ${avalon_framework_version} + + + avalon + avalon-logkit + ${avalon_logkit_version} + + + + + junit + junit + ${junit_version} + + + commons-logging + commons-logging + ${commons_logging_version} + http://jakarta.apache.org/commons/logging/ + + + log4j + log4j + ${log4j_version} + http://jakarta.apache.org/log4j + + + + + geronimo-spec + geronimo-spec-j2ee + ${geronimo_spec_j2ee_version} + + + + + commons-beanutils + commons-beanutils + ${commons_beanutils_version} + + + commons-collections + commons-collections + ${commons_collections_version} + + + + + geronimo + geronimo-kernel + ${geronimo_version} + + + geronimo + geronimo-j2ee + ${geronimo_version} + + + mx4j + mx4j + ${mx4j_version} + + + + + + + src/java + + + src/resources + + **/* + + + + src/test + + + **/*Test.java + + + + src/test + + **/*.properties + **/*.xml + + + + + + + + maven-license-plugin + maven-pmd-plugin + maven-jdepend-plugin + maven-jxr-plugin + maven-javadoc-plugin + maven-junit-report-plugin + maven-faq-plugin + maven-changes-plugin + + maven-checkstyle-plugin + maven-simian-plugin + maven-clover-plugin + + + + diff --git a/activeio/src/java/org/activeio/AcceptListener.java b/activeio/src/java/org/activeio/AcceptListener.java new file mode 100644 index 0000000000..82501ec757 --- /dev/null +++ b/activeio/src/java/org/activeio/AcceptListener.java @@ -0,0 +1,44 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; + + +/** + * An AcceptListener object is used to receive accepted {@see org.activeio.Channel} connections. + * + * @version $Revision$ + */ +public interface AcceptListener { + + /** + * A {@see AsyncChannelServer} will call this method to when a new channel connection has been + * accepted. + * + * @param channel + */ + void onAccept(Channel channel); + + /** + * A {@see AsyncChannelServer} will call this method when a async failure occurs when accepting + * a connection. + * + * @param error the exception that describes the failure. + */ + void onAcceptError(IOException error); +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/Adaptable.java b/activeio/src/java/org/activeio/Adaptable.java new file mode 100644 index 0000000000..cbf3dcb171 --- /dev/null +++ b/activeio/src/java/org/activeio/Adaptable.java @@ -0,0 +1,35 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + + +/** + * Provides an Adaptable interface inspired by eclipse's IAdaptable class. Highly used in ActiveIO since Channel and Packet + * implementations may be layered and application code may want request the higher level layers/abstractions to adapt to give access + * to the lower layer implementation details. + * + * @version $Revision$ + */ +public interface Adaptable { + + /** + * @Return object that is an instance of requested type and is associated this this object. May return null if no + * object of that type is associated. + */ + Object getAdapter(Class target); +} diff --git a/activeio/src/java/org/activeio/AsyncChannel.java b/activeio/src/java/org/activeio/AsyncChannel.java new file mode 100644 index 0000000000..d025c82fe9 --- /dev/null +++ b/activeio/src/java/org/activeio/AsyncChannel.java @@ -0,0 +1,30 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + + + +/** + * AsyncChannel objects asynchronously push 'up' {@see org.activeio.Packet} objects + * to a registered {@see org.activeio.ChannelConsumer}. + * + * @version $Revision$ + */ +public interface AsyncChannel extends InputAsyncChannel, OutputChannel { + +} diff --git a/activeio/src/java/org/activeio/AsyncChannelFactory.java b/activeio/src/java/org/activeio/AsyncChannelFactory.java new file mode 100644 index 0000000000..ef579d35e0 --- /dev/null +++ b/activeio/src/java/org/activeio/AsyncChannelFactory.java @@ -0,0 +1,47 @@ +/** +* +* Copyright 2004 Hiram Chirino +* +* Licensed 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.activeio; + +import java.io.IOException; +import java.net.URI; + + +/** + * AsyncChannelFactory objects can create {@see org.activeio.AsyncChannel} + * and {@see org.activeio.AsyncChannelServer} objects. + * + * @version $Revision$ + */ +public interface AsyncChannelFactory { + + /** + * Opens a connection to server. + * + * @param location + * @return + */ + public AsyncChannel openAsyncChannel(URI location) throws IOException; + + /** + * Binds a server at the URI location. + * + * @param location + * @return + */ + public AsyncChannelServer bindAsyncChannel(URI location) throws IOException; + +} diff --git a/activeio/src/java/org/activeio/AsyncChannelListener.java b/activeio/src/java/org/activeio/AsyncChannelListener.java new file mode 100644 index 0000000000..b980c721cd --- /dev/null +++ b/activeio/src/java/org/activeio/AsyncChannelListener.java @@ -0,0 +1,48 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; + + +/** + * A ChannelConsumer object is used to receive 'up' {@see org.activeio.Packet} objects. + * + * TODO: describe the threading model so that the implementor of this interface can know if + * the methods in this interface can block for a long time or not. I'm thinking that it would + * be best if these methods are not allowed to block for a long time to encourage SEDA style + * processing. + * + * @version $Revision$ + */ +public interface AsyncChannelListener { + + /** + * A {@see AsyncChannel} will call this method to deliver an 'up' packet to a consumer. + * + * @param packet + */ + void onPacket(Packet packet); + + /** + * A {@see AsyncChannel} will call this method when a async failure occurs in the channel. + * + * @param error the exception that describes the failure. + */ + void onPacketError(IOException error); + +} diff --git a/activeio/src/java/org/activeio/AsyncChannelServer.java b/activeio/src/java/org/activeio/AsyncChannelServer.java new file mode 100644 index 0000000000..a663743e43 --- /dev/null +++ b/activeio/src/java/org/activeio/AsyncChannelServer.java @@ -0,0 +1,36 @@ +/** +* +* Copyright 2004 Hiram Chirino +* +* Licensed 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.activeio; + + +/** + * AsyncChannelServer objects asynchronously accept and create {@see org.activeio.Channel} objects + * and then delivers those objects to a {@see org.activeio.AcceptConsumer}. + * + * @version $Revision$ + */ +public interface AsyncChannelServer extends ChannelServer { + + /** + * Registers an AcceptListener which is notified of accepted channels. + * + * @param acceptListener + */ + void setAcceptListener(AcceptListener acceptListener); + + +} diff --git a/activeio/src/java/org/activeio/AsynchChannel.java b/activeio/src/java/org/activeio/AsynchChannel.java new file mode 100644 index 0000000000..3f2d2e64c3 --- /dev/null +++ b/activeio/src/java/org/activeio/AsynchChannel.java @@ -0,0 +1,24 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio; + +/** + * @deprecated Use AsyncChannel instead. This class will be removed very soon. + */ +public interface AsynchChannel extends AsyncChannel{ + +} diff --git a/activeio/src/java/org/activeio/AsynchChannelListener.java b/activeio/src/java/org/activeio/AsynchChannelListener.java new file mode 100644 index 0000000000..a1f182b767 --- /dev/null +++ b/activeio/src/java/org/activeio/AsynchChannelListener.java @@ -0,0 +1,24 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio; + +/** + * @deprecated Use AsyncChannelListener instead. This class will be removed very soon. + */ +public interface AsynchChannelListener extends AsyncChannelListener { + +} diff --git a/activeio/src/java/org/activeio/AsynchChannelServer.java b/activeio/src/java/org/activeio/AsynchChannelServer.java new file mode 100644 index 0000000000..994df98724 --- /dev/null +++ b/activeio/src/java/org/activeio/AsynchChannelServer.java @@ -0,0 +1,24 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio; + +/** + * @deprecated Use AsyncChannelServer instead. This class will be removed very soon. + */ +public interface AsynchChannelServer extends AsyncChannelServer { + +} diff --git a/activeio/src/java/org/activeio/ByteArrayInputStream.java b/activeio/src/java/org/activeio/ByteArrayInputStream.java new file mode 100644 index 0000000000..b2fa7be658 --- /dev/null +++ b/activeio/src/java/org/activeio/ByteArrayInputStream.java @@ -0,0 +1,82 @@ +package org.activeio; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Very similar to the java.io.ByteArrayInputStream but this version + * is not thread safe. + */ +public class ByteArrayInputStream extends InputStream { + + byte buffer[]; + int limit; + int pos; + int mark; + + public ByteArrayInputStream(byte data[]) { + this(data, 0, data.length); + } + + public ByteArrayInputStream(ByteSequence sequence) { + this(sequence.getData(), sequence.getOffset(), sequence.getLength()); + } + + public ByteArrayInputStream(byte data[], int offset, int size) { + this.buffer = data; + this.mark= this.pos = offset; + this.limit = offset+size; + } + + public int read() throws IOException { + if( pos < limit ) + return buffer[pos++] & 0xff; + else + return -1; + } + + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + public int read(byte b[], int off, int len) { + if (pos < limit) { + len = Math.min(len, limit-pos); + if (len > 0) { + System.arraycopy(buffer, pos, b, off, len); + pos += len; + } + return len; + } else { + return -1; + } + } + + public long skip(long len) throws IOException { + if (pos < limit) { + len = Math.min(len, limit-pos); + if (len > 0) { + pos += len; + } + return len; + } else { + return -1; + } + } + + public int available() { + return limit - pos; + } + + public boolean markSupported() { + return true; + } + + public void mark(int markpos) { + mark = pos; + } + + public void reset() { + pos = mark; + } +} diff --git a/activeio/src/java/org/activeio/ByteArrayOutputStream.java b/activeio/src/java/org/activeio/ByteArrayOutputStream.java new file mode 100644 index 0000000000..0aff4e055f --- /dev/null +++ b/activeio/src/java/org/activeio/ByteArrayOutputStream.java @@ -0,0 +1,65 @@ +package org.activeio; + +import java.io.OutputStream; + +/** + * Very similar to the java.io.ByteArrayOutputStream but this version + * is not thread safe and the resulting data is returned in a ByteSequence + * to avoid an extra byte[] allocation. + */ +public class ByteArrayOutputStream extends OutputStream { + + byte buffer[]; + int size; + + public ByteArrayOutputStream() { + this(512); + } + public ByteArrayOutputStream(int capacity) { + buffer = new byte[capacity]; + } + + public void write(int b) { + int newsize = size + 1; + checkCapacity(newsize); + buffer[size] = (byte) b; + size = newsize; + } + + public void write(byte b[], int off, int len) { + int newsize = size + len; + checkCapacity(newsize); + System.arraycopy(b, off, buffer, size, len); + size = newsize; + } + + /** + * Ensures the the buffer has at least the minimumCapacity specified. + * @param i + */ + private void checkCapacity(int minimumCapacity) { + if (minimumCapacity > buffer.length) { + byte b[] = new byte[Math.max(buffer.length << 1, minimumCapacity)]; + System.arraycopy(buffer, 0, b, 0, size); + buffer = b; + } + } + + public void reset() { + size = 0; + } + + public ByteSequence toByteSequence() { + return new ByteSequence(buffer, 0, size); + } + + public byte[] toByteArray() { + byte rc[] = new byte[size]; + System.arraycopy(buffer, 0, rc, 0, size); + return rc; + } + + public int size() { + return size; + } +} diff --git a/activeio/src/java/org/activeio/ByteSequence.java b/activeio/src/java/org/activeio/ByteSequence.java new file mode 100644 index 0000000000..656917e540 --- /dev/null +++ b/activeio/src/java/org/activeio/ByteSequence.java @@ -0,0 +1,42 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * + * Licensed 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.activeio; + +public class ByteSequence { + final byte[] data; + final int offset; + final int length; + + public ByteSequence(byte data[], int offset, int length) { + this.data = data; + this.offset = offset; + this.length = length; + } + public byte[] getData() { + return data; + } + public int getLength() { + return length; + } + public int getOffset() { + return offset; + } + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/Channel.java b/activeio/src/java/org/activeio/Channel.java new file mode 100644 index 0000000000..abbc26caf2 --- /dev/null +++ b/activeio/src/java/org/activeio/Channel.java @@ -0,0 +1,31 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + + +/** + * A Channel provides a standard procedure for regulating data transmission between + * applications. + * + * The activeio API encourages that layered wire protocols be created by wiring + * together a chain of Channel objects. + * + * @version $Revision$ + */ +public interface Channel extends Disposable, Service, Adaptable { +} diff --git a/activeio/src/java/org/activeio/ChannelFactory.java b/activeio/src/java/org/activeio/ChannelFactory.java new file mode 100644 index 0000000000..316293ef4a --- /dev/null +++ b/activeio/src/java/org/activeio/ChannelFactory.java @@ -0,0 +1,139 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activeio; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.SynchronousQueue; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +import org.activeio.adapter.AsyncToSyncChannelFactory; +import org.activeio.adapter.SyncToAsyncChannelFactory; + +import java.io.IOException; +import java.net.URI; +import java.util.HashMap; + +/** + * A {@see ChannelFactory}uses the requested URI's scheme to determine the + * actual {@see org.activeio.SynchChannelFactory}or + * {@see org.activeio.AsyncChannelFactory}implementation to use to create it's + * {@see org.activeio.Channel}s and {@see org.activeio.ChannelServer}s. + * + * Each URI scheme that {@see ChannelFactory}object handles will have a + * properties file located at: "META-INF/org.activeio.ChannelFactory/{scheme}". + * + */ +public class ChannelFactory implements SyncChannelFactory, AsyncChannelFactory { + + private final HashMap syncChannelFactoryMap = new HashMap(); + private final HashMap asyncChannelFactoryMap = new HashMap(); + + + static public final Executor DEFAULT_EXECUTOR = new ThreadPoolExecutor(10, Integer.MAX_VALUE, 5, TimeUnit.SECONDS, new SynchronousQueue()); + static { + ((ThreadPoolExecutor) DEFAULT_EXECUTOR).setThreadFactory(new ThreadFactory() { + public Thread newThread(Runnable run) { + Thread thread = new Thread(run); + thread.setDaemon(true); + return thread; + } + }); + } + + private static FactoryFinder finder = new FactoryFinder("META-INF/org.activeio.ChannelFactory/"); + + public SyncChannel openSyncChannel(URI location) throws IOException { + SyncChannelFactory factory = getSynchChannelFactory(location.getScheme()); + return factory.openSyncChannel(location); + } + + public SyncChannelServer bindSyncChannel(URI location) throws IOException { + SyncChannelFactory factory = getSynchChannelFactory(location.getScheme()); + return factory.bindSyncChannel(location); + } + + public AsyncChannel openAsyncChannel(URI location) throws IOException { + AsyncChannelFactory factory = getAsyncChannelFactory(location.getScheme()); + return factory.openAsyncChannel(location); + } + + public AsyncChannelServer bindAsyncChannel(URI location) throws IOException { + AsyncChannelFactory factory = getAsyncChannelFactory(location.getScheme()); + return factory.bindAsyncChannel(location); + } + + private SyncChannelFactory getSynchChannelFactory(String protocol) throws IOException { + try { + SyncChannelFactory rc = (SyncChannelFactory) syncChannelFactoryMap.get(protocol); + if (rc == null) { + try { + rc = (SyncChannelFactory) finder.newInstance(protocol, "SyncChannelFactory."); + } catch (Throwable original) { + // try to recovery by using AsyncChannelFactory and adapt + // it to be sync. + try { + AsyncChannelFactory f = (AsyncChannelFactory) finder.newInstance(protocol, + "AsyncChannelFactory."); + rc = AsyncToSyncChannelFactory.adapt(f); + } catch (Throwable e) { + // Recovery strategy failed.. throw original exception. + throw original; + } + } + syncChannelFactoryMap.put(protocol, rc); + } + return rc; + } catch (Throwable e) { + throw (IOException) new IOException("Could not load a SyncChannelFactory for protcol: " + protocol + + ", reason: " + e).initCause(e); + } + } + + private AsyncChannelFactory getAsyncChannelFactory(String protocol) throws IOException { + try { + AsyncChannelFactory rc = (AsyncChannelFactory) asyncChannelFactoryMap.get(protocol); + if (rc == null) { + + try { + rc = (AsyncChannelFactory) finder.newInstance(protocol, "AsyncChannelFactory."); + } catch (Throwable original) { + // try to recovery by using SynchChannelFactory and adapt it + // to be async. + try { + SyncChannelFactory f = (SyncChannelFactory) finder.newInstance(protocol, + "SyncChannelFactory."); + rc = SyncToAsyncChannelFactory.adapt(f); + } catch (Throwable e) { + // Recovery strategy failed.. throw original exception. + throw original; + } + } + + asyncChannelFactoryMap.put(protocol, rc); + } + return rc; + } catch (Throwable e) { + throw (IOException) new IOException("Could not load a AsyncChannelFactory for protcol: " + protocol + + ", reason: " + e).initCause(e); + } + } + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/ChannelServer.java b/activeio/src/java/org/activeio/ChannelServer.java new file mode 100644 index 0000000000..568b92af86 --- /dev/null +++ b/activeio/src/java/org/activeio/ChannelServer.java @@ -0,0 +1,51 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.net.URI; + +/** + * A ChannelServer is used to accept incoming requests to establish new Channel sessions. + * + * Like a normal {@see org.activeio.Channel}, A ChannelServer comes in two falvors, either: + * {@see org.activeio.AsyncChannelServer} or + * {@see org.activeio.SynchChannelServer}. + * + * @version $Revision$ + */ +public interface ChannelServer extends Channel { + + /** + * The URI that was used when the channel was bound. This could be different + * than what is used by a client to connect to the ChannelServer. For example, + * the bind URI might be tcp://localhost:0 which means the channel should bind to + * an anonymous port. + * + * @return The URI that was used when the channel was bound + */ + public URI getBindURI(); + + /** + * Once bound, the channel may be able to construct a URI that is more sutible for when + * a client needs to connect to the server. For examle the port of the URI may be + * updated to reflect the actual local port that the channel server is listening on. + * + * @return a URI that a client can use to connect to the server or null if the channel cannot construct the URI. + */ + public URI getConnectURI(); + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/Disposable.java b/activeio/src/java/org/activeio/Disposable.java new file mode 100644 index 0000000000..c0afa8539e --- /dev/null +++ b/activeio/src/java/org/activeio/Disposable.java @@ -0,0 +1,34 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +/** + * The Disposable interface is implemented by objects the aquire resources whoes life cycle must be + * managed. Once a Disposable has been disposed, it cannot be un-disposed. + * + * @version $Revision$ + */ +public interface Disposable { + + /** + * This method should not throw any exceptions. Cleaning up a Disposable object + * should be easy of an end user therefore do not make him have to handle an Exception. + */ + void dispose(); + +} diff --git a/activeio/src/java/org/activeio/FactoryFinder.java b/activeio/src/java/org/activeio/FactoryFinder.java new file mode 100644 index 0000000000..3673f7b350 --- /dev/null +++ b/activeio/src/java/org/activeio/FactoryFinder.java @@ -0,0 +1,92 @@ +/** + * + */ +package org.activeio; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + + +public class FactoryFinder { + + private final String path; + private final ConcurrentHashMap classMap = new ConcurrentHashMap(); + + public FactoryFinder(String path) { + this.path = path; + } + + /** + * Creates a new instance of the given key + * + * @param key is the key to add to the path to find a text file + * containing the factory name + * @return a newly created instance + */ + public Object newInstance(String key) + throws IllegalAccessException, InstantiationException, IOException, ClassNotFoundException + { + return newInstance(key, null); + } + + public Object newInstance(String key, String propertyPrefix) + throws IllegalAccessException, InstantiationException, IOException, ClassNotFoundException + { + if (propertyPrefix == null) + propertyPrefix = ""; + + Class clazz = (Class) classMap.get(propertyPrefix + key); + if (clazz == null) { + clazz = newInstance(doFindFactoryProperies(key), propertyPrefix); + classMap.put(propertyPrefix + key, clazz); + } + return clazz.newInstance(); + } + + private Class newInstance(Properties properties, String propertyPrefix) throws ClassNotFoundException, IOException { + + String className = properties.getProperty(propertyPrefix + "class"); + if (className == null) { + throw new IOException("Expected property is missing: " + propertyPrefix + "class"); + } + Class clazz; + try { + clazz = Thread.currentThread().getContextClassLoader().loadClass(className); + } catch (ClassNotFoundException e) { + clazz = FactoryFinder.class.getClassLoader().loadClass(className); + } + + return clazz; + } + + private Properties doFindFactoryProperies(String key) throws IOException { + String uri = path + key; + + // lets try the thread context class loader first + InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(uri); + if (in == null) { + in = FactoryFinder.class.getClassLoader().getResourceAsStream(uri); + if (in == null) { + throw new IOException("Could not find factory class for resource: " + uri); + } + } + + // lets load the file + BufferedInputStream reader = null; + try { + reader = new BufferedInputStream(in); + Properties properties = new Properties(); + properties.load(reader); + return properties; + } finally { + try { + reader.close(); + } catch (Exception e) { + } + } + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/FilterAsyncChannel.java b/activeio/src/java/org/activeio/FilterAsyncChannel.java new file mode 100644 index 0000000000..2fb30f48a5 --- /dev/null +++ b/activeio/src/java/org/activeio/FilterAsyncChannel.java @@ -0,0 +1,121 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; + + +/** + * A AsyncChannelFilter can be used as a filter between a {@see org.activeio.AsyncChannel} + * and it's {@see org.activeio.ChannelConsumer}. Most {@see org.activeio.AsyncChannel} + * that are not directly accessing the network will extends the AsyncChannelFilter since they act as a + * filter between the client and the network. O + * + * @version $Revision$ + */ +public class FilterAsyncChannel implements AsyncChannel, AsyncChannelListener { + + final protected AsyncChannel next; + protected AsyncChannelListener channelListener; + + public FilterAsyncChannel(AsyncChannel next) { + this.next = next; + } + + /** + */ + public void setAsyncChannelListener(AsyncChannelListener channelListener) { + this.channelListener = channelListener; + if (channelListener == null) + next.setAsyncChannelListener(null); + else + next.setAsyncChannelListener(this); + } + + public void write(Packet packet) throws IOException { + next.write(packet); + } + + public void flush() throws IOException { + next.flush(); + } + + /** + * @see org.activeio.Disposable#dispose() + */ + public void dispose() { + next.dispose(); + } + + /** + * @see org.activeio.Service#start() + * @throws IOException if the next channel has not been set. + */ + public void start() throws IOException { + if( next == null ) + throw new IOException("The next channel has not been set."); + if( channelListener ==null ) + throw new IOException("The UpPacketListener has not been set."); + next.start(); + } + + /** + * @see org.activeio.Service#stop(long) + */ + public void stop(long timeout) throws IOException { + next.stop(timeout); + } + + /** + * @see org.activeio.AsyncChannelListener#onPacket(org.activeio.Packet) + */ + public void onPacket(Packet packet) { + channelListener.onPacket(packet); + } + + /** + * @see org.activeio.AsyncChannelListener#onPacketError(org.activeio.ChannelException) + */ + public void onPacketError(IOException error) { + channelListener.onPacketError(error); + } + + /** + * @return Returns the next. + */ + public AsyncChannel getNext() { + return next; + } + + /** + * @return Returns the packetListener. + */ + public AsyncChannelListener getAsyncChannelListener() { + return channelListener; + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return next.getAdapter(target); + } + + public String toString() { + return next.toString(); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/FilterAsyncChannelServer.java b/activeio/src/java/org/activeio/FilterAsyncChannelServer.java new file mode 100644 index 0000000000..98a229670c --- /dev/null +++ b/activeio/src/java/org/activeio/FilterAsyncChannelServer.java @@ -0,0 +1,101 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; +import java.net.URI; + + +/** + * A AsyncChannelFilter can be used as a filter between a {@see org.activeio.AsyncChannel} + * and it's {@see org.activeio.ChannelConsumer}. Most {@see org.activeio.AsyncChannel} + * that are not directly accessing the network will extends the AsyncChannelFilter since they act as a + * filter between the client and the network. O + * + * @version $Revision$ + */ +public class FilterAsyncChannelServer implements AsyncChannelServer, AcceptListener { + + final protected AsyncChannelServer next; + protected AcceptListener acceptListener; + + public FilterAsyncChannelServer(AsyncChannelServer next) { + this.next = next; + if( next == null ) + throw new IllegalArgumentException("The next AsyncChannelServer cannot be null."); + } + + public void setAcceptListener(AcceptListener acceptListener) { + this.acceptListener = acceptListener; + if (acceptListener == null) + next.setAcceptListener(null); + else + next.setAcceptListener(this); + + } + + /** + * @see org.activeio.Disposable#dispose() + */ + public void dispose() { + next.dispose(); + } + + /** + * @see org.activeio.Service#start() + * @throws IOException if the next channel has not been set. + */ + public void start() throws IOException { + if( acceptListener ==null ) + throw new IOException("The AcceptListener has not been set."); + next.start(); + } + + /** + * @see org.activeio.Service#stop(long) + */ + public void stop(long timeout) throws IOException { + next.stop(timeout); + } + + public void onAccept(Channel channel) { + acceptListener.onAccept(channel); + } + + public void onAcceptError(IOException error) { + acceptListener.onAcceptError(error); + } + + public URI getBindURI() { + return next.getBindURI(); + } + + public URI getConnectURI() { + return next.getConnectURI(); + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return next.getAdapter(target); + } + + public String toString() { + return next.toString(); + } + } \ No newline at end of file diff --git a/activeio/src/java/org/activeio/FilterSyncChannel.java b/activeio/src/java/org/activeio/FilterSyncChannel.java new file mode 100644 index 0000000000..dd78520f58 --- /dev/null +++ b/activeio/src/java/org/activeio/FilterSyncChannel.java @@ -0,0 +1,96 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; + + +/** + * A SynchChannelFilter can be used as a filter another {@see org.activeio.SynchChannel} + * Most {@see org.activeio.SynchChannel} that are not directly accessing the network will + * extends the SynchChannelFilter since they act as a filter between the client and the network. + * + * @version $Revision$ + */ +public class FilterSyncChannel implements SyncChannel { + + private final SyncChannel next; + + public FilterSyncChannel(SyncChannel next) { + this.next = next; + } + + /** + * @see org.activeio.Channel#write(org.activeio.Packet) + */ + public void write(Packet packet) throws IOException { + next.write(packet); + } + + /** + * @see org.activeio.Channel#flush() + */ + public void flush() throws IOException { + next.flush(); + } + + /** + * @see org.activeio.Disposable#dispose() + */ + public void dispose() { + next.dispose(); + } + + /** + * @see org.activeio.Service#start() + */ + public void start() throws IOException { + next.start(); + } + + /** + * @see org.activeio.Service#stop(long) + */ + public void stop(long timeout) throws IOException { + next.stop(timeout); + } + + /** + * @return Returns the next. + */ + public SyncChannel getNext() { + return next; + } + + /** + * @see org.activeio.SyncChannel#read(long) + */ + public Packet read(long timeout) throws IOException { + return next.read(timeout); + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return next.getAdapter(target); + } + + public String toString() { + return next.toString(); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/FilterSyncChannelServer.java b/activeio/src/java/org/activeio/FilterSyncChannelServer.java new file mode 100644 index 0000000000..58c12f36e9 --- /dev/null +++ b/activeio/src/java/org/activeio/FilterSyncChannelServer.java @@ -0,0 +1,97 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; +import java.net.URI; + + +/** + * A SynchChannelFilter can be used as a filter another {@see org.activeio.SynchChannel} + * Most {@see org.activeio.SynchChannel} that are not directly accessing the network will + * extends the SynchChannelFilter since they act as a filter between the client and the network. + * + * @version $Revision$ + */ +public class FilterSyncChannelServer implements SyncChannelServer { + + private final SyncChannelServer next; + + public FilterSyncChannelServer(SyncChannelServer next) { + this.next = next; + } + + /** + * @see org.activeio.Disposable#dispose() + */ + public void dispose() { + next.dispose(); + } + + /** + * @see org.activeio.Service#start() + */ + public void start() throws IOException { + next.start(); + } + + /** + * @see org.activeio.Service#stop(long) + */ + public void stop(long timeout) throws IOException { + next.stop(timeout); + } + + /** + * @return Returns the next. + */ + public SyncChannelServer getNext() { + return next; + } + + /** + * @see org.activeio.SyncChannelServer#accept(long) + */ + public Channel accept(long timeout) throws IOException { + return next.accept(timeout); + } + + /** + * @see org.activeio.ChannelServer#getBindURI() + */ + public URI getBindURI() { + return next.getBindURI(); + } + + /** + * @see org.activeio.ChannelServer#getConnectURI() + */ + public URI getConnectURI() { + return next.getConnectURI(); + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return next.getAdapter(target); + } + + public String toString() { + return next.toString(); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/InputAsyncChannel.java b/activeio/src/java/org/activeio/InputAsyncChannel.java new file mode 100644 index 0000000000..e24bc28f82 --- /dev/null +++ b/activeio/src/java/org/activeio/InputAsyncChannel.java @@ -0,0 +1,41 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +/** + * InputAsyncChannel objects asynchronously push 'up' {@see org.activeio.Packet} objects + * to a registered {@see org.activeio.AsyncChannelListener}. + * + * @version $Revision$ + */ +public interface InputAsyncChannel extends Channel { + + /** + * Registers the {@see ChannelConsumer} that the protcol will use to deliver packets + * coming 'up' the channel. + * + * @param packetListener + */ + void setAsyncChannelListener(AsyncChannelListener channelListener); + + /** + * @return the registered Packet consumer + */ + AsyncChannelListener getAsyncChannelListener(); + +} diff --git a/activeio/src/java/org/activeio/InputStreamChannel.java b/activeio/src/java/org/activeio/InputStreamChannel.java new file mode 100644 index 0000000000..c2d53c65cb --- /dev/null +++ b/activeio/src/java/org/activeio/InputStreamChannel.java @@ -0,0 +1,37 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; + + +/** + * @version $Revision$ + */ +public interface InputStreamChannel extends Channel { + + public int available() throws IOException; + public void mark(int arg0); + public boolean markSupported(); + public int read(byte[] arg0, int arg1, int arg2) throws IOException; + public int read(byte[] arg0) throws IOException; + public void reset() throws IOException; + public long skip(long arg0) throws IOException; + public int read() throws IOException; + +} diff --git a/activeio/src/java/org/activeio/InputSyncChannel.java b/activeio/src/java/org/activeio/InputSyncChannel.java new file mode 100644 index 0000000000..515a3bb1c1 --- /dev/null +++ b/activeio/src/java/org/activeio/InputSyncChannel.java @@ -0,0 +1,40 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; + + +/** + * SynchChannel objects allow threaded to synchronously block on the read + * method to get {@see org.activeio.Packet} objects when they arrive from the peer. + * + * @version $Revision$ + */ +public interface InputSyncChannel extends Channel { + + /** + * Used to synchronously receive a packet of information going 'up' the channel. + * This method blocks until a packet is received or the operation experiences timeout. + * + * @param timeout + * @return the packet received or null if the timeout occurred. + * @throws IOException + */ + Packet read(long timeout) throws IOException; +} diff --git a/activeio/src/java/org/activeio/OutputChannel.java b/activeio/src/java/org/activeio/OutputChannel.java new file mode 100644 index 0000000000..6cbedd6c75 --- /dev/null +++ b/activeio/src/java/org/activeio/OutputChannel.java @@ -0,0 +1,41 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; + +/** + * @version $Revision$ + */ +public interface OutputChannel extends Channel { + + /** + * Sends a packet down the channel towards the media. + * + * @param packet + * @throws IOException + */ + void write(Packet packet) throws IOException; + + /** + * Some channels may buffer data which may be sent down if flush() is called. + * + * @throws IOException + */ + void flush() throws IOException; +} diff --git a/activeio/src/java/org/activeio/OutputStreamChannel.java b/activeio/src/java/org/activeio/OutputStreamChannel.java new file mode 100644 index 0000000000..f8e9699a74 --- /dev/null +++ b/activeio/src/java/org/activeio/OutputStreamChannel.java @@ -0,0 +1,37 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; + +/** + * @version $Revision$ + */ +public interface OutputStreamChannel extends Channel { + + public void write(byte[] data, int pos, int length) throws IOException; + public void write(byte[] data) throws IOException; + public void write(int data) throws IOException; + + /** + * Some channels may buffer data which may be sent down if flush() is called. + * + * @throws IOException + */ + void flush() throws IOException; +} diff --git a/activeio/src/java/org/activeio/Packet.java b/activeio/src/java/org/activeio/Packet.java new file mode 100644 index 0000000000..95be00a536 --- /dev/null +++ b/activeio/src/java/org/activeio/Packet.java @@ -0,0 +1,66 @@ +/** + * + * Copyright 2004 Hiram Chrino + * + * Licensed 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.activeio; + +import java.io.DataOutput; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Provides a ByteBuffer like interface to work with IO channel packets of data. + * + * @version $Revision$ + */ +public interface Packet extends Disposable, Adaptable { + + public int position(); + public void position(int position); + public int limit(); + public void limit(int limit); + public void flip(); + public int remaining(); + public void rewind(); + public boolean hasRemaining(); + public void clear(); + public Packet slice(); + public Packet duplicate(); + public Object duplicate(ClassLoader cl) throws IOException; + public int capacity(); + + public ByteSequence asByteSequence(); + public byte[] sliceAsBytes(); + + + /** + * Writes the remaing bytes in the packet to the output stream. + * + * @param out + * @return + */ + void writeTo(OutputStream out) throws IOException; + void writeTo(DataOutput out) throws IOException; + + // To read data out of the packet. + public int read(); + public int read(byte data[], int offset, int length); + + // To write data into the packet. + public boolean write( int data ); + public int write( byte data[], int offset, int length ); + public int read(Packet dest); + +} diff --git a/activeio/src/java/org/activeio/PacketData.java b/activeio/src/java/org/activeio/PacketData.java new file mode 100644 index 0000000000..1fd04c87df --- /dev/null +++ b/activeio/src/java/org/activeio/PacketData.java @@ -0,0 +1,382 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.EOFException; +import java.io.IOException; + + +/** + * Used to write and read primitives to and from a Packet. + */ +final public class PacketData { + + final private Packet packet; + private final boolean bigEndian; + + public PacketData(Packet packet) { + this(packet, true); + } + + public PacketData(Packet packet, boolean bigEndian) { + this.packet = packet; + this.bigEndian = bigEndian; + } + + private static void spaceNeeded(Packet packet, int space) throws IOException { + if (packet.remaining() < space) + throw new EOFException("Not enough space left in the packet."); + } + + public void readFully(byte[] b) throws IOException { + readFully(packet, b, 0, b.length); + } + + public static void readFully(Packet packet, byte[] b) throws IOException { + readFully(packet, b, 0, b.length); + } + + public void readFully(byte[] b, int off, int len) throws IOException { + readFully(packet, b, off, len); + } + public static void readFully(Packet packet, byte[] b, int off, int len) throws IOException { + spaceNeeded(packet, len); + packet.read(b, off, len); + } + + public int skipBytes(int n) throws IOException { + return skipBytes(packet, n); + } + public static int skipBytes(Packet packet, int n) throws IOException { + int rc = Math.min(n, packet.remaining()); + packet.position(packet.position() + rc); + return rc; + } + + public boolean readBoolean() throws IOException { + return readBoolean(packet); + } + public static boolean readBoolean(Packet packet) throws IOException { + spaceNeeded(packet, 1); + return packet.read() != 0; + } + + public byte readByte() throws IOException { + return readByte(packet); + } + public static byte readByte(Packet packet) throws IOException { + spaceNeeded(packet, 1); + return (byte) packet.read(); + } + + public int readUnsignedByte() throws IOException { + return readUnsignedByte(packet); + } + public static int readUnsignedByte(Packet packet) throws IOException { + spaceNeeded(packet, 1); + return packet.read(); + } + + public short readShort() throws IOException { + if( bigEndian ) { + return readShortBig(packet); + } else { + return readShortLittle(packet); + } + } + public static short readShortBig(Packet packet) throws IOException { + spaceNeeded(packet, 2); + return (short) ((packet.read() << 8) + (packet.read() << 0)); + } + public static short readShortLittle(Packet packet) throws IOException { + spaceNeeded(packet, 2); + return (short) ((packet.read() << 0) + (packet.read() << 8) ); + } + + public int readUnsignedShort() throws IOException { + if( bigEndian ) { + return readUnsignedShortBig(packet); + } else { + return readUnsignedShortLittle(packet); + } + } + public static int readUnsignedShortBig(Packet packet) throws IOException { + spaceNeeded(packet, 2); + return ((packet.read() << 8) + (packet.read() << 0)); + } + public static int readUnsignedShortLittle(Packet packet) throws IOException { + spaceNeeded(packet, 2); + return ((packet.read() << 0) + (packet.read() << 8) ); + } + + public char readChar() throws IOException { + if( bigEndian ) { + return readCharBig(packet); + } else { + return readCharLittle(packet); + } + } + public static char readCharBig(Packet packet) throws IOException { + spaceNeeded(packet, 2); + return (char) ((packet.read() << 8) + (packet.read() << 0)); + } + public static char readCharLittle(Packet packet) throws IOException { + spaceNeeded(packet, 2); + return (char) ((packet.read() << 0) + (packet.read() << 8) ); + } + + public int readInt() throws IOException { + if( bigEndian ) { + return readIntBig(packet); + } else { + return readIntLittle(packet); + } + } + public static int readIntBig(Packet packet) throws IOException { + spaceNeeded(packet, 4); + return ((packet.read() << 24) + + (packet.read() << 16) + + (packet.read() << 8) + + (packet.read() << 0)); + } + public static int readIntLittle(Packet packet) throws IOException { + spaceNeeded(packet, 4); + return ((packet.read() << 0) + + (packet.read() << 8) + + (packet.read() << 16) + + (packet.read() << 24)); + } + + public long readLong() throws IOException { + if( bigEndian ) { + return readLongBig(packet); + } else { + return readLongLittle(packet); + } + } + public static long readLongBig(Packet packet) throws IOException { + spaceNeeded(packet, 8); + return (((long) packet.read() << 56) + + ((long) packet.read() << 48) + + ((long) packet.read() << 40) + + ((long) packet.read() << 32) + + ((long) packet.read() << 24) + + ((packet.read()) << 16) + + ((packet.read()) << 8) + + ((packet.read()) << 0)); + } + public static long readLongLittle(Packet packet) throws IOException { + spaceNeeded(packet, 8); + return ((packet.read() << 0) + + (packet.read() << 8) + + (packet.read() << 16) + + ((long) packet.read() << 24) + + ((long) packet.read() << 32) + + ((long) packet.read() << 40) + + ((long) packet.read() << 48) + + ((long) packet.read() << 56)); + } + + public double readDouble() throws IOException { + return Double.longBitsToDouble(readLong()); + } + public static double readDoubleBig(Packet packet) throws IOException { + return Double.longBitsToDouble(readLongBig(packet)); + } + public static double readDoubleLittle(Packet packet) throws IOException { + return Double.longBitsToDouble(readLongLittle(packet)); + } + + public float readFloat() throws IOException { + return Float.intBitsToFloat(readInt()); + } + public static float readFloatBig(Packet packet) throws IOException { + return Float.intBitsToFloat(readIntBig(packet)); + } + public static float readFloatLittle(Packet packet) throws IOException { + return Float.intBitsToFloat(readIntLittle(packet)); + } + + public void write(int b) throws IOException { + write(packet, b); + } + public static void write(Packet packet, int b) throws IOException { + spaceNeeded(packet, 1); + packet.write(b); + } + + public void write(byte[] b) throws IOException { + write(packet, b, 0, b.length); + } + public static void write(Packet packet, byte[] b) throws IOException { + write(packet, b, 0, b.length); + } + + public void write(byte[] b, int off, int len) throws IOException { + write(packet, b, off, len); + } + public static void write(Packet packet, byte[] b, int off, int len) throws IOException { + spaceNeeded(packet, len); + packet.write(b, off, len); + } + + public void writeBoolean(boolean v) throws IOException { + writeBoolean(packet, v); + } + public static void writeBoolean(Packet packet, boolean v) throws IOException { + spaceNeeded(packet, 1); + packet.write(v ? 1 : 0); + } + + public void writeByte(int v) throws IOException { + writeByte(packet, v); + } + public static void writeByte(Packet packet, int v) throws IOException { + spaceNeeded(packet, 1); + packet.write(v); + } + + public void writeShort(int v) throws IOException { + if (bigEndian) { + writeShortBig(packet,v); + } else { + writeShortLittle(packet,v); + } + } + public static void writeShortBig(Packet packet, int v) throws IOException { + spaceNeeded(packet, 2); + packet.write((v >>> 8) & 0xFF); + packet.write((v >>> 0) & 0xFF); + } + public static void writeShortLittle(Packet packet, int v) throws IOException { + spaceNeeded(packet, 2); + packet.write((v >>> 0) & 0xFF); + packet.write((v >>> 8) & 0xFF); + } + + public void writeChar(int v) throws IOException { + if (bigEndian) { + writeCharBig(packet, v); + } else { + writeCharLittle(packet, v); + } + } + public static void writeCharBig(Packet packet, int v) throws IOException { + spaceNeeded(packet, 2); + packet.write((v >>> 8) & 0xFF); + packet.write((v >>> 0) & 0xFF); + } + public static void writeCharLittle(Packet packet, int v) throws IOException { + spaceNeeded(packet, 2); + packet.write((v >>> 0) & 0xFF); + packet.write((v >>> 8) & 0xFF); + } + + public void writeInt(int v) throws IOException { + if (bigEndian) { + writeIntBig(packet, v); + } else { + writeIntLittle(packet, v); + } + } + public static void writeIntBig(Packet packet, int v) throws IOException { + spaceNeeded(packet, 4); + packet.write((v >>> 24) & 0xFF); + packet.write((v >>> 16) & 0xFF); + packet.write((v >>> 8) & 0xFF); + packet.write((v >>> 0) & 0xFF); + } + public static void writeIntLittle(Packet packet, int v) throws IOException { + spaceNeeded(packet, 4); + packet.write((v >>> 0) & 0xFF); + packet.write((v >>> 8) & 0xFF); + packet.write((v >>> 16) & 0xFF); + packet.write((v >>> 24) & 0xFF); + } + + public void writeLong(long v) throws IOException { + if (bigEndian) { + writeLongBig(packet, v); + } else { + writeLongLittle(packet, v); + } + } + public static void writeLongBig(Packet packet, long v) throws IOException { + spaceNeeded(packet, 8); + packet.write((int) (v >>> 56) & 0xFF); + packet.write((int) (v >>> 48) & 0xFF); + packet.write((int) (v >>> 40) & 0xFF); + packet.write((int) (v >>> 32) & 0xFF); + packet.write((int) (v >>> 24) & 0xFF); + packet.write((int) (v >>> 16) & 0xFF); + packet.write((int) (v >>> 8) & 0xFF); + packet.write((int) (v >>> 0) & 0xFF); + } + public static void writeLongLittle(Packet packet, long v) throws IOException { + spaceNeeded(packet, 8); + packet.write((int) (v >>> 0) & 0xFF); + packet.write((int) (v >>> 8) & 0xFF); + packet.write((int) (v >>> 16) & 0xFF); + packet.write((int) (v >>> 24) & 0xFF); + packet.write((int) (v >>> 32) & 0xFF); + packet.write((int) (v >>> 40) & 0xFF); + packet.write((int) (v >>> 48) & 0xFF); + packet.write((int) (v >>> 56) & 0xFF); + } + + public void writeDouble(double v) throws IOException { + writeLong(Double.doubleToLongBits(v)); + } + public static void writeDoubleBig(Packet packet, double v) throws IOException { + writeLongBig(packet, Double.doubleToLongBits(v)); + } + public static void writeDoubleLittle(Packet packet, double v) throws IOException { + writeLongLittle(packet, Double.doubleToLongBits(v)); + } + + public void writeFloat(float v) throws IOException { + writeInt(Float.floatToIntBits(v)); + } + public static void writeFloatBig(Packet packet, float v) throws IOException { + writeIntBig(packet, Float.floatToIntBits(v)); + } + public static void writeFloatLittle(Packet packet, float v) throws IOException { + writeIntLittle(packet, Float.floatToIntBits(v)); + } + + public void writeRawDouble(double v) throws IOException { + writeLong(Double.doubleToRawLongBits(v)); + } + public static void writeRawDoubleBig(Packet packet, double v) throws IOException { + writeLongBig(packet, Double.doubleToRawLongBits(v)); + } + public static void writeRawDoubleLittle(Packet packet, double v) throws IOException { + writeLongLittle(packet, Double.doubleToRawLongBits(v)); + } + + public void writeRawFloat(float v) throws IOException { + writeInt(Float.floatToRawIntBits(v)); + } + public static void writeRawFloatBig(Packet packet, float v) throws IOException { + writeIntBig(packet, Float.floatToRawIntBits(v)); + } + public static void writeRawFloatLittle(Packet packet, float v) throws IOException { + writeIntLittle(packet, Float.floatToRawIntBits(v)); + } + +} diff --git a/activeio/src/java/org/activeio/RequestChannel.java b/activeio/src/java/org/activeio/RequestChannel.java new file mode 100644 index 0000000000..d5985d437e --- /dev/null +++ b/activeio/src/java/org/activeio/RequestChannel.java @@ -0,0 +1,59 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; + + +/** + * RequestChannel are used to model the request/reponse exchange that is used + * by higher level protcols such as HTTP and RMI. + * + * @version $Revision$ + */ +public interface RequestChannel extends Channel { + + /** + * Used to send a packet of information going 'down' the channel and wait for + * it's reponse 'up' packet. + * + * This method blocks until the response packet is received or the operation + * experiences a timeout. + * + * @param request + * @param timeout + * @return the respnse packet or null if the timeout occured. + * @throws IOException + */ + Packet request(Packet request, long timeout) throws IOException; + + /** + * Registers the {@see RequestListener} that the protcol will use to deliver request packets + * comming 'up' the channel. + * + * @param packetListener + * @throws IOException + */ + void setRequestListener(RequestListener requestListener) throws IOException; + + /** + * @return the registered RequestListener + */ + RequestListener getRequestListener(); + +} diff --git a/activeio/src/java/org/activeio/RequestListener.java b/activeio/src/java/org/activeio/RequestListener.java new file mode 100644 index 0000000000..bcb3439b19 --- /dev/null +++ b/activeio/src/java/org/activeio/RequestListener.java @@ -0,0 +1,42 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; + + +/** + * An RequestListener object is used to receive remote requests from a a {@see org.activeio.RequestChannel} + * + * @version $Revision$ + */ +public interface RequestListener { + + /** + * A {@see RequestChannel} will call this method when a new request arrives. + * + * @param packet + */ + Packet onRequest(Packet request); + + /** + * A {@see RequestChannel} will call this method when a async failure occurs when receiving a request. + * + * @param error the exception that describes the failure. + */ + void onRquestError(IOException error); +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/Service.java b/activeio/src/java/org/activeio/Service.java new file mode 100644 index 0000000000..64d2b8a8d8 --- /dev/null +++ b/activeio/src/java/org/activeio/Service.java @@ -0,0 +1,53 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; + +/** + * The Service interface is used control the running state of a channel. + * + * Some channels may use background threads to provide SEDA style processing. By + * implenting the Service interface, a protcol can allow a container to + * control those threads. + * + * @version $Revision$ + */ +public interface Service { + + static final public long NO_WAIT_TIMEOUT=0; + static final public long WAIT_FOREVER_TIMEOUT=-1; + + /** + * Starts the channel. Once started, the channel is in the running state. + * + * @throws IOException + */ + void start() throws IOException; + + /** + * Stops the channel. Once stopped, the channel is in the stopped state. + * + * @param timeout The amount of time the channel is allowed to take to gracefully stop. If the timeout + * is exceeded, the channel should do a forcefull stop. + * + * @throws IOException + */ + void stop(long timeout) throws IOException; + +} diff --git a/activeio/src/java/org/activeio/StreamChannel.java b/activeio/src/java/org/activeio/StreamChannel.java new file mode 100644 index 0000000000..a676c27f5e --- /dev/null +++ b/activeio/src/java/org/activeio/StreamChannel.java @@ -0,0 +1,24 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +/** + * @version $Revision$ + */ +public interface StreamChannel extends OutputStreamChannel, InputStreamChannel { +} diff --git a/activeio/src/java/org/activeio/StreamChannelFactory.java b/activeio/src/java/org/activeio/StreamChannelFactory.java new file mode 100644 index 0000000000..d0c050783c --- /dev/null +++ b/activeio/src/java/org/activeio/StreamChannelFactory.java @@ -0,0 +1,46 @@ +/** +* +* Copyright 2004 Hiram Chirino +* +* Licensed 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.activeio; + +import java.io.IOException; +import java.net.URI; + +/** + * StreamChannelFactory objects can create {@see org.activeio.StreamChannel} + * and {@see org.activeio.StreamChannelServer} objects. + * + * @version $Revision$ + */ +public interface StreamChannelFactory { + + /** + * Opens a connection to server. + * + * @param location + * @return + */ + public StreamChannel openStreamChannel(URI location) throws IOException; + + /** + * Binds a server at the URI location. + * + * @param location + * @return + */ + public StreamChannelServer bindStreamChannel(URI location) throws IOException; + +} diff --git a/activeio/src/java/org/activeio/StreamChannelServer.java b/activeio/src/java/org/activeio/StreamChannelServer.java new file mode 100644 index 0000000000..5777bda534 --- /dev/null +++ b/activeio/src/java/org/activeio/StreamChannelServer.java @@ -0,0 +1,36 @@ +/** +* +* Copyright 2004 Hiram Chirino +* +* Licensed 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.activeio; + +import java.io.IOException; + + + +/** + * A StreamChannelServer object provides an accept method to synchronously + * accept and create {@see org.activeio.channel.Channel} objects. + * + * @version $Revision$ + */ +public interface StreamChannelServer extends ChannelServer { + + static final public long NO_WAIT_TIMEOUT=0; + static final public long WAIT_FOREVER_TIMEOUT=-1; + + public Channel accept(long timeout) throws IOException; + +} diff --git a/activeio/src/java/org/activeio/SyncChannel.java b/activeio/src/java/org/activeio/SyncChannel.java new file mode 100644 index 0000000000..e183986076 --- /dev/null +++ b/activeio/src/java/org/activeio/SyncChannel.java @@ -0,0 +1,27 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +/** + * SyncChannel objets allow threadd to synchronously block on the receiveUpPacket + * method to get 'up' {@see org.activeio.Packet} objects when they arrive. + * + * @version $Revision$ + */ +public interface SyncChannel extends OutputChannel, InputSyncChannel { +} diff --git a/activeio/src/java/org/activeio/SyncChannelFactory.java b/activeio/src/java/org/activeio/SyncChannelFactory.java new file mode 100644 index 0000000000..fc60411c63 --- /dev/null +++ b/activeio/src/java/org/activeio/SyncChannelFactory.java @@ -0,0 +1,46 @@ +/** +* +* Copyright 2004 Hiram Chirino +* +* Licensed 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.activeio; + +import java.io.IOException; +import java.net.URI; + +/** + * SynchChannelFactory objects can create {@see org.activeio.SynchChannel} + * and {@see org.activeio.SynchChannelServer} objects. + * + * @version $Revision$ + */ +public interface SyncChannelFactory { + + /** + * Opens a connection to server. + * + * @param location + * @return + */ + public SyncChannel openSyncChannel(URI location) throws IOException; + + /** + * Binds a server at the URI location. + * + * @param location + * @return + */ + public SyncChannelServer bindSyncChannel(URI location) throws IOException; + +} diff --git a/activeio/src/java/org/activeio/SyncChannelServer.java b/activeio/src/java/org/activeio/SyncChannelServer.java new file mode 100644 index 0000000000..ac7a9f58d8 --- /dev/null +++ b/activeio/src/java/org/activeio/SyncChannelServer.java @@ -0,0 +1,36 @@ +/** +* +* Copyright 2004 Hiram Chirino +* +* Licensed 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.activeio; + +import java.io.IOException; + + + +/** + * A SynchChannelServer object provides an accept method to synchronously + * accept and create {@see org.activeio.Channel} objects. + * + * @version $Revision$ + */ +public interface SyncChannelServer extends ChannelServer { + + static final public long NO_WAIT_TIMEOUT=0; + static final public long WAIT_FOREVER_TIMEOUT=-1; + + public Channel accept(long timeout) throws IOException; + +} diff --git a/activeio/src/java/org/activeio/adapter/AsyncChannelToClientRequestChannel.java b/activeio/src/java/org/activeio/adapter/AsyncChannelToClientRequestChannel.java new file mode 100644 index 0000000000..401a0f8e88 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/AsyncChannelToClientRequestChannel.java @@ -0,0 +1,70 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.adapter; + +import java.io.IOException; + +import org.activeio.Packet; +import org.activeio.RequestChannel; +import org.activeio.RequestListener; +import org.activeio.SyncChannel; + + +/** + * Creates a {@see org.activeio.RequestChannel} out of a {@see org.activeio.SyncChannel}. + * Does not support handing requests. It can only be used to send requests. + * + * @version $Revision$ + */ +final public class AsyncChannelToClientRequestChannel implements RequestChannel { + + private final SyncChannel next; + + public AsyncChannelToClientRequestChannel(SyncChannel next) { + this.next = next; + } + + public Packet request(Packet request, long timeout) throws IOException { + next.write(request); + next.flush(); + return next.read(timeout); + } + + public void setRequestListener(RequestListener requestListener) throws IOException { + throw new IOException("Operation not supported."); + } + + public RequestListener getRequestListener() { + return null; + } + + public Object getAdapter(Class target) { + return next.getAdapter(target); + } + + public void dispose() { + next.dispose(); + } + + public void start() throws IOException { + next.start(); + } + + public void stop(long timeout) throws IOException { + next.stop(timeout); + } +} diff --git a/activeio/src/java/org/activeio/adapter/AsyncChannelToConcurrentRequestChannel.java b/activeio/src/java/org/activeio/adapter/AsyncChannelToConcurrentRequestChannel.java new file mode 100644 index 0000000000..fd07facb76 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/AsyncChannelToConcurrentRequestChannel.java @@ -0,0 +1,189 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.io.InterruptedIOException; + +import org.activeio.AsyncChannel; +import org.activeio.ChannelFactory; +import org.activeio.FilterAsyncChannel; +import org.activeio.Packet; +import org.activeio.PacketData; +import org.activeio.RequestChannel; +import org.activeio.RequestListener; +import org.activeio.packet.AppendedPacket; +import org.activeio.packet.ByteArrayPacket; + +import edu.emory.mathcs.backport.java.util.concurrent.ArrayBlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + + +/** + * Creates a {@see org.activeio.RequestChannel} out of a {@see org.activeio.AsyncChannel}. This + * {@see org.activeio.RequestChannel} is thread safe and mutiplexes concurrent requests and responses over + * the underlying {@see org.activeio.AsyncChannel}. + * + * @version $Revision$ + */ +final public class AsyncChannelToConcurrentRequestChannel extends FilterAsyncChannel implements RequestChannel { + + private static final byte PASSTHROUGH = 0x00; + private static final byte REQUEST = 0x01; + private static final byte RESPONSE = 0x02; + private static final ByteArrayPacket PASSTHROUGH_PACKET = new ByteArrayPacket(new byte[]{PASSTHROUGH}); + + private final ConcurrentHashMap requestMap = new ConcurrentHashMap(); + private final Executor requestExecutor; + private short nextRequestId = 0; + private final Object writeMutex = new Object(); + + private RequestListener requestListener; + + public AsyncChannelToConcurrentRequestChannel(AsyncChannel next) { + this(next, ChannelFactory.DEFAULT_EXECUTOR); + } + + public AsyncChannelToConcurrentRequestChannel(AsyncChannel next, Executor requestExecutor) { + super(next); + this.requestExecutor=requestExecutor; + } + + synchronized short getNextRequestId() { + return nextRequestId++; + } + + /** + * @see org.activeio.FilterAsyncChannel#write(org.activeio.Packet) + */ + public void write(Packet packet) throws IOException { + Packet passThrough = AppendedPacket.join(PASSTHROUGH_PACKET.duplicate(), packet); + synchronized(writeMutex) { + super.write(passThrough); + } + } + + /** + * @see org.activeio.FilterAsyncChannel#onPacket(org.activeio.Packet) + */ + public void onPacket(final Packet packet) { + switch( packet.read() ) { + case PASSTHROUGH: + super.onPacket(packet); + break; + case REQUEST: + requestExecutor.execute(new Runnable(){ + public void run() { + serviceRequest(packet); + } + }); + break; + case RESPONSE: + serviceReponse(packet); + break; + } + } + + private void serviceRequest(Packet packet) { + try { + if( requestListener ==null ) + throw new IOException("The RequestListener has not been set."); + + PacketData data = new PacketData(packet); + short requestId = data.readShort(); + Packet reponse = requestListener.onRequest(packet); + + // Send the response... + Packet header = createHeaderPacket(RESPONSE, requestId); + Packet rc = AppendedPacket.join(header, packet); + synchronized(writeMutex) { + super.write(rc); + } + } catch (IOException e) { + super.onPacketError(e); + } + + } + + private void serviceReponse(Packet packet) { + + try { + + PacketData data = new PacketData(packet); + short requestId = data.readShort(); + + ArrayBlockingQueue responseSlot = (ArrayBlockingQueue) requestMap.get(new Short(requestId)); + responseSlot.put(packet); + + } catch (IOException e) { + super.onPacketError(e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + + } + + public Packet request(Packet request, long timeout) throws IOException { + + Short requestId = new Short(getNextRequestId()); + ArrayBlockingQueue responseSlot = new ArrayBlockingQueue(1); + requestMap.put(requestId, responseSlot); + + Packet header = createHeaderPacket(REQUEST, requestId.shortValue()); + Packet packet = AppendedPacket.join(header, request); + + synchronized(writeMutex) { + super.write(packet); + } + + try { + + if( timeout == WAIT_FOREVER_TIMEOUT ) { + return (Packet) responseSlot.take(); + } else if (timeout == NO_WAIT_TIMEOUT ) { + return (Packet) responseSlot.poll(1, TimeUnit.MILLISECONDS); + } else { + return (Packet) responseSlot.poll(timeout, TimeUnit.MILLISECONDS); + } + + } catch (InterruptedException e) { + throw new InterruptedIOException(e.getMessage()); + } finally { + requestMap.remove(requestId); + } + } + + private Packet createHeaderPacket(byte type, short requestId) throws IOException { + ByteArrayPacket header = new ByteArrayPacket(new byte[]{3}); + PacketData data = new PacketData(header); + data.writeByte(type); + data.writeShort(requestId); + header.flip(); + return header; + } + + public void setRequestListener(RequestListener requestListener) throws IOException { + this.requestListener = requestListener; + } + + public RequestListener getRequestListener() { + return requestListener; + } +} diff --git a/activeio/src/java/org/activeio/adapter/AsyncChannelToOutputStream.java b/activeio/src/java/org/activeio/adapter/AsyncChannelToOutputStream.java new file mode 100644 index 0000000000..3ec56ccc71 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/AsyncChannelToOutputStream.java @@ -0,0 +1,72 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.io.OutputStream; + +import org.activeio.AsyncChannel; +import org.activeio.packet.ByteArrayPacket; +import org.activeio.packet.BytePacket; + +/** + */ +public class AsyncChannelToOutputStream extends OutputStream { + + private final AsyncChannel channel; + private boolean closed; + + /** + * @param channel + */ + public AsyncChannelToOutputStream(AsyncChannel channel) { + this.channel = channel; + } + + /** + * @see java.io.OutputStream#write(int) + */ + public void write(int b) throws IOException { + channel.write(new BytePacket((byte) b)); + } + + /** + * @see java.io.OutputStream#write(byte[], int, int) + */ + public void write(byte[] b, int off, int len) throws IOException { + channel.write(new ByteArrayPacket(b, off, len)); + } + + /** + * @see java.io.OutputStream#flush() + */ + public void flush() throws IOException { + channel.flush(); + } + + /** + * @see java.io.InputStream#close() + */ + public void close() throws IOException { + closed=true; + super.close(); + } + + public boolean isClosed() { + return closed; + } +} diff --git a/activeio/src/java/org/activeio/adapter/AsyncChannelToServerRequestChannel.java b/activeio/src/java/org/activeio/adapter/AsyncChannelToServerRequestChannel.java new file mode 100644 index 0000000000..379abba117 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/AsyncChannelToServerRequestChannel.java @@ -0,0 +1,89 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.adapter; + +import java.io.IOException; + +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelListener; +import org.activeio.Packet; +import org.activeio.RequestChannel; +import org.activeio.RequestListener; +import org.activeio.packet.EOSPacket; + + +/** + * Creates a {@see org.activeio.RequestChannel} out of a {@see org.activeio.AsyncChannel}. + * Does not support sending requests. It can only be used to handle requests. + * + * @version $Revision$ + */ +public class AsyncChannelToServerRequestChannel implements RequestChannel, AsyncChannelListener { + + private final AsyncChannel next; + private RequestListener requestListener; + + public AsyncChannelToServerRequestChannel(AsyncChannel next) throws IOException { + this.next = next; + next.setAsyncChannelListener(this); + } + + public Packet request(Packet request, long timeout) throws IOException { + throw new IOException("Operation not supported."); + } + + public void setRequestListener(RequestListener requestListener) throws IOException { + this.requestListener = requestListener; + } + + public RequestListener getRequestListener() { + return requestListener; + } + + public Object getAdapter(Class target) { + return next.getAdapter(target); + } + + public void dispose() { + next.dispose(); + } + + public void start() throws IOException { + next.start(); + } + + public void stop(long timeout) throws IOException { + next.stop(timeout); + } + + public void onPacket(Packet packet) { + if( packet == EOSPacket.EOS_PACKET ) { + return; + } + try { + Packet response = requestListener.onRequest(packet); + next.write(response); + next.flush(); + } catch (IOException e) { + requestListener.onRquestError(e); + } + } + + public void onPacketError(IOException error) { + requestListener.onRquestError(error); + } +} diff --git a/activeio/src/java/org/activeio/adapter/AsyncToSyncChannel.java b/activeio/src/java/org/activeio/adapter/AsyncToSyncChannel.java new file mode 100644 index 0000000000..89e09a6095 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/AsyncToSyncChannel.java @@ -0,0 +1,181 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.io.InterruptedIOException; + +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelListener; +import org.activeio.Packet; +import org.activeio.SyncChannel; + +import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +/** + * Adapts a {@see org.activeio.AsyncChannel} so that it provides an + * {@see org.activeio.SynchChannel} interface. + * + * This object buffers asynchronous messages from the {@see org.activeio.AsyncChannel} + * and buffers them in a {@see edu.emory.mathcs.backport.java.util.concurrent.Channel} util the client receives them. + * + * @version $Revision$ + */ +final public class AsyncToSyncChannel implements SyncChannel, AsyncChannelListener { + + private final AsyncChannel asyncChannel; + private final BlockingQueue buffer; + + static public SyncChannel adapt(org.activeio.Channel channel) { + return adapt(channel, new LinkedBlockingQueue()); + } + + static public SyncChannel adapt(org.activeio.Channel channel, BlockingQueue upPacketChannel) { + + // It might not need adapting + if( channel instanceof SyncChannel ) { + return (SyncChannel) channel; + } + + // Can we just just undo the adaptor + if( channel.getClass() == SyncToAsyncChannel.class ) { + return ((SyncToAsyncChannel)channel).getSynchChannel(); + } + + return new AsyncToSyncChannel((AsyncChannel)channel, upPacketChannel); + } + + /** + * @deprecated {@see #adapt(AsyncChannel)} + */ + public AsyncToSyncChannel(AsyncChannel asyncChannel) { + this(asyncChannel, new LinkedBlockingQueue()); + } + + /** + * @deprecated {@see #adapt(AsyncChannel, Channel)} + */ + public AsyncToSyncChannel(AsyncChannel asyncChannel, BlockingQueue upPacketChannel){ + this.asyncChannel = asyncChannel; + this.asyncChannel.setAsyncChannelListener(this); + this.buffer=upPacketChannel; + } + + /** + * @see org.activeio.Channel#write(org.activeio.Packet) + */ + public void write(org.activeio.Packet packet) throws IOException { + asyncChannel.write(packet); + } + + /** + * @see org.activeio.Channel#flush() + */ + public void flush() throws IOException { + asyncChannel.flush(); + } + + /** + * @see org.activeio.SyncChannel#read(long) + */ + public Packet read(long timeout) throws IOException { + try { + + Object o; + if( timeout == NO_WAIT_TIMEOUT ) { + o = buffer.poll(0, TimeUnit.MILLISECONDS); + } else if( timeout == WAIT_FOREVER_TIMEOUT ) { + o = buffer.take(); + } else { + o = buffer.poll(timeout, TimeUnit.MILLISECONDS); + } + + if( o == null ) + return null; + + if( o instanceof Packet ) + return (Packet)o; + + Throwable e = (Throwable)o; + throw (IOException)new IOException("Async error occurred: "+e).initCause(e); + + } catch (InterruptedException e) { + throw new InterruptedIOException(e.getMessage()); + } + } + + /** + * @see org.activeio.Disposable#dispose() + */ + public void dispose() { + asyncChannel.dispose(); + } + + /** + * @see org.activeio.Service#start() + */ + public void start() throws IOException { + asyncChannel.start(); + } + + /** + * @see org.activeio.Service#stop(long) + */ + public void stop(long timeout) throws IOException { + asyncChannel.stop(timeout); + } + + /** + * @see org.activeio.AsyncChannelListener#onPacket(org.activeio.Packet) + */ + public void onPacket(Packet packet) { + try { + buffer.put(packet); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + /** + * @see org.activeio.AsyncChannelListener#onPacketError(org.activeio.ChannelException) + */ + public void onPacketError(IOException error) { + try { + buffer.put(error); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return asyncChannel.getAdapter(target); + } + + public AsyncChannel getAsyncChannel() { + return asyncChannel; + } + + public String toString() { + return asyncChannel.toString(); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/adapter/AsyncToSyncChannelFactory.java b/activeio/src/java/org/activeio/adapter/AsyncToSyncChannelFactory.java new file mode 100644 index 0000000000..ef4f898d09 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/AsyncToSyncChannelFactory.java @@ -0,0 +1,66 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.net.URI; + +import org.activeio.AsyncChannelFactory; +import org.activeio.SyncChannel; +import org.activeio.SyncChannelFactory; +import org.activeio.SyncChannelServer; + +/** + * @version $Revision$ + */ +final public class AsyncToSyncChannelFactory implements SyncChannelFactory { + + private AsyncChannelFactory asyncChannelFactory; + + static public SyncChannelFactory adapt(AsyncChannelFactory channelFactory ) { + + // It might not need adapting + if( channelFactory instanceof SyncChannelServer ) { + return (SyncChannelFactory) channelFactory; + } + + // Can we just just undo the adaptor + if( channelFactory.getClass() == SyncToAsyncChannelFactory.class ) { + return ((SyncToAsyncChannelFactory)channelFactory).getSyncChannelFactory(); + } + + return new AsyncToSyncChannelFactory((AsyncChannelFactory)channelFactory); + } + + + private AsyncToSyncChannelFactory(AsyncChannelFactory asyncChannelFactory) { + this.asyncChannelFactory = asyncChannelFactory; + } + + public SyncChannel openSyncChannel(URI location) throws IOException { + return AsyncToSyncChannel.adapt( asyncChannelFactory.openAsyncChannel(location) ); + } + + public SyncChannelServer bindSyncChannel(URI location) throws IOException { + return AsyncToSyncChannelServer.adapt(asyncChannelFactory.bindAsyncChannel(location)); + } + + public AsyncChannelFactory getAsyncChannelFactory() { + return asyncChannelFactory; + } +} diff --git a/activeio/src/java/org/activeio/adapter/AsyncToSyncChannelServer.java b/activeio/src/java/org/activeio/adapter/AsyncToSyncChannelServer.java new file mode 100644 index 0000000000..8fe8d1fa54 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/AsyncToSyncChannelServer.java @@ -0,0 +1,177 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.URI; + +import org.activeio.AcceptListener; +import org.activeio.AsyncChannelServer; +import org.activeio.Channel; +import org.activeio.ChannelServer; +import org.activeio.SyncChannelServer; + +import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +/** + * Adapts a {@see org.activeio.AsyncChannelServer} so that it provides an + * {@see org.activeio.SynchChannelServer} interface. + * + * This object buffers asynchronous accepts from the {@see org.activeio.AsyncChannelServer} + * abs buffers them in a {@see edu.emory.mathcs.backport.java.util.concurrent.Channel} util the client accepts the + * connection. + * + * @version $Revision$ + */ +final public class AsyncToSyncChannelServer implements SyncChannelServer, AcceptListener { + + private final AsyncChannelServer asyncChannelServer; + private final BlockingQueue acceptBuffer; + + static public SyncChannelServer adapt(ChannelServer channel) { + return adapt(channel, new LinkedBlockingQueue()); + } + + static public SyncChannelServer adapt(ChannelServer channel, BlockingQueue upPacketChannel) { + + // It might not need adapting + if( channel instanceof SyncChannelServer ) { + return (SyncChannelServer) channel; + } + + // Can we just just undo the adaptor + if( channel.getClass() == SyncToAsyncChannel.class ) { + return ((SyncToAsyncChannelServer)channel).getSynchChannelServer(); + } + + return new AsyncToSyncChannelServer((AsyncChannelServer)channel, upPacketChannel); + } + + /** + * @deprecated {@see #adapt(ChannelServer)} + */ + public AsyncToSyncChannelServer(AsyncChannelServer asyncChannelServer) { + this(asyncChannelServer,new LinkedBlockingQueue()); + } + + /** + * @deprecated {@see #adapt(ChannelServer, edu.emory.mathcs.backport.java.util.concurrent.Channel)} + */ + public AsyncToSyncChannelServer(AsyncChannelServer asyncChannelServer, BlockingQueue acceptBuffer) { + this.asyncChannelServer = asyncChannelServer; + this.acceptBuffer=acceptBuffer; + this.asyncChannelServer.setAcceptListener(this); + } + + /** + * @see org.activeio.SyncChannelServer#accept(long) + */ + public org.activeio.Channel accept(long timeout) throws IOException { + try { + + Object o; + if( timeout == NO_WAIT_TIMEOUT ) { + o = acceptBuffer.poll(0, TimeUnit.MILLISECONDS); + } else if( timeout == WAIT_FOREVER_TIMEOUT ) { + o = acceptBuffer.take(); + } else { + o = acceptBuffer.poll(timeout, TimeUnit.MILLISECONDS); + } + + if( o == null ) + return null; + + if( o instanceof Channel ) + return (Channel)o; + + Throwable e = (Throwable)o; + throw (IOException)new IOException("Async error occurred: "+e).initCause(e); + + } catch (InterruptedException e) { + throw new InterruptedIOException(e.getMessage()); + } + } + /** + * @see org.activeio.Disposable#dispose() + */ + public void dispose() { + asyncChannelServer.dispose(); + } + + /** + * @see org.activeio.Service#start() + */ + public void start() throws IOException { + asyncChannelServer.start(); + } + + /** + * @see org.activeio.Service#stop(long) + */ + public void stop(long timeout) throws IOException { + asyncChannelServer.stop(timeout); + } + + public URI getBindURI() { + return asyncChannelServer.getBindURI(); + } + + public URI getConnectURI() { + return asyncChannelServer.getConnectURI(); + } + + /** + * @see org.activeio.AcceptListener#onAccept(org.activeio.Channel) + */ + public void onAccept(org.activeio.Channel channel) { + try { + acceptBuffer.put(channel); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + /** + * @see org.activeio.AcceptListener#onAcceptError(java.io.IOException) + */ + public void onAcceptError(IOException error) { + try { + acceptBuffer.put(error); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + public AsyncChannelServer getAsyncChannelServer() { + return asyncChannelServer; + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return asyncChannelServer.getAdapter(target); + } + + public String toString() { + return asyncChannelServer.toString(); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/adapter/InputStreamChannelToInputStream.java b/activeio/src/java/org/activeio/adapter/InputStreamChannelToInputStream.java new file mode 100644 index 0000000000..0b88d6572b --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/InputStreamChannelToInputStream.java @@ -0,0 +1,85 @@ +/** +* +* Copyright 2004 Hiram Chirino +* +* Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.io.InputStream; + +import org.activeio.InputStreamChannel; + +/** + * Provides an InputStream for a given InputStreamChannel. + * + * @version $Revision$ + */ +public class InputStreamChannelToInputStream extends InputStream { + + private final InputStreamChannel channel; + + /** + * @param channel + */ + public InputStreamChannelToInputStream(final InputStreamChannel channel) { + this.channel = channel; + } + + public int available() throws IOException { + return channel.available(); + } + + public synchronized void mark(int arg0) { + channel.mark(arg0); + } + + public boolean markSupported() { + return channel.markSupported(); + } + + public int read(byte[] arg0) throws IOException { + return channel.read(arg0); + } + + public synchronized void reset() throws IOException { + channel.reset(); + } + + public long skip(long arg0) throws IOException { + return channel.skip(arg0); + } + + /** + * @see java.io.InputStream#read() + */ + public int read() throws IOException { + return channel.read(); + } + + /** + * @see java.io.InputStream#read(byte[], int, int) + */ + public int read(byte[] b, int off, int len) throws IOException { + return channel.read(b,off,len); + } + + /** + * @see java.io.InputStream#close() + */ + public void close() throws IOException { + channel.dispose(); + super.close(); + } +} diff --git a/activeio/src/java/org/activeio/adapter/OutputStreamChannelToOutputStream.java b/activeio/src/java/org/activeio/adapter/OutputStreamChannelToOutputStream.java new file mode 100644 index 0000000000..f7453e53e1 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/OutputStreamChannelToOutputStream.java @@ -0,0 +1,70 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.io.OutputStream; + +import org.activeio.OutputStreamChannel; + +/** + */ +public class OutputStreamChannelToOutputStream extends OutputStream { + + private final OutputStreamChannel channel; + + /** + * @param channel + */ + public OutputStreamChannelToOutputStream(OutputStreamChannel channel) { + this.channel = channel; + } + + /** + * @see java.io.OutputStream#write(int) + */ + public void write(int b) throws IOException { + channel.write(b); + } + + /** + * @see java.io.OutputStream#write(byte[], int, int) + */ + public void write(byte[] b, int off, int len) throws IOException { + channel.write(b, off, len); + } + + /** + * @see java.io.OutputStream#flush() + */ + public void flush() throws IOException { + channel.flush(); + } + + public void write(byte[] b) throws IOException { + channel.write(b); + } + + /** + * @see java.io.InputStream#close() + */ + public void close() throws IOException { + channel.dispose(); + super.close(); + } + +} diff --git a/activeio/src/java/org/activeio/adapter/PacketByteArrayOutputStream.java b/activeio/src/java/org/activeio/adapter/PacketByteArrayOutputStream.java new file mode 100644 index 0000000000..e3d0370a53 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/PacketByteArrayOutputStream.java @@ -0,0 +1,110 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.io.OutputStream; + +import org.activeio.Packet; +import org.activeio.packet.AppendedPacket; +import org.activeio.packet.ByteArrayPacket; + +/** + * + */ +final public class PacketByteArrayOutputStream extends OutputStream { + + private Packet result; + private Packet current; + int nextAllocationSize=0; + + public PacketByteArrayOutputStream() { + this( 1024 ); + } + + public PacketByteArrayOutputStream(int initialSize) { + nextAllocationSize = initialSize; + current = allocate(); + } + + protected Packet allocate() { + ByteArrayPacket packet = new ByteArrayPacket(new byte[nextAllocationSize]); + nextAllocationSize <<= 3; // x by 8 + return packet; + } + + public void skip(int size) { + while( size > 0 ) { + if( !current.hasRemaining() ) { + allocatedNext(); + } + + int skip = ((size <= current.remaining()) ? size : current.remaining()); + current.position(current.position()+skip); + size -= skip; + } + } + + public void write(int b) throws IOException { + if( !current.hasRemaining() ) { + allocatedNext(); + } + current.write(b); + } + + public void write(byte[] b, int off, int len) throws IOException { + while( len > 0 ) { + if( !current.hasRemaining() ) { + allocatedNext(); + } + int wrote = current.write(b,off,len); + off+=wrote; + len-=wrote; + } + } + + private void allocatedNext() { + if( result == null ) { + current.flip(); + result = current; + } else { + current.flip(); + result = AppendedPacket.join(result, current); + } + current = allocate(); + } + + public Packet getPacket() { + if( result == null ) { + current.flip(); + return current.slice(); + } else { + current.flip(); + return AppendedPacket.join(result, current); + } + } + + public void reset() { + result = null; + current.clear(); + } + + public int position() { + return current.position() + (result==null ? 0 : result.remaining()); + } +} diff --git a/activeio/src/java/org/activeio/adapter/PacketInputStream.java b/activeio/src/java/org/activeio/adapter/PacketInputStream.java new file mode 100644 index 0000000000..e1371e6a47 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/PacketInputStream.java @@ -0,0 +1,28 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio.adapter; + +import org.activeio.Packet; + +/** + * @deprecated Use PacketToInputStream instead. This class will be removed very soon. + */ +public class PacketInputStream extends PacketToInputStream { + public PacketInputStream(Packet packet) { + super(packet); + } +} diff --git a/activeio/src/java/org/activeio/adapter/PacketOutputStream.java b/activeio/src/java/org/activeio/adapter/PacketOutputStream.java new file mode 100644 index 0000000000..f960c48b44 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/PacketOutputStream.java @@ -0,0 +1,46 @@ +/** +* +* Copyright 2004 Hiram Chirino +* +* Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.io.OutputStream; + +import org.activeio.Packet; + +/** + * Provides an OutputStream for a given Packet. + * + * @version $Revision$ + */ +public class PacketOutputStream extends OutputStream { + + final Packet packet; + + public PacketOutputStream(Packet packet) { + this.packet = packet; + } + + public void write(int b) throws IOException { + if( !packet.write(b) ) + throw new IOException("Packet does not have any remaining space to write to."); + } + + public void write(byte[] b, int off, int len) throws IOException { + if( packet.write(b, off, len)!=len ) + throw new IOException("Packet does not have "+len+" byte(s) left to write to."); + } +} diff --git a/activeio/src/java/org/activeio/adapter/PacketToInputStream.java b/activeio/src/java/org/activeio/adapter/PacketToInputStream.java new file mode 100644 index 0000000000..3594e290d6 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/PacketToInputStream.java @@ -0,0 +1,54 @@ +/** +* +* Copyright 2004 Hiram Chirino +* +* Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.io.InputStream; + +import org.activeio.Packet; + +/** + * Provides an InputStream for a given Packet. + * + * @version $Revision$ + */ +public class PacketToInputStream extends InputStream { + + final Packet packet; + + /** + * @param packet + */ + public PacketToInputStream(Packet packet) { + this.packet = packet; + } + + /** + * @see java.io.InputStream#read() + */ + public int read() throws IOException { + return packet.read(); + } + + /** + * @see java.io.InputStream#read(byte[], int, int) + */ + public int read(byte[] b, int off, int len) throws IOException { + return packet.read(b, off, len); + } + +} diff --git a/activeio/src/java/org/activeio/adapter/SyncChannelServerToServerSocket.java b/activeio/src/java/org/activeio/adapter/SyncChannelServerToServerSocket.java new file mode 100644 index 0000000000..0fba55f3b2 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/SyncChannelServerToServerSocket.java @@ -0,0 +1,139 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.URI; +import java.nio.channels.ServerSocketChannel; + +import org.activeio.Channel; +import org.activeio.SyncChannel; +import org.activeio.SyncChannelServer; + +/** + */ +public class SyncChannelServerToServerSocket extends ServerSocket { + + private final SyncChannelServer channelServer; + private long timeout = Channel.WAIT_FOREVER_TIMEOUT; + boolean closed; + private InetAddress inetAddress; + private int localPort; + private SocketAddress localSocketAddress; + private int receiveBufferSize; + private boolean reuseAddress; + + /** + * @throws IOException + */ + public SyncChannelServerToServerSocket(SyncChannelServer channelServer) throws IOException { + this.channelServer = channelServer; + URI connectURI = channelServer.getConnectURI(); + localPort = connectURI.getPort(); + inetAddress = InetAddress.getByName(connectURI.getHost()); + localSocketAddress = new InetSocketAddress(inetAddress, localPort); + } + + public synchronized void setSoTimeout(int timeout) throws SocketException { + if( timeout <= 0 ) + this.timeout = Channel.WAIT_FOREVER_TIMEOUT; + else + this.timeout = timeout; + } + + public synchronized int getSoTimeout() throws IOException { + if( timeout == Channel.WAIT_FOREVER_TIMEOUT ) + return 0; + return (int) timeout; + } + + public Socket accept() throws IOException { + Channel channel = channelServer.accept(timeout); + if( channel==null ) + throw new InterruptedIOException(); + + SyncChannel syncChannel = AsyncToSyncChannel.adapt(channel); + syncChannel.start(); + return new SyncChannelToSocket(syncChannel); + + } + + public void bind(SocketAddress endpoint, int backlog) throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + throw new SocketException("Already bound"); + } + + public void bind(SocketAddress endpoint) throws IOException { + if (isClosed()) + throw new SocketException("Socket is closed"); + throw new SocketException("Already bound"); + } + + public void close() throws IOException { + if (!isClosed()) { + channelServer.dispose(); + } + } + + public ServerSocketChannel getChannel() { + return null; + } + + public InetAddress getInetAddress() { + return inetAddress; + } + public int getLocalPort() { + return localPort; + } + public SocketAddress getLocalSocketAddress() { + return localSocketAddress; + } + public synchronized int getReceiveBufferSize() throws SocketException { + return receiveBufferSize; + } + + public boolean getReuseAddress() throws SocketException { + return reuseAddress; + } + + public boolean isBound() { + return true; + } + + public boolean isClosed() { + return closed; + } + + public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { + } + + public synchronized void setReceiveBufferSize(int size) throws SocketException { + this.receiveBufferSize = size; + } + + public void setReuseAddress(boolean on) throws SocketException { + reuseAddress = on; + } +} diff --git a/activeio/src/java/org/activeio/adapter/SyncChannelToInputStream.java b/activeio/src/java/org/activeio/adapter/SyncChannelToInputStream.java new file mode 100644 index 0000000000..09230545c4 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/SyncChannelToInputStream.java @@ -0,0 +1,114 @@ +/** +* +* Copyright 2004 Hiram Chirino +* +* Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.io.InputStream; + +import org.activeio.Channel; +import org.activeio.Packet; +import org.activeio.SyncChannel; +import org.activeio.packet.EOSPacket; + +/** + * Provides an InputStream for a given SynchChannel. + * + * @version $Revision$ + */ +public class SyncChannelToInputStream extends InputStream { + + private final SyncChannel channel; + private Packet lastPacket; + private boolean closed; + private long timeout = Channel.WAIT_FOREVER_TIMEOUT; + + /** + * @param channel + */ + public SyncChannelToInputStream(final SyncChannel channel) { + this.channel = channel; + } + + /** + * @see java.io.InputStream#read() + */ + public int read() throws IOException { + while( true ) { + if( lastPacket==null ) { + try { + lastPacket = channel.read(timeout); + } catch (IOException e) { + throw (IOException)new IOException("Channel failed: "+e.getMessage()).initCause(e); + } + } + if( lastPacket.hasRemaining() ) { + return lastPacket.read(); + } + } + } + + /** + * @see java.io.InputStream#read(byte[], int, int) + */ + public int read(byte[] b, int off, int len) throws IOException { + while( true ) { + if( lastPacket==null || !lastPacket.hasRemaining() ) { + try { + lastPacket = channel.read(timeout); + } catch (IOException e) { + throw (IOException)new IOException("Channel failed: "+e.getMessage()).initCause(e); + } + } + if( lastPacket==EOSPacket.EOS_PACKET ) { + return -1; + } + if( lastPacket!=null && lastPacket.hasRemaining() ) { + return lastPacket.read(b, off, len); + } + } + } + + /** + * @see java.io.InputStream#close() + */ + public void close() throws IOException { + closed=true; + super.close(); + } + + public boolean isClosed() { + return closed; + } + + /** + * @param timeout + */ + public void setTimeout(long timeout) { + if( timeout <= 0 ) + timeout = Channel.WAIT_FOREVER_TIMEOUT; + this.timeout = timeout; + } + + /** + * @return + */ + public long getTimeout() { + if( timeout == Channel.WAIT_FOREVER_TIMEOUT ) + return 0; + return timeout; + } +} diff --git a/activeio/src/java/org/activeio/adapter/SyncChannelToOutputStream.java b/activeio/src/java/org/activeio/adapter/SyncChannelToOutputStream.java new file mode 100644 index 0000000000..d7297274f9 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/SyncChannelToOutputStream.java @@ -0,0 +1,72 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.io.OutputStream; + +import org.activeio.SyncChannel; +import org.activeio.packet.ByteArrayPacket; +import org.activeio.packet.BytePacket; + +/** + */ +public class SyncChannelToOutputStream extends OutputStream { + + private final SyncChannel channel; + private boolean closed; + + /** + * @param channel + */ + public SyncChannelToOutputStream(SyncChannel channel) { + this.channel = channel; + } + + /** + * @see java.io.OutputStream#write(int) + */ + public void write(int b) throws IOException { + channel.write(new BytePacket((byte) b)); + } + + /** + * @see java.io.OutputStream#write(byte[], int, int) + */ + public void write(byte[] b, int off, int len) throws IOException { + channel.write(new ByteArrayPacket(b, off, len)); + } + + /** + * @see java.io.OutputStream#flush() + */ + public void flush() throws IOException { + channel.flush(); + } + + /** + * @see java.io.InputStream#close() + */ + public void close() throws IOException { + closed=true; + super.close(); + } + + public boolean isClosed() { + return closed; + } +} diff --git a/activeio/src/java/org/activeio/adapter/SyncChannelToSocket.java b/activeio/src/java/org/activeio/adapter/SyncChannelToSocket.java new file mode 100644 index 0000000000..ce8b21b2a1 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/SyncChannelToSocket.java @@ -0,0 +1,221 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.channels.SocketChannel; + +import org.activeio.Packet; +import org.activeio.SyncChannel; +import org.activeio.net.SocketMetadata; +import org.activeio.packet.ByteArrayPacket; + +/** + * Provides a {@see java.net.Socket} interface to a {@see org.activeio.SynchChannel}. + * + * If the {@see org.activeio.SynchChannel} being adapted can not be + * {@see org.activeio.Channel#narrow(Class)}ed to a {@see org.activeio.net.SocketMetadata} + * then all methods accessing socket metadata will throw a {@see java.net.SocketException}. + * + */ +public class SyncChannelToSocket extends Socket { + + private final SyncChannel channel; + private final SyncChannelToInputStream inputStream; + private final SyncChannelToOutputStream outputStream; + private final SocketMetadata socketMetadata; + private final Packet urgentPackget = new ByteArrayPacket(new byte[1]); + boolean closed; + + public SyncChannelToSocket(SyncChannel channel) { + this(channel, (SocketMetadata)channel.getAdapter(SocketMetadata.class)); + } + + public SyncChannelToSocket(SyncChannel channel, SocketMetadata socketMetadata) { + this.channel = channel; + this.socketMetadata = socketMetadata; + this.inputStream = new SyncChannelToInputStream(channel); + this.outputStream = new SyncChannelToOutputStream(channel); + } + + public boolean isConnected() { + return true; + } + + public boolean isBound() { + return true; + } + + public boolean isClosed() { + return closed; + } + + public void bind(SocketAddress bindpoint) throws IOException { + throw new IOException("Not supported"); + } + + public synchronized void close() throws IOException { + if( closed ) + return; + closed = true; + inputStream.close(); + outputStream.close(); + channel.dispose(); + } + + public void connect(SocketAddress endpoint) throws IOException { + throw new IOException("Not supported"); + } + + public void connect(SocketAddress endpoint, int timeout) throws IOException { + throw new IOException("Not supported"); + } + + public SocketChannel getChannel() { + return null; + } + + public InputStream getInputStream() throws IOException { + return inputStream; + } + + public OutputStream getOutputStream() throws IOException { + return outputStream; + } + + public boolean isInputShutdown() { + return inputStream.isClosed(); + } + + public boolean isOutputShutdown() { + return outputStream.isClosed(); + } + + public void sendUrgentData(int data) throws IOException { + urgentPackget.clear(); + urgentPackget.write(data); + urgentPackget.flip(); + channel.write(urgentPackget); + } + + public int getSoTimeout() throws SocketException { + return (int) inputStream.getTimeout(); + } + + public synchronized void setSoTimeout(int timeout) throws SocketException { + inputStream.setTimeout(timeout); + } + + public void shutdownOutput() throws IOException { + outputStream.close(); + } + + public void shutdownInput() throws IOException { + inputStream.close(); + } + + protected SocketMetadata getSocketMetadata() throws SocketException { + if( socketMetadata == null ) + throw new SocketException("No socket metadata available."); + return socketMetadata; + } + + public InetAddress getInetAddress() { + if( socketMetadata ==null ) + return null; + return socketMetadata.getInetAddress(); + } + public boolean getKeepAlive() throws SocketException { + return getSocketMetadata().getKeepAlive(); + } + public InetAddress getLocalAddress() { + if( socketMetadata ==null ) + return null; + return socketMetadata.getLocalAddress(); + } + public int getLocalPort() { + if( socketMetadata ==null ) + return -1; + return socketMetadata.getLocalPort(); + } + public SocketAddress getLocalSocketAddress() { + if( socketMetadata ==null ) + return null; + return socketMetadata.getLocalSocketAddress(); + } + public boolean getOOBInline() throws SocketException { + return getSocketMetadata().getOOBInline(); + } + public int getPort() { + if( socketMetadata ==null ) + return -1; + return socketMetadata.getPort(); + } + public int getReceiveBufferSize() throws SocketException { + return getSocketMetadata().getReceiveBufferSize(); + } + public SocketAddress getRemoteSocketAddress() { + if( socketMetadata ==null ) + return null; + return socketMetadata.getRemoteSocketAddress(); + } + public boolean getReuseAddress() throws SocketException { + return getSocketMetadata().getReuseAddress(); + } + public int getSendBufferSize() throws SocketException { + return getSocketMetadata().getSendBufferSize(); + } + public int getSoLinger() throws SocketException { + return getSocketMetadata().getSoLinger(); + } + public boolean getTcpNoDelay() throws SocketException { + return getSocketMetadata().getTcpNoDelay(); + } + public int getTrafficClass() throws SocketException { + return getSocketMetadata().getTrafficClass(); + } + public void setKeepAlive(boolean on) throws SocketException { + getSocketMetadata().setKeepAlive(on); + } + public void setOOBInline(boolean on) throws SocketException { + getSocketMetadata().setOOBInline(on); + } + public void setReceiveBufferSize(int size) throws SocketException { + getSocketMetadata().setReceiveBufferSize(size); + } + public void setReuseAddress(boolean on) throws SocketException { + getSocketMetadata().setReuseAddress(on); + } + public void setSendBufferSize(int size) throws SocketException { + getSocketMetadata().setSendBufferSize(size); + } + public void setSoLinger(boolean on, int linger) throws SocketException { + getSocketMetadata().setSoLinger(on, linger); + } + public void setTcpNoDelay(boolean on) throws SocketException { + getSocketMetadata().setTcpNoDelay(on); + } + public void setTrafficClass(int tc) throws SocketException { + getSocketMetadata().setTrafficClass(tc); + } +} diff --git a/activeio/src/java/org/activeio/adapter/SyncToAsyncChannel.java b/activeio/src/java/org/activeio/adapter/SyncToAsyncChannel.java new file mode 100644 index 0000000000..e7d06d89c7 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/SyncToAsyncChannel.java @@ -0,0 +1,223 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activeio.adapter; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelListener; +import org.activeio.Channel; +import org.activeio.ChannelFactory; +import org.activeio.Packet; +import org.activeio.Service; +import org.activeio.SyncChannel; +import org.activeio.packet.EOSPacket; + +import java.io.IOException; + +/** + * Adapts a {@see org.activeio.SynchChannel} so that it provides an + * {@see org.activeio.AsyncChannel} interface. When this channel + * is started, a background thread is used to poll the {@see org.activeio.SynchChannel} + * for packets comming up the channel which are then delivered to the + * {@see org.activeio.ChannelConsumer}. + * + * @version $Revision$ + */ +public class SyncToAsyncChannel implements AsyncChannel, Runnable { + + private final AtomicBoolean running = new AtomicBoolean(false); + private final SyncChannel syncChannel; + private final Executor executor; + private AsyncChannelListener channelListener; + private CountDownLatch doneCountDownLatch; + + + static public AsyncChannel adapt(Channel channel) { + return adapt(channel, ChannelFactory.DEFAULT_EXECUTOR); + } + + static public AsyncChannel adapt(Channel channel, Executor executor) { + + // It might not need adapting + if( channel instanceof AsyncChannel ) { + return (AsyncChannel) channel; + } + + // Can we just just undo the adaptor + if( channel.getClass() == SyncToAsyncChannel.class ) { + return ((AsyncToSyncChannel)channel).getAsyncChannel(); + } + + return new SyncToAsyncChannel((SyncChannel) channel, executor); + + } + + + /** + * @deprecated {@see #adapt(SynchChannel)} + */ + public SyncToAsyncChannel(SyncChannel syncChannel) { + this(syncChannel, ChannelFactory.DEFAULT_EXECUTOR); + } + + /** + * @deprecated {@see #adapt(SynchChannel, Executor)} + */ + public SyncToAsyncChannel(SyncChannel syncChannel, Executor executor) { + this.syncChannel = syncChannel; + this.executor = executor; + } + + synchronized public void start() throws IOException { + if (running.compareAndSet(false, true)) { + + if (channelListener == null) + throw new IllegalStateException("UpPacketListener must be set before object can be started."); + + syncChannel.start(); + + doneCountDownLatch = new CountDownLatch(1); + executor.execute(this); + } + } + + synchronized public void stop(long timeout) throws IOException { + if (running.compareAndSet(true, false)) { + try { + if( timeout == NO_WAIT_TIMEOUT ) { + syncChannel.stop(NO_WAIT_TIMEOUT); + } else if( timeout == WAIT_FOREVER_TIMEOUT ) { + doneCountDownLatch.await(); + syncChannel.stop(WAIT_FOREVER_TIMEOUT); + } else { + + long start = System.currentTimeMillis(); + if( doneCountDownLatch.await(timeout, TimeUnit.MILLISECONDS) ) { + timeout -= (System.currentTimeMillis() - start); + } else { + timeout=0; + } + + if( timeout <= 0 ) { + syncChannel.stop(NO_WAIT_TIMEOUT); + } else { + syncChannel.stop(timeout); + } + } + } catch (IOException e) { + throw e; + } catch (Throwable e) { + throw (IOException)new IOException("stop failed: " + e.getMessage()).initCause(e); + } + } + } + + /** + * reads packets from a Socket + */ + public void run() { + + // Change the thread name. + String oldName = Thread.currentThread().getName(); + Thread.currentThread().setName( syncChannel.toString() ); + try { + while (running.get()) { + try { + Packet packet = syncChannel.read(500); + if( packet==null ) + continue; + + if( packet == EOSPacket.EOS_PACKET ) { + channelListener.onPacket(packet); + return; + } + + if( packet.hasRemaining() ) { + channelListener.onPacket(packet); + } + + } catch (IOException e) { + channelListener.onPacketError(e); + } catch (Throwable e) { + channelListener.onPacketError((IOException)new IOException("Unexpected Error: "+e).initCause(e)); + } + } + } finally { + if( doneCountDownLatch!=null ) + doneCountDownLatch.countDown(); + Thread.currentThread().setName(oldName); + } + } + + /** + * @see org.activeio.AsyncChannel#setAsyncChannelListener(org.activeio.UpPacketListener) + */ + public void setAsyncChannelListener(AsyncChannelListener channelListener) { + if (running.get()) + throw new IllegalStateException("Cannot change the UpPacketListener while the object is running."); + this.channelListener = channelListener; + } + + /** + * @see org.activeio.Channel#write(org.activeio.Packet) + */ + public void write(org.activeio.Packet packet) throws IOException { + syncChannel.write(packet); + } + + /** + * @see org.activeio.Channel#flush() + */ + public void flush() throws IOException { + syncChannel.flush(); + } + + /** + * @see org.activeio.Disposable#dispose() + */ + public void dispose() { + try { + stop(Service.NO_WAIT_TIMEOUT); + } catch ( IOException ignore) { + } + syncChannel.dispose(); + } + + public AsyncChannelListener getAsyncChannelListener() { + return channelListener; + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return syncChannel.getAdapter(target); + } + + public SyncChannel getSynchChannel() { + return syncChannel; + } + + public String toString() { + return syncChannel.toString(); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/adapter/SyncToAsyncChannelFactory.java b/activeio/src/java/org/activeio/adapter/SyncToAsyncChannelFactory.java new file mode 100644 index 0000000000..9a824fc327 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/SyncToAsyncChannelFactory.java @@ -0,0 +1,84 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.adapter; + +import java.io.IOException; +import java.net.URI; + +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelFactory; +import org.activeio.AsyncChannelServer; +import org.activeio.ChannelFactory; +import org.activeio.SyncChannelFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; + +/** + * @version $Revision$ + */ +public class SyncToAsyncChannelFactory implements AsyncChannelFactory { + + private final SyncChannelFactory syncChannelFactory; + private final Executor executor; + + static public AsyncChannelFactory adapt(SyncChannelFactory channelFactory) { + return adapt(channelFactory, ChannelFactory.DEFAULT_EXECUTOR); + } + + static public AsyncChannelFactory adapt(SyncChannelFactory channelFactory, Executor executor ) { + + // It might not need adapting + if( channelFactory instanceof AsyncChannelFactory ) { + return (AsyncChannelFactory) channelFactory; + } + + // Can we just just undo the adaptor + if( channelFactory.getClass() == AsyncToSyncChannelFactory.class ) { + return ((AsyncToSyncChannelFactory)channelFactory).getAsyncChannelFactory(); + } + + return new SyncToAsyncChannelFactory((SyncChannelFactory)channelFactory, executor); + } + + /** + * @deprecated {@see #adapt(SyncChannelFactory)} + */ + public SyncToAsyncChannelFactory(final SyncChannelFactory next) { + this(next, ChannelFactory.DEFAULT_EXECUTOR); + } + + /** + * @deprecated {@see #adapt(SyncChannelFactory, Executor)} + */ + public SyncToAsyncChannelFactory(final SyncChannelFactory next, Executor executor) { + this.syncChannelFactory = next; + this.executor = executor; + } + + public AsyncChannel openAsyncChannel(URI location) throws IOException { + return SyncToAsyncChannel.adapt(syncChannelFactory.openSyncChannel(location),executor); + } + + public AsyncChannelServer bindAsyncChannel(URI location) throws IOException { + return new SyncToAsyncChannelServer(syncChannelFactory.bindSyncChannel(location),executor); + } + + public SyncChannelFactory getSyncChannelFactory() { + return syncChannelFactory; + } +} diff --git a/activeio/src/java/org/activeio/adapter/SyncToAsyncChannelServer.java b/activeio/src/java/org/activeio/adapter/SyncToAsyncChannelServer.java new file mode 100644 index 0000000000..509fea890f --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/SyncToAsyncChannelServer.java @@ -0,0 +1,199 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activeio.adapter; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +import org.activeio.AcceptListener; +import org.activeio.AsyncChannelServer; +import org.activeio.Channel; +import org.activeio.ChannelFactory; +import org.activeio.ChannelServer; +import org.activeio.Disposable; +import org.activeio.Service; +import org.activeio.SyncChannelServer; + +import java.io.IOException; +import java.net.URI; + +/** + * Adapts a {@see org.activeio,SynchChannelServer} so that it provides an + * {@see org.activeio.AsyncChannelServer} interface. When this channel + * is started, a background thread is used to poll the (@see org.activeio.SynchChannelServer} + * for accepted channel connections which are then delivered to the {@see org.activeio.AcceptConsumer}. + * + * @version $Revision$ + */ +final public class SyncToAsyncChannelServer implements AsyncChannelServer, Runnable { + + private final SyncChannelServer syncChannelServer; + private final AtomicBoolean running = new AtomicBoolean(false); + private final Executor executor; + private AcceptListener acceptListener; + private CountDownLatch doneCountDownLatch; + + + static public AsyncChannelServer adapt(ChannelServer channel) { + return adapt(channel, ChannelFactory.DEFAULT_EXECUTOR); + } + + static public AsyncChannelServer adapt(ChannelServer channel, Executor executor) { + + // It might not need adapting + if( channel instanceof AsyncChannelServer ) { + return (AsyncChannelServer) channel; + } + + // Can we just just undo the adaptor + if( channel.getClass() == SyncToAsyncChannel.class ) { + return ((AsyncToSyncChannelServer)channel).getAsyncChannelServer(); + } + + return new SyncToAsyncChannelServer((SyncChannelServer)channel, executor); + } + + public SyncToAsyncChannelServer(SyncChannelServer syncServer) { + this(syncServer, ChannelFactory.DEFAULT_EXECUTOR); + } + + public SyncToAsyncChannelServer(SyncChannelServer syncServer, Executor executor) { + this.syncChannelServer = syncServer; + this.executor=executor; + } + + synchronized public void start() throws IOException { + if (running.compareAndSet(false, true)) { + + if( acceptListener == null ) + throw new IllegalStateException("AcceptListener must be set before object can be started."); + + syncChannelServer.start(); + + doneCountDownLatch = new CountDownLatch(1); + executor.execute(this); + } + } + + synchronized public void stop(long timeout) throws IOException { + if (running.compareAndSet(true, false)) { + try { + + if( timeout == NO_WAIT_TIMEOUT ) { + syncChannelServer.stop(NO_WAIT_TIMEOUT); + } else if( timeout == WAIT_FOREVER_TIMEOUT ) { + doneCountDownLatch.await(); + syncChannelServer.stop(WAIT_FOREVER_TIMEOUT); + } else { + + long start = System.currentTimeMillis(); + if( doneCountDownLatch.await(timeout, TimeUnit.MILLISECONDS) ) { + timeout -= (System.currentTimeMillis() - start); + } else { + timeout=0; + } + + if( timeout <= 0 ) { + syncChannelServer.stop(NO_WAIT_TIMEOUT); + } else { + syncChannelServer.stop(timeout); + } + } + + } catch (IOException e) { + throw e; + } catch (Throwable e) { + throw (IOException)new IOException("stop failed: " + e.getMessage()).initCause(e); + } + } + } + + public void run() { + // Change the thread name. + String oldName = Thread.currentThread().getName(); + Thread.currentThread().setName( syncChannelServer.toString() ); + try { + while (running.get()) { + try { + Channel channel = syncChannelServer.accept(500); + if( channel == null ) + continue; + acceptListener.onAccept(channel); + } catch (IOException e) { + if( running.get() ) + acceptListener.onAcceptError(e); + } catch (Throwable e) { + if( running.get() ) + acceptListener.onAcceptError((IOException)new IOException("Unexpected Error: "+e).initCause(e)); + } + } + } finally { + if( doneCountDownLatch!=null ) + doneCountDownLatch.countDown(); + Thread.currentThread().setName(oldName); + } + } + + /** + * @see org.activeio.AsyncChannelServer#setAcceptListener(org.activeio.AcceptListener) + */ + public void setAcceptListener(AcceptListener acceptListener) { + if(running.get()) + throw new IllegalStateException("Cannot change the AcceptListener while the object is running."); + this.acceptListener = acceptListener; + } + + /** + * @see org.activeio.Disposable#dispose() + */ + public void dispose() { + try { + stop(Service.NO_WAIT_TIMEOUT); + } catch ( IOException ignore) { + } + if( syncChannelServer instanceof Disposable ) { + ((Disposable)syncChannelServer).dispose(); + } + } + + public URI getBindURI() { + return syncChannelServer.getBindURI(); + } + + public URI getConnectURI() { + return syncChannelServer.getConnectURI(); + } + + public SyncChannelServer getSynchChannelServer() { + return syncChannelServer; + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return syncChannelServer.getAdapter(target); + } + + public String toString() { + return syncChannelServer.toString(); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/adapter/SynchToAsynchChannelAdapter.java b/activeio/src/java/org/activeio/adapter/SynchToAsynchChannelAdapter.java new file mode 100644 index 0000000000..5afb2b1f25 --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/SynchToAsynchChannelAdapter.java @@ -0,0 +1,54 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio.adapter; + +import org.activeio.SyncChannel; +import org.activeio.AsyncChannel; +import org.activeio.Channel; +import edu.emory.mathcs.backport.java.util.concurrent.Executor; + +/** + * @deprecated Use AsyncChannelServer instead. This class will be removed very soon. + */ +public class SynchToAsynchChannelAdapter extends SyncToAsyncChannel{ + public SynchToAsynchChannelAdapter(SyncChannel syncChannel) { + super(syncChannel); + } + + public SynchToAsynchChannelAdapter(SyncChannel syncChannel, Executor executor) { + super(syncChannel, executor); + } + static public AsyncChannel adapt(Channel channel, Executor executor) { + + // It might not need adapting + if( channel instanceof AsyncChannel ) { + return (AsyncChannel) channel; + } + + // Can we just just undo the adaptor + if( channel.getClass() == SyncToAsyncChannel.class ) { + return ((AsyncToSyncChannel)channel).getAsyncChannel(); + } + // Can we just just undo the adaptor + if( channel.getClass() == org.activeio.adapter.SynchToAsynchChannelAdapter.class ) { + return ((AsyncToSyncChannel)channel).getAsyncChannel(); + } + + return new SyncToAsyncChannel((SyncChannel) channel, executor); + + } +} diff --git a/activeio/src/java/org/activeio/adapter/package.html b/activeio/src/java/org/activeio/adapter/package.html new file mode 100644 index 0000000000..eb0d0e5f6a --- /dev/null +++ b/activeio/src/java/org/activeio/adapter/package.html @@ -0,0 +1,12 @@ + + + + + +

+The Adapter package provides classes that make it easy ot bridge between the the +SynchChannel, AsyncChannel, InputStream, OutputStream, Socket, and ServerSocket domains. +

+ + + diff --git a/activeio/src/java/org/activeio/command/AsyncChannelToAsyncCommandChannel.java b/activeio/src/java/org/activeio/command/AsyncChannelToAsyncCommandChannel.java new file mode 100644 index 0000000000..7101d1af58 --- /dev/null +++ b/activeio/src/java/org/activeio/command/AsyncChannelToAsyncCommandChannel.java @@ -0,0 +1,82 @@ +/** + * + * Copyright 2005 Protique Ltd + * + * Licensed 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.activeio.command; + +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelListener; +import org.activeio.Packet; +import org.activeio.packet.EOSPacket; + +import java.io.EOFException; +import java.io.IOException; + +/** + * @version $Revision: 1.1 $ + */ +public class AsyncChannelToAsyncCommandChannel implements AsyncCommandChannel { + private AsyncChannel channel; + private WireFormat wireFormat; + + public AsyncChannelToAsyncCommandChannel(AsyncChannel channel, WireFormat wireFormat) { + this.channel = channel; + this.wireFormat = wireFormat; + } + + public void writeCommand(Object command) throws IOException { + channel.write(wireFormat.marshal(command)); + channel.flush(); + } + + public Object getAdapter(Class target) { + return channel.getAdapter(target); + } + + public void dispose() { + channel.dispose(); + } + + public void start() throws IOException { + channel.start(); + } + + public void stop(long timeout) throws IOException { + channel.stop(timeout); + } + + public void setCommandListener(final CommandListener listener) { + channel.setAsyncChannelListener(new AsyncChannelListener() { + public void onPacket(Packet packet) { + if( packet == EOSPacket.EOS_PACKET ) { + listener.onError(new EOFException("Peer disconnected.")); + return; + } + try { + Object command = wireFormat.unmarshal(packet); + listener.onCommand(command); + } + catch (IOException e) { + listener.onError(e); + } + } + + public void onPacketError(IOException error) { + listener.onError(error); + } + }); + } +} diff --git a/activeio/src/java/org/activeio/command/AsyncCommandChannel.java b/activeio/src/java/org/activeio/command/AsyncCommandChannel.java new file mode 100644 index 0000000000..9416b64f4e --- /dev/null +++ b/activeio/src/java/org/activeio/command/AsyncCommandChannel.java @@ -0,0 +1,46 @@ +/** + * + * Copyright 2005 James Strachan + * + * Licensed 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.activeio.command; + +import org.activeio.Channel; + +import java.io.IOException; + +/** + * Allows command objects to be written into a channel + * + * @version $Revision: 1.1 $ + */ +public interface AsyncCommandChannel extends Channel { + + /** + * Sends a command down the channel towards the media, using a WireFormat + * to decide how to marshal the command onto the media. + * + * @param command + * @throws java.io.IOException + */ + void writeCommand(Object command) throws IOException; + + /** + * Allows a listener to be added for commands + * + * @param listener + */ + void setCommandListener(CommandListener listener); +} diff --git a/activeio/src/java/org/activeio/command/ClassLoading.java b/activeio/src/java/org/activeio/command/ClassLoading.java new file mode 100644 index 0000000000..61bc85e0b5 --- /dev/null +++ b/activeio/src/java/org/activeio/command/ClassLoading.java @@ -0,0 +1,239 @@ +/** + * + * Copyright 2004 The Apache Software Foundation + * + * Licensed 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.activeio.command; + +import java.lang.reflect.Array; +import java.util.HashMap; +import java.util.Map; + +/** + * Utilities for loading classes. + * + * @version $Rev: 109957 $ $Date: 2005/03/11 21:14:53 $ + */ +public class ClassLoading { + + /** + * Load a class for the given name.

+ *

+ * Handles loading primitive types as well as VM class and array syntax. + * + * @param className + * The name of the Class to be loaded. + * @param classLoader + * The class loader to load the Class object from. + * @return The Class object for the given name. + * @throws ClassNotFoundException + * Failed to load Class object. + */ + public static Class loadClass(final String className, final ClassLoader classLoader) throws ClassNotFoundException { + if (className == null) { + throw new IllegalArgumentException("className is null"); + } + + // First just try to load + try { + return load(className, classLoader); + } catch (ClassNotFoundException ignore) { + // handle special cases below + } + + Class type = null; + + // Check if it is a primitive type + type = getPrimitiveType(className); + if (type != null) + return type; + + // Check if it is a vm primitive + type = getVMPrimitiveType(className); + if (type != null) + return type; + + // Handle VM class syntax (Lclassname;) + if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { + String name = className.substring(1, className.length() - 1); + return load(name, classLoader); + } + + // Handle VM array syntax ([type) + if (className.charAt(0) == '[') { + int arrayDimension = className.lastIndexOf('[') + 1; + String componentClassName = className.substring(arrayDimension, className.length()); + type = loadClass(componentClassName, classLoader); + + int dim[] = new int[arrayDimension]; + java.util.Arrays.fill(dim, 0); + return Array.newInstance(type, dim).getClass(); + } + + // Handle user friendly type[] syntax + if (className.endsWith("[]")) { + // get the base component class name and the arrayDimensions + int arrayDimension = 0; + String componentClassName = className; + while (componentClassName.endsWith("[]")) { + componentClassName = componentClassName.substring(0, componentClassName.length() - 2); + arrayDimension++; + } + + // load the base type + type = loadClass(componentClassName, classLoader); + + // return the array type + int[] dim = new int[arrayDimension]; + java.util.Arrays.fill(dim, 0); + return Array.newInstance(type, dim).getClass(); + } + + // Else we can not load (give up) + throw new ClassNotFoundException(className); + } + + private static Class load(final String className, final ClassLoader classLoader) throws ClassNotFoundException { + if (classLoader == null) + return Class.forName(className); + else + return classLoader.loadClass(className); + } + + public static String getClassName(Class clazz) { + StringBuffer rc = new StringBuffer(); + while (clazz.isArray()) { + rc.append('['); + clazz = clazz.getComponentType(); + } + if (!clazz.isPrimitive()) { + rc.append('L'); + rc.append(clazz.getName()); + rc.append(';'); + } else { + rc.append(VM_PRIMITIVES_REVERSE.get(clazz)); + } + return rc.toString(); + } + + /** + * Primitive type name -> class map. + */ + private static final Map PRIMITIVES = new HashMap(); + + /** Setup the primitives map. */ + static { + PRIMITIVES.put("boolean", Boolean.TYPE); + PRIMITIVES.put("byte", Byte.TYPE); + PRIMITIVES.put("char", Character.TYPE); + PRIMITIVES.put("short", Short.TYPE); + PRIMITIVES.put("int", Integer.TYPE); + PRIMITIVES.put("long", Long.TYPE); + PRIMITIVES.put("float", Float.TYPE); + PRIMITIVES.put("double", Double.TYPE); + PRIMITIVES.put("void", Void.TYPE); + } + + /** + * Get the primitive type for the given primitive name. + * + * @param name + * Primitive type name (boolean, byte, int, ...) + * @return Primitive type or null. + */ + private static Class getPrimitiveType(final String name) { + return (Class) PRIMITIVES.get(name); + } + + /** + * VM primitive type name -> primitive type + */ + private static final HashMap VM_PRIMITIVES = new HashMap(); + + /** Setup the vm primitives map. */ + static { + VM_PRIMITIVES.put("B", byte.class); + VM_PRIMITIVES.put("C", char.class); + VM_PRIMITIVES.put("D", double.class); + VM_PRIMITIVES.put("F", float.class); + VM_PRIMITIVES.put("I", int.class); + VM_PRIMITIVES.put("J", long.class); + VM_PRIMITIVES.put("S", short.class); + VM_PRIMITIVES.put("Z", boolean.class); + VM_PRIMITIVES.put("V", void.class); + } + + /** + * VM primitive type primitive type -> name + */ + private static final HashMap VM_PRIMITIVES_REVERSE = new HashMap(); + + /** Setup the vm primitives reverse map. */ + static { + VM_PRIMITIVES_REVERSE.put(byte.class, "B"); + VM_PRIMITIVES_REVERSE.put(char.class, "C"); + VM_PRIMITIVES_REVERSE.put(double.class, "D"); + VM_PRIMITIVES_REVERSE.put(float.class, "F"); + VM_PRIMITIVES_REVERSE.put(int.class, "I"); + VM_PRIMITIVES_REVERSE.put(long.class, "J"); + VM_PRIMITIVES_REVERSE.put(short.class, "S"); + VM_PRIMITIVES_REVERSE.put(boolean.class, "Z"); + VM_PRIMITIVES_REVERSE.put(void.class, "V"); + } + + /** + * Get the primitive type for the given VM primitive name.

+ *

+ * Mapping: + * + *

+     * 
+     *    B - byte
+     *    C - char
+     *    D - double
+     *    F - float
+     *    I - int
+     *    J - long
+     *    S - short
+     *    Z - boolean
+     *    V - void
+     *  
+     * 
+ * + * @param name + * VM primitive type name (B, C, J, ...) + * @return Primitive type or null. + */ + private static Class getVMPrimitiveType(final String name) { + return (Class) VM_PRIMITIVES.get(name); + } + + /** + * Map of primitive types to their wrapper classes + */ + private static final Map PRIMITIVE_WRAPPERS = new HashMap(); + + /** Setup the wrapper map. */ + static { + PRIMITIVE_WRAPPERS.put(Boolean.TYPE, Boolean.class); + PRIMITIVE_WRAPPERS.put(Byte.TYPE, Byte.class); + PRIMITIVE_WRAPPERS.put(Character.TYPE, Character.class); + PRIMITIVE_WRAPPERS.put(Double.TYPE, Double.class); + PRIMITIVE_WRAPPERS.put(Float.TYPE, Float.class); + PRIMITIVE_WRAPPERS.put(Integer.TYPE, Integer.class); + PRIMITIVE_WRAPPERS.put(Long.TYPE, Long.class); + PRIMITIVE_WRAPPERS.put(Short.TYPE, Short.class); + PRIMITIVE_WRAPPERS.put(Void.TYPE, Void.class); + } +} diff --git a/activeio/src/java/org/activeio/command/ClassLoadingAwareObjectInputStream.java b/activeio/src/java/org/activeio/command/ClassLoadingAwareObjectInputStream.java new file mode 100644 index 0000000000..f7f81f39aa --- /dev/null +++ b/activeio/src/java/org/activeio/command/ClassLoadingAwareObjectInputStream.java @@ -0,0 +1,68 @@ +/** + * + * Copyright 2005 James Strachan + * + * Licensed 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.activeio.command; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; +import java.lang.reflect.Proxy; + +/** + * An input stream which uses the {@link org.activeio.command.ClassLoading} helper class + * + * @version $Revision: 1.1 $ + */ +public class ClassLoadingAwareObjectInputStream extends ObjectInputStream { + + private static final ClassLoader myClassLoader = DefaultWireFormat.class.getClassLoader(); + + public ClassLoadingAwareObjectInputStream(InputStream in) throws IOException { + super(in); + } + + protected Class resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + return loadClass(classDesc.getName(), classLoader); + } + + protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + Class[] interfaceClasses = new Class[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + interfaceClasses[i] = loadClass(interfaces[i], classLoader); + } + + try { + return Proxy.getProxyClass(interfaceClasses[0].getClassLoader(), interfaceClasses); + } + catch (IllegalArgumentException e) { + throw new ClassNotFoundException(null, e); + } + } + + protected Class loadClass(String className, ClassLoader classLoader) throws ClassNotFoundException { + try { + return ClassLoading.loadClass(className, classLoader); + } + catch (ClassNotFoundException e) { + return ClassLoading.loadClass(className, myClassLoader); + } + } + +} diff --git a/activeio/src/java/org/activeio/command/CommandListener.java b/activeio/src/java/org/activeio/command/CommandListener.java new file mode 100644 index 0000000000..7a0f8aa4b0 --- /dev/null +++ b/activeio/src/java/org/activeio/command/CommandListener.java @@ -0,0 +1,39 @@ +/** + * + * Copyright 2005 Protique Ltd + * + * Licensed 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.activeio.command; + + +/** + * A listener of command objects + * + * @version $Revision: 1.1 $ + */ +public interface CommandListener { + + /** + * Called when a command is received + * + */ + public void onCommand(Object command); + + /** + * Called when an error occurs trying to + * read a new command. + */ + void onError(Exception e); +} diff --git a/activeio/src/java/org/activeio/command/DefaultWireFormat.java b/activeio/src/java/org/activeio/command/DefaultWireFormat.java new file mode 100644 index 0000000000..c6d93b2319 --- /dev/null +++ b/activeio/src/java/org/activeio/command/DefaultWireFormat.java @@ -0,0 +1,75 @@ +/** + * + * Copyright 2005 James Strachan + * + * Licensed 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.activeio.command; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; + +import org.activeio.ByteArrayOutputStream; +import org.activeio.Packet; +import org.activeio.adapter.PacketToInputStream; +import org.activeio.packet.ByteArrayPacket; + +/** + * A default implementation which uses serialization + * + * @version $Revision: 1.1 $ + */ +public class DefaultWireFormat implements WireFormat { + + public Packet marshal(Object command) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream ds = new DataOutputStream(baos); + marshal(command, ds); + ds.close(); + return new ByteArrayPacket(baos.toByteSequence()); + } + + public Object unmarshal(Packet packet) throws IOException { + return unmarshal(new DataInputStream(new PacketToInputStream(packet))); + } + + public void marshal(Object command, DataOutputStream ds) throws IOException { + ObjectOutputStream out = new ObjectOutputStream(ds); + out.writeObject(command); + out.flush(); + out.reset(); + } + + public Object unmarshal(DataInputStream ds) throws IOException { + try { + ClassLoadingAwareObjectInputStream in = new ClassLoadingAwareObjectInputStream(ds); + Object command; + command = in.readObject(); + in.close(); + return command; + } catch (ClassNotFoundException e) { + throw (IOException)new IOException("unmarshal failed: "+e).initCause(e); + } + } + + public void setVersion(int version) { + } + + public int getVersion() { + return 0; + } + +} diff --git a/activeio/src/java/org/activeio/command/WireFormat.java b/activeio/src/java/org/activeio/command/WireFormat.java new file mode 100644 index 0000000000..eb5f694491 --- /dev/null +++ b/activeio/src/java/org/activeio/command/WireFormat.java @@ -0,0 +1,64 @@ +/** + * + * Copyright 2005 James Strachan + * + * Licensed 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.activeio.command; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activeio.Packet; + +/** + * Provides a mechanism to marshal commands into and out of packets + * or into and out of streams, Channels and Datagrams. + * + * @version $Revision: 1.1 $ + */ +public interface WireFormat { + + /** + * Packet based marshaling + */ + Packet marshal(Object command) throws IOException; + + /** + * Packet based un-marshaling + */ + Object unmarshal(Packet packet) throws IOException; + + /** + * Stream based marshaling + */ + void marshal(Object command, DataOutputStream out) throws IOException; + + /** + * Packet based un-marshaling + */ + Object unmarshal(DataInputStream in) throws IOException; + + /** + * @param the version of the wire format + */ + public void setVersion(int version); + + /** + * @return the version of the wire format + */ + public int getVersion(); + +} diff --git a/activeio/src/java/org/activeio/command/WireFormatFactory.java b/activeio/src/java/org/activeio/command/WireFormatFactory.java new file mode 100644 index 0000000000..8a8b576c20 --- /dev/null +++ b/activeio/src/java/org/activeio/command/WireFormatFactory.java @@ -0,0 +1,5 @@ +package org.activeio.command; + +public interface WireFormatFactory { + WireFormat createWireFormat(); +} diff --git a/activeio/src/java/org/activeio/command/package.html b/activeio/src/java/org/activeio/command/package.html new file mode 100644 index 0000000000..d183d7829d --- /dev/null +++ b/activeio/src/java/org/activeio/command/package.html @@ -0,0 +1,13 @@ + + + + + +

+An API and helper classes for working with Command objects; which are application level objects +along with a WireFormat to reader/write directly with the media allowing higher layers to work directly +with command objects and WireFormat instances instead of turning things into Packets and byte arrays etc. +

+ + + diff --git a/activeio/src/java/org/activeio/filter/AsyncWriteAsyncChannel.java b/activeio/src/java/org/activeio/filter/AsyncWriteAsyncChannel.java new file mode 100644 index 0000000000..c858ff4f62 --- /dev/null +++ b/activeio/src/java/org/activeio/filter/AsyncWriteAsyncChannel.java @@ -0,0 +1,162 @@ +package org.activeio.filter; + +import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingDeque; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +import org.activeio.AsyncChannel; +import org.activeio.ChannelFactory; +import org.activeio.FilterAsyncChannel; +import org.activeio.Packet; + +import java.io.IOException; +import java.io.InterruptedIOException; + +public class AsyncWriteAsyncChannel extends FilterAsyncChannel { + + static public class ObjectDispatcherX implements Runnable { + + private final Executor executor; + private final BlockingQueue queue; + private final AtomicInteger size = new AtomicInteger(0); + private final AsyncWriteAsyncChannel objectListener; + private long pollDelay=10; + + public ObjectDispatcherX(AsyncWriteAsyncChannel objectListener) { + this(objectListener, 10); + } + + public ObjectDispatcherX(AsyncWriteAsyncChannel objectListener, int queueSize) { + this(objectListener, ChannelFactory.DEFAULT_EXECUTOR, new LinkedBlockingDeque(queueSize)); + } + + public ObjectDispatcherX(AsyncWriteAsyncChannel objectListener, Executor executor, BlockingQueue queue) { + this.objectListener = objectListener; + this.executor = executor; + this.queue=queue; + } + + public void add(Object o) throws InterruptedException { + int t = size.incrementAndGet(); + queue.put(o); + if( t==1 ) { + executor.execute(this); + } + } + + synchronized public void run() { + int t = size.get(); + while( t > 0 ) { + int count=0; + try { + Object o; + while( (o=queue.poll(pollDelay, TimeUnit.MILLISECONDS))!=null ) { + count++; + objectListener.onObject(o); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } finally { + t = size.addAndGet(-count); + } + } + } + + } + static public class ObjectDispatcher { + + private final ThreadPoolExecutor executor; + private final AsyncWriteAsyncChannel objectListener; + + public ObjectDispatcher(AsyncWriteAsyncChannel objectListener) { + this(objectListener, 10); + } + + public ObjectDispatcher(AsyncWriteAsyncChannel objectListener, int queueSize) { + this.objectListener = objectListener; + executor = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingDeque(queueSize)); + // executor.waitWhenBlocked(); + } + + public void add(final Object o) throws InterruptedException { + executor.execute(new Runnable(){ + public void run() { + objectListener.onObject(o); + } + }); + } + } + + private final ObjectDispatcher dispatcher; + private static final Object FLUSH_COMMAND = new Object(); + + public AsyncWriteAsyncChannel(AsyncChannel next) { + this(next, 10); + } + + public AsyncWriteAsyncChannel(AsyncChannel next, int queueSize) { + super(next); + this.dispatcher = new ObjectDispatcher(this, queueSize); + } + + public void onObject(Object o) { + try { + if( o == FLUSH_COMMAND ) { + next.flush(); + return; + } + if( o.getClass() == CountDownLatch.class ) { + next.flush(); + ((CountDownLatch)o).countDown(); + return; + } + next.write((Packet)o); + } catch (IOException e) { + channelListener.onPacketError(e); + } + } + + public void write(Packet packet) throws IOException { + try { + dispatcher.add(packet); + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + + public void flush() throws IOException { + flush(NO_WAIT_TIMEOUT); + } + + public void stop(long timeout) throws IOException { + flush(WAIT_FOREVER_TIMEOUT); + } + + + /** + * @param timeout + * @throws InterruptedIOException + */ + private void flush(long timeout) throws InterruptedIOException { + try { + if( timeout == NO_WAIT_TIMEOUT ) { + dispatcher.add(FLUSH_COMMAND); + } else if( timeout == WAIT_FOREVER_TIMEOUT ) { + CountDownLatch l = new CountDownLatch(1); + dispatcher.add(l); + l.await(); + } else { + CountDownLatch l = new CountDownLatch(1); + dispatcher.add(l); + l.await(timeout, TimeUnit.MILLISECONDS); + } + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } +} diff --git a/activeio/src/java/org/activeio/filter/CounterAsyncChannel.java b/activeio/src/java/org/activeio/filter/CounterAsyncChannel.java new file mode 100644 index 0000000000..6c23add409 --- /dev/null +++ b/activeio/src/java/org/activeio/filter/CounterAsyncChannel.java @@ -0,0 +1,73 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.filter; + +import java.io.IOException; + +import org.activeio.AsyncChannel; +import org.activeio.FilterAsyncChannel; +import org.activeio.Packet; + + +/** + * A CounterAsyncChannel is a simple {@see org.activeio.AsyncChannelFilter} + * that counts the number bytes that been sent down and up through the channel. + * + * The {@see org.activeio.counter.CounterAttribueEnum.COUNTER_INBOUND_COUNT} + * and {@see org.activeio.counter.CounterAttribueEnum.COUNTER_OUTBOUND_COUNT} + * attributes can be used to find query the channel to get the current inbound and outbound + * byte counts. + * + * @version $Revision$ + */ +final public class CounterAsyncChannel extends FilterAsyncChannel { + + long inBoundCounter = 0; + + long outBoundCounter = 0; + + /** + * @param next + */ + public CounterAsyncChannel(AsyncChannel next) { + super(next); + } + + /** + * @see org.activeio.FilterAsyncChannel#onPacket(org.activeio.Packet) + */ + public void onPacket(Packet packet) { + inBoundCounter += packet.remaining(); + super.onPacket(packet); + } + + /** + * @see org.activeio.FilterAsyncChannel#write(org.activeio.Packet) + */ + public void write(Packet packet) throws IOException { + outBoundCounter += packet.position(); + super.write(packet); + } + + public long getInBoundCounter() { + return inBoundCounter; + } + + public long getOutBoundCounter() { + return outBoundCounter; + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/filter/PacketAggregatingAsyncChannel.java b/activeio/src/java/org/activeio/filter/PacketAggregatingAsyncChannel.java new file mode 100644 index 0000000000..12a96bd0e4 --- /dev/null +++ b/activeio/src/java/org/activeio/filter/PacketAggregatingAsyncChannel.java @@ -0,0 +1,64 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.filter; + +import java.io.IOException; + +import org.activeio.AsyncChannel; +import org.activeio.FilterAsyncChannel; +import org.activeio.Packet; + +/** + * This PacketAggregatingAsyncChannel can be used when the client is sending a + * 'record' style packet down the channel stack and needs receiving end to + * receive the same 'record' packets. + * + * This is very useful since in general, a channel does not grantee that a + * Packet that is sent down will not be fragmented or combined with other Packet + * objects. + * + * This {@see org.activeio.AsyncChannel} adds a 4 byte header + * to each packet that is sent down. + * + * @version $Revision$ + */ +final public class PacketAggregatingAsyncChannel extends FilterAsyncChannel { + + private final PacketAggregator aggregator = new PacketAggregator() { + protected void packetAssembled(Packet packet) { + getAsyncChannelListener().onPacket(packet); + } + }; + + public PacketAggregatingAsyncChannel(AsyncChannel next) { + super(next); + } + + public void onPacket(Packet packet) { + try { + aggregator.addRawPacket(packet); + } catch (IOException e) { + getAsyncChannelListener().onPacketError(e); + } + } + + public void write(Packet packet) throws IOException { + getNext().write(aggregator.getHeader(packet)); + getNext().write(packet); + } + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/filter/PacketAggregatingSyncChannel.java b/activeio/src/java/org/activeio/filter/PacketAggregatingSyncChannel.java new file mode 100644 index 0000000000..d2d42d5c92 --- /dev/null +++ b/activeio/src/java/org/activeio/filter/PacketAggregatingSyncChannel.java @@ -0,0 +1,93 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.filter; + +import java.io.IOException; +import java.util.LinkedList; + +import org.activeio.FilterSyncChannel; +import org.activeio.Packet; +import org.activeio.SyncChannel; + +/** + * This PacketAggregatingSynchChannel can be used when the client is sending a + * 'record' style packet down the channel stack and needs receiving end to + * receive the same 'record' packets. + * + * This is very usefull since in general, a channel does not garantee that a + * Packet that is sent down will not be fragmented or combined with other Packet + * objects. + * + * This {@see org.activeio.SynchChannel} adds a 4 byte header + * to each packet that is sent down. + * + * @version $Revision$ + */ +final public class PacketAggregatingSyncChannel extends FilterSyncChannel { + + private final LinkedList assembledPackets = new LinkedList(); + private final PacketAggregator aggregator = new PacketAggregator() { + protected void packetAssembled(Packet packet) { + assembledPackets.addLast(packet); + } + }; + + /** + * @param next + */ + public PacketAggregatingSyncChannel(SyncChannel next) { + super(next); + } + + public Packet read(long timeout) throws IOException { + long start = System.currentTimeMillis(); + if( assembledPackets.isEmpty() ) { + while( true ) { + + Packet packet = getNext().read(timeout); + if( packet==null ) { + return null; + } + + aggregator.addRawPacket(packet); + + // Should we try to get more packets? + if( assembledPackets.isEmpty() ) { + if( timeout == WAIT_FOREVER_TIMEOUT ) + continue; + + timeout = Math.max(0, timeout-(System.currentTimeMillis()-start)); + if( timeout != 0 ) + continue; + + return null; + } else { + return (Packet) assembledPackets.removeFirst(); + } + } + + } else { + return (Packet) assembledPackets.removeFirst(); + } + + } + + public void write(Packet packet) throws IOException { + getNext().write(aggregator.getHeader(packet)); + getNext().write(packet); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/filter/PacketAggregator.java b/activeio/src/java/org/activeio/filter/PacketAggregator.java new file mode 100644 index 0000000000..2e1a24b5cd --- /dev/null +++ b/activeio/src/java/org/activeio/filter/PacketAggregator.java @@ -0,0 +1,101 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.filter; + +import java.io.IOException; + +import org.activeio.Packet; +import org.activeio.PacketData; +import org.activeio.packet.AppendedPacket; +import org.activeio.packet.ByteArrayPacket; +import org.activeio.packet.EOSPacket; + +/** + * @version $Revision$ + */ +abstract public class PacketAggregator { + + private static final int HEADER_LENGTH = 4; + + private final ByteArrayPacket headerBuffer = new ByteArrayPacket(new byte[HEADER_LENGTH]); + private final PacketData headerData = new PacketData(headerBuffer); + + Packet incompleteUpPacket; + boolean headerLoaded; + private int upPacketLength; + + public void addRawPacket(Packet packet) throws IOException { + + // Passthrough the EOS packet. + if( packet == EOSPacket.EOS_PACKET ) { + packetAssembled(packet); + return; + } + + if (incompleteUpPacket != null) { + packet = AppendedPacket.join(incompleteUpPacket, packet); + incompleteUpPacket = null; + } + + while (true) { + + if (!headerLoaded) { + headerLoaded = packet.remaining() >= HEADER_LENGTH; + if( headerLoaded ) { + PacketData data = new PacketData(packet); + upPacketLength = data.readInt(); + if( upPacketLength < 0 ) { + throw new IOException("Up packet lenth was invalid: "+upPacketLength); + } + packet = packet.slice(); + } + if( !headerLoaded ) + break; + } + + if (packet.remaining() < upPacketLength ) + break; + + // Get ready to create a slice to send up. + int origLimit = packet.limit(); + packet.limit(upPacketLength); + packetAssembled(packet.slice()); + + // Get a slice of the remaining since that will dump + // the first packets of an AppendedPacket + packet.position(upPacketLength); + packet.limit(origLimit); + packet = packet.slice(); + + // Need to load a header again now. + headerLoaded = false; + } + if (packet.hasRemaining()) { + incompleteUpPacket = packet; + } + + } + + protected abstract void packetAssembled(Packet packet); + + public Packet getHeader( Packet packet ) throws IOException { + headerBuffer.clear(); + headerData.writeInt(packet.remaining()); + headerBuffer.flip(); + return headerBuffer; + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/filter/PushbackSyncChannel.java b/activeio/src/java/org/activeio/filter/PushbackSyncChannel.java new file mode 100644 index 0000000000..8b3ec813b0 --- /dev/null +++ b/activeio/src/java/org/activeio/filter/PushbackSyncChannel.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.filter; + +import java.io.IOException; + +import org.activeio.FilterSyncChannel; +import org.activeio.Packet; +import org.activeio.SyncChannel; + +/** + * + */ +public class PushbackSyncChannel extends FilterSyncChannel { + + private Packet putback; + + public PushbackSyncChannel(SyncChannel next) { + this(next, null); + } + + public PushbackSyncChannel(SyncChannel next, Packet putback) { + super(next); + this.putback=putback; + } + + public void putback(Packet packet) { + this.putback = packet; + } + + public Packet read(long timeout) throws IOException { + if(putback!=null ) { + Packet p = putback; + putback=null; + return p; + } + return super.read(timeout); + } + +} diff --git a/activeio/src/java/org/activeio/filter/SynchornizedAsyncChannel.java b/activeio/src/java/org/activeio/filter/SynchornizedAsyncChannel.java new file mode 100644 index 0000000000..ea9a53e328 --- /dev/null +++ b/activeio/src/java/org/activeio/filter/SynchornizedAsyncChannel.java @@ -0,0 +1,88 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.filter; + +import edu.emory.mathcs.backport.java.util.concurrent.locks.Lock; +import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock; + +import org.activeio.AsyncChannel; +import org.activeio.FilterAsyncChannel; +import org.activeio.Packet; + +import java.io.IOException; + +/** + * Used to synchronize concurrent access to an ASynchChannel. + * + * Uses a {@see edu.emory.mathcs.backport.java.util.concurrent.Sync} object + * for write operations. All other operations such as {@see #stop(long)} + * and {@see #stop} just do a normal java synchronization against the SynchornizedSynchChannel + * object instance. It is assumed that the Async message delivery is not + * concurrent and therefore does not require synchronization. + * + */ +public class SynchornizedAsyncChannel extends FilterAsyncChannel { + + private final Lock writeLock; + + public SynchornizedAsyncChannel(AsyncChannel next) { + this(next, new ReentrantLock()); + } + + public SynchornizedAsyncChannel(AsyncChannel next, Lock writeLock) { + super(next); + this.writeLock = writeLock; + } + + public void write(Packet packet) throws IOException { + writeLock.lock(); + try { + getNext().write(packet); + } finally { + writeLock.unlock(); + } + } + + public void flush() throws IOException { + writeLock.lock(); + try { + getNext().flush(); + } finally { + writeLock.unlock(); + } + } + + synchronized public void dispose() { + super.dispose(); + } + + synchronized public Object getAdapter(Class target) { + return super.getAdapter(target); + } + + synchronized public void start() throws IOException { + super.start(); + } + + synchronized public void stop(long timeout) throws IOException { + super.stop(timeout); + } + + public Lock getWriteLock() { + return writeLock; + } +} diff --git a/activeio/src/java/org/activeio/filter/SynchornizedSyncChannel.java b/activeio/src/java/org/activeio/filter/SynchornizedSyncChannel.java new file mode 100644 index 0000000000..15cabda83c --- /dev/null +++ b/activeio/src/java/org/activeio/filter/SynchornizedSyncChannel.java @@ -0,0 +1,122 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.filter; + +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; +import edu.emory.mathcs.backport.java.util.concurrent.locks.Lock; +import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock; + +import org.activeio.FilterSyncChannel; +import org.activeio.Packet; +import org.activeio.SyncChannel; +import org.activeio.SyncChannelServer; + +import java.io.IOException; +import java.io.InterruptedIOException; + +/** + * Used to synchronize concurrent access to a SynchChannel. + * + * Uses two different {@see edu.emory.mathcs.backport.java.util.concurrent.Sync} objects + * for write and read operations. All other operations such as {@see #stop(long)} + * and {@see #stop} just do a normal java synchronization against the SynchornizedSynchChannel + * object instance. + * + */ +public class SynchornizedSyncChannel extends FilterSyncChannel { + + private final Lock readLock; + private final Lock writeLock; + + public SynchornizedSyncChannel(SyncChannel next) { + this(next, new ReentrantLock(), new ReentrantLock()); + } + + public SynchornizedSyncChannel(SyncChannel next, Lock readLock, Lock writeLock) { + super(next); + this.readLock = readLock; + this.writeLock = writeLock; + } + + public Packet read(long timeout) throws IOException { + try { + + if( timeout==SyncChannelServer.WAIT_FOREVER_TIMEOUT ) { + readLock.lock(); + } else { + long start = System.currentTimeMillis(); + if( !readLock.tryLock(0, TimeUnit.MILLISECONDS) ) { + return null; + } + // Adjust the resulting timeout down to account for time taken to + // get the readLock. + timeout = Math.max(0, timeout-(System.currentTimeMillis()-start)); + } + + } catch (InterruptedException e) { + throw new InterruptedIOException(e.getMessage()); + } + + try { + return getNext().read(timeout); + } finally { + readLock.unlock(); + } + } + + public void write(Packet packet) throws IOException { + writeLock.lock(); + try { + getNext().write(packet); + } finally { + writeLock.unlock(); + } + } + + public void flush() throws IOException { + writeLock.lock(); + try { + getNext().flush(); + } finally { + writeLock.unlock(); + } + } + + synchronized public void dispose() { + super.dispose(); + } + + synchronized public Object getAdapter(Class target) { + return super.getAdapter(target); + } + + synchronized public void start() throws IOException { + super.start(); + } + + synchronized public void stop(long timeout) throws IOException { + super.stop(timeout); + } + + public Lock getReadLock() { + return readLock; + } + + public Lock getWriteLock() { + return writeLock; + } +} diff --git a/activeio/src/java/org/activeio/filter/WriteBufferedAsyncChannel.java b/activeio/src/java/org/activeio/filter/WriteBufferedAsyncChannel.java new file mode 100644 index 0000000000..5e8ea1b577 --- /dev/null +++ b/activeio/src/java/org/activeio/filter/WriteBufferedAsyncChannel.java @@ -0,0 +1,70 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.filter; + +import java.io.IOException; + +import org.activeio.AsyncChannel; +import org.activeio.FilterAsyncChannel; +import org.activeio.Packet; +import org.activeio.packet.ByteArrayPacket; + +/** + */ +public class WriteBufferedAsyncChannel extends FilterAsyncChannel { + + private static final int DEFAULT_BUFFER_SIZE = 1024*64; + private final Packet buffer; + private final boolean enableDirectWrites; + + public WriteBufferedAsyncChannel(AsyncChannel channel) { + this(channel, new ByteArrayPacket(new byte[DEFAULT_BUFFER_SIZE])); + } + + public WriteBufferedAsyncChannel(AsyncChannel channel, Packet buffer) { + this(channel, buffer, true); + } + + public WriteBufferedAsyncChannel(AsyncChannel channel, Packet buffer, boolean enableDirectWrites) { + super(channel); + this.buffer = buffer; + this.enableDirectWrites = enableDirectWrites; + } + + public void write(Packet packet) throws IOException { + + while( packet.hasRemaining() ) { + packet.read(buffer); + if( !buffer.hasRemaining() ) { + flush(); + + // Should we just direct write the rest? + if( enableDirectWrites && packet.remaining() > buffer.capacity()) { + getNext().write(packet); + return; + } + } + } + + } + + public void flush() throws IOException { + buffer.flip(); + getNext().write(buffer); + buffer.clear(); + } +} diff --git a/activeio/src/java/org/activeio/filter/WriteBufferedSyncChannel.java b/activeio/src/java/org/activeio/filter/WriteBufferedSyncChannel.java new file mode 100644 index 0000000000..70d13b6360 --- /dev/null +++ b/activeio/src/java/org/activeio/filter/WriteBufferedSyncChannel.java @@ -0,0 +1,69 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.filter; + +import java.io.IOException; + +import org.activeio.FilterSyncChannel; +import org.activeio.Packet; +import org.activeio.SyncChannel; +import org.activeio.packet.ByteArrayPacket; + +/** + */ +public class WriteBufferedSyncChannel extends FilterSyncChannel { + + private static final int DEFAULT_BUFFER_SIZE = 1024*64; + private final Packet buffer; + private final boolean enableDirectWrites; + + public WriteBufferedSyncChannel(SyncChannel channel) { + this(channel, new ByteArrayPacket(new byte[DEFAULT_BUFFER_SIZE])); + } + + public WriteBufferedSyncChannel(SyncChannel channel, Packet buffer) { + this(channel, buffer, true); + } + + public WriteBufferedSyncChannel(SyncChannel channel, Packet buffer, boolean enableDirectWrites) { + super(channel); + this.buffer = buffer; + this.enableDirectWrites = enableDirectWrites; + } + + public void write(Packet packet) throws IOException { + + while( packet.hasRemaining() ) { + packet.read(buffer); + if( !buffer.hasRemaining() ) { + flush(); + + // Should we just direct write the rest? + if( enableDirectWrites && packet.remaining() > buffer.capacity()) { + getNext().write(packet); + return; + } + } + } + } + + public void flush() throws IOException { + buffer.flip(); + getNext().write(buffer); + buffer.clear(); + } +} diff --git a/activeio/src/java/org/activeio/filter/package.html b/activeio/src/java/org/activeio/filter/package.html new file mode 100644 index 0000000000..bee0281f7a --- /dev/null +++ b/activeio/src/java/org/activeio/filter/package.html @@ -0,0 +1,11 @@ + + + + + +

+Some simple filters that may be added to your channels. +

+ + + diff --git a/activeio/src/java/org/activeio/journal/InvalidRecordLocationException.java b/activeio/src/java/org/activeio/journal/InvalidRecordLocationException.java new file mode 100644 index 0000000000..70dce59635 --- /dev/null +++ b/activeio/src/java/org/activeio/journal/InvalidRecordLocationException.java @@ -0,0 +1,60 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal; + +/** + * Exception thrown by a Journal to indicate that an invalid RecordLocation was detected. + * + * @version $Revision: 1.1 $ + */ +public class InvalidRecordLocationException extends Exception { + + /** + * Comment for serialVersionUID + */ + private static final long serialVersionUID = 3618414947307239475L; + + /** + * + */ + public InvalidRecordLocationException() { + super(); + } + + /** + * @param msg + */ + public InvalidRecordLocationException(String msg) { + super(msg); + } + + /** + * @param msg + * @param rootCause + */ + public InvalidRecordLocationException(String msg, Throwable rootCause) { + super(msg, rootCause); + } + + /** + * @param rootCause + */ + public InvalidRecordLocationException(Throwable rootCause) { + super(rootCause); + } +} diff --git a/activeio/src/java/org/activeio/journal/Journal.java b/activeio/src/java/org/activeio/journal/Journal.java new file mode 100644 index 0000000000..9f65910eb5 --- /dev/null +++ b/activeio/src/java/org/activeio/journal/Journal.java @@ -0,0 +1,128 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal; + +import java.io.IOException; + +import org.activeio.Packet; + +/** + * A Journal is a record logging Interface that can be used to implement + * a transaction log. + * + * + * This interface was largely extracted out of the HOWL project to allow + * ActiveMQ to switch between different Journal implementations verry easily. + * + * @version $Revision: 1.1 $ + */ +public interface Journal { + + /** + * Writes a {@see Packet} of data to the journal. If sync + * is true, then this call blocks until the data has landed on the physical + * disk. Otherwise, this enqueues the write request and returns. + * + * @param record - the data to be written to disk. + * @param sync - If this call should block until the data lands on disk. + * + * @return RecordLocation the location where the data will be written to on disk. + * + * @throws IOException if the write failed. + * @throws IllegalStateException if the journal is closed. + */ + public RecordLocation write(Packet packet, boolean sync) throws IOException, IllegalStateException; + + /** + * Reads a previously written record from the journal. + * + * @param location is where to read the record from. + * + * @return the data previously written at the location. + * + * @throws InvalidRecordLocationException if location parameter is out of range. + * It cannot be a location that is before the current mark. + * @throws IOException if the record could not be read. + * @throws IllegalStateException if the journal is closed. + */ + public Packet read(RecordLocation location) throws InvalidRecordLocationException, IOException, IllegalStateException; + + /** + * Informs the journal that all the journal space up to the location is no longer + * needed and can be reclaimed for reuse. + * + * @param location the location of the record to mark. All record locations before the marked + * location will no longger be vaild. + * + * @param sync if this call should block until the mark is set on the journal. + * + * @throws InvalidRecordLocationException if location parameter is out of range. + * It cannot be a location that is before the current mark. + * @throws IOException if the record could not be read. + * @throws IllegalStateException if the journal is closed. + */ + public abstract void setMark(RecordLocation location, boolean sync) + throws InvalidRecordLocationException, IOException, IllegalStateException; + + /** + * Obtains the mark that was set in the Journal. + * + * @see read(RecordLocation location); + * @return the mark that was set in the Journal. + * @throws IllegalStateException if the journal is closed. + */ + public RecordLocation getMark() throws IllegalStateException; + + + /** + * Close the Journal. + * This is blocking operation that waits for any pending put opperations to be forced to disk. + * Once the Journal is closed, all other methods of the journal should throw IllegalStateException. + * + * @throws IOException if an error occurs while the journal is being closed. + */ + public abstract void close() throws IOException; + + /** + * Allows you to get the next RecordLocation after the location that + * is in the journal. + * + * @param location the reference location the is used to find the next location. + * To get the oldest location available in the journal, location + * should be set to null. + * + * + * @return the next record location + * + * @throws InvalidRecordLocationException if location parameter is out of range. + * It cannot be a location that is before the current mark. + * @throws IllegalStateException if the journal is closed. + */ + public abstract RecordLocation getNextRecordLocation(RecordLocation location) + throws InvalidRecordLocationException, IOException, IllegalStateException; + + + /** + * Registers a JournalEventListener that will receive notifications from the Journal. + * + * @param listener object that will receive journal events. + * @throws IllegalStateException if the journal is closed. + */ + public abstract void setJournalEventListener(JournalEventListener listener) throws IllegalStateException; + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/journal/JournalEventListener.java b/activeio/src/java/org/activeio/journal/JournalEventListener.java new file mode 100644 index 0000000000..44427e063c --- /dev/null +++ b/activeio/src/java/org/activeio/journal/JournalEventListener.java @@ -0,0 +1,38 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal; + +/** + * Defines an object which listens for Journal Events. + * + * @version $Revision: 1.1 $ + */ +public interface JournalEventListener { + + /** + * This event is issues when a Journal implementations wants to recover + * disk space used by old records. If journal space is not reliquised + * by setting the Journal's mark at or past the safeLocation + * further write opperations against the Journal may casuse IOExceptions + * to occur due to a log overflow condition. + * + * @param safeLocation the oldest location that the journal recomends the mark to be set. + */ + void overflowNotification(RecordLocation safeLocation); + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/journal/RecordLocation.java b/activeio/src/java/org/activeio/journal/RecordLocation.java new file mode 100644 index 0000000000..a48173d9a7 --- /dev/null +++ b/activeio/src/java/org/activeio/journal/RecordLocation.java @@ -0,0 +1,31 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal; + +/** + * A RecordLocation is used to locate data records that have been + * logged to a Journal via the Journal.put() method call. + * + * RecordLocation are comparable on the position in the Journal + * where they reside. + * + * @version $Revision: 1.1 $ + */ +public interface RecordLocation extends Comparable { + +} diff --git a/activeio/src/java/org/activeio/journal/active/BatchedWrite.java b/activeio/src/java/org/activeio/journal/active/BatchedWrite.java new file mode 100644 index 0000000000..9f7920116d --- /dev/null +++ b/activeio/src/java/org/activeio/journal/active/BatchedWrite.java @@ -0,0 +1,143 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.active; + +import org.activeio.Packet; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; + +/** + * This contains all the data needed to write and force a list of records to a + * LogFile. The more records that can be cramed into a single BatchedWrite, the + * higher throughput that can be achived by a write and force operation. + * + * @version $Revision: 1.1 $ + */ +final public class BatchedWrite { + + private final Packet packet; + public Throwable error; + private Location mark; + private boolean appendDisabled = false; + private boolean appendInProgress = false; + private CountDownLatch writeDoneCountDownLatch; + + /** + * @param packet + */ + public BatchedWrite(Packet packet) { + this.packet = packet; + } + + /** + * @throws InterruptedException + * + */ + synchronized private void disableAppend() throws InterruptedException { + appendDisabled = true; + while (appendInProgress) { + wait(); + } + } + + /** + * @param packet2 + * @param mark2 + * @return + */ + public boolean append(Record record, Location recordMark, boolean force) { + + synchronized (this) { + if (appendDisabled) + return false; + appendInProgress = true; + } + + + if( force && writeDoneCountDownLatch==null) + writeDoneCountDownLatch = new CountDownLatch(1); + + record.read(packet); + + // if we fit the record in this batch + if ( !record.hasRemaining() ) { + if (recordMark != null) + mark = recordMark; + } + + synchronized (this) { + appendInProgress = false; + this.notify(); + + if (appendDisabled) + return false; + else + return packet.remaining() > 0; + } + } + + public void waitForForce() throws Throwable { + if( writeDoneCountDownLatch!=null ) { + writeDoneCountDownLatch.await(); + synchronized (this) { + if (error != null) + throw error; + } + } + } + + public void forced() { + if( writeDoneCountDownLatch!=null ) { + writeDoneCountDownLatch.countDown(); + } + } + + public void writeFailed(Throwable error) { + if( writeDoneCountDownLatch!=null ) { + synchronized (this) { + this.error = error; + } + writeDoneCountDownLatch.countDown(); + } + } + + public Packet getPacket() { + return packet; + } + + /** + * @return + */ + public Location getMark() { + return mark; + } + + /** + * @throws InterruptedException + * + */ + public void flip() throws InterruptedException { + disableAppend(); + packet.flip(); + } + + public boolean getForce() { + return writeDoneCountDownLatch!=null; + } + +} diff --git a/activeio/src/java/org/activeio/journal/active/ControlFile.java b/activeio/src/java/org/activeio/journal/active/ControlFile.java new file mode 100644 index 0000000000..34c6278d31 --- /dev/null +++ b/activeio/src/java/org/activeio/journal/active/ControlFile.java @@ -0,0 +1,191 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.active; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; + +import org.activeio.Disposable; +import org.activeio.Packet; +import org.activeio.packet.ByteBufferPacket; + +/** + * Control file holds the last known good state of the journal. It stores the state in + * record that is versioned and repeated twice in the file so that a failure in the + * middle of the write of the first or second record do not not result in an unknown + * state. + * + * @version $Revision: 1.1 $ + */ +final public class ControlFile implements Disposable { + + /** The File that holds the control data. */ + private final RandomAccessFile file; + private final FileChannel channel; + private final ByteBufferPacket controlData; + + private long controlDataVersion=0; + private FileLock lock; + private boolean disposed; + private static Set lockSet; + private String canonicalPath; + + public ControlFile(File fileName, int controlDataSize) throws IOException { + canonicalPath = fileName.getCanonicalPath(); + boolean existed = fileName.exists(); + file = new RandomAccessFile(fileName, "rw"); + channel = file.getChannel(); + controlData = new ByteBufferPacket(ByteBuffer.allocateDirect(controlDataSize)); + + } + + /** + * Locks the control file. + * @throws IOException + */ + public void lock() throws IOException { + if( lock==null ) { + Set set = getVmLockSet(); + synchronized(set) { + if( !set.add(canonicalPath) ) { + throw new IOException("Journal is already opened by this application."); + } + + lock = channel.tryLock(); + if( lock ==null ) { + set.remove(canonicalPath); + throw new IOException("Journal is already opened by another application"); + } + } + } + } + + /** + * Un locks the control file. + * @throws IOException + */ + public void unlock() throws IOException { + if( lock != null ) { + Set set = getVmLockSet(); + synchronized(set) { + lock.release(); + lock=null; + set.remove(canonicalPath); + } + } + } + + static private Set getVmLockSet() { + if ( lockSet == null ) { + Properties properties = System.getProperties(); + synchronized(properties) { + lockSet = (Set) properties.get("org.activeio.journal.active.lockMap"); + if( lockSet == null ) { + lockSet = new HashSet(); + } + properties.put("org.activeio.journal.active.lockMap", lockSet); + } + } + return lockSet; + } + + + public boolean load() throws IOException { + long l = file.length(); + if( l < controlData.capacity() ) { + controlDataVersion=0; + controlData.position(0); + controlData.limit(0); + return false; + } else { + file.seek(0); + long v1 = file.readLong(); + file.seek(controlData.capacity()+8); + long v1check = file.readLong(); + + file.seek(controlData.capacity()+16); + long v2 = file.readLong(); + file.seek((controlData.capacity()*2)+24); + long v2check = file.readLong(); + + if( v2 == v2check ) { + controlDataVersion = v2; + file.seek(controlData.capacity()+24); + controlData.clear(); + channel.read(controlData.getByteBuffer()); + } else if ( v1 == v1check ){ + controlDataVersion = v1; + file.seek(controlData.capacity()+8); + controlData.clear(); + channel.read(controlData.getByteBuffer()); + } else { + // Bummer.. Both checks are screwed. we don't know + // if any of the two buffer are ok. This should + // only happen is data got corrupted. + throw new IOException("Control data corrupted."); + } + return true; + } + } + + public void store() throws IOException { + controlDataVersion++; + file.setLength((controlData.capacity()*2)+32); + file.seek(0); + + // Write the first copy of the control data. + file.writeLong(controlDataVersion); + controlData.clear(); + channel.write(controlData.getByteBuffer()); + file.writeLong(controlDataVersion); + + // Write the second copy of the control data. + file.writeLong(controlDataVersion); + controlData.clear(); + channel.write(controlData.getByteBuffer()); + file.writeLong(controlDataVersion); + + channel.force(false); + } + + public Packet getControlData() { + controlData.clear(); + return controlData; + } + + public void dispose() { + if( disposed ) + return; + disposed=true; + try { + unlock(); + } catch (IOException e) { + } + try { + file.close(); + } catch (IOException e) { + } + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/journal/active/JournalImpl.java b/activeio/src/java/org/activeio/journal/active/JournalImpl.java new file mode 100644 index 0000000000..23372abe53 --- /dev/null +++ b/activeio/src/java/org/activeio/journal/active/JournalImpl.java @@ -0,0 +1,463 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.active; + +import java.io.File; +import java.io.IOException; +import java.io.InterruptedIOException; + +import org.activeio.Disposable; +import org.activeio.Packet; +import org.activeio.journal.InvalidRecordLocationException; +import org.activeio.journal.Journal; +import org.activeio.journal.JournalEventListener; +import org.activeio.journal.RecordLocation; +import org.activeio.packet.ByteArrayPacket; +import org.activeio.packet.ByteBufferPacketPool; + +import edu.emory.mathcs.backport.java.util.concurrent.Callable; +import edu.emory.mathcs.backport.java.util.concurrent.ExecutionException; +import edu.emory.mathcs.backport.java.util.concurrent.FutureTask; +import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +/** + * A high speed Journal implementation. Inspired by the ideas of the Howl project but tailored to the needs + * of ActiveMQ.

This Journal provides the following features: + *

+ *

+ * + * @version $Revision: 1.1 $ + */ +final public class JournalImpl implements Journal, Disposable { + + public static final int DEFAULT_POOL_SIZE = Integer.parseInt(System.getProperty("org.activeio.journal.active.DefaultPoolSize", ""+(5))); + public static final int DEFAULT_PACKET_SIZE = Integer.parseInt(System.getProperty("org.activeio.journal.active.DefaultPacketSize", ""+(1024*1024*4))); + + static final private int OVERFLOW_RENOTIFICATION_DELAY = 500; + + static private ByteBufferPacketPool lastPool; + + private boolean disposed = false; + + // The id of the current log file that is being filled. + private int appendLogFileId = 0; + + // The offset in the current log file that is being filled. + private int appendLogFileOffset = 0; + + // Used to batch writes together. + private BatchedWrite pendingBatchWrite; + + private Location lastMarkedLocation; + private LogFileManager file; + private ThreadPoolExecutor executor; + private int rolloverFence; + private JournalEventListener eventListener; + private ByteBufferPacketPool packetPool; + private long overflowNotificationTime = System.currentTimeMillis(); + private Packet markPacket = new ByteArrayPacket(new byte[Location.SERIALIZED_SIZE]); + + public JournalImpl(File logDirectory) throws IOException { + this(new LogFileManager(logDirectory)); + } + + public JournalImpl(File logDirectory, int logFileCount, int logFileSize) throws IOException { + this(new LogFileManager(logDirectory, logFileCount, logFileSize, null)); + } + + public JournalImpl(File logDirectory, int logFileCount, int logFileSize, File archiveDirectory) throws IOException { + this(new LogFileManager(logDirectory, logFileCount, logFileSize, archiveDirectory)); + } + + public JournalImpl(LogFileManager logFile) { + this.file = logFile; + this.packetPool = createBufferPool(); + this.executor = new ThreadPoolExecutor(1, 1, 30, TimeUnit.SECONDS, new LinkedBlockingQueue(), new ThreadFactory() { + public Thread newThread(Runnable runnable) { + Thread answer = new Thread(runnable, "Journal Writer"); + answer.setPriority(Thread.MAX_PRIORITY); + answer.setDaemon(true); + return answer; + } + }); + executor.allowCoreThreadTimeOut(true); + + lastMarkedLocation = file.getLastMarkedRecordLocation(); + Location nextAppendLocation = file.getNextAppendLocation(); + appendLogFileId = nextAppendLocation.getLogFileId(); + appendLogFileOffset = nextAppendLocation.getLogFileOffset(); + + rolloverFence = (file.getInitialLogFileSize() / 10) * 9; + } + + + /** + * When running unit tests we may not be able to create new pools fast enough + * since the old pools are not being gc'ed fast enough. So we pool the pool. + * @return + */ + synchronized static private ByteBufferPacketPool createBufferPool() { + if( lastPool !=null ) { + ByteBufferPacketPool rc = lastPool; + lastPool = null; + return rc; + } else { + return new ByteBufferPacketPool(DEFAULT_POOL_SIZE, DEFAULT_PACKET_SIZE); + } + } + + /** + * When running unit tests we may not be able to create new pools fast enough + * since the old pools are not being gc'ed fast enough. So we pool the pool. + * @return + */ + synchronized static private void disposeBufferPool(ByteBufferPacketPool pool) { + if( lastPool!=null ) { + pool.dispose(); + } else { + pool.waitForPacketsToReturn(); + lastPool = pool; + } + } + + + + public RecordLocation write(Packet data, boolean sync) throws IOException { + return write(LogFileManager.DATA_RECORD_TYPE, data, sync, null); + } + + private Location write(byte recordType, Packet data, boolean sync, Location mark) throws IOException { + try { + Location location; + BatchedWrite writeCommand; + + Record record = new Record(recordType, data, mark); + + // The following synchronized block is the bottle neck of the journal. Make this + // code faster and the journal should speed up. + synchronized (this) { + if (disposed) { + throw new IOException("Journal has been closed."); + } + + // Create our record + location = new Location(appendLogFileId, appendLogFileOffset); + record.setLocation(location); + + // Piggy back the packet on the pending write batch. + writeCommand = addToPendingWriteBatch(record, mark, sync); + + // Update where the next record will land. + appendLogFileOffset += data.limit() + Record.RECORD_BASE_SIZE; + rolloverCheck(); + } + + if (sync) { + writeCommand.waitForForce(); + } + + return location; + } catch (IOException e) { + throw e; + } catch (InterruptedException e) { + throw (IOException) new InterruptedIOException().initCause(e); + } catch (Throwable e) { + throw (IOException) new IOException("Write failed: " + e).initCause(e); + } + } + + /** + * @param record + * @return + * @throws InterruptedException + */ + private BatchedWrite addToPendingWriteBatch(Record record, Location mark, boolean force) throws InterruptedException { + + // Load the write batch up with data from our record. + // it may take more than one write batch if the record is large. + BatchedWrite answer = null; + while (record.hasRemaining()) { + + // Do we need another BatchWrite? + boolean queueTheWrite=false; + if (pendingBatchWrite == null) { + pendingBatchWrite = new BatchedWrite(packetPool.getPacket()); + queueTheWrite = true; + } + answer = pendingBatchWrite; + + // Can we continue to use the pendingBatchWrite? + boolean full = !pendingBatchWrite.append(record, mark, force); + + if( queueTheWrite ) { + final BatchedWrite queuedWrite = pendingBatchWrite; + executor.execute(new Runnable() { + public void run() { + try { + queuedWrite(queuedWrite); + } catch (InterruptedException e) { + } + } + }); + } + + if( full ) + pendingBatchWrite = null; + } + return answer; + + } + + /** + * This is a blocking call + * + * @param write + * @throws InterruptedException + */ + private void queuedWrite(BatchedWrite write) throws InterruptedException { + + // Stop other threads from appending more pendingBatchWrite. + write.flip(); + + // Do the write. + try { + file.append(write); + write.forced(); + } catch (Throwable e) { + write.writeFailed(e); + } finally { + write.getPacket().dispose(); + } + } + + /** + * + */ + private void rolloverCheck() throws IOException { + + // See if we need to issue an overflow notification. + if (eventListener != null && file.isPastHalfActive() + && overflowNotificationTime + OVERFLOW_RENOTIFICATION_DELAY < System.currentTimeMillis()) { + + // We need to send an overflow notification to free up + // some logFiles. + Location safeSpot = file.getFirstRecordLocationOfSecondActiveLogFile(); + eventListener.overflowNotification(safeSpot); + overflowNotificationTime = System.currentTimeMillis(); + } + + // Is it time to roll over? + if (appendLogFileOffset > rolloverFence ) { + + // Can we roll over? + if ( !file.canActivateNextLogFile() ) { + // don't delay the next overflow notification. + overflowNotificationTime -= OVERFLOW_RENOTIFICATION_DELAY; + + } else { + + try { + final FutureTask result = new FutureTask(new Callable() { + public Object call() throws Exception { + return queuedActivateNextLogFile(); + }}); + executor.execute(result); + Location location = (Location) result.get(); + appendLogFileId = location.getLogFileId(); + appendLogFileOffset = location.getLogFileOffset(); + + } catch (InterruptedException e) { + throw (IOException) new IOException("Interrupted.").initCause(e); + } + catch (ExecutionException e) { + throw handleExecutionException(e); + } + } + } + } + + /** + * This is a blocking call + */ + private Location queuedActivateNextLogFile() throws IOException { + file.activateNextLogFile(); + return file.getNextAppendLocation(); + } + + + + /** + * @param recordLocator + * @param force + * @return + * @throws InvalidRecordLocationException + * @throws IOException + * @throws InterruptedException + */ + synchronized public void setMark(RecordLocation l, boolean force) throws InvalidRecordLocationException, + IOException { + + Location location = (Location) l; + if (location == null) + throw new InvalidRecordLocationException("The location cannot be null."); + if (lastMarkedLocation != null && location.compareTo(lastMarkedLocation) < 0) + throw new InvalidRecordLocationException("The location is less than the last mark."); + + markPacket.clear(); + location.writeToPacket(markPacket); + markPacket.flip(); + write(LogFileManager.MARK_RECORD_TYPE, markPacket, force, location); + + lastMarkedLocation = location; + } + + /** + * @return + */ + public RecordLocation getMark() { + return lastMarkedLocation; + } + + /** + * @param lastLocation + * @return + * @throws IOException + * @throws InvalidRecordLocationException + */ + public RecordLocation getNextRecordLocation(final RecordLocation lastLocation) throws IOException, + InvalidRecordLocationException { + + if (lastLocation == null) { + if (lastMarkedLocation != null) { + return lastMarkedLocation; + } else { + return file.getFirstActiveLogLocation(); + } + } + + // Run this in the queued executor thread. + try { + final FutureTask result = new FutureTask(new Callable() { + public Object call() throws Exception { + return queuedGetNextRecordLocation((Location) lastLocation); + }}); + executor.execute(result); + return (Location) result.get(); + } catch (InterruptedException e) { + throw (IOException) new IOException("Interrupted.").initCause(e); + } + catch (ExecutionException e) { + throw handleExecutionException(e); + } + } + + protected IOException handleExecutionException(ExecutionException e) throws IOException { + Throwable cause = e.getCause(); + if (cause instanceof IOException) { + return (IOException) cause; + } + else { + return (IOException) new IOException(cause.getMessage()).initCause(cause); + } + } + + private Location queuedGetNextRecordLocation(Location location) throws IOException, InvalidRecordLocationException { + return file.getNextDataRecordLocation(location); + } + + /** + * @param location + * @return + * @throws InvalidRecordLocationException + * @throws IOException + */ + public Packet read(final RecordLocation l) throws IOException, InvalidRecordLocationException { + final Location location = (Location) l; + // Run this in the queued executor thread. + try { + final FutureTask result = new FutureTask(new Callable() { + public Object call() throws Exception { + return file.readPacket(location); + }}); + executor.execute(result); + return (Packet) result.get(); + } catch (InterruptedException e) { + throw (IOException) new IOException("Interrupted.").initCause(e); + } + catch (ExecutionException e) { + throw handleExecutionException(e); + } + } + + public void setJournalEventListener(JournalEventListener eventListener) { + this.eventListener = eventListener; + } + + /** + * @deprecated @see #dispose() + */ + public void close() throws IOException { + dispose(); + } + + /** + */ + public void dispose() { + if (disposed) + return; + disposed=true; + executor.shutdown(); + file.dispose(); + ByteBufferPacketPool pool = packetPool; + packetPool=null; + disposeBufferPool(pool); + } + + /** + * @return + */ + public File getLogDirectory() { + return file.getLogDirectory(); + } + + public int getInitialLogFileSize() { + return file.getInitialLogFileSize(); + } + + public String toString() { + return "Active Journal: using "+file.getOnlineLogFileCount()+" x " + (file.getInitialLogFileSize()/(1024*1024f)) + " Megs at: " + getLogDirectory(); + } + +} diff --git a/activeio/src/java/org/activeio/journal/active/Location.java b/activeio/src/java/org/activeio/journal/active/Location.java new file mode 100644 index 0000000000..6b44930a4f --- /dev/null +++ b/activeio/src/java/org/activeio/journal/active/Location.java @@ -0,0 +1,97 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.active; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +import org.activeio.Packet; +import org.activeio.PacketData; +import org.activeio.journal.RecordLocation; + +/** + * Defines a where a record can be located in the Journal. + * + * @version $Revision: 1.1 $ + */ +final public class Location implements RecordLocation { + + static final public int SERIALIZED_SIZE=8; + + final private int logFileId; + final private int logFileOffset; + + public Location(int logFileId, int fileOffset) { + this.logFileId = logFileId; + this.logFileOffset = fileOffset; + } + + public int compareTo(Object o) { + int rc = logFileId - ((Location) o).logFileId; + if (rc != 0) + return rc; + + return logFileOffset - ((Location) o).logFileOffset; + } + + public int hashCode() { + return logFileOffset ^ logFileId; + } + + public boolean equals(Object o) { + if (o == null || o.getClass() != Location.class) + return false; + Location rl = (Location) o; + return rl.logFileId == this.logFileId && rl.logFileOffset == this.logFileOffset; + } + + public String toString() { + return "" + logFileId + ":" + logFileOffset; + } + + public int getLogFileId() { + return logFileId; + } + + public int getLogFileOffset() { + return logFileOffset; + } + + public void writeToPacket(Packet packet) throws IOException { + PacketData data = new PacketData(packet); + data.writeInt(logFileId); + data.writeInt(logFileOffset); + } + + public void writeToDataOutput(DataOutput data) throws IOException { + data.writeInt(logFileId); + data.writeInt(logFileOffset); + } + + static public Location readFromPacket(Packet packet) throws IOException { + PacketData data = new PacketData(packet); + return new Location(data.readInt(), data.readInt()); + } + + public static Location readFromDataInput(DataInput data) throws IOException { + return new Location(data.readInt(), data.readInt()); + } + + +} diff --git a/activeio/src/java/org/activeio/journal/active/LogFile.java b/activeio/src/java/org/activeio/journal/active/LogFile.java new file mode 100644 index 0000000000..94eb7a9a8e --- /dev/null +++ b/activeio/src/java/org/activeio/journal/active/LogFile.java @@ -0,0 +1,156 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.active; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +import org.activeio.Disposable; + +/** + * Allows read/append access to a LogFile. + * + * @version $Revision: 1.1 $ + */ +final public class LogFile implements Disposable { + + private final RandomAccessFile file; + private final FileChannel channel; + + /** Prefered size. The size that the log file is set to when initilaized. */ + private final int initialSize; + + /** Where the we are in the file right now */ + private int currentOffset; + private boolean disposed; + + public LogFile(File file, int initialSize) throws IOException { + this.initialSize = initialSize; + boolean initializationNeeeded = !file.exists(); + this.file = new RandomAccessFile(file, "rw"); + channel = this.file.getChannel(); + if( initializationNeeeded ) + resize(); + channel.position(0); + reloadCurrentOffset(); + } + + /** + * To avoid doing un-needed seeks. + */ + private void seek(int offset) throws IOException { + if( offset == currentOffset ) { + if( currentOffset != channel.position() ) + throw new RuntimeException(" "+currentOffset+", "+channel.position() ); + return; + } + channel.position(offset); + currentOffset = offset; + } + private void reloadCurrentOffset() throws IOException { + currentOffset= (int) channel.position(); + } + private void addToCurrentOffset(int rc) { + currentOffset+=rc; + } + + public boolean loadAndCheckRecord(int offset, Record record) throws IOException { + + try { + // Read the next header + seek(offset); + record.readHeader(file); + + if (Record.isChecksumingEnabled()) { + record.checksum(file); + } + // Load the footer. + seek(offset+record.getPayloadLength()+Record.RECORD_HEADER_SIZE); + record.readFooter(file); + + addToCurrentOffset(record.getRecordLength()); + return true; + + } catch (IOException e) { + reloadCurrentOffset(); + return false; + } + } + + public void resize() throws IOException { + file.setLength(initialSize); + } + + public void force() throws IOException { + channel.force(false); + } + + public void dispose() { + if( disposed ) + return; + disposed=true; + try { + this.file.close(); + } catch (IOException e) { + } + } + + public void write(int offset, ByteBuffer buffer) throws IOException { + + try { + + int size = buffer.remaining(); + seek(offset); + while (buffer.hasRemaining()) { + channel.write(buffer); + } + addToCurrentOffset(size); + + } catch (IOException e) { + reloadCurrentOffset(); + } + } + + public void readRecordHeader(int offset, Record record) throws IOException { + seek(offset); + try { + record.readHeader(file); + } catch ( IOException e ) { + reloadCurrentOffset(); + throw e; + } + addToCurrentOffset(Record.RECORD_HEADER_SIZE); + } + + public void read(int offset, byte[] answer) throws IOException { + seek(offset); + file.readFully(answer); + addToCurrentOffset(answer.length); + } + + public void copyTo(File location) throws IOException { + FileOutputStream fos = new FileOutputStream(location); + channel.transferTo(0, channel.size(), fos.getChannel()); + fos.getChannel().force(false); + fos.close(); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/journal/active/LogFileManager.java b/activeio/src/java/org/activeio/journal/active/LogFileManager.java new file mode 100644 index 0000000000..31154b62e1 --- /dev/null +++ b/activeio/src/java/org/activeio/journal/active/LogFileManager.java @@ -0,0 +1,531 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.active; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.text.NumberFormat; +import java.util.HashMap; + +import org.activeio.Packet; +import org.activeio.adapter.PacketOutputStream; +import org.activeio.adapter.PacketToInputStream; +import org.activeio.journal.InvalidRecordLocationException; +import org.activeio.packet.ByteArrayPacket; +import org.activeio.packet.ByteBufferPacket; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +/** + * Provides a logical view of many separate files as one single long log file. + * The separate files that compose the LogFile are Segments of the LogFile. + *

This class is not thread safe. + * + * @version $Revision: 1.1 $ + */ +final public class LogFileManager { + + public static final int DEFAULT_LOGFILE_COUNT = Integer.parseInt(System.getProperty("org.activeio.journal.active.DefaultLogFileCount", ""+(2))); + public static final int DEFAULT_LOGFILE_SIZE = Integer.parseInt(System.getProperty("org.activeio.journal.active.DefaultLogFileSize", ""+(1024*1024*20))); + + static final public int SERIALIZED_SIZE = 6+Location.SERIALIZED_SIZE; + + static final public byte DATA_RECORD_TYPE = 1; + static final public byte MARK_RECORD_TYPE = 2; + static final private NumberFormat onlineLogNameFormat = NumberFormat.getNumberInstance(); + static { + onlineLogNameFormat.setMinimumIntegerDigits(3); + onlineLogNameFormat.setMaximumIntegerDigits(3); + onlineLogNameFormat.setGroupingUsed(false); + onlineLogNameFormat.setParseIntegerOnly(true); + onlineLogNameFormat.setMaximumFractionDigits(0); + } + + static final private NumberFormat archiveLogNameFormat = NumberFormat.getNumberInstance(); + static { + archiveLogNameFormat.setMinimumIntegerDigits(8); + archiveLogNameFormat.setMaximumIntegerDigits(8); + archiveLogNameFormat.setGroupingUsed(false); + archiveLogNameFormat.setParseIntegerOnly(true); + archiveLogNameFormat.setMaximumFractionDigits(0); + } + + // Config + private final File logDirectory; + private final int initialLogFileSize; + private final int onlineLogFileCount; + private final AtomicInteger activeLogFileCount = new AtomicInteger(0); + + // Keeps track of the online log file. + private LogFileNode firstNode; + private LogFileNode firstActiveNode; + private LogFileNode firstInactiveNode; + private LogFileNode appendNode; + + private ControlFile controlFile; + private int lastLogFileId = -1; + private Location lastMark; + private boolean disposed; + private boolean loadedFromCleanShutDown; + + private File archiveDirectory; + HashMap openArchivedLogs = new HashMap(); + + public LogFileManager(File logDirectory) throws IOException { + this(logDirectory, DEFAULT_LOGFILE_COUNT, DEFAULT_LOGFILE_SIZE, null); + } + + public LogFileManager(File logDirectory, int onlineLogFileCount, int initialLogFileSize, File archiveDirectory) throws IOException { + this.logDirectory = logDirectory; + this.onlineLogFileCount = onlineLogFileCount; + this.initialLogFileSize = initialLogFileSize; + initialize(onlineLogFileCount); + this.archiveDirectory=archiveDirectory; + } + + void initialize(int onlineLogFileCount) throws IOException { + + LogFileNode logFiles[] = new LogFileNode[onlineLogFileCount]; + + // Create the log directory if it does not exist. + if (!logDirectory.exists()) { + if (!logDirectory.mkdirs()) { + throw new IOException("Could not create directory: " + logDirectory); + } + } + + // Open the control file. + int controlDataSize = SERIALIZED_SIZE + (LogFileNode.SERIALIZED_SIZE*onlineLogFileCount); + controlFile = new ControlFile(new File(logDirectory, "control.dat"), controlDataSize); + // Make sure we are the only process using the control file. + controlFile.lock(); + + // Initialize the nodes. + for (int i = 0; i < onlineLogFileCount; i++) { + LogFile file = new LogFile(new File(logDirectory, "log-" + onlineLogNameFormat.format(i) + ".dat"), + initialLogFileSize); + logFiles[i] = new LogFileNode(file); + } + + // Link the nodes together. + for (int i = 0; i < onlineLogFileCount; i++) { + if (i == (onlineLogFileCount - 1)) { + logFiles[i].setNext(logFiles[0]); + } else { + logFiles[i].setNext(logFiles[i + 1]); + } + } + + firstNode = logFiles[0]; + loadState(); + + // Find the first active node + for (int i = 0; i < onlineLogFileCount; i++) { + if( logFiles[i].isActive() ) { + if( firstActiveNode == null || logFiles[i].getId() < firstActiveNode.getId() ) { + firstActiveNode = logFiles[i]; + } + } + } + + // None was active? activate one. + if ( firstActiveNode == null ) { + firstInactiveNode = logFiles[0]; + activateNextLogFile(); + } else { + // Find the append log and the first inactive node + firstInactiveNode = null; + LogFileNode log = firstActiveNode; + do { + if( !log.isActive() ) { + firstInactiveNode = log; + break; + } else { + appendNode = log; + } + log = log.getNext(); + } while (log != firstActiveNode); + } + + // If we did not have a clean shut down then we have to check the state + // of the append log. + if( !this.loadedFromCleanShutDown ) { + checkAppendLog(); + } + + loadedFromCleanShutDown = false; + storeState(); + } + + private void checkAppendLog() throws IOException { + + // We are trying to get the true append offset and the last Mark that was written in + // the append log. + + int offset = 0; + Record record = new Record(); + LogFile logFile = appendNode.getLogFile(); + Location markLocation=null; + + while( logFile.loadAndCheckRecord(offset, record) ) { + + if( record.getLocation().getLogFileId()!= appendNode.getId() || record.getLocation().getLogFileOffset()!=offset ) { + // We must have run past the end of the append location. + break; + } + + if ( record.getRecordType()==LogFileManager.MARK_RECORD_TYPE) { + markLocation = record.getLocation(); + } + + offset += record.getRecordLength(); + } + + appendNode.setAppendOffset(offset); + + if( markLocation!=null ) { + try { + Packet packet = readPacket(markLocation); + markLocation = Location.readFromPacket(packet); + } catch (InvalidRecordLocationException e) { + throw (IOException)new IOException(e.getMessage()).initCause(e); + } + updateMark(markLocation); + } + + } + + private void storeState() throws IOException { + Packet controlData = controlFile.getControlData(); + if( controlData.remaining() == 0 ) + return; + + DataOutput data = new DataOutputStream(new PacketOutputStream(controlData)); + + data.writeInt(lastLogFileId); + data.writeBoolean(lastMark!=null); + if( lastMark!=null ) + lastMark.writeToDataOutput(data); + data.writeBoolean(loadedFromCleanShutDown); + + // Load each node's state + LogFileNode log = firstNode; + do { + log.writeExternal( data ); + log = log.getNext(); + } while (log != firstNode); + + controlFile.store(); + } + + private void loadState() throws IOException { + if( controlFile.load() ) { + Packet controlData = controlFile.getControlData(); + if( controlData.remaining() == 0 ) + return; + + DataInput data = new DataInputStream(new PacketToInputStream(controlData)); + + lastLogFileId =data.readInt(); + if( data.readBoolean() ) + lastMark = Location.readFromDataInput(data); + else + lastMark = null; + loadedFromCleanShutDown = data.readBoolean(); + + // Load each node's state + LogFileNode log = firstNode; + do { + log.readExternal( data ); + log = log.getNext(); + } while (log != firstNode); + } + } + + public void dispose() { + + if (disposed) + return; + this.disposed = true; + + try { + // Close all the opened log files. + LogFileNode log = firstNode; + do { + log.getLogFile().dispose(); + log = log.getNext(); + } while (log != firstNode); + + loadedFromCleanShutDown=true; + storeState(); + controlFile.dispose(); + } catch ( IOException e ) { + } + + } + + private int getNextLogFileId() { + return ++lastLogFileId; + } + + /** + * @param write + * @throws IOException + */ + public void append(BatchedWrite write) throws IOException { + + if (!appendNode.isActive()) + throw new IllegalStateException("Log file is not active. Writes are not allowed"); + if (appendNode.isReadOnly()) + throw new IllegalStateException("Log file has been marked Read Only. Writes are not allowed"); + + // Write and force the data to disk. + LogFile logFile = appendNode.getLogFile(); + ByteBuffer buffer = ((ByteBufferPacket)write.getPacket().getAdapter(ByteBufferPacket.class)).getByteBuffer(); + int size = buffer.remaining(); + logFile.write(appendNode.getAppendOffset(), buffer); + if( write.getForce() ) + logFile.force(); + + // Update state + appendNode.appended(size); + if (write.getMark() != null) { + updateMark(write.getMark()); + } + } + + /** + * @param write + * @throws IOException + */ + synchronized private void updateMark(Location mark) throws IOException { + // If we wrote a mark we may need to deactivate some log files. + this.lastMark = mark; + while (firstActiveNode != appendNode) { + if (firstActiveNode.getId() < lastMark.getLogFileId()) { + + if( archiveDirectory!=null ) { + File file = getArchiveFile(firstActiveNode.getId()); + firstActiveNode.getLogFile().copyTo(file); + } + + firstActiveNode.deactivate(); + activeLogFileCount.decrementAndGet(); + if( firstInactiveNode == null ) + firstInactiveNode = firstActiveNode; + firstActiveNode = firstActiveNode.getNextActive(); + + } else { + break; + } + } + } + + private File getArchiveFile(int logId) { + return new File(archiveDirectory, "" + archiveLogNameFormat.format(logId) + ".log"); + } + + RecordInfo readRecordInfo(Location location) throws IOException, InvalidRecordLocationException { + + LogFile logFile; + LogFileNode logFileState = getLogFileWithId(location.getLogFileId()); + if( logFileState !=null ) { + // There can be no record at the append offset. + if (logFileState.getAppendOffset() == location.getLogFileOffset()) { + throw new InvalidRecordLocationException("No record at (" + location + + ") found. Location past end of logged data."); + } + logFile = logFileState.getLogFile(); + } else { + if( archiveDirectory==null ) { + throw new InvalidRecordLocationException("Log file: " + location.getLogFileId() + " is not active."); + } else { + logFile = getArchivedLogFile(location.getLogFileId()); + } + } + + // Is there a record header at the seeked location? + try { + Record header = new Record(); + logFile.readRecordHeader(location.getLogFileOffset(), header); + return new RecordInfo(location, header, logFileState, logFile); + } catch (IOException e) { + throw new InvalidRecordLocationException("No record at (" + location + ") found."); + } + } + + private LogFile getArchivedLogFile(int logFileId) throws InvalidRecordLocationException, IOException { + Integer key = new Integer(logFileId); + LogFile rc = (LogFile) openArchivedLogs.get(key); + if( rc == null ) { + File archiveFile = getArchiveFile(logFileId); + if( !archiveFile.canRead() ) + throw new InvalidRecordLocationException("Log file: " + logFileId + " does not exist."); + rc = new LogFile(archiveFile, getInitialLogFileSize()); + openArchivedLogs.put(key, rc); + + // TODO: turn openArchivedLogs into LRU cache and close old log files. + } + return rc; + } + + LogFileNode getLogFileWithId(int logFileId) throws InvalidRecordLocationException { + for (LogFileNode lf = firstActiveNode; lf != null; lf = lf.getNextActive()) { + if (lf.getId() == logFileId) { + return lf; + } + + // Short cut since id's will only increment + if (logFileId < lf.getId()) + break; + } + return null; + } + + /** + * @param lastLocation + * @return + */ + public Location getNextDataRecordLocation(Location lastLocation) throws IOException, InvalidRecordLocationException { + RecordInfo ri = readRecordInfo(lastLocation); + while (true) { + + int logFileId = ri.getLocation().getLogFileId(); + int offset = ri.getNextLocation(); + + // Are we overflowing into next logFile? + if (offset >= ri.getLogFileState().getAppendOffset()) { + LogFileNode nextActive = ri.getLogFileState().getNextActive(); + if (nextActive == null) { + return null; + } + logFileId = nextActive.getId(); + offset = 0; + } + + try { + ri = readRecordInfo(new Location(logFileId, offset)); + } catch (InvalidRecordLocationException e) { + return null; + } + + // Is the next record the right record type? + if (ri.getHeader().getRecordType() == DATA_RECORD_TYPE) { + return ri.getLocation(); + } + // No? go onto the next record. + } + } + + /** + * @param logFileIndex + * @param logFileOffset + * @return + * @throws IOException + * @throws InvalidRecordLocationException + */ + public Packet readPacket(Location location) throws IOException, InvalidRecordLocationException { + + // Is there a record header at the seeked location? + RecordInfo recordInfo = readRecordInfo(location); + + byte data[] = new byte[recordInfo.getHeader().getPayloadLength()]; + + LogFile logFile = recordInfo.getLogFile(); + logFile.read(recordInfo.getDataOffset(), data); + + return new ByteArrayPacket(data); + + } + + public int getInitialLogFileSize() { + return initialLogFileSize; + } + + public Location getFirstActiveLogLocation() { + if (firstActiveNode == null) + return null; + if (firstActiveNode.getAppendOffset() == 0) + return null; + return new Location(firstActiveNode.getId(), 0); + } + + void activateNextLogFile() throws IOException { + + // The current append logFile becomes readonly + if (appendNode != null) { + appendNode.setReadOnly(true); + } + + LogFileNode next = firstInactiveNode; + synchronized (this) { + firstInactiveNode = firstInactiveNode.getNextInactive(); + next.activate(getNextLogFileId()); + if (firstActiveNode == null) { + firstActiveNode = next; + } + } + activeLogFileCount.incrementAndGet(); + appendNode = next; + + storeState(); + } + + /** + * @return Returns the logDirectory. + */ + public File getLogDirectory() { + return logDirectory; + } + + /** + * @return Returns the lastMark. + */ + public Location getLastMarkedRecordLocation() { + return lastMark; + } + + public Location getNextAppendLocation() { + return new Location(appendNode.getId(), appendNode.getAppendOffset()); + } + + /** + * @return Returns the onlineLogFileCount. + */ + public int getOnlineLogFileCount() { + return onlineLogFileCount; + } + + public boolean isPastHalfActive() { + return (onlineLogFileCount/2.f) < activeLogFileCount.get(); + } + + synchronized public Location getFirstRecordLocationOfSecondActiveLogFile() { + return firstActiveNode.getNextActive().getFirstRecordLocation(); + } + + synchronized public boolean canActivateNextLogFile() { + return firstInactiveNode!=null; + } + +} diff --git a/activeio/src/java/org/activeio/journal/active/LogFileNode.java b/activeio/src/java/org/activeio/journal/active/LogFileNode.java new file mode 100644 index 0000000000..37eda421e8 --- /dev/null +++ b/activeio/src/java/org/activeio/journal/active/LogFileNode.java @@ -0,0 +1,161 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.active; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +/** + * @version $Revision: 1.1 $ + */ +final class LogFileNode { + + static final public int SERIALIZED_SIZE = 10; + + private final LogFile logFile; + private LogFileNode next; + + /** The id of the log file. */ + private int id; + /** Does it have live records in it? */ + private boolean active = false; + /** Is the log file in readonly mode */ + private boolean readOnly; + /** The location of the next append offset */ + private int appendOffset = 0; + + public LogFileNode(LogFile logFile) { + this.logFile = logFile; + } + + public LogFile getLogFile() { + return logFile; + } + + ///////////////////////////////////////////////////////////// + // + // Method used to mange the state of the log file. + // + ///////////////////////////////////////////////////////////// + + public void activate(int id) { + if (active) + throw new IllegalStateException("Log already active."); + this.id = id; + this.readOnly = false; + this.active = true; + this.appendOffset = 0; + } + + public int getId() { + return id; + } + + public void setReadOnly(boolean enable) { + if (!active) + throw new IllegalStateException("Log not active."); + this.readOnly = enable; + } + + public void deactivate() throws IOException { + if (!active) + throw new IllegalStateException("Log already inactive."); + this.active=false; + this.id = -1; + this.readOnly = true; + this.appendOffset = 0; + getLogFile().resize(); + } + + public boolean isActive() { + return active; + } + + public int getAppendOffset() { + return appendOffset; + } + + public Location getFirstRecordLocation() { + if (isActive() && appendOffset > 0) + return new Location(getId(), 0); + return null; + } + + public boolean isReadOnly() { + return readOnly; + } + + public void appended(int i) { + appendOffset += i; + } + + ///////////////////////////////////////////////////////////// + // + // Method used to maintain the list of LogFileNodes used by + // the LogFileManager + // + ///////////////////////////////////////////////////////////// + + public LogFileNode getNext() { + return next; + } + + public void setNext(LogFileNode state) { + next = state; + } + + public LogFileNode getNextActive() { + if (getNext().isActive()) + return getNext(); + return null; + } + + public LogFileNode getNextInactive() { + if (!getNext().isActive()) + return getNext(); + return null; + } + + /** + * @param data + * @throws IOException + */ + public void writeExternal(DataOutput data) throws IOException { + data.writeInt(id); + data.writeBoolean(active); + data.writeBoolean(readOnly); + data.writeInt(appendOffset); + } + + /** + * @param data + * @throws IOException + */ + public void readExternal(DataInput data) throws IOException { + id = data.readInt(); + active = data.readBoolean(); + readOnly = data.readBoolean(); + appendOffset = data.readInt(); + } + + public void setAppendOffset(int offset) { + appendOffset = offset; + } + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/journal/active/Record.java b/activeio/src/java/org/activeio/journal/active/Record.java new file mode 100644 index 0000000000..21d654b658 --- /dev/null +++ b/activeio/src/java/org/activeio/journal/active/Record.java @@ -0,0 +1,309 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.active; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.zip.CRC32; + +import org.activeio.Disposable; +import org.activeio.Packet; +import org.activeio.adapter.PacketToInputStream; +import org.activeio.adapter.PacketOutputStream; +import org.activeio.packet.ByteArrayPacket; + + +/** + * Serializes/Deserializes data records. + * + * @version $Revision: 1.1 $ + */ +final public class Record implements Disposable { + + static final public int RECORD_HEADER_SIZE=8+Location.SERIALIZED_SIZE; + static final public int RECORD_FOOTER_SIZE=12+Location.SERIALIZED_SIZE; + static final public int RECORD_BASE_SIZE=RECORD_HEADER_SIZE+RECORD_FOOTER_SIZE; + + static final public byte[] START_OF_RECORD = new byte[] { 'S', 'o', 'R' }; + static final public byte[] END_OF_RECORD = new byte[] { 'E', 'o', 'R', '.' }; + + static final public int SELECTED_CHECKSUM_ALGORITHIM; + static final public int NO_CHECKSUM_ALGORITHIM=0; + static final public int HASH_CHECKSUM_ALGORITHIM=1; + static final public int CRC32_CHECKSUM_ALGORITHIM=2; + + static { + String type = System.getProperty("org.activeio.journal.active.SELECTED_CHECKSUM_ALGORITHIM", "none"); + if( "none".equals(type) ) { + SELECTED_CHECKSUM_ALGORITHIM = NO_CHECKSUM_ALGORITHIM; + } else if( "crc32".equals(type) ) { + SELECTED_CHECKSUM_ALGORITHIM = CRC32_CHECKSUM_ALGORITHIM; + } else if( "hash".equals(type) ) { + SELECTED_CHECKSUM_ALGORITHIM = HASH_CHECKSUM_ALGORITHIM; + } else { + System.err.println("System property 'org.activeio.journal.active.SELECTED_CHECKSUM_ALGORITHIM' not set properly. Valid values are: 'none', 'hash', or 'crc32'"); + SELECTED_CHECKSUM_ALGORITHIM = NO_CHECKSUM_ALGORITHIM; + } + } + + static public boolean isChecksumingEnabled() { + return SELECTED_CHECKSUM_ALGORITHIM!=NO_CHECKSUM_ALGORITHIM; + } + + private final ByteArrayPacket headerFooterPacket = new ByteArrayPacket(new byte[RECORD_BASE_SIZE]); + private final DataOutputStream headerFooterData = new DataOutputStream(new PacketOutputStream(headerFooterPacket)); + + private int payloadLength; + private Location location; + private byte recordType; + private long checksum; + private Location mark; + private Packet payload; + + public Record() { + } + + public Record(byte recordType, Packet payload, Location mark) throws IOException { + this(null, recordType, payload, mark); + } + + public Record(Location location, byte recordType, Packet payload, Location mark) throws IOException { + this.location = location; + this.recordType = recordType; + this.mark = mark; + this.payload = payload.slice(); + this.payloadLength = payload.remaining(); + if( isChecksumingEnabled() ) { + checksum(new DataInputStream(new PacketToInputStream(this.payload))); + } + + writeHeader(headerFooterData); + writeFooter(headerFooterData); + } + + public void setLocation(Location location) throws IOException { + this.location = location; + headerFooterPacket.clear(); + headerFooterPacket.position(8); + location.writeToDataOutput(headerFooterData); + headerFooterPacket.position(RECORD_HEADER_SIZE+8); + location.writeToDataOutput(headerFooterData); + payload.clear(); + headerFooterPacket.position(0); + headerFooterPacket.limit(RECORD_HEADER_SIZE); + } + + private void writeHeader( DataOutput out ) throws IOException { + out.write(START_OF_RECORD); + out.writeByte(recordType); + out.writeInt(payloadLength); + if( location!=null ) + location.writeToDataOutput(out); + else + out.writeLong(0); + } + + public void readHeader( DataInput in ) throws IOException { + readAndCheckConstant(in, START_OF_RECORD, "Invalid record header: start of record constant missing."); + recordType = in.readByte(); + payloadLength = in.readInt(); + if( payloadLength < 0 ) + throw new IOException("Invalid record header: record length cannot be less than zero."); + location = Location.readFromDataInput(in); + } + + private void writeFooter( DataOutput out ) throws IOException { + out.writeLong(checksum); + if( location!=null ) + location.writeToDataOutput(out); + else + out.writeLong(0); + out.write(END_OF_RECORD); + } + + public void readFooter( DataInput in ) throws IOException { + long l = in.readLong(); + if( isChecksumingEnabled() ) { + if( l!=checksum ) + throw new IOException("Invalid record footer: checksum does not match."); + } else { + checksum = l; + } + + Location loc = Location.readFromDataInput(in); + if( !loc.equals(location) ) + throw new IOException("Invalid record footer: location id does not match."); + + readAndCheckConstant(in, END_OF_RECORD, "Invalid record header: end of record constant missing."); + } + + /** + * @param randomAccessFile + * @throws IOException + */ + public void checksum(DataInput in) throws IOException { + if( SELECTED_CHECKSUM_ALGORITHIM==HASH_CHECKSUM_ALGORITHIM ) { + + byte buffer[] = new byte[1024]; + byte rc[] = new byte[8]; + for (int i = 0; i < payloadLength;) { + int l = Math.min(buffer.length, payloadLength-i); + in.readFully(buffer,0,l); + for (int j = 0; j < l; j++) { + rc[j%8] ^= buffer[j]; + } + i+=l; + } + checksum = (rc[0])|(rc[1]<<1)|(rc[2]<<2)|(rc[3]<<3)|(rc[4]<<4)|(rc[5]<<5)|(rc[6]<<6)|(rc[7]<<7) ; + + } else if( SELECTED_CHECKSUM_ALGORITHIM==CRC32_CHECKSUM_ALGORITHIM ) { + byte buffer[] = new byte[1024]; + CRC32 crc32 = new CRC32(); + for (int i = 0; i < payloadLength;) { + int l = Math.min(buffer.length, payloadLength-i); + in.readFully(buffer,0,l); + crc32.update(buffer,0,l); + i+=l; + } + checksum = crc32.getValue(); + } else { + checksum = 0L; + } + } + + + /** + */ + private void readAndCheckConstant(DataInput in, byte[] byteConstant, String errorMessage ) throws IOException { + for (int i = 0; i < byteConstant.length; i++) { + byte checkByte = byteConstant[i]; + if( in.readByte()!= checkByte ) { + throw new IOException(errorMessage); + } + } + } + + public boolean readFromPacket(Packet packet) throws IOException { + Packet dup = packet.duplicate(); + + if( dup.remaining() < RECORD_HEADER_SIZE ) + return false; + DataInputStream is = new DataInputStream(new PacketToInputStream(dup)); + readHeader( is ); + if( dup.remaining() < payloadLength+RECORD_FOOTER_SIZE ) { + return false; + } + + // Set limit to create a slice of the payload. + dup.limit(dup.position()+payloadLength); + this.payload = dup.slice(); + if( isChecksumingEnabled() ) { + checksum(new DataInputStream(new PacketToInputStream(payload))); + } + + // restore the limit and seek to the footer. + dup.limit(packet.limit()); + dup.position(dup.position()+payloadLength); + readFooter(is); + + // If every thing went well.. advance the position of the orignal packet. + packet.position(dup.position()); + dup.dispose(); + return true; + } + + /** + * @return Returns the checksum. + */ + public long getChecksum() { + return checksum; + } + + /** + * @return Returns the length. + */ + public int getPayloadLength() { + return payloadLength; + } + + /** + * @return Returns the length of the record . + */ + public int getRecordLength() { + return payloadLength+Record.RECORD_BASE_SIZE; + } + + /** + * @return Returns the location. + */ + public Location getLocation() { + return location; + } + + /** + * @return Returns the mark. + */ + public Location getMark() { + return mark; + } + + /** + * @return Returns the payload. + */ + public Packet getPayload() { + return payload; + } + + /** + * @return Returns the recordType. + */ + public byte getRecordType() { + return recordType; + } + + public boolean hasRemaining() { + return headerFooterPacket.position()!=RECORD_BASE_SIZE; + } + + public void read(Packet packet) { + + // push the header + headerFooterPacket.read(packet); + // push the payload. + payload.read(packet); + + // Can we switch to the footer now? + if( !payload.hasRemaining() && headerFooterPacket.position()==RECORD_HEADER_SIZE ) { + headerFooterPacket.position(RECORD_HEADER_SIZE); + headerFooterPacket.limit(RECORD_BASE_SIZE); + headerFooterPacket.read(packet); + } + + } + + public void dispose() { + if( payload!=null ) { + payload.dispose(); + payload=null; + } + } + +} diff --git a/activeio/src/java/org/activeio/journal/active/RecordInfo.java b/activeio/src/java/org/activeio/journal/active/RecordInfo.java new file mode 100644 index 0000000000..6269faa520 --- /dev/null +++ b/activeio/src/java/org/activeio/journal/active/RecordInfo.java @@ -0,0 +1,60 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.active; + +/** + * @version $Revision: 1.1 $ + */ +final public class RecordInfo { + + private final Location location; + private final Record header; + private final LogFileNode logFileState; + private final LogFile logFile; + + public RecordInfo(Location location, Record header, LogFileNode logFileState, LogFile logFile) { + this.location = location; + this.header = header; + this.logFileState = logFileState; + this.logFile = logFile; + } + + int getNextLocation() { + return location.getLogFileOffset() + header.getPayloadLength() + Record.RECORD_BASE_SIZE; + } + + public Record getHeader() { + return header; + } + + public Location getLocation() { + return location; + } + + public LogFileNode getLogFileState() { + return logFileState; + } + + public LogFile getLogFile() { + return logFile; + } + + public int getDataOffset() { + return location.getLogFileOffset() + Record.RECORD_HEADER_SIZE; + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/journal/active/package.html b/activeio/src/java/org/activeio/journal/active/package.html new file mode 100644 index 0000000000..a8fe582695 --- /dev/null +++ b/activeio/src/java/org/activeio/journal/active/package.html @@ -0,0 +1,12 @@ + + + + + +

+The Active Journal is a high performance Journal implemenation which does not +place limits on how big the data being logged can be. +

+ + + diff --git a/activeio/src/java/org/activeio/journal/howl/HowlJournal.java b/activeio/src/java/org/activeio/journal/howl/HowlJournal.java new file mode 100644 index 0000000000..3174f62625 --- /dev/null +++ b/activeio/src/java/org/activeio/journal/howl/HowlJournal.java @@ -0,0 +1,202 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.howl; + +import java.io.IOException; +import java.io.InterruptedIOException; + +import org.activeio.Packet; +import org.activeio.journal.InvalidRecordLocationException; +import org.activeio.journal.Journal; +import org.activeio.journal.JournalEventListener; +import org.activeio.journal.RecordLocation; +import org.activeio.packet.ByteArrayPacket; +import org.objectweb.howl.log.Configuration; +import org.objectweb.howl.log.InvalidFileSetException; +import org.objectweb.howl.log.InvalidLogBufferException; +import org.objectweb.howl.log.InvalidLogKeyException; +import org.objectweb.howl.log.LogConfigurationException; +import org.objectweb.howl.log.LogEventListener; +import org.objectweb.howl.log.LogRecord; +import org.objectweb.howl.log.Logger; + +/** + * An implementation of the Journal interface using a HOWL logger. This is is a thin + * wrapper around a HOWL logger. + * + * This implementation can be used to write records but not to retreive them + * yet. Once the HOWL logger implements the methods needed to retreive + * previously stored records, this class can be completed. + * + * @version $Revision: 1.2 $ + */ +public class HowlJournal implements Journal { + + private final Logger logger; + + private RecordLocation lastMark; + + public HowlJournal(Configuration configuration) + throws InvalidFileSetException, LogConfigurationException, + InvalidLogBufferException, ClassNotFoundException, IOException, + InterruptedException { + this.logger = new Logger(configuration); + this.logger.open(); + lastMark = new LongRecordLocation(logger.getActiveMark()); + } + + /** + * @see org.activeio.journal.Journal#write(byte[], boolean) + */ + public RecordLocation write(Packet packet, boolean sync) throws IOException { + try { + return new LongRecordLocation(logger.put(packet.sliceAsBytes(), sync)); + } catch (InterruptedException e) { + throw (InterruptedIOException) new InterruptedIOException() + .initCause(e); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw (IOException) new IOException("Journal write failed: " + e) + .initCause(e); + } + } + + /** + * @see org.activeio.journal.Journal#setMark(org.codehaus.activemq.journal.RecordLocation, boolean) + */ + public void setMark(RecordLocation recordLocator, boolean force) + throws InvalidRecordLocationException, IOException { + try { + long location = toLong(recordLocator); + logger.mark(location, force); + lastMark = recordLocator; + + } catch (InterruptedException e) { + throw (InterruptedIOException) new InterruptedIOException() + .initCause(e); + } catch (IOException e) { + throw e; + } catch (InvalidLogKeyException e) { + throw new InvalidRecordLocationException(e.getMessage(), e); + } catch (Exception e) { + throw (IOException) new IOException("Journal write failed: " + e) + .initCause(e); + } + } + + /** + * @param recordLocator + * @return + * @throws InvalidRecordLocationException + */ + private long toLong(RecordLocation recordLocator) throws InvalidRecordLocationException { + if (recordLocator == null + || recordLocator.getClass() != LongRecordLocation.class) + throw new InvalidRecordLocationException(); + + long location = ((LongRecordLocation) recordLocator) + .getLongLocation(); + return location; + } + + /** + * @see org.activeio.journal.Journal#getMark() + */ + public RecordLocation getMark() { + return lastMark; + } + + /** + * @see org.activeio.journal.Journal#close() + */ + public void close() throws IOException { + try { + logger.close(); + } catch (IOException e) { + throw e; + } catch (InterruptedException e) { + throw (InterruptedIOException) new InterruptedIOException() + .initCause(e); + } catch (Exception e) { + throw (IOException) new IOException("Journal close failed: " + e) + .initCause(e); + } + } + + /** + * @see org.activeio.journal.Journal#setJournalEventListener(org.codehaus.activemq.journal.JournalEventListener) + */ + public void setJournalEventListener(final JournalEventListener eventListener) { + logger.setLogEventListener(new LogEventListener() { + public void logOverflowNotification(long key) { + eventListener.overflowNotification(new LongRecordLocation(key)); + } + }); + } + + /** + * @see org.activeio.journal.Journal#getNextRecordLocation(org.codehaus.activemq.journal.RecordLocation) + */ + public RecordLocation getNextRecordLocation(RecordLocation lastLocation) + throws InvalidRecordLocationException { + + if( lastLocation ==null ) { + if( this.lastMark !=null ) { + lastLocation = lastMark; + } else { + return null; + } + } + + try { + while(true) { + LogRecord record = logger.get(null, toLong(lastLocation)); + // I assume getNext will return null if there is no next record. + LogRecord next = logger.getNext(record); + if( next==null || next.length == 0 ) + return null; + lastLocation = new LongRecordLocation(next.key); + if( !next.isCTRL() ) + return lastLocation; + } + } catch (Exception e) { + throw (InvalidRecordLocationException)new InvalidRecordLocationException().initCause(e); + } + + } + + /** + * @see org.activeio.journal.Journal#read(org.codehaus.activemq.journal.RecordLocation) + */ + public Packet read(RecordLocation location) + throws InvalidRecordLocationException, IOException { + + try { + LogRecord record = logger.get(null, toLong(location)); + return new ByteArrayPacket(record.data); + } catch (InvalidLogKeyException e) { + throw new InvalidRecordLocationException(e.getMessage(), e); + } catch (Exception e) { + throw (IOException) new IOException("Journal write failed: " + e) + .initCause(e); + } + + } + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/journal/howl/LongRecordLocation.java b/activeio/src/java/org/activeio/journal/howl/LongRecordLocation.java new file mode 100644 index 0000000000..4e34656adc --- /dev/null +++ b/activeio/src/java/org/activeio/journal/howl/LongRecordLocation.java @@ -0,0 +1,74 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.howl; + +import org.activeio.journal.RecordLocation; + +/** + * Provides a RecordLocation implementation for the long based + * location pointers that HOWL uses. + * + * @version $Revision: 1.1 $ + */ +public class LongRecordLocation implements RecordLocation { + + final private long location; + + public LongRecordLocation(long l) { + this.location = l; + } + + /** + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + public int compareTo(Object o) { + return (int) (location - ((LongRecordLocation) o).location); + } + + /** + * @return the original long location provided by HOWL + */ + public long getLongLocation() { + return location; + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + int lowPart = (int) (0xFFFFFFFF & location); + int highPart = (int) (0xFFFFFFFF & (location >> 4)); + return lowPart ^ highPart; + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object o) { + if (o == null || o.getClass() != LongRecordLocation.class) + return false; + return ((LongRecordLocation) o).location == location; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + return "0x" + Long.toHexString(location); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/journal/howl/package.html b/activeio/src/java/org/activeio/journal/howl/package.html new file mode 100644 index 0000000000..396dfa4a29 --- /dev/null +++ b/activeio/src/java/org/activeio/journal/howl/package.html @@ -0,0 +1,9 @@ + + + + + +

A Journal implemenation using using a high performance transaction log + implemented using Howl

+ + diff --git a/activeio/src/java/org/activeio/journal/package.html b/activeio/src/java/org/activeio/journal/package.html new file mode 100644 index 0000000000..bee47bb0b5 --- /dev/null +++ b/activeio/src/java/org/activeio/journal/package.html @@ -0,0 +1,11 @@ + + + + + +

+Provides the API for storing and accessing record based binary data in sequential log files. +

+ + + diff --git a/activeio/src/java/org/activeio/net/AIOAsyncChannel.java b/activeio/src/java/org/activeio/net/AIOAsyncChannel.java new file mode 100644 index 0000000000..ae98594c2a --- /dev/null +++ b/activeio/src/java/org/activeio/net/AIOAsyncChannel.java @@ -0,0 +1,265 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; + +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelListener; +import org.activeio.Packet; +import org.activeio.packet.ByteBufferPacket; +import org.activeio.packet.EOSPacket; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +import com.ibm.io.async.AsyncSocketChannel; +import com.ibm.io.async.IAbstractAsyncFuture; +import com.ibm.io.async.IAsyncFuture; +import com.ibm.io.async.ICompletionListener; + +/** + * @version $Revision$ + */ +final public class AIOAsyncChannel implements AsyncChannel, ICompletionListener, SocketMetadata { + + protected static final int DEFAULT_BUFFER_SIZE = ByteBufferPacket.DEFAULT_DIRECT_BUFFER_SIZE; + + private final AsyncSocketChannel socketChannel; + private final Socket socket; + + private AsyncChannelListener channelListener; + private ByteBuffer inputByteBuffer; + + private final AtomicBoolean running = new AtomicBoolean(false); + private CountDownLatch doneCountDownLatch; + + protected AIOAsyncChannel(AsyncSocketChannel socketChannel) throws IOException { + this.socketChannel = socketChannel; + this.socket = socketChannel.socket(); + this.socket.setSendBufferSize(DEFAULT_BUFFER_SIZE); + this.socket.setReceiveBufferSize(DEFAULT_BUFFER_SIZE); + this.socket.setSoTimeout(0); + } + + private ByteBuffer allocateBuffer() { + return ByteBuffer.allocateDirect(DEFAULT_BUFFER_SIZE); + } + + public void setAsyncChannelListener(AsyncChannelListener channelListener) { + this.channelListener = channelListener; + } + + public AsyncChannelListener getAsyncChannelListener() { + return channelListener; + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return null; + } + + public void dispose() { + if( running.get() && channelListener!=null ) { + channelListener.onPacketError(new SocketException("Socket closed.")); + } + try { + stop(NO_WAIT_TIMEOUT); + } catch (IOException e) { + } + try { + socketChannel.close(); + } catch (IOException e) { + } + } + + public void start() throws IOException { + if( running.compareAndSet(false, true) ) { + doneCountDownLatch = new CountDownLatch(1); + requestNextRead(); + } + } + + public void stop(long timeout) throws IOException { + if( running.compareAndSet(true, false) ) { + try { + if( timeout == NO_WAIT_TIMEOUT ) { + doneCountDownLatch.await(0, TimeUnit.MILLISECONDS); + } else if( timeout == WAIT_FOREVER_TIMEOUT ) { + doneCountDownLatch.await(); + } else { + doneCountDownLatch.await(timeout, TimeUnit.MILLISECONDS); + } + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + } + + public void write(Packet packet) throws IOException { + ByteBuffer data = ((ByteBufferPacket)packet).getByteBuffer(); + while( data.hasRemaining() ) { + IAsyncFuture future = socketChannel.write(data); + try { + future.getByteCount(); + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + } + + public void flush() throws IOException { + } + + public void futureCompleted(IAbstractAsyncFuture abstractFuture, Object attribute) { + IAsyncFuture future = (IAsyncFuture)abstractFuture; + try { + + if( inputByteBuffer.position()>0 ) { + ByteBuffer remaining = inputByteBuffer.slice(); + Packet data = new ByteBufferPacket(((ByteBuffer)inputByteBuffer.flip()).slice()); + + channelListener.onPacket(data); + // Keep the remaining buffer around to fill with data. + inputByteBuffer = remaining; + requestNextRead(); + + } else { + channelListener.onPacket(EOSPacket.EOS_PACKET); + } + + } catch (IOException e) { + channelListener.onPacketError(e); + } + } + + private void requestNextRead() throws InterruptedIOException { + + // Don't do next read if we have stopped running. + if( !running.get() ) { + doneCountDownLatch.countDown(); + return; + } + + try { + + if( inputByteBuffer==null || !inputByteBuffer.hasRemaining() ) { + inputByteBuffer = allocateBuffer(); + } + + IAsyncFuture future = socketChannel.read(inputByteBuffer); + future.addCompletionListener(this, null, false); + + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + + } + + public InetAddress getInetAddress() { + return socket.getInetAddress(); + } + public boolean getKeepAlive() throws SocketException { + return socket.getKeepAlive(); + } + public InetAddress getLocalAddress() { + return socket.getLocalAddress(); + } + public int getLocalPort() { + return socket.getLocalPort(); + } + public SocketAddress getLocalSocketAddress() { + return socket.getLocalSocketAddress(); + } + public boolean getOOBInline() throws SocketException { + return socket.getOOBInline(); + } + public int getPort() { + return socket.getPort(); + } + public int getReceiveBufferSize() throws SocketException { + return socket.getReceiveBufferSize(); + } + public SocketAddress getRemoteSocketAddress() { + return socket.getRemoteSocketAddress(); + } + public boolean getReuseAddress() throws SocketException { + return socket.getReuseAddress(); + } + public int getSendBufferSize() throws SocketException { + return socket.getSendBufferSize(); + } + public int getSoLinger() throws SocketException { + return socket.getSoLinger(); + } + public int getSoTimeout() throws SocketException { + return socket.getSoTimeout(); + } + public boolean getTcpNoDelay() throws SocketException { + return socket.getTcpNoDelay(); + } + public int getTrafficClass() throws SocketException { + return socket.getTrafficClass(); + } + public boolean isBound() { + return socket.isBound(); + } + public boolean isClosed() { + return socket.isClosed(); + } + public boolean isConnected() { + return socket.isConnected(); + } + public void setKeepAlive(boolean on) throws SocketException { + socket.setKeepAlive(on); + } + public void setOOBInline(boolean on) throws SocketException { + socket.setOOBInline(on); + } + public void setReceiveBufferSize(int size) throws SocketException { + socket.setReceiveBufferSize(size); + } + public void setReuseAddress(boolean on) throws SocketException { + socket.setReuseAddress(on); + } + public void setSendBufferSize(int size) throws SocketException { + socket.setSendBufferSize(size); + } + public void setSoLinger(boolean on, int linger) throws SocketException { + socket.setSoLinger(on, linger); + } + public void setTcpNoDelay(boolean on) throws SocketException { + socket.setTcpNoDelay(on); + } + public void setTrafficClass(int tc) throws SocketException { + socket.setTrafficClass(tc); + } + public String toString() { + return "AIO Connection: "+getLocalSocketAddress()+" -> "+getRemoteSocketAddress(); + } + } \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/AIOAsyncChannelFactory.java b/activeio/src/java/org/activeio/net/AIOAsyncChannelFactory.java new file mode 100644 index 0000000000..ff546e160e --- /dev/null +++ b/activeio/src/java/org/activeio/net/AIOAsyncChannelFactory.java @@ -0,0 +1,115 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelFactory; +import org.activeio.AsyncChannelServer; +import org.activeio.adapter.SyncToAsyncChannelServer; +import org.activeio.filter.WriteBufferedAsyncChannel; +import org.activeio.packet.ByteBufferPacket; + +import com.ibm.io.async.AsyncServerSocketChannel; +import com.ibm.io.async.AsyncSocketChannel; + +/** + * A TcpAsyncChannelFactory creates {@see org.activeio.net.TcpAsyncChannel} + * and {@see org.activeio.net.TcpAsyncChannelServer} objects. + * + * @version $Revision$ + */ +public class AIOAsyncChannelFactory implements AsyncChannelFactory { + + protected static final int DEFAULT_BACKLOG = 500; + private int backlog = DEFAULT_BACKLOG; + + /** + * Uses the {@param location}'s host and port to create a tcp connection to a remote host. + * + * @see org.activeio.AsyncChannelFactory#openAsyncChannel(java.net.URI) + */ + public AsyncChannel openAsyncChannel(URI location) throws IOException { + AsyncSocketChannel channel = AsyncSocketChannel.open(); + channel.connect(new InetSocketAddress(location.getHost(), location.getPort())); + return createAsyncChannel(channel); + } + + /** + * @param channel + * @return + * @throws IOException + */ + protected AsyncChannel createAsyncChannel(AsyncSocketChannel socketChannel) throws IOException { + AsyncChannel channel = new AIOAsyncChannel(socketChannel); + channel = new WriteBufferedAsyncChannel(channel, ByteBufferPacket.createDefaultBuffer(true), false); + return channel; + } + + /** + * Binds a server socket a the {@param location}'s port. + * + * @see org.activeio.AsyncChannelFactory#bindAsyncChannel(java.net.URI) + */ + public AsyncChannelServer bindAsyncChannel(URI bindURI) throws IOException { + + String host = bindURI.getHost(); + InetSocketAddress address; + if( host == null || host.length() == 0 || host.equals("localhost") || host.equals("0.0.0.0") || InetAddress.getLocalHost().getHostName().equals(host) ) { + address = new InetSocketAddress(bindURI.getPort()); + } else { + address = new InetSocketAddress(bindURI.getHost(), bindURI.getPort()); + } + + AsyncServerSocketChannel serverSocketChannel = AsyncServerSocketChannel.open(); + serverSocketChannel.socket().bind(address,backlog); + + URI connectURI = bindURI; + try { +// connectURI = URISupport.changeHost(connectURI, InetAddress.getLocalHost().getHostName()); + connectURI = URISupport.changePort(connectURI, serverSocketChannel.socket().getLocalPort()); + } catch (URISyntaxException e) { + throw (IOException)new IOException("Could not build connect URI: "+e).initCause(e); + } + + return SyncToAsyncChannelServer.adapt( + new AIOSyncChannelServer(serverSocketChannel, bindURI, connectURI)); + } + + /** + * @return Returns the backlog. + */ + public int getBacklog() { + return backlog; + } + + /** + * @param backlog + * The backlog to set. + */ + public void setBacklog(int backlog) { + this.backlog = backlog; + } + + +} diff --git a/activeio/src/java/org/activeio/net/AIOSyncChannelServer.java b/activeio/src/java/org/activeio/net/AIOSyncChannelServer.java new file mode 100644 index 0000000000..d905eeaa7d --- /dev/null +++ b/activeio/src/java/org/activeio/net/AIOSyncChannelServer.java @@ -0,0 +1,109 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.URI; + +import org.activeio.AsyncChannel; +import org.activeio.Channel; +import org.activeio.SyncChannelServer; +import org.activeio.filter.WriteBufferedAsyncChannel; +import org.activeio.packet.ByteBufferPacket; + +import com.ibm.io.async.AsyncServerSocketChannel; + +/** + * @version $Revision$ + */ +public class AIOSyncChannelServer implements SyncChannelServer { + + private final AsyncServerSocketChannel serverSocket; + private final URI bindURI; + private final URI connectURI; + private int curentSoTimeout; + + public AIOSyncChannelServer(AsyncServerSocketChannel serverSocket, URI bindURI, URI connectURI) throws IOException { + this.serverSocket=serverSocket; + this.bindURI=bindURI; + this.connectURI=connectURI; + this.curentSoTimeout = serverSocket.socket().getSoTimeout(); + } + + public URI getBindURI() { + return bindURI; + } + + public URI getConnectURI() { + return this.connectURI; + } + + public void dispose() { + try { + serverSocket.close(); + } catch (IOException e) { + } + } + + synchronized public void start() throws IOException { + } + + synchronized public void stop(long timeout) { + } + + public Channel accept(long timeout) throws IOException { + try { + + if (timeout == SyncChannelServer.WAIT_FOREVER_TIMEOUT) + setSoTimeout(0); + else if (timeout == SyncChannelServer.NO_WAIT_TIMEOUT) + setSoTimeout(1); + else + setSoTimeout((int) timeout); + + AsyncChannel channel = new AIOAsyncChannel(serverSocket.accept()); + channel = new WriteBufferedAsyncChannel(channel, ByteBufferPacket.createDefaultBuffer(true), false); + return channel; + + } catch (SocketTimeoutException ignore) { + } + return null; + } + + private void setSoTimeout(int i) throws SocketException { + if (curentSoTimeout != i) { + serverSocket.socket().setSoTimeout(i); + curentSoTimeout = i; + } + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return null; + } + + public String toString() { + return "AIO Server: "+getConnectURI(); + } + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/DatagramContext.java b/activeio/src/java/org/activeio/net/DatagramContext.java new file mode 100644 index 0000000000..db47902f48 --- /dev/null +++ b/activeio/src/java/org/activeio/net/DatagramContext.java @@ -0,0 +1,46 @@ +/* + * Created on Dec 22, 2004 + * + * TODO To change the template for this generated file go to + * Window - Preferences - Java - Code Style - Code Templates + */ +package org.activeio.net; + +import java.net.DatagramPacket; +import java.net.InetAddress; + + +final public class DatagramContext { + + public InetAddress address; + public Integer port; + + public DatagramContext() { + } + + public DatagramContext(DatagramPacket datagramPacket) { + this(datagramPacket.getAddress(), new Integer(datagramPacket.getPort())); + } + + public DatagramContext(InetAddress address, Integer port) { + this.address = address; + this.port = port; + } + + public InetAddress getAddress() { + return address; + } + + public void setAddress(InetAddress address) { + this.address = address; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/DatagramSocketSyncChannel.java b/activeio/src/java/org/activeio/net/DatagramSocketSyncChannel.java new file mode 100644 index 0000000000..bc31ccc3d4 --- /dev/null +++ b/activeio/src/java/org/activeio/net/DatagramSocketSyncChannel.java @@ -0,0 +1,166 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.SocketException; +import java.net.SocketTimeoutException; + +import org.activeio.ByteSequence; +import org.activeio.Packet; +import org.activeio.SyncChannel; +import org.activeio.SyncChannelServer; +import org.activeio.packet.ByteArrayPacket; +import org.activeio.packet.FilterPacket; + +/** + * A {@see org.activeio.SynchChannel}implementation that uses + * TCP to talk to the network. + * + * @version $Revision$ + */ +public class DatagramSocketSyncChannel implements SyncChannel { + + private final class UDPFilterPacket extends FilterPacket { + private final DatagramPacket packet; + + private UDPFilterPacket(Packet next, DatagramPacket packet) { + super(next); + this.packet = packet; + } + + public Object getAdapter(Class target) { + if( target == DatagramContext.class ) { + return new DatagramContext(packet); + } + return super.getAdapter(target); + } + + public Packet filter(Packet packet) { + return new UDPFilterPacket(packet, this.packet); + } + } + + private static final int DEFAULT_BUFFER_SIZE = 64 * 1024; + + private final DatagramSocket socket; + + private boolean disposed; + + private int curentSoTimeout; + + /** + * Construct basic helpers + * + * @param wireFormat + * @throws IOException + */ + protected DatagramSocketSyncChannel(DatagramSocket socket) throws IOException { + this.socket = socket; + socket.setReceiveBufferSize(DEFAULT_BUFFER_SIZE); + socket.setSendBufferSize(DEFAULT_BUFFER_SIZE); + } + + protected DatagramSocket getSocket() { + return socket; + } + + /** + * @see org.activeio.SyncChannel#read(long) + */ + public org.activeio.Packet read(long timeout) throws IOException { + try { + + if (timeout == SyncChannelServer.WAIT_FOREVER_TIMEOUT) + setSoTimeout(0); + else if (timeout == SyncChannelServer.NO_WAIT_TIMEOUT) + setSoTimeout(1); + else + setSoTimeout((int) timeout); + + // FYI: message data is truncated if biger than this buffer. + final byte data[] = new byte[DEFAULT_BUFFER_SIZE]; + final DatagramPacket packet = new DatagramPacket(data, data.length); + socket.receive(packet); + + // A FilterPacket is used to provide the UdpDatagramContext via narrow. + return new UDPFilterPacket(new ByteArrayPacket(data, 0, packet.getLength()), packet); + + } catch (SocketTimeoutException e) { + return null; + } + } + + private void setSoTimeout(int i) throws SocketException { + if (curentSoTimeout != i) { + socket.setSoTimeout(i); + curentSoTimeout = i; + } + } + + /** + * @see org.activeio.Channel#write(org.activeio.Packet) + */ + public void write(org.activeio.Packet packet) throws IOException { + ByteSequence sequence = packet.asByteSequence(); + + DatagramContext context = (DatagramContext) packet.getAdapter(DatagramContext.class); + if( context!=null ) { + socket.send(new DatagramPacket(sequence.getData(),sequence.getOffset(), sequence.getLength(), context.address, context.port.intValue())); + } else { + socket.send(new DatagramPacket(sequence.getData(),sequence.getOffset(), sequence.getLength())); + } + } + + /** + * @see org.activeio.Channel#flush() + */ + public void flush() throws IOException { + } + + /** + * @see org.activeio.Disposable#dispose() + */ + public void dispose() { + if (disposed) + return; + socket.close(); + disposed = true; + } + + public void start() throws IOException { + } + + public void stop(long timeout) throws IOException { + } + + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return null; + } + + public String toString() { + return "Datagram Connection: "+socket.getLocalSocketAddress()+" -> "+socket.getRemoteSocketAddress(); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/DatagramSocketSyncChannelFactory.java b/activeio/src/java/org/activeio/net/DatagramSocketSyncChannelFactory.java new file mode 100644 index 0000000000..c424aa5e28 --- /dev/null +++ b/activeio/src/java/org/activeio/net/DatagramSocketSyncChannelFactory.java @@ -0,0 +1,85 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.URI; + +import org.activeio.SyncChannel; +import org.activeio.SyncChannelFactory; +import org.activeio.SyncChannelServer; + +/** + * A TcpSynchChannelFactory creates {@see org.activeio.net.TcpSynchChannel} + * and {@see org.activeio.net.TcpSynchChannelServer} objects. + * + * @version $Revision$ + */ +public class DatagramSocketSyncChannelFactory implements SyncChannelFactory { + + /** + * Uses the {@param location}'s host and port to create a tcp connection to a remote host. + * + * @see org.activeio.SynchChannelFactory#openSyncChannel(java.net.URI) + */ + public SyncChannel openSyncChannel(URI location) throws IOException { + DatagramSocket socket=null; + socket = new DatagramSocket(); + if( location != null ) { + InetAddress address = InetAddress.getByName(location.getHost()); + socket.connect(address, location.getPort()); + } + return createSyncChannel(socket); + } + + /** + * Uses the {@param location}'s host and port to create a tcp connection to a remote host. + * + */ + public SyncChannel openSyncChannel(URI location, URI localLocation) throws IOException { + DatagramSocket socket=null; + InetAddress address = InetAddress.getByName(localLocation.getHost()); + socket = new DatagramSocket(localLocation.getPort(), address); + + if( location != null ) { + address = InetAddress.getByName(location.getHost()); + socket.connect(address, location.getPort()); + } + return createSyncChannel(socket); + } + + /** + * @param socket + * @return + * @throws IOException + */ + protected SyncChannel createSyncChannel(DatagramSocket socket) throws IOException { + return new DatagramSocketSyncChannel(socket); + } + + /** + * @throws IOException allways thrown. + * @see org.activeio.SynchChannelFactory#bindSynchChannel(java.net.URI) + */ + public SyncChannelServer bindSyncChannel(URI location) throws IOException { + throw new IOException("A SynchChannelServer is not available for this channel."); + } + +} diff --git a/activeio/src/java/org/activeio/net/JxtaSocketSyncChannelFactory.java b/activeio/src/java/org/activeio/net/JxtaSocketSyncChannelFactory.java new file mode 100644 index 0000000000..735141b710 --- /dev/null +++ b/activeio/src/java/org/activeio/net/JxtaSocketSyncChannelFactory.java @@ -0,0 +1,90 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; + +import javax.net.ServerSocketFactory; +import javax.net.SocketFactory; + +import org.p2psockets.P2PServerSocket; +import org.p2psockets.P2PSocket; + +/** + * A SslSynchChannelFactory creates {@see org.activeio.net.TcpSynchChannel} + * and {@see org.activeio.net.TcpSynchChannelServer} objects that use SSL. + * + * @version $Revision$ + */ +public class JxtaSocketSyncChannelFactory extends SocketSyncChannelFactory { + + static public final class JxtaServerSocketFactory extends ServerSocketFactory { + + private static JxtaServerSocketFactory defaultJxtaServerSocketFactory = new JxtaServerSocketFactory(); + public static ServerSocketFactory getDefault() { + return defaultJxtaServerSocketFactory; + } + + private JxtaServerSocketFactory() {} + + public ServerSocket createServerSocket(int localPort) throws IOException { + return new P2PServerSocket(localPort); + } + + public ServerSocket createServerSocket(int localPort, int backlog) throws IOException { + return new P2PServerSocket(localPort, backlog); + } + + public ServerSocket createServerSocket(int localPort, int backlog, InetAddress localHost) throws IOException { + return new P2PServerSocket(localPort, backlog, localHost); + } + } + + static public final class JxtaSocketFactory extends SocketFactory { + + private static JxtaSocketFactory defaultJxtaSocketFactory = new JxtaSocketFactory(); + public static SocketFactory getDefault() { + return defaultJxtaSocketFactory; + } + private JxtaSocketFactory() {} + + public Socket createSocket(String remoteHost, int remotePort) throws IOException, UnknownHostException { + return new P2PSocket(remoteHost, remotePort); + } + + public Socket createSocket(String remoteHost, int remotePort, InetAddress localHost, int localPort) throws IOException, UnknownHostException { + return new P2PSocket(remoteHost, remotePort, localHost, localPort); + } + + public Socket createSocket(InetAddress remoteHost, int remotePort) throws IOException { + return new P2PSocket(remoteHost, remotePort); + } + + public Socket createSocket(InetAddress remoteHost, int remotePort, InetAddress localHost, int localPort) throws IOException { + return new P2PSocket(remoteHost, remotePort, localHost, localPort); + } + } + + public JxtaSocketSyncChannelFactory() { + super(JxtaSocketFactory.getDefault(), JxtaServerSocketFactory.getDefault()); + } +} diff --git a/activeio/src/java/org/activeio/net/MulticastSocketSyncChannel.java b/activeio/src/java/org/activeio/net/MulticastSocketSyncChannel.java new file mode 100644 index 0000000000..63ef600683 --- /dev/null +++ b/activeio/src/java/org/activeio/net/MulticastSocketSyncChannel.java @@ -0,0 +1,48 @@ +/** + * + * Copyright 2005 (C) The original author or authors. + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.MulticastSocket; + + +/** + * @version $Revision$ + */ +final public class MulticastSocketSyncChannel extends DatagramSocketSyncChannel { + + private final InetAddress groupAddress; + + + protected MulticastSocketSyncChannel(MulticastSocket socket, InetAddress groupAddress) throws IOException { + super(socket); + this.groupAddress = groupAddress; + } + + public void start() throws IOException { + ((MulticastSocket) getSocket()).joinGroup(groupAddress); + } + + public void stop(long timeout) throws IOException { + ((MulticastSocket) getSocket()).leaveGroup(groupAddress); + } + + public String toString() { + return "MulticastSocket Connection: " + getSocket().getLocalSocketAddress() + " -> " + getSocket().getRemoteSocketAddress(); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/MulticastSocketSyncChannelFactory.java b/activeio/src/java/org/activeio/net/MulticastSocketSyncChannelFactory.java new file mode 100644 index 0000000000..400ea4a3a3 --- /dev/null +++ b/activeio/src/java/org/activeio/net/MulticastSocketSyncChannelFactory.java @@ -0,0 +1,59 @@ +/** + * + * Copyright 2005 (C) The original author or authors. + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.MulticastSocket; +import java.net.URI; + +import org.activeio.SyncChannel; +import org.activeio.SyncChannelServer; + + +/** + * @version $Revision: $ $Date: $ + */ +public class MulticastSocketSyncChannelFactory { + + public SyncChannel openSyncChannel(URI groupURI) throws IOException { + if (groupURI == null) throw new IllegalArgumentException("group URI cannot be null"); + + MulticastSocket socket = new MulticastSocket(groupURI.getPort()); + + return createSyncChannel(socket, InetAddress.getByName(groupURI.getHost())); + } + + public SyncChannel openSyncChannel(URI groupURI, URI localLocation) throws IOException { + if (groupURI == null) throw new IllegalArgumentException("group URI cannot be null"); + + MulticastSocket socket = new MulticastSocket(groupURI.getPort()); + if (localLocation != null) { + socket.setInterface(InetAddress.getByName(localLocation.getHost())); + } + + return createSyncChannel(socket, InetAddress.getByName(groupURI.getHost())); + } + + protected SyncChannel createSyncChannel(MulticastSocket socket, InetAddress groupAddress) throws IOException { + return new MulticastSocketSyncChannel(socket, groupAddress); + } + + public SyncChannelServer bindSyncChannel(URI location) throws IOException { + throw new IOException("A SyncChannelServer is not available for this channel."); + } +} diff --git a/activeio/src/java/org/activeio/net/NIOAsyncChannel.java b/activeio/src/java/org/activeio/net/NIOAsyncChannel.java new file mode 100644 index 0000000000..30c4f825cb --- /dev/null +++ b/activeio/src/java/org/activeio/net/NIOAsyncChannel.java @@ -0,0 +1,182 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; + +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelListener; +import org.activeio.ByteSequence; +import org.activeio.Packet; +import org.activeio.net.NIOAsyncChannelSelectorManager.SelectorManagerListener; +import org.activeio.net.NIOAsyncChannelSelectorManager.SocketChannelAsyncChannelSelection; +import org.activeio.packet.ByteBufferPacket; +import org.activeio.packet.EOSPacket; + +/** + * @version $Revision$ + */ +final public class NIOAsyncChannel extends NIOBaseChannel implements AsyncChannel { + + private AsyncChannelListener channelListener; + private SocketChannelAsyncChannelSelection selection; + private ByteBuffer inputByteBuffer; + private boolean running; + + public NIOAsyncChannel(SocketChannel socketChannel, boolean useDirect) throws IOException { + super(socketChannel, useDirect); + + socketChannel.configureBlocking(false); + selection = NIOAsyncChannelSelectorManager.register(socketChannel, new SelectorManagerListener(){ + public void onSelect(SocketChannelAsyncChannelSelection selection) { + String origName = Thread.currentThread().getName(); + if (selection.isReadable()) + try { + Thread.currentThread().setName(NIOAsyncChannel.this.toString()); + serviceRead(); + } catch ( Throwable e ) { + System.err.println("ActiveIO unexpected error: "); + e.printStackTrace(System.err); + } finally { + Thread.currentThread().setName(origName); + } + } + }); + + } + + private void serviceRead() { + try { + + while( true ) { + + if( inputByteBuffer==null || !inputByteBuffer.hasRemaining() ) { + inputByteBuffer = allocateBuffer(); + } + + int size = socketChannel.read(inputByteBuffer); + if( size == -1 ) { + this.channelListener.onPacket( EOSPacket.EOS_PACKET ); + selection.close(); + break; + } + + if( size==0 ) { + break; + } + + // Per Mike Spile, some plaforms read 1 byte of data on the first read, and then + // a but load of data on the second read. Try to load the butload here + if( size == 1 && inputByteBuffer.hasRemaining() ) { + int size2 = socketChannel.read(inputByteBuffer); + if( size2 > 0 ) + size += size2; + } + + ByteBuffer remaining = inputByteBuffer.slice(); + Packet data = new ByteBufferPacket(((ByteBuffer)inputByteBuffer.flip()).slice()); + this.channelListener.onPacket( data ); + + // Keep the remaining buffer around to fill with data. + inputByteBuffer = remaining; + + if( inputByteBuffer.hasRemaining() ) + break; + } + + } catch (IOException e) { + this.channelListener.onPacketError(e); + } + } + + synchronized public void write(Packet packet) throws IOException { + + ByteBuffer data; + if( packet.getClass()==ByteBufferPacket.class ) { + data = ((ByteBufferPacket)packet).getByteBuffer(); + } else { + ByteSequence sequence = packet.asByteSequence(); + data = ByteBuffer.wrap(sequence.getData(), sequence.getOffset(), sequence.getLength()); + } + + long delay=1; + while( data.hasRemaining() ) { + + // Since the write is non-blocking, all the data may not have been written. + int r1 = data.remaining(); + socketChannel.write( data ); + int r2 = data.remaining(); + + // We may need to do a little bit of sleeping to avoid a busy loop. + // Slow down if no data was written out.. + if( r2>0 && r1-r2==0 ) { + try { + // Use exponential rollback to increase sleep time. + Thread.sleep(delay); + delay *= 5; + if( delay > 1000*1 ) { + delay = 1000; + } + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } else { + delay = 1; + } + } + } + + public void flush() throws IOException { + } + + public void setAsyncChannelListener(AsyncChannelListener channelListener) { + this.channelListener = channelListener; + } + + public AsyncChannelListener getAsyncChannelListener() { + return channelListener; + } + + public void dispose() { + if( running && channelListener!=null ) { + channelListener.onPacketError(new SocketException("Socket closed.")); + } + selection.close(); + super.dispose(); + } + + public void start() throws IOException { + if( running ) + return; + running=true; + selection.setInterestOps(SelectionKey.OP_READ); + } + + public void stop(long timeout) throws IOException { + if( !running ) + return; + running=false; + selection.setInterestOps(0); + } + } \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/NIOAsyncChannelFactory.java b/activeio/src/java/org/activeio/net/NIOAsyncChannelFactory.java new file mode 100644 index 0000000000..e227c1dd08 --- /dev/null +++ b/activeio/src/java/org/activeio/net/NIOAsyncChannelFactory.java @@ -0,0 +1,131 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelFactory; +import org.activeio.AsyncChannelServer; +import org.activeio.adapter.SyncToAsyncChannelServer; +import org.activeio.filter.WriteBufferedAsyncChannel; +import org.activeio.packet.ByteBufferPacket; + +/** + * A TcpAsyncChannelFactory creates {@see org.activeio.net.TcpAsyncChannel} + * and {@see org.activeio.net.TcpAsyncChannelServer} objects. + * + * @version $Revision$ + */ +public class NIOAsyncChannelFactory implements AsyncChannelFactory { + + protected static final int DEFAULT_BUFFER_SIZE = Integer.parseInt(System.getProperty("org.activeio.net.nio.BufferSize", ""+(64*1024))); + + protected static final int DEFAULT_BACKLOG = 500; + boolean useDirectBuffers = true; + private final boolean createWriteBufferedChannels; + private int backlog = DEFAULT_BACKLOG; + + public NIOAsyncChannelFactory() { + this(true); + } + + public NIOAsyncChannelFactory(boolean createWriteBufferedChannels) { + this.createWriteBufferedChannels = createWriteBufferedChannels; + } + + + /** + * Uses the {@param location}'s host and port to create a tcp connection to a remote host. + * + * @see org.activeio.AsyncChannelFactory#openAsyncChannel(java.net.URI) + */ + public AsyncChannel openAsyncChannel(URI location) throws IOException { + SocketChannel channel = SocketChannel.open(); + channel.connect(new InetSocketAddress(location.getHost(), location.getPort())); + return createAsyncChannel(channel); + } + + /** + * @param channel + * @return + * @throws IOException + */ + protected AsyncChannel createAsyncChannel(SocketChannel socketChannel) throws IOException { + AsyncChannel channel = new NIOAsyncChannel(socketChannel, useDirectBuffers); + if( createWriteBufferedChannels ) { + channel = new WriteBufferedAsyncChannel(channel, ByteBufferPacket.createDefaultBuffer(useDirectBuffers), false); + } + return channel; + } + + /** + * Binds a server socket a the {@param location}'s port. + * + * @see org.activeio.AsyncChannelFactory#bindAsyncChannel(java.net.URI) + */ + public AsyncChannelServer bindAsyncChannel(URI bindURI) throws IOException { + + String host = bindURI.getHost(); + InetSocketAddress address; + if( host == null || host.length() == 0 || host.equals("localhost") || host.equals("0.0.0.0") || InetAddress.getLocalHost().getHostName().equals(host) ) { + address = new InetSocketAddress(bindURI.getPort()); + } else { + address = new InetSocketAddress(bindURI.getHost(), bindURI.getPort()); + } + + ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); + serverSocketChannel.socket().bind(address,backlog); + + URI connectURI = bindURI; + try { +// connectURI = URISupport.changeHost(connectURI, InetAddress.getLocalHost().getHostName()); + connectURI = URISupport.changePort(connectURI, serverSocketChannel.socket().getLocalPort()); + } catch (URISyntaxException e) { + throw (IOException)new IOException("Could not build connect URI: "+e).initCause(e); + } + + // We won't use non blocking NIO for the server since you only need 1 thread for him anyways. + // Just resuing the SocketChannelSynchChannelServer. + return SyncToAsyncChannelServer.adapt( + new NIOAsyncChannelServer(serverSocketChannel, bindURI, connectURI, createWriteBufferedChannels, useDirectBuffers)); + } + + /** + * @return Returns the backlog. + */ + public int getBacklog() { + return backlog; + } + + /** + * @param backlog + * The backlog to set. + */ + public void setBacklog(int backlog) { + this.backlog = backlog; + } + + +} diff --git a/activeio/src/java/org/activeio/net/NIOAsyncChannelSelectorManager.java b/activeio/src/java/org/activeio/net/NIOAsyncChannelSelectorManager.java new file mode 100644 index 0000000000..7d3edc5644 --- /dev/null +++ b/activeio/src/java/org/activeio/net/NIOAsyncChannelSelectorManager.java @@ -0,0 +1,255 @@ +/** + * + * Copyright 2003-2004 The Apache Software Foundation + * + * Licensed 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.activeio.net; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor; + +import org.activeio.ChannelFactory; + +import java.io.IOException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Set; + +/** + * The SelectorManager will manage one Selector and the thread that checks the + * selector. + * + * We may need to consider running more than one thread to check the selector if + * servicing the selector takes too long. + * + * @version $Rev: 46019 $ $Date: 2004-09-14 05:56:06 -0400 (Tue, 14 Sep 2004) $ + */ +final public class NIOAsyncChannelSelectorManager { + + static private Executor selectorExecutor = ChannelFactory.DEFAULT_EXECUTOR; + static private Executor channelExecutor = ChannelFactory.DEFAULT_EXECUTOR; + + static private LinkedList freeManagers = new LinkedList(); + static private LinkedList fullManagers = new LinkedList(); + private static final int MAX_CHANNELS_PER_SELECTOR = 50; + + static { + String os = System.getProperty("os.name"); + if( os.startsWith("Linux") ) { + channelExecutor = new ScheduledThreadPoolExecutor(1); + } + } + + public static interface SelectorManagerListener { + public void onSelect(SocketChannelAsyncChannelSelection selector); + } + + final public class SocketChannelAsyncChannelSelection { + + private final SelectionKey key; + private final SelectorManagerListener listener; + private boolean closed; + private int interest; + + private SocketChannelAsyncChannelSelection(SocketChannel socketChannel, SelectorManagerListener listener) + throws ClosedChannelException { + this.listener = listener; + this.key = socketChannel.register(selector, 0, this); + incrementUseCounter(); + } + + public void setInterestOps(int ops) { + if( closed ) + return; + interest = ops; + enable(); + } + + public void enable() { + if( closed ) + return; + key.interestOps(interest); + selector.wakeup(); + } + + public void disable() { + if( closed ) + return; + key.interestOps(0); + } + + public void close() { + if( closed ) + return; + + key.cancel(); + decrementUseCounter(); + selector.wakeup(); + closed=true; + } + + public void onSelect() { + if( !key.isValid() ) + return; + listener.onSelect(this); + } + + public boolean isWritable() { + return key.isWritable(); + } + + public boolean isReadable() { + return key.isReadable(); + } + } + + public synchronized static SocketChannelAsyncChannelSelection register( + SocketChannel socketChannel, SelectorManagerListener listener) + throws IOException { + + NIOAsyncChannelSelectorManager manager = null; + synchronized (freeManagers) { + if (freeManagers.size() > 0) + manager = (NIOAsyncChannelSelectorManager) freeManagers.getFirst(); + if (manager == null) { + manager = new NIOAsyncChannelSelectorManager(); + freeManagers.addFirst(manager); + } + + // That manager may have filled up. + SocketChannelAsyncChannelSelection selection = manager.new SocketChannelAsyncChannelSelection( + socketChannel, listener); + if (manager.useCounter >= MAX_CHANNELS_PER_SELECTOR) { + freeManagers.removeFirst(); + fullManagers.addLast(manager); + } + return selection; + } + } + + public synchronized static void setSelectorExecutor(Executor executor) { + NIOAsyncChannelSelectorManager.selectorExecutor = executor; + } + + public synchronized static void setChannelExecutor(Executor executor) { + NIOAsyncChannelSelectorManager.channelExecutor = executor; + } + + private class SelectorWorker implements Runnable { + + public void run() { + + String origName = Thread.currentThread().getName(); + try { + Thread.currentThread().setName("Selector Worker: "+getId()); + while ( isRunning() ) { + + int count = selector.select(10); + if (count == 0) + continue; + if( !isRunning() ) + return; + + // Get a java.util.Set containing the SelectionKey objects + // for all channels that are ready for I/O. + Set keys = selector.selectedKeys(); + + for (Iterator i = keys.iterator(); i.hasNext();) { + final SelectionKey key = (SelectionKey) i.next(); + i.remove(); + + if( !key.isValid() ) + continue; + + final SocketChannelAsyncChannelSelection s = (SocketChannelAsyncChannelSelection) key.attachment(); + s.disable(); + + // Kick off another thread to find newly selected keys while we process the + // currently selected keys + channelExecutor.execute(new Runnable() { + public void run() { + try { + s.onSelect(); + s.enable(); + } catch ( Throwable e ) { + System.err.println("ActiveIO unexpected error: "); + e.printStackTrace(System.err); + } + } + }); + } + + } + } catch (Throwable e) { + System.err.println("Unexpected exception: " + e); + e.printStackTrace(); + } finally { + Thread.currentThread().setName(origName); + } + } + } + + /** + * The selector used to wait for non-blocking events. + */ + private Selector selector; + + /** + * How many SelectionKeys does the selector have active. + */ + private int useCounter; + private int id = getNextId(); + private static int nextId; + + private NIOAsyncChannelSelectorManager() throws IOException { + selector = Selector.open(); + } + + synchronized private static int getNextId() { + return nextId++; + } + + private int getId() { + return id ; + } + + synchronized private void incrementUseCounter() { + useCounter++; + if (useCounter == 1) { + selectorExecutor.execute(new SelectorWorker()); + } + } + + synchronized private void decrementUseCounter() { + useCounter--; + synchronized(freeManagers) { + if( useCounter == 0 ) { + freeManagers.remove(this); + } + else if( useCounter < MAX_CHANNELS_PER_SELECTOR ) { + fullManagers.remove(this); + freeManagers.addLast(this); + } + } + } + + synchronized private boolean isRunning() { + return useCounter > 0; + } +} diff --git a/activeio/src/java/org/activeio/net/NIOAsyncChannelServer.java b/activeio/src/java/org/activeio/net/NIOAsyncChannelServer.java new file mode 100644 index 0000000000..868ae3e67d --- /dev/null +++ b/activeio/src/java/org/activeio/net/NIOAsyncChannelServer.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; +import java.nio.channels.ServerSocketChannel; + +import org.activeio.AsyncChannel; +import org.activeio.Channel; +import org.activeio.filter.WriteBufferedAsyncChannel; +import org.activeio.packet.ByteBufferPacket; + +/** + * A SynchChannelServer that creates + * {@see org.activeio.net.TcpSynchChannel}objects from accepted + * tcp socket connections. + * + * @version $Revision$ + */ +public class NIOAsyncChannelServer extends SocketSyncChannelServer { + + private final boolean createWriteBufferedChannels; + private final boolean useDirectBuffers; + + public NIOAsyncChannelServer(ServerSocketChannel socketChannel, URI bindURI, URI connectURI, boolean createWriteBufferedChannels, boolean useDirectBuffers) { + super(socketChannel.socket(), bindURI, connectURI); + this.createWriteBufferedChannels = createWriteBufferedChannels; + this.useDirectBuffers = useDirectBuffers; + } + + protected Channel createChannel(SocketStreamChannel c) throws IOException { + AsyncChannel channel = new NIOAsyncChannel(c.getSocket().getChannel(), useDirectBuffers); + if( createWriteBufferedChannels ) { + channel = new WriteBufferedAsyncChannel(channel, ByteBufferPacket.createDefaultBuffer(useDirectBuffers), false); + } + return channel; + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/NIOBaseChannel.java b/activeio/src/java/org/activeio/net/NIOBaseChannel.java new file mode 100644 index 0000000000..ee537042ce --- /dev/null +++ b/activeio/src/java/org/activeio/net/NIOBaseChannel.java @@ -0,0 +1,182 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; + +import org.activeio.Disposable; +import org.activeio.packet.ByteBufferPacket; + +/** + * Base class for the Async and Sync implementations of NIO channels. + * + * @version $Revision$ + */ +public class NIOBaseChannel implements SocketMetadata, Disposable { + + protected final SocketChannel socketChannel; + protected final Socket socket; + private final boolean useDirect; + private int curentSoTimeout; + private boolean disposed; + private final String name; + + protected NIOBaseChannel(SocketChannel socketChannel, boolean useDirect) throws IOException { + this.socketChannel = socketChannel; + this.useDirect = useDirect; + this.socket = this.socketChannel.socket(); + + if( useDirect ) { + socket.setSendBufferSize(ByteBufferPacket.DEFAULT_DIRECT_BUFFER_SIZE); + socket.setReceiveBufferSize(ByteBufferPacket.DEFAULT_DIRECT_BUFFER_SIZE); + } else { + socket.setSendBufferSize(ByteBufferPacket.DEFAULT_BUFFER_SIZE); + socket.setReceiveBufferSize(ByteBufferPacket.DEFAULT_BUFFER_SIZE); + } + + this.name = "NIO Socket Connection: "+getLocalSocketAddress()+" -> "+getRemoteSocketAddress(); + } + + protected ByteBuffer allocateBuffer() { + if( useDirect ) { + return ByteBuffer.allocateDirect(ByteBufferPacket.DEFAULT_DIRECT_BUFFER_SIZE); + } else { + return ByteBuffer.allocate(ByteBufferPacket.DEFAULT_BUFFER_SIZE); + } + } + + protected void setSoTimeout(int i) throws SocketException { + if( curentSoTimeout != i ) { + socket.setSoTimeout(i); + curentSoTimeout = i; + } + } + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return null; + } + + public String toString() { + return name; + } + + public void dispose() { + if (disposed) + return; + + try { + socketChannel.close(); + } catch (IOException ignore) { + } + disposed = true; + } + + /** + * @see org.activeio.Channel#flush() + */ + public void flush() throws IOException { + } + + public InetAddress getInetAddress() { + return socket.getInetAddress(); + } + public boolean getKeepAlive() throws SocketException { + return socket.getKeepAlive(); + } + public InetAddress getLocalAddress() { + return socket.getLocalAddress(); + } + public int getLocalPort() { + return socket.getLocalPort(); + } + public SocketAddress getLocalSocketAddress() { + return socket.getLocalSocketAddress(); + } + public boolean getOOBInline() throws SocketException { + return socket.getOOBInline(); + } + public int getPort() { + return socket.getPort(); + } + public int getReceiveBufferSize() throws SocketException { + return socket.getReceiveBufferSize(); + } + public SocketAddress getRemoteSocketAddress() { + return socket.getRemoteSocketAddress(); + } + public boolean getReuseAddress() throws SocketException { + return socket.getReuseAddress(); + } + public int getSendBufferSize() throws SocketException { + return socket.getSendBufferSize(); + } + public int getSoLinger() throws SocketException { + return socket.getSoLinger(); + } + public int getSoTimeout() throws SocketException { + return socket.getSoTimeout(); + } + public boolean getTcpNoDelay() throws SocketException { + return socket.getTcpNoDelay(); + } + public int getTrafficClass() throws SocketException { + return socket.getTrafficClass(); + } + public boolean isBound() { + return socket.isBound(); + } + public boolean isClosed() { + return socket.isClosed(); + } + public boolean isConnected() { + return socket.isConnected(); + } + public void setKeepAlive(boolean on) throws SocketException { + socket.setKeepAlive(on); + } + public void setOOBInline(boolean on) throws SocketException { + socket.setOOBInline(on); + } + public void setReceiveBufferSize(int size) throws SocketException { + socket.setReceiveBufferSize(size); + } + public void setReuseAddress(boolean on) throws SocketException { + socket.setReuseAddress(on); + } + public void setSendBufferSize(int size) throws SocketException { + socket.setSendBufferSize(size); + } + public void setSoLinger(boolean on, int linger) throws SocketException { + socket.setSoLinger(on, linger); + } + public void setTcpNoDelay(boolean on) throws SocketException { + socket.setTcpNoDelay(on); + } + public void setTrafficClass(int tc) throws SocketException { + socket.setTrafficClass(tc); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/NIOSyncChannel.java b/activeio/src/java/org/activeio/net/NIOSyncChannel.java new file mode 100644 index 0000000000..4a15fe63ac --- /dev/null +++ b/activeio/src/java/org/activeio/net/NIOSyncChannel.java @@ -0,0 +1,105 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.SocketTimeoutException; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; + +import org.activeio.ByteSequence; +import org.activeio.Packet; +import org.activeio.SyncChannel; +import org.activeio.SyncChannelServer; +import org.activeio.packet.ByteBufferPacket; +import org.activeio.packet.EOSPacket; +import org.activeio.packet.EmptyPacket; + +/** + * A {@see org.activeio.SynchChannel} implementation that uses a {@see java.nio.channels.SocketChannel} + * to talk to the network. + * + * Using a SocketChannelSynchChannel should be more efficient than using a SocketSynchChannel since + * direct ByteBuffer can be used to reduce the jvm overhead needed to copy byte[]s. + * + * @version $Revision$ + */ +final public class NIOSyncChannel extends NIOBaseChannel implements SyncChannel { + + private ByteBuffer inputByteBuffer; +// private Packet data2; + + protected NIOSyncChannel(SocketChannel socketChannel) throws IOException { + this(socketChannel, true ); + } + + protected NIOSyncChannel(SocketChannel socketChannel, boolean useDirect) throws IOException { + super(socketChannel, useDirect); + } + + public Packet read(long timeout) throws IOException { + try { + + if( timeout==SyncChannelServer.WAIT_FOREVER_TIMEOUT ) + setSoTimeout( 0 ); + else if( timeout==SyncChannelServer.NO_WAIT_TIMEOUT ) + setSoTimeout( 1 ); + else + setSoTimeout( (int)timeout ); + + if( inputByteBuffer==null || !inputByteBuffer.hasRemaining() ) { + inputByteBuffer = allocateBuffer(); + } + + int size = socketChannel.read(inputByteBuffer); + if( size == -1 ) + return EOSPacket.EOS_PACKET; + if( size == 0 ) + return EmptyPacket.EMPTY_PACKET; + + ByteBuffer remaining = inputByteBuffer.slice(); + Packet data = new ByteBufferPacket(((ByteBuffer)inputByteBuffer.flip()).slice()); + + // Keep the remaining buffer around to fill with data. + inputByteBuffer = remaining; + return data; + + } catch (SocketTimeoutException e) { + return null; + } + } + + public void write(Packet packet) throws IOException { + ByteBuffer data; + if( packet.getClass()==ByteBufferPacket.class ) { + data = ((ByteBufferPacket)packet).getByteBuffer(); + } else { + ByteSequence sequence = packet.asByteSequence(); + data = ByteBuffer.wrap(sequence.getData(), sequence.getOffset(), sequence.getLength()); + } + socketChannel.write( data ); + } + + public void start() throws IOException { + } + + public void stop(long timeout) throws IOException { + } + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/NIOSyncChannelFactory.java b/activeio/src/java/org/activeio/net/NIOSyncChannelFactory.java new file mode 100644 index 0000000000..da75a8ba09 --- /dev/null +++ b/activeio/src/java/org/activeio/net/NIOSyncChannelFactory.java @@ -0,0 +1,127 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +import org.activeio.SyncChannel; +import org.activeio.SyncChannelFactory; +import org.activeio.SyncChannelServer; +import org.activeio.filter.WriteBufferedSyncChannel; +import org.activeio.packet.ByteBufferPacket; + +/** + * A TcpSynchChannelFactory creates {@see org.activeio.net.TcpSynchChannel} + * and {@see org.activeio.net.TcpSynchChannelServer} objects. + * + * @version $Revision$ + */ +public class NIOSyncChannelFactory implements SyncChannelFactory { + + protected static final int DEFAULT_BUFFER_SIZE = Integer.parseInt(System.getProperty("org.activeio.net.nio.BufferSize", ""+(64*1024))); + + protected static final int DEFAULT_BACKLOG = 500; + boolean useDirectBuffers = true; + private final boolean createWriteBufferedChannels; + private int backlog = DEFAULT_BACKLOG; + + public NIOSyncChannelFactory() { + this(true); + } + + public NIOSyncChannelFactory(boolean createWriteBufferedChannels) { + this.createWriteBufferedChannels = createWriteBufferedChannels; + } + + + /** + * Uses the {@param location}'s host and port to create a tcp connection to a remote host. + * + * @see org.activeio.SynchChannelFactory#openSyncChannel(java.net.URI) + */ + public SyncChannel openSyncChannel(URI location) throws IOException { + SocketChannel channel = SocketChannel.open(); + channel.connect(new InetSocketAddress(location.getHost(), location.getPort())); + return createSynchChannel(channel); + } + + /** + * @param channel + * @return + * @throws IOException + */ + protected SyncChannel createSynchChannel(SocketChannel socketChannel) throws IOException { + SyncChannel channel = new NIOSyncChannel(socketChannel); + if( createWriteBufferedChannels ) { + channel = new WriteBufferedSyncChannel(channel, ByteBufferPacket.createDefaultBuffer(useDirectBuffers)); + } + return channel; + } + + /** + * Binds a server socket a the {@param location}'s port. + * + * @see org.activeio.SynchChannelFactory#bindSynchChannel(java.net.URI) + */ + public SyncChannelServer bindSyncChannel(URI bindURI) throws IOException { + + String host = bindURI.getHost(); + InetSocketAddress address; + if( host == null || host.length() == 0 || host.equals("localhost") || host.equals("0.0.0.0") || InetAddress.getLocalHost().getHostName().equals(host) ) { + address = new InetSocketAddress(bindURI.getPort()); + } else { + address = new InetSocketAddress(bindURI.getHost(), bindURI.getPort()); + } + + ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); + serverSocketChannel.socket().bind(address,backlog); + + URI connectURI = bindURI; + try { +// connectURI = URISupport.changeHost(connectURI, InetAddress.getLocalHost().getHostName()); + connectURI = URISupport.changePort(connectURI, serverSocketChannel.socket().getLocalPort()); + } catch (URISyntaxException e) { + throw (IOException)new IOException("Could not build connect URI: "+e).initCause(e); + } + + return new NIOSyncChannelServer(serverSocketChannel, bindURI, connectURI, createWriteBufferedChannels, useDirectBuffers); + } + + /** + * @return Returns the backlog. + */ + public int getBacklog() { + return backlog; + } + + /** + * @param backlog + * The backlog to set. + */ + public void setBacklog(int backlog) { + this.backlog = backlog; + } + + +} diff --git a/activeio/src/java/org/activeio/net/NIOSyncChannelServer.java b/activeio/src/java/org/activeio/net/NIOSyncChannelServer.java new file mode 100644 index 0000000000..9d9238cb30 --- /dev/null +++ b/activeio/src/java/org/activeio/net/NIOSyncChannelServer.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; +import java.nio.channels.ServerSocketChannel; + +import org.activeio.Channel; +import org.activeio.SyncChannel; +import org.activeio.filter.WriteBufferedSyncChannel; +import org.activeio.packet.ByteBufferPacket; + +/** + * A SynchChannelServer that creates + * {@see org.activeio.net.TcpSynchChannel}objects from accepted + * tcp socket connections. + * + * @version $Revision$ + */ +public class NIOSyncChannelServer extends SocketSyncChannelServer { + + private final boolean createWriteBufferedChannels; + private final boolean useDirectBuffers; + + public NIOSyncChannelServer(ServerSocketChannel socketChannel, URI bindURI, URI connectURI, boolean createWriteBufferedChannels, boolean useDirectBuffers) { + super(socketChannel.socket(), bindURI, connectURI); + this.createWriteBufferedChannels = createWriteBufferedChannels; + this.useDirectBuffers = useDirectBuffers; + } + + protected Channel createChannel(SocketStreamChannel c) throws IOException { + SyncChannel channel = new NIOSyncChannel(c.getSocket().getChannel()); + if( createWriteBufferedChannels ) { + channel = new WriteBufferedSyncChannel(channel, ByteBufferPacket.createDefaultBuffer(useDirectBuffers)); + } + return channel; + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/SocketMetadata.java b/activeio/src/java/org/activeio/net/SocketMetadata.java new file mode 100644 index 0000000000..d54cfe6949 --- /dev/null +++ b/activeio/src/java/org/activeio/net/SocketMetadata.java @@ -0,0 +1,78 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.net.InetAddress; +import java.net.SocketAddress; +import java.net.SocketException; + +/** + */ +public interface SocketMetadata { + public InetAddress getInetAddress(); + + public boolean getKeepAlive() throws SocketException; + + public InetAddress getLocalAddress(); + + public int getLocalPort(); + + public SocketAddress getLocalSocketAddress(); + + public boolean getOOBInline() throws SocketException; + + public int getPort(); + + public int getReceiveBufferSize() throws SocketException; + + public SocketAddress getRemoteSocketAddress(); + + public boolean getReuseAddress() throws SocketException; + + public int getSendBufferSize() throws SocketException; + + public int getSoLinger() throws SocketException; + + public int getSoTimeout() throws SocketException; + + public boolean getTcpNoDelay() throws SocketException; + + public int getTrafficClass() throws SocketException; + + public boolean isBound(); + + public boolean isClosed(); + + public boolean isConnected(); + + public void setKeepAlive(boolean on) throws SocketException; + + public void setOOBInline(boolean on) throws SocketException; + + public void setReceiveBufferSize(int size) throws SocketException; + + public void setReuseAddress(boolean on) throws SocketException; + + public void setSendBufferSize(int size) throws SocketException; + + public void setSoLinger(boolean on, int linger) throws SocketException; + + public void setTcpNoDelay(boolean on) throws SocketException; + + public void setTrafficClass(int tc) throws SocketException; +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/SocketStreamChannel.java b/activeio/src/java/org/activeio/net/SocketStreamChannel.java new file mode 100644 index 0000000000..3cf28bdef9 --- /dev/null +++ b/activeio/src/java/org/activeio/net/SocketStreamChannel.java @@ -0,0 +1,234 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; + +import org.activeio.StreamChannel; + +/** + * A {@see org.activeio.StreamChannel} implementation that uses a {@see java.net.Socket} + * to talk to the network. + * + * @version $Revision$ + */ +public class SocketStreamChannel implements StreamChannel, SocketMetadata { + + private final Socket socket; + private final OutputStream out; + private final InputStream in; + private boolean disposed; + private int curentSoTimeout; + + protected SocketStreamChannel(Socket socket) throws IOException { + this.socket = socket; + in = socket.getInputStream(); + out = socket.getOutputStream(); + } + + protected void setSoTimeout(int i) throws SocketException { + if( curentSoTimeout != i ) { + socket.setSoTimeout(i); + curentSoTimeout = i; + } + } + + /** + * @see org.activeio.Channel#flush() + */ + public void flush() throws IOException { + out.flush(); + } + + /** + * @see org.activeio.Disposable#dispose() + */ + public void dispose() { + if (disposed) + return; + + try { + out.close(); + } catch (IOException ignore) { + } + try { + in.close(); + } catch (IOException ignore) { + } + try { + socket.close(); + } catch (IOException ignore) { + } + disposed = true; + } + + public void start() throws IOException { + } + public void stop(long timeout) throws IOException { + } + + public InetAddress getInetAddress() { + return socket.getInetAddress(); + } + public boolean getKeepAlive() throws SocketException { + return socket.getKeepAlive(); + } + public InetAddress getLocalAddress() { + return socket.getLocalAddress(); + } + public int getLocalPort() { + return socket.getLocalPort(); + } + public SocketAddress getLocalSocketAddress() { + return socket.getLocalSocketAddress(); + } + public boolean getOOBInline() throws SocketException { + return socket.getOOBInline(); + } + public int getPort() { + return socket.getPort(); + } + public int getReceiveBufferSize() throws SocketException { + return socket.getReceiveBufferSize(); + } + public SocketAddress getRemoteSocketAddress() { + return socket.getRemoteSocketAddress(); + } + public boolean getReuseAddress() throws SocketException { + return socket.getReuseAddress(); + } + public int getSendBufferSize() throws SocketException { + return socket.getSendBufferSize(); + } + public int getSoLinger() throws SocketException { + return socket.getSoLinger(); + } + public int getSoTimeout() throws SocketException { + return socket.getSoTimeout(); + } + public boolean getTcpNoDelay() throws SocketException { + return socket.getTcpNoDelay(); + } + public int getTrafficClass() throws SocketException { + return socket.getTrafficClass(); + } + public boolean isBound() { + return socket.isBound(); + } + public boolean isClosed() { + return socket.isClosed(); + } + public boolean isConnected() { + return socket.isConnected(); + } + public void setKeepAlive(boolean on) throws SocketException { + socket.setKeepAlive(on); + } + public void setOOBInline(boolean on) throws SocketException { + socket.setOOBInline(on); + } + public void setReceiveBufferSize(int size) throws SocketException { + socket.setReceiveBufferSize(size); + } + public void setReuseAddress(boolean on) throws SocketException { + socket.setReuseAddress(on); + } + public void setSendBufferSize(int size) throws SocketException { + socket.setSendBufferSize(size); + } + public void setSoLinger(boolean on, int linger) throws SocketException { + socket.setSoLinger(on, linger); + } + public void setTcpNoDelay(boolean on) throws SocketException { + socket.setTcpNoDelay(on); + } + public void setTrafficClass(int tc) throws SocketException { + socket.setTrafficClass(tc); + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return null; + } + + public String toString() { + return "Socket Connection: "+getLocalSocketAddress()+" -> "+getRemoteSocketAddress(); + } + + public void write(byte[] data, int pos, int length) throws IOException { + out.write(data,pos,length); + } + + public void write(byte[] data) throws IOException { + out.write(data); + } + + public void write(int data) throws IOException { + out.write(data); + } + + public int available() throws IOException { + return in.available(); + } + + public void mark(int pos) { + in.mark(pos); + } + + public boolean markSupported() { + return in.markSupported(); + } + + public int read(byte[] data, int offset, int length) throws IOException { + try { + return in.read(data, offset, length); + } catch (ArrayIndexOutOfBoundsException e) { + e.printStackTrace(); + return 0; + } + } + + public int read(byte[] data) throws IOException { + return in.read(data); + } + + public void reset() throws IOException { + in.reset(); + } + + public long skip(long count) throws IOException { + return in.skip(count); + } + + public int read() throws IOException { + return in.read(); + } + + public Socket getSocket() { + return socket; + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/SocketStreamChannelFactory.java b/activeio/src/java/org/activeio/net/SocketStreamChannelFactory.java new file mode 100644 index 0000000000..5d29224890 --- /dev/null +++ b/activeio/src/java/org/activeio/net/SocketStreamChannelFactory.java @@ -0,0 +1,126 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.net.URISyntaxException; + +import javax.net.ServerSocketFactory; +import javax.net.SocketFactory; + +import org.activeio.StreamChannel; +import org.activeio.StreamChannelFactory; +import org.activeio.StreamChannelServer; + +/** + * A TcpStreamChannelFactory creates {@see org.activeio.net.TcpStreamChannel} + * and {@see org.activeio.net.TcpStreamChannelServer} objects. + * + * @version $Revision$ + */ +public class SocketStreamChannelFactory implements StreamChannelFactory { + + protected static final int DEFAULT_BACKLOG = 500; + + private final SocketFactory socketFactory; + private final ServerSocketFactory serverSocketFactory; + private int backlog = DEFAULT_BACKLOG; + + public SocketStreamChannelFactory() { + this(SocketFactory.getDefault(), ServerSocketFactory.getDefault()); + } + + public SocketStreamChannelFactory(SocketFactory socketFactory, ServerSocketFactory serverSocketFactory) { + this.socketFactory = socketFactory; + this.serverSocketFactory = serverSocketFactory; + } + + /** + * Uses the {@param location}'s host and port to create a tcp connection to a remote host. + * + * @see org.activeio.StreamChannelFactory#openStreamChannel(java.net.URI) + */ + public StreamChannel openStreamChannel(URI location) throws IOException { + Socket socket=null; + socket = socketFactory.createSocket(location.getHost(), location.getPort()); + return createStreamChannel(socket); + } + + /** + * @param socket + * @return + * @throws IOException + */ + protected StreamChannel createStreamChannel(Socket socket) throws IOException { + return new SocketStreamChannel(socket); + } + + /** + * Binds a server socket a the {@param bindURI}'s port. + * + * @see org.activeio.StreamChannelFactory#bindStreamChannel(java.net.URI) + */ + public StreamChannelServer bindStreamChannel(URI bindURI) throws IOException { + + String host = bindURI.getHost(); + InetAddress addr; + if( host == null || host.length() == 0 || host.equals("localhost") || host.equals("0.0.0.0") || InetAddress.getLocalHost().getHostName().equals(host) ) { + addr = InetAddress.getLocalHost(); + } else { + addr = InetAddress.getByName(host); + } + ServerSocket serverSocket; + + if (addr.equals(InetAddress.getLocalHost())) { + serverSocket = serverSocketFactory.createServerSocket(bindURI.getPort(), backlog); + } else { + serverSocket = serverSocketFactory.createServerSocket(bindURI.getPort(), backlog, addr); + } + + URI connectURI=bindURI; + try { + // connectURI = URISupport.changeHost(connectURI, addr.getHostName()); + connectURI = URISupport.changePort(connectURI, serverSocket.getLocalPort()); + } catch (URISyntaxException e) { + throw (IOException)new IOException("Could build connect URI: "+e).initCause(e); + } + + return new SocketStreamChannelServer(serverSocket, bindURI, connectURI); + } + + /** + * @return Returns the backlog. + */ + public int getBacklog() { + return backlog; + } + + /** + * @param backlog + * The backlog to set. + */ + public void setBacklog(int backlog) { + this.backlog = backlog; + } + + +} diff --git a/activeio/src/java/org/activeio/net/SocketStreamChannelServer.java b/activeio/src/java/org/activeio/net/SocketStreamChannelServer.java new file mode 100644 index 0000000000..7dcc01ab3d --- /dev/null +++ b/activeio/src/java/org/activeio/net/SocketStreamChannelServer.java @@ -0,0 +1,128 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.URI; + +import org.activeio.Channel; +import org.activeio.StreamChannelServer; + +/** + * A StreamChannelServer that creates + * {@see org.activeio.net.TcpStreamChannel}objects from accepted + * tcp socket connections. + * + * @version $Revision$ + */ +public class SocketStreamChannelServer implements StreamChannelServer { + + private ServerSocket serverSocket; + private int curentSoTimeout = 0; + private final URI bindURI; + private final URI connectURI; + + + /** + * @param serverSocket + * @param bindURI + * @param connectURI + */ + public SocketStreamChannelServer(ServerSocket serverSocket, URI bindURI, URI connectURI) { + this.serverSocket=serverSocket; + this.bindURI=bindURI; + this.connectURI=connectURI; + } + + public Channel accept(long timeout) throws IOException { + try { + if (timeout == StreamChannelServer.WAIT_FOREVER_TIMEOUT) + setSoTimeout(0); + else if (timeout == StreamChannelServer.NO_WAIT_TIMEOUT) + setSoTimeout(1); + else + setSoTimeout((int) timeout); + + Socket socket = serverSocket.accept(); + return createChannel(socket); + + } catch (SocketTimeoutException ignore) { + } + return null; + } + + protected Channel createChannel(Socket socket) throws IOException { + return new SocketStreamChannel(socket); + } + + private void setSoTimeout(int i) throws SocketException { + if (curentSoTimeout != i) { + serverSocket.setSoTimeout(i); + curentSoTimeout = i; + } + } + + /** + * @see org.activeio.Disposable#dispose() + */ + public void dispose() { + if (serverSocket == null) + return; + try { + serverSocket.close(); + } catch (IOException ignore) { + } + serverSocket = null; + } + + /** + * @return Returns the bindURI. + */ + public URI getBindURI() { + return bindURI; + } + + /** + * @return Returns the connectURI. + */ + public URI getConnectURI() { + return connectURI; + } + + public void start() throws IOException { + } + + public void stop(long timeout) throws IOException { + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return null; + } + + public String toString() { + return "Socket Server: "+getConnectURI(); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/SocketSyncChannel.java b/activeio/src/java/org/activeio/net/SocketSyncChannel.java new file mode 100644 index 0000000000..2b25093e4e --- /dev/null +++ b/activeio/src/java/org/activeio/net/SocketSyncChannel.java @@ -0,0 +1,226 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketTimeoutException; + +import org.activeio.ByteSequence; +import org.activeio.Packet; +import org.activeio.SyncChannel; +import org.activeio.SyncChannelServer; +import org.activeio.adapter.OutputStreamChannelToOutputStream; +import org.activeio.packet.ByteArrayPacket; +import org.activeio.packet.EOSPacket; +import org.activeio.packet.EmptyPacket; + +/** + * A {@see org.activeio.SynchChannel} implementation that uses a {@see java.net.Socket} + * to talk to the network. + * + * @version $Revision$ + */ +public class SocketSyncChannel implements SyncChannel, SocketMetadata { + + protected static final int DEFAULT_BUFFER_SIZE = 64 * 1024; + private final SocketStreamChannel channel; + private Packet inputPacket; + private final OutputStreamChannelToOutputStream outputStream; + + protected SocketSyncChannel(Socket socket) throws IOException { + this(new SocketStreamChannel(socket)); + } + + public SocketSyncChannel(SocketStreamChannel channel) throws IOException { + this.channel = channel; + outputStream = new OutputStreamChannelToOutputStream(channel); + setReceiveBufferSize(DEFAULT_BUFFER_SIZE); + setSendBufferSize(DEFAULT_BUFFER_SIZE); + } + + /** + * @see org.activeio.SynchChannel#read(long) + */ + synchronized public org.activeio.Packet read(long timeout) throws IOException { + try { + + if( timeout==SyncChannelServer.WAIT_FOREVER_TIMEOUT ) + setSoTimeout( 0 ); + else if( timeout==SyncChannelServer.NO_WAIT_TIMEOUT ) + setSoTimeout( 1 ); + else + setSoTimeout( (int)timeout ); + + if( inputPacket==null || !inputPacket.hasRemaining() ) { + inputPacket = allocatePacket(); + } + + ByteSequence sequence = inputPacket.asByteSequence(); + int size = channel.read(sequence.getData(), sequence.getOffset(), sequence.getLength()); + if( size == -1 ) + return EOSPacket.EOS_PACKET; + if( size == 0 ) + return EmptyPacket.EMPTY_PACKET; + inputPacket.position(size); + + Packet remaining = inputPacket.slice(); + + inputPacket.flip(); + Packet data = inputPacket.slice(); + + // Keep the remaining buffer around to fill with data. + inputPacket = remaining; + return data; + + } catch (SocketTimeoutException e) { + return null; + } + } + + private Packet allocatePacket() { + byte[] data = new byte[DEFAULT_BUFFER_SIZE]; + return new ByteArrayPacket(data); + } + + protected void setSoTimeout(int i) throws SocketException { + channel.setSoTimeout(i); + } + + /** + * @see org.activeio.Channel#write(org.activeio.Packet) + */ + public void write(Packet packet) throws IOException { + packet.writeTo(outputStream); + } + + /** + * @see org.activeio.Channel#flush() + */ + public void flush() throws IOException { + channel.flush(); + } + + /** + * @see org.activeio.Disposable#dispose() + */ + public void dispose() { + channel.dispose(); + } + + public void start() throws IOException { + channel.start(); + } + public void stop(long timeout) throws IOException { + channel.stop(timeout); + } + + public InetAddress getInetAddress() { + return channel.getInetAddress(); + } + public boolean getKeepAlive() throws SocketException { + return channel.getKeepAlive(); + } + public InetAddress getLocalAddress() { + return channel.getLocalAddress(); + } + public int getLocalPort() { + return channel.getLocalPort(); + } + public SocketAddress getLocalSocketAddress() { + return channel.getLocalSocketAddress(); + } + public boolean getOOBInline() throws SocketException { + return channel.getOOBInline(); + } + public int getPort() { + return channel.getPort(); + } + public int getReceiveBufferSize() throws SocketException { + return channel.getReceiveBufferSize(); + } + public SocketAddress getRemoteSocketAddress() { + return channel.getRemoteSocketAddress(); + } + public boolean getReuseAddress() throws SocketException { + return channel.getReuseAddress(); + } + public int getSendBufferSize() throws SocketException { + return channel.getSendBufferSize(); + } + public int getSoLinger() throws SocketException { + return channel.getSoLinger(); + } + public int getSoTimeout() throws SocketException { + return channel.getSoTimeout(); + } + public boolean getTcpNoDelay() throws SocketException { + return channel.getTcpNoDelay(); + } + public int getTrafficClass() throws SocketException { + return channel.getTrafficClass(); + } + public boolean isBound() { + return channel.isBound(); + } + public boolean isClosed() { + return channel.isClosed(); + } + public boolean isConnected() { + return channel.isConnected(); + } + public void setKeepAlive(boolean on) throws SocketException { + channel.setKeepAlive(on); + } + public void setOOBInline(boolean on) throws SocketException { + channel.setOOBInline(on); + } + public void setReceiveBufferSize(int size) throws SocketException { + channel.setReceiveBufferSize(size); + } + public void setReuseAddress(boolean on) throws SocketException { + channel.setReuseAddress(on); + } + public void setSendBufferSize(int size) throws SocketException { + channel.setSendBufferSize(size); + } + public void setSoLinger(boolean on, int linger) throws SocketException { + channel.setSoLinger(on, linger); + } + public void setTcpNoDelay(boolean on) throws SocketException { + channel.setTcpNoDelay(on); + } + public void setTrafficClass(int tc) throws SocketException { + channel.setTrafficClass(tc); + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return channel.getAdapter(target); + } + + public String toString() { + return channel.toString(); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/SocketSyncChannelFactory.java b/activeio/src/java/org/activeio/net/SocketSyncChannelFactory.java new file mode 100644 index 0000000000..28402d667e --- /dev/null +++ b/activeio/src/java/org/activeio/net/SocketSyncChannelFactory.java @@ -0,0 +1,94 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; + +import javax.net.ServerSocketFactory; +import javax.net.SocketFactory; + +import org.activeio.StreamChannelServer; +import org.activeio.SyncChannel; +import org.activeio.SyncChannelFactory; +import org.activeio.SyncChannelServer; + +/** + * A TcpSynchChannelFactory creates {@see org.activeio.net.TcpSynchChannel} + * and {@see org.activeio.net.TcpSynchChannelServer} objects. + * + * @version $Revision$ + */ +public class SocketSyncChannelFactory implements SyncChannelFactory { + + SocketStreamChannelFactory factory; + + public SocketSyncChannelFactory() { + this(SocketFactory.getDefault(), ServerSocketFactory.getDefault()); + } + + public SocketSyncChannelFactory(SocketFactory socketFactory, ServerSocketFactory serverSocketFactory) { + factory = new SocketStreamChannelFactory(socketFactory, serverSocketFactory); + } + + /** + * Uses the {@param location}'s host and port to create a tcp connection to a remote host. + * + * @see org.activeio.SyncChannelFactory#openSyncChannel(java.net.URI) + */ + public SyncChannel openSyncChannel(URI location) throws IOException { + SocketStreamChannel channel = (SocketStreamChannel) factory.openStreamChannel(location); + return createSynchChannel(channel); + } + + /** + * @param channel + * @return + * @throws IOException + */ + protected SyncChannel createSynchChannel(SocketStreamChannel channel) throws IOException { + return new SocketSyncChannel(channel); + } + + /** + * Binds a server socket a the {@param bindURI}'s port. + * + * @see org.activeio.SyncChannelFactory#bindSyncChannel(java.net.URI) + */ + public SyncChannelServer bindSyncChannel(URI bindURI) throws IOException { + StreamChannelServer server = factory.bindStreamChannel(bindURI); + return new SocketSyncChannelServer((SocketStreamChannelServer) server); + } + + /** + * @return Returns the backlog. + */ + public int getBacklog() { + return factory.getBacklog(); + } + + /** + * @param backlog + * The backlog to set. + */ + public void setBacklog(int backlog) { + factory.setBacklog(backlog); + } + + +} diff --git a/activeio/src/java/org/activeio/net/SocketSyncChannelServer.java b/activeio/src/java/org/activeio/net/SocketSyncChannelServer.java new file mode 100644 index 0000000000..0ba0df4355 --- /dev/null +++ b/activeio/src/java/org/activeio/net/SocketSyncChannelServer.java @@ -0,0 +1,98 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.URI; + +import org.activeio.Channel; +import org.activeio.SyncChannelServer; + +/** + * A SynchChannelServer that creates + * {@see org.activeio.net.TcpSynchChannel}objects from accepted + * TCP socket connections. + * + * @version $Revision$ + */ +public class SocketSyncChannelServer implements SyncChannelServer { + + private final SocketStreamChannelServer server; + + public SocketSyncChannelServer(SocketStreamChannelServer server) { + this.server = server; + } + + public SocketSyncChannelServer(ServerSocket socket, URI bindURI, URI connectURI) { + this(new SocketStreamChannelServer(socket, bindURI, connectURI)); + } + + public Channel accept(long timeout) throws IOException { + Channel channel = server.accept(timeout); + if( channel != null ) { + channel = createChannel((SocketStreamChannel) channel); + } + return channel; + } + + protected Channel createChannel(SocketStreamChannel channel) throws IOException { + return new SocketSyncChannel(channel); + } + + /** + * @see org.activeio.Disposable#dispose() + */ + public void dispose() { + server.dispose(); + } + + /** + * @return Returns the bindURI. + */ + public URI getBindURI() { + return server.getBindURI(); + } + + /** + * @return Returns the connectURI. + */ + public URI getConnectURI() { + return server.getConnectURI(); + } + + public void start() throws IOException { + server.start(); + } + + public void stop(long timeout) throws IOException { + server.stop(timeout); + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return server.getAdapter(target); + } + + public String toString() { + return server.toString(); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/SslSocketSyncChannelFactory.java b/activeio/src/java/org/activeio/net/SslSocketSyncChannelFactory.java new file mode 100644 index 0000000000..bf74b73616 --- /dev/null +++ b/activeio/src/java/org/activeio/net/SslSocketSyncChannelFactory.java @@ -0,0 +1,34 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocketFactory; + +/** + * A SslSynchChannelFactory creates {@see org.activeio.net.TcpSynchChannel} + * and {@see org.activeio.net.TcpSynchChannelServer} objects that use SSL. + * + * @version $Revision$ + */ +public class SslSocketSyncChannelFactory extends SocketSyncChannelFactory { + + public SslSocketSyncChannelFactory() { + super(SSLSocketFactory.getDefault(), SSLServerSocketFactory.getDefault()); + } +} diff --git a/activeio/src/java/org/activeio/net/URISupport.java b/activeio/src/java/org/activeio/net/URISupport.java new file mode 100644 index 0000000000..3f018b37f2 --- /dev/null +++ b/activeio/src/java/org/activeio/net/URISupport.java @@ -0,0 +1,52 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.net.URI; +import java.net.URISyntaxException; + +/** + * The URISupport class provides a few static methods that provides a few usefull + * operations to manipulate URIs. + * + * @version $Revision$ + */ +public class URISupport { + + static public URI changePort(URI bindAddr, int port) throws URISyntaxException { + return new URI(bindAddr.getScheme(), bindAddr.getUserInfo(), bindAddr.getHost(), port, bindAddr.getPath(), bindAddr.getQuery(), bindAddr.getFragment()); + } + static public URI changeScheme(URI bindAddr, String scheme) throws URISyntaxException { + return new URI(scheme, bindAddr.getUserInfo(), bindAddr.getHost(), bindAddr.getPort(), bindAddr.getPath(), bindAddr.getQuery(), bindAddr.getFragment()); + } + static public URI changeUserInfo(URI bindAddr, String userInfo) throws URISyntaxException { + return new URI(bindAddr.getScheme(), userInfo, bindAddr.getHost(), bindAddr.getPort(), bindAddr.getPath(), bindAddr.getQuery(), bindAddr.getFragment()); + } + static public URI changeHost(URI bindAddr, String host) throws URISyntaxException { + return new URI(bindAddr.getScheme(), bindAddr.getUserInfo(), host, bindAddr.getPort(), bindAddr.getPath(), bindAddr.getQuery(), bindAddr.getFragment()); + } + static public URI changePath(URI bindAddr, String path) throws URISyntaxException { + return new URI(bindAddr.getScheme(), bindAddr.getUserInfo(), bindAddr.getHost(), bindAddr.getPort(), path, bindAddr.getQuery(), bindAddr.getFragment()); + } + static public URI changeQuery(URI bindAddr, String query) throws URISyntaxException { + return new URI(bindAddr.getScheme(), bindAddr.getUserInfo(), bindAddr.getHost(), bindAddr.getPort(), bindAddr.getPath(), query, bindAddr.getFragment()); + } + static public URI changeFragment(URI bindAddr, String fragment) throws URISyntaxException { + return new URI(bindAddr.getScheme(), bindAddr.getUserInfo(), bindAddr.getHost(), bindAddr.getPort(), bindAddr.getPath(), bindAddr.getQuery(), fragment); + } + +} diff --git a/activeio/src/java/org/activeio/net/VMPipeAsyncChannelFactory.java b/activeio/src/java/org/activeio/net/VMPipeAsyncChannelFactory.java new file mode 100644 index 0000000000..ece27011ce --- /dev/null +++ b/activeio/src/java/org/activeio/net/VMPipeAsyncChannelFactory.java @@ -0,0 +1,253 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelFactory; +import org.activeio.AsyncChannelListener; +import org.activeio.AsyncChannelServer; +import org.activeio.Packet; + +/** + * + * @version $Revision$ + */ +final public class VMPipeAsyncChannelFactory implements AsyncChannelFactory { + + // + // We do all this crazy stuff of looking the server map using System + // properties + // because this class could be loaded multiple times in different + // classloaders. + // + private static final String SERVER_MAP_LOCATION = VMPipeAsyncChannelFactory.class.getName() + ".SERVER_MAP"; + + private static final Map SERVER_MAP; + static { + Map m = null; + m = (Map) System.getProperties().get(SERVER_MAP_LOCATION); + if (m == null) { + m = Collections.synchronizedMap(new HashMap()); + System.getProperties().put(SERVER_MAP_LOCATION, m); + } + SERVER_MAP = m; + } + + private final static ClassLoader MY_CLASSLOADER = Packet.class.getClassLoader(); + + + /** + * Used to marshal calls to a PipeChannel in a different classloader. + */ + static public class ClassloaderAsyncChannelAdapter implements AsyncChannel { + + private final ClassLoader cl; + private final Object channel; + private final Method writeMethod; + private final Method setListenerMethod; + private final Class listenerClazz; + private final Class packetClazz; + private final Object listenerProxy; + private final Method duplicateMethod; + private final Method startMethod; + private final Method stopMethod; + private final Method disposeMethod; + + private AsyncChannelListener channelListener; + + public class ListenerProxyHandler implements InvocationHandler { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + switch (method.getName().length()) { + case 8: // onPacket + Object packet = duplicateMethod.invoke(args[0], new Object[]{MY_CLASSLOADER}); + channelListener.onPacket((Packet) packet); + break; + case 13: // onPacketError + channelListener.onPacketError((IOException) args[0]); + break; + default: + channelListener.onPacketError(new IOException("Unknown proxy method invocation: "+method.getName())); + } + return null; + } + } + + public ClassloaderAsyncChannelAdapter(Object channel) throws SecurityException, NoSuchMethodException, + ClassNotFoundException { + this.channel = channel; + Class clazz = channel.getClass(); + cl = clazz.getClassLoader(); + + listenerClazz = cl.loadClass(AsyncChannelListener.class.getName()); + packetClazz = cl.loadClass(Packet.class.getName()); + writeMethod = clazz.getMethod("write", new Class[] { packetClazz }); + startMethod = clazz.getMethod("start", new Class[] { }); + stopMethod = clazz.getMethod("stop", new Class[] { long.class }); + disposeMethod = clazz.getMethod("dispose", new Class[] { }); + + setListenerMethod = clazz.getMethod("setAsyncChannelListener", new Class[] { listenerClazz }); + duplicateMethod = packetClazz.getMethod("duplicate", new Class[] { ClassLoader.class }); + + ListenerProxyHandler handler = new ListenerProxyHandler(); + listenerProxy = Proxy.newProxyInstance(cl, new Class[] { listenerClazz }, handler); + } + + public void write(Packet packet) throws IOException { + callIOExceptionMethod(writeMethod, new Object[] { packet.duplicate(cl) }); + } + + public void setAsyncChannelListener(AsyncChannelListener channelListener) { + this.channelListener = channelListener; + callMethod(setListenerMethod, new Object[] { channelListener == null ? null : listenerProxy }); + } + + public AsyncChannelListener getAsyncChannelListener() { + return channelListener; + } + + public void dispose() { + callMethod(disposeMethod, new Object[] { }); + } + + public void start() throws IOException { + callIOExceptionMethod(startMethod, new Object[] {}); + } + + public void stop(long timeout) throws IOException { + callIOExceptionMethod(stopMethod, new Object[] {new Long(timeout)}); + } + + private void callMethod(Method method, Object[] args) { + try { + method.invoke(channel, args); + } catch (InvocationTargetException e) { + if (e.getTargetException() instanceof RuntimeException) { + throw (RuntimeException) e.getTargetException(); + } + throw new RuntimeException(e.getTargetException()); + } catch (Throwable e) { + throw new RuntimeException("Reflexive invocation failed: " + e, e); + } + } + + private void callIOExceptionMethod(Method method, Object[] args) throws IOException { + try { + method.invoke(channel, args); + } catch (InvocationTargetException e) { + if (e.getTargetException() instanceof IOException) { + throw (IOException) e.getTargetException(); + } + if (e.getTargetException() instanceof RuntimeException) { + throw (RuntimeException) e.getTargetException(); + } + throw new RuntimeException(e.getTargetException()); + } catch (Throwable e) { + throw (IOException) new IOException("Reflexive invocation failed: " + e).initCause(e); + } + } + + // + // The following methods do not need to delegate since they + // are implemented as noops in the PipeChannel + // + public Object getAdapter(Class target) { + if (target.isAssignableFrom(getClass())) { + return this; + } + return null; + } + + public void flush() throws IOException { + } + + } + + private boolean forceRefelection; + + public AsyncChannel openAsyncChannel(URI location) throws IOException { + + Object server = lookupServer(location); + if (!forceRefelection && server.getClass() == VMPipeAsyncChannelServer.class) { + return ((VMPipeAsyncChannelServer) server).connect(); + } + + // Asume server is in a different classloader. + // Use reflection to connect. + try { + Method method = server.getClass().getMethod("connect", new Class[] {}); + Object channel = method.invoke(server, new Object[] {}); + return new ClassloaderAsyncChannelAdapter(channel); + } catch (Throwable e) { + throw (IOException) new IOException("Connection could not be established: " + e).initCause(e); + } + } + + public AsyncChannelServer bindAsyncChannel(URI bindURI) throws IOException { + VMPipeAsyncChannelServer server = new VMPipeAsyncChannelServer(bindURI); + bindServer(bindURI, server); + return server; + } + + private static Map getServerMap() { + return SERVER_MAP; + } + + static public String getServerKeyForURI(URI location) { + return location.getHost(); + } + + public static void bindServer(URI bindURI, VMPipeAsyncChannelServer server) throws IOException { + String key = getServerKeyForURI(bindURI); + if (getServerMap().get(key) != null) + throw new IOException("Server is allready bound at: " + bindURI); + getServerMap().put(key, server); + } + + public static Object lookupServer(URI location) throws IOException { + String key = getServerKeyForURI(location); + Object server = getServerMap().get(key); + if (server == null) { + throw new IOException("Connection refused."); + } + return server; + } + + public static void unbindServer(URI bindURI) { + String key = getServerKeyForURI(bindURI); + getServerMap().remove(key); + } + + public boolean isForceRefelection() { + return forceRefelection; + } + + public void setForceRefelection(boolean forceRefelection) { + this.forceRefelection = forceRefelection; + } + +} diff --git a/activeio/src/java/org/activeio/net/VMPipeAsyncChannelPipe.java b/activeio/src/java/org/activeio/net/VMPipeAsyncChannelPipe.java new file mode 100644 index 0000000000..51093418c0 --- /dev/null +++ b/activeio/src/java/org/activeio/net/VMPipeAsyncChannelPipe.java @@ -0,0 +1,172 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.io.InterruptedIOException; + +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelListener; +import org.activeio.Packet; +import org.activeio.packet.EOSPacket; + +import edu.emory.mathcs.backport.java.util.concurrent.Semaphore; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +/** + * Used to connect the bottom ends of two Async channel stacks. + * + */ +final public class VMPipeAsyncChannelPipe { + + final PipeChannel leftChannel = new PipeChannel(); + final PipeChannel rightChannel = new PipeChannel(); + + final public static class PipeChannel implements AsyncChannel { + + private PipeChannel sibiling; + private AsyncChannelListener channelListener; + private final Semaphore runMutext = new Semaphore(0); + private boolean disposed; + private boolean running; + + public PipeChannel() { + } + + public void setAsyncChannelListener(AsyncChannelListener channelListener) { + this.channelListener = channelListener; + } + public AsyncChannelListener getAsyncChannelListener() { + return channelListener; + } + + public void write(Packet packet) throws IOException { + if( disposed ) + throw new IOException("Conneciton closed."); + sibiling.onPacket(packet, WAIT_FOREVER_TIMEOUT); + } + + private void onPacket(Packet packet, long timeout) throws IOException { + try { + if( timeout == NO_WAIT_TIMEOUT ) { + if( !runMutext.tryAcquire(0, TimeUnit.MILLISECONDS) ) + return; + } else if( timeout == WAIT_FOREVER_TIMEOUT ) { + runMutext.acquire(); + } else { + if( !runMutext.tryAcquire(timeout, TimeUnit.MILLISECONDS) ) + return; + } + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + try { + if( disposed ) { + throw new IOException("Peer connection closed."); + } + channelListener.onPacket(packet); + } finally { + runMutext.release(); + } + } + + public void flush() throws IOException { + } + + public void start() throws IOException { + if(running) + return; + if( channelListener==null ) + throw new IOException("channelListener has not been set."); + running=true; + runMutext.release(); + } + + public void stop(long timeout) throws IOException { + if(!running) + return; + try { + if( timeout == NO_WAIT_TIMEOUT ) { + if( !runMutext.tryAcquire(0, TimeUnit.MILLISECONDS) ) + return; + } else if( timeout == WAIT_FOREVER_TIMEOUT ) { + runMutext.acquire(); + } else { + if( !runMutext.tryAcquire(timeout, TimeUnit.MILLISECONDS) ) + return; + } + running=false; + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + + public void dispose() { + if( disposed ) + return; + + if( running && channelListener!=null ) { + this.channelListener.onPacketError(new IOException("Pipe closed.")); + running=false; + } + disposed = true; + runMutext.release(); + + try { + // Inform the peer of the End Of Stream if he's listening. + sibiling.onPacket(EOSPacket.EOS_PACKET, NO_WAIT_TIMEOUT); + } catch (IOException e) { + } + } + + public PipeChannel getSibiling() { + return sibiling; + } + public void setSibiling(PipeChannel sibiling) { + this.sibiling = sibiling; + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return null; + } + + public String getId() { + return "0x"+Integer.toHexString(System.identityHashCode(this)); + } + + public String toString() { + return "Pipe Channel from "+getId()+" to "+sibiling.getId(); + } + } + + public VMPipeAsyncChannelPipe() { + leftChannel.setSibiling(rightChannel); + rightChannel.setSibiling(leftChannel); + } + + public AsyncChannel getLeftAsyncChannel() { + return leftChannel; + } + + public AsyncChannel getRightAsyncChannel() { + return rightChannel; + } +} diff --git a/activeio/src/java/org/activeio/net/VMPipeAsyncChannelServer.java b/activeio/src/java/org/activeio/net/VMPipeAsyncChannelServer.java new file mode 100644 index 0000000000..e4bbe78ac9 --- /dev/null +++ b/activeio/src/java/org/activeio/net/VMPipeAsyncChannelServer.java @@ -0,0 +1,87 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; + +import org.activeio.AcceptListener; +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelServer; + +/** + * @version $Revision$ + */ +final public class VMPipeAsyncChannelServer implements AsyncChannelServer { + + private final URI bindURI; + private final URI connectURI; + private AcceptListener acceptListener; + private boolean disposed; + + public VMPipeAsyncChannelServer(URI bindURI) { + this.bindURI = this.connectURI = bindURI; + } + + public URI getBindURI() { + return bindURI; + } + + public URI getConnectURI() { + return this.connectURI; + } + + public void dispose() { + if( disposed ) + return; + + VMPipeAsyncChannelFactory.unbindServer(bindURI); + disposed=true; + } + + public void start() throws IOException { + if( acceptListener==null ) + throw new IOException("acceptListener has not been set."); + } + + public void stop(long timeout) { + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return null; + } + + public String toString() { + return "VM Pipe Server: "+getConnectURI(); + } + + public void setAcceptListener(AcceptListener acceptListener) { + this.acceptListener = acceptListener; + } + + public AsyncChannel connect() { + VMPipeAsyncChannelPipe pipe = new VMPipeAsyncChannelPipe(); + acceptListener.onAccept(pipe.getRightAsyncChannel()); + return pipe.getLeftAsyncChannel(); + } + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/net/package.html b/activeio/src/java/org/activeio/net/package.html new file mode 100644 index 0000000000..967fe9d5d3 --- /dev/null +++ b/activeio/src/java/org/activeio/net/package.html @@ -0,0 +1,11 @@ + + + + + +

+A set of Channel implementations that are implemented using the Socket and DatagramSocket classes. +

+ + + diff --git a/activeio/src/java/org/activeio/oneport/HttpRecognizer.java b/activeio/src/java/org/activeio/oneport/HttpRecognizer.java new file mode 100644 index 0000000000..6f776e61fa --- /dev/null +++ b/activeio/src/java/org/activeio/oneport/HttpRecognizer.java @@ -0,0 +1,67 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.oneport; + +import java.util.HashSet; + +import org.activeio.Packet; + + +public class HttpRecognizer implements ProtocolRecognizer { + + static private HashSet methods = new HashSet(); + static { + // This list built using: http://www.w3.org/Protocols/HTTP/Methods.html + methods.add("GET "); + methods.add("PUT "); + methods.add("POST "); + methods.add("HEAD "); + methods.add("LINK "); + methods.add("TRACE "); + methods.add("UNLINK "); + methods.add("SEARCH "); + methods.add("DELETE "); + methods.add("CHECKIN "); + methods.add("OPTIONS "); + methods.add("CONNECT "); + methods.add("CHECKOUT "); + methods.add("SPACEJUMP "); + methods.add("SHOWMETHOD "); + methods.add("TEXTSEARCH "); + } + + static final public HttpRecognizer HTTP_RECOGNIZER = new HttpRecognizer(); + + private HttpRecognizer() {} + + public boolean recognizes(Packet packet) { + + StringBuffer b = new StringBuffer(12); + for (int i = 0; i < 11; i++) { + int c = (char)packet.read(); + if( c == -1) + return false; + + b.append((char)c); + if(((char)c)==' ') + break; + } + + return methods.contains(b.toString()); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/oneport/IIOPRecognizer.java b/activeio/src/java/org/activeio/oneport/IIOPRecognizer.java new file mode 100644 index 0000000000..841a4bd7f4 --- /dev/null +++ b/activeio/src/java/org/activeio/oneport/IIOPRecognizer.java @@ -0,0 +1,37 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.oneport; + +import org.activeio.Packet; + + +public class IIOPRecognizer implements ProtocolRecognizer { + + static final public IIOPRecognizer IIOP_RECOGNIZER = new IIOPRecognizer(); + + private IIOPRecognizer() {} + + public boolean recognizes(Packet packet) { + return ( + packet.read()=='G' && + packet.read()=='I' && + packet.read()=='O' && + packet.read()=='P' + ); + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/oneport/JettyOnePortSocketListener.java b/activeio/src/java/org/activeio/oneport/JettyOnePortSocketListener.java new file mode 100644 index 0000000000..fc1a267a3e --- /dev/null +++ b/activeio/src/java/org/activeio/oneport/JettyOnePortSocketListener.java @@ -0,0 +1,52 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.oneport; + +import java.io.IOException; +import java.net.ServerSocket; + +import org.activeio.SyncChannelServer; +import org.activeio.adapter.AsyncToSyncChannelServer; +import org.activeio.adapter.SyncChannelServerToServerSocket; +import org.mortbay.http.SocketListener; +import org.mortbay.util.InetAddrPort; + +/** + * + */ +public class JettyOnePortSocketListener extends SocketListener { + + private static final long serialVersionUID = 3257567321504561206L; + + private final OnePortAsyncChannelServer channelServer; + + public JettyOnePortSocketListener(OnePortAsyncChannelServer channelServer) { + this.channelServer = channelServer; + } + + public JettyOnePortSocketListener(OnePortAsyncChannelServer channelServer, InetAddrPort arg0) { + super(arg0); + this.channelServer = channelServer; + } + + protected ServerSocket newServerSocket(InetAddrPort addrPort, int backlog) throws IOException { + SyncChannelServer syncServer = AsyncToSyncChannelServer.adapt(channelServer.bindAsyncChannel(HttpRecognizer.HTTP_RECOGNIZER)); + syncServer.start(); + return new SyncChannelServerToServerSocket(syncServer); + } +} diff --git a/activeio/src/java/org/activeio/oneport/OnePortAsyncChannelServer.java b/activeio/src/java/org/activeio/oneport/OnePortAsyncChannelServer.java new file mode 100644 index 0000000000..88e0ad0011 --- /dev/null +++ b/activeio/src/java/org/activeio/oneport/OnePortAsyncChannelServer.java @@ -0,0 +1,224 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.oneport; + +import java.io.IOException; +import java.net.URI; +import java.util.Iterator; + +import org.activeio.AcceptListener; +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelListener; +import org.activeio.AsyncChannelServer; +import org.activeio.Channel; +import org.activeio.FilterAsyncChannel; +import org.activeio.FilterAsyncChannelServer; +import org.activeio.Packet; +import org.activeio.SyncChannel; +import org.activeio.adapter.AsyncToSyncChannel; +import org.activeio.adapter.SyncToAsyncChannel; +import org.activeio.filter.PushbackSyncChannel; +import org.activeio.packet.AppendedPacket; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * Allows multiple protocols share a single ChannelServer. All protocols sharing the server + * must have a distinct magic number at the beginning of the client's request. + * + * TODO: handle the case where a client opens a connection but sends no data down the stream. We need + * to timeout that client. + * + * @version $Revision$ + */ +final public class OnePortAsyncChannelServer extends FilterAsyncChannelServer { + + /** + * The OnePortAsyncChannelServer listens for incoming connection + * from a normal AsyncChannelServer. This s the listner used + * to receive the accepted channels. + */ + final private class OnePortAcceptListener implements AcceptListener { + + public void onAccept(Channel channel) { + try { + AsyncChannel asyncChannel = SyncToAsyncChannel.adapt(channel); + ProtocolInspectingAsyncChannel inspector = new ProtocolInspectingAsyncChannel(asyncChannel); + inspector.start(); + } catch (IOException e) { + onAcceptError(e); + } + } + + public void onAcceptError(IOException error) { + dispose(); + } + } + + /** + * This channel filter sniffs the first few bytes of the byte stream + * to see if a ProtocolRecognizer recognizes the protocol. If it does not + * it just closes the channel, otherwise the associated SubPortAsyncChannelServer + * is notified that it accepted a channel. + * + */ + final private class ProtocolInspectingAsyncChannel extends FilterAsyncChannel { + private Packet buffer; + + public ProtocolInspectingAsyncChannel(AsyncChannel next) throws IOException { + super(next); + setAsyncChannelListener(new AsyncChannelListener() { + public void onPacket(Packet packet) { + if (buffer == null) { + buffer = packet; + } else { + buffer = AppendedPacket.join(buffer, packet); + } + findMagicNumber(); + } + + public void onPacketError(IOException error) { + dispose(); + } + }); + } + + private void findMagicNumber() { + for (Iterator iter = recognizerMap.keySet().iterator(); iter.hasNext();) { + ProtocolRecognizer recognizer = (ProtocolRecognizer) iter.next(); + if (recognizer.recognizes(buffer.duplicate())) { + + if( UnknownRecognizer.UNKNOWN_RECOGNIZER == recognizer ) { + // Dispose the channel.. don't know what to do with it. + dispose(); + } + + SubPortAsyncChannelServer onePort = (SubPortAsyncChannelServer) recognizerMap.get(recognizer); + if( onePort == null ) { + // Dispose the channel.. don't know what to do with it. + dispose(); + } + + // Once the magic number is found: + // Stop the channel so that a decision can be taken on what to + // do with the + // channel. When the channel is restarted, the buffered up + // packets wiil get + // delivered. + try { + stop(NO_WAIT_TIMEOUT); + setAsyncChannelListener(null); + } catch (IOException e) { + getAsyncChannelListener().onPacketError(e); + } + + Channel channel = getNext(); + channel = AsyncToSyncChannel.adapt(channel); + channel = new PushbackSyncChannel((SyncChannel) channel, buffer); + channel = SyncToAsyncChannel.adapt(channel); + + onePort.onAccept(channel); + break; + } + } + } + } + + /** + * Clients bind against the OnePortAsyncChannelServer and get + * SubPortAsyncChannelServer which can be used to accept connections. + */ + final private class SubPortAsyncChannelServer implements AsyncChannelServer { + + private final ProtocolRecognizer recognizer; + private AcceptListener acceptListener; + private boolean started; + + /** + * @param recognizer + */ + public SubPortAsyncChannelServer(ProtocolRecognizer recognizer) { + this.recognizer = recognizer; + } + + public void setAcceptListener(AcceptListener acceptListener) { + this.acceptListener = acceptListener; + } + + public URI getBindURI() { + return next.getBindURI(); + } + + public URI getConnectURI() { + return next.getConnectURI(); + } + + public void dispose() { + started = false; + recognizerMap.remove(recognizer); + } + + public void start() throws IOException { + started = true; + } + public void stop(long timeout) throws IOException { + started = false; + } + + void onAccept(Channel channel) { + if( started && acceptListener!=null ) { + acceptListener.onAccept(channel); + } else { + // Dispose the channel.. don't know what to do with it. + channel.dispose(); + } + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return OnePortAsyncChannelServer.this.getAdapter(target); + } + + } + + + private final ConcurrentHashMap recognizerMap = new ConcurrentHashMap(); + + public OnePortAsyncChannelServer(AsyncChannelServer server) throws IOException { + super(server); + super.setAcceptListener(new OnePortAcceptListener()); + } + + public void setAcceptListener(AcceptListener acceptListener) { + throw new IllegalAccessError("Not supported"); + } + + public AsyncChannelServer bindAsyncChannel(ProtocolRecognizer recognizer) throws IOException { + + if( recognizerMap.contains(recognizer) ) + throw new IOException("That recognizer is allredy bound."); + + SubPortAsyncChannelServer server = new SubPortAsyncChannelServer(recognizer); + Object old = recognizerMap.put(recognizer, server); + return server; + } + + +} diff --git a/activeio/src/java/org/activeio/oneport/OpenORBOpenPortFeatureInitializer.java b/activeio/src/java/org/activeio/oneport/OpenORBOpenPortFeatureInitializer.java new file mode 100644 index 0000000000..b8603577c9 --- /dev/null +++ b/activeio/src/java/org/activeio/oneport/OpenORBOpenPortFeatureInitializer.java @@ -0,0 +1,41 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.oneport; + +import org.omg.PortableInterceptor.ORBInitInfo; +import org.openorb.orb.pi.FeatureInitInfo; +import org.openorb.orb.pi.FeatureInitializer; + +/** + * Used to hook in the OpenORBOpenPortSocketFactory into the ORB. + */ +public class OpenORBOpenPortFeatureInitializer implements FeatureInitializer { + + static final private ThreadLocal socketFatory = new ThreadLocal(); + + static public void setContextSocketFactory( OpenORBOpenPortSocketFactory sf ) { + socketFatory.set(sf); + } + + public void init(ORBInitInfo orbinfo, FeatureInitInfo featureinfo) { + OpenORBOpenPortSocketFactory sf = (OpenORBOpenPortSocketFactory) socketFatory.get(); + if( sf!=null ) { + featureinfo.setFeature("IIOP.SocketFactory", sf); + } + } +} diff --git a/activeio/src/java/org/activeio/oneport/OpenORBOpenPortSocketFactory.java b/activeio/src/java/org/activeio/oneport/OpenORBOpenPortSocketFactory.java new file mode 100644 index 0000000000..d4cb2d148c --- /dev/null +++ b/activeio/src/java/org/activeio/oneport/OpenORBOpenPortSocketFactory.java @@ -0,0 +1,57 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.oneport; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; + +import org.activeio.SyncChannelServer; +import org.activeio.adapter.AsyncToSyncChannelServer; +import org.activeio.adapter.SyncChannelServerToServerSocket; +import org.openorb.orb.net.SocketFactory; + +/** + * + */ +public class OpenORBOpenPortSocketFactory implements SocketFactory { + + private final OnePortAsyncChannelServer channelServer; + + public OpenORBOpenPortSocketFactory(OnePortAsyncChannelServer channelServer) { + this.channelServer = channelServer; + } + + /** + * Outbound sockets are normal. + */ + public Socket createSocket(InetAddress address, int port) throws IOException { + return new Socket(address, port); + } + + /** + * Server sockets bind against the OnePortAsyncChannelServer. + */ + public ServerSocket createServerSocket(InetAddress address, int port) throws IOException { + SyncChannelServer sychServer = AsyncToSyncChannelServer.adapt(channelServer.bindAsyncChannel(IIOPRecognizer.IIOP_RECOGNIZER)); + sychServer.start(); + return new SyncChannelServerToServerSocket(sychServer); + } + +} diff --git a/activeio/src/java/org/activeio/oneport/ProtocolRecognizer.java b/activeio/src/java/org/activeio/oneport/ProtocolRecognizer.java new file mode 100644 index 0000000000..5bb21c29e2 --- /dev/null +++ b/activeio/src/java/org/activeio/oneport/ProtocolRecognizer.java @@ -0,0 +1,27 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.oneport; + +import org.activeio.Packet; + +/** + * + */ +public interface ProtocolRecognizer { + boolean recognizes(Packet packet); +} diff --git a/activeio/src/java/org/activeio/oneport/UnknownRecognizer.java b/activeio/src/java/org/activeio/oneport/UnknownRecognizer.java new file mode 100644 index 0000000000..afaf91cc53 --- /dev/null +++ b/activeio/src/java/org/activeio/oneport/UnknownRecognizer.java @@ -0,0 +1,35 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.oneport; + +import org.activeio.Packet; + + +class UnknownRecognizer implements ProtocolRecognizer { + + static public final ProtocolRecognizer UNKNOWN_RECOGNIZER = new UnknownRecognizer(); + + private UnknownRecognizer() { + } + + public boolean recognizes(Packet packet) { + if( packet.limit() > 15 ) + return true; + return false; + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/oneport/package.html b/activeio/src/java/org/activeio/oneport/package.html new file mode 100644 index 0000000000..ddb2424838 --- /dev/null +++ b/activeio/src/java/org/activeio/oneport/package.html @@ -0,0 +1,15 @@ + + + + + +

+This package provides the implementing classes that allow you to run multiple protocols +from a sigle port. + +The only restriction is that all the protococols sharing the port must provide a magic +number in the intial request message to the server. +

+ + + diff --git a/activeio/src/java/org/activeio/package.html b/activeio/src/java/org/activeio/package.html new file mode 100644 index 0000000000..ac4f3b8833 --- /dev/null +++ b/activeio/src/java/org/activeio/package.html @@ -0,0 +1,13 @@ + + + + + +

+The core activeio API is centered around having Channels that move Packets of data. It +models both the Synch and Async nature of channels and encourages protocol abstractions +via the use of Channel filters. +

+ + + diff --git a/activeio/src/java/org/activeio/packet/AppendedPacket.java b/activeio/src/java/org/activeio/packet/AppendedPacket.java new file mode 100644 index 0000000000..4dd7fdabd8 --- /dev/null +++ b/activeio/src/java/org/activeio/packet/AppendedPacket.java @@ -0,0 +1,247 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.packet; + +import java.io.DataOutput; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Constructor; + +import org.activeio.ByteSequence; +import org.activeio.Packet; + +/** + * Appends two packets together. + * + * @version $Revision$ + */ +final public class AppendedPacket implements Packet { + + private final Packet first; + private final Packet last; + + private final int capacity; + private final int firstCapacity; + + static public Packet join(Packet first, Packet last) { + if( first.hasRemaining() ) { + if( last.hasRemaining() ) { + + //TODO: this might even be a rejoin of the same continous buffer. + //It would be good if we detected that and avoided just returned the buffer. + + return new AppendedPacket(first.slice(), last.slice()); + } else { + return first.slice(); + } + } else { + if( last.hasRemaining() ) { + return last.slice(); + } else { + return EmptyPacket.EMPTY_PACKET; + } + } + } + + /** + * @deprecated use {@see #join(Packet, Packet)} instead. + */ + public AppendedPacket(Packet first, Packet second) { + this.first = first; + this.last = second; + this.firstCapacity = first.capacity(); + this.capacity = first.capacity()+last.capacity(); + clear(); + } + + public void position(int position) { + if( position <= firstCapacity ) { + last.position(0); + first.position(position); + } else { + last.position(position-firstCapacity); + first.position(firstCapacity); + } + } + + public void limit(int limit) { + if( limit <= firstCapacity ) { + last.limit(0); + first.limit(limit); + } else { + last.limit(limit-firstCapacity); + first.limit(firstCapacity); + } + } + + public Packet slice() { + return join(first,last); + } + + public Packet duplicate() { + return new AppendedPacket(first.duplicate(), last.duplicate()); + } + + public Object duplicate(ClassLoader cl) throws IOException { + try { + Class pclazz = cl.loadClass(Packet.class.getName()); + Class clazz = cl.loadClass(AppendedPacket.class.getName()); + Constructor constructor = clazz.getConstructor(new Class[]{pclazz, pclazz}); + return constructor.newInstance(new Object[]{first.duplicate(cl), last.duplicate(cl)}); + } catch (Throwable e) { + throw (IOException)new IOException("Could not duplicate packet in a different classloader: "+e).initCause(e); + } + } + + public void flip() { + limit(position()); + position(0); + } + + public int position() { + return first.position()+last.position(); + } + + public int limit() { + return first.limit()+last.limit(); + } + + public int remaining() { + return first.remaining()+last.remaining(); + } + + public void rewind() { + first.rewind(); + last.rewind(); + } + + public boolean hasRemaining() { + return first.hasRemaining()||last.hasRemaining(); + } + + public void clear() { + first.clear(); + last.clear(); + } + + public int capacity() { + return capacity; + } + + public void writeTo(OutputStream out) throws IOException { + first.writeTo(out); + last.writeTo(out); + } + + public void writeTo(DataOutput out) throws IOException { + first.writeTo(out); + last.writeTo(out); + } + + + /** + * @see org.activeio.Packet#read() + */ + public int read() { + if( first.hasRemaining() ) { + return first.read(); + } else if( last.hasRemaining() ) { + return last.read(); + } else { + return -1; + } + } + + /** + * @see org.activeio.Packet#read(byte[], int, int) + */ + public int read(byte[] data, int offset, int length) { + + int rc1 = first.read(data, offset, length); + if( rc1==-1 ) { + int rc2 = last.read(data, offset, length); + return ( rc2==-1 ) ? -1 : rc2; + } else { + int rc2 = last.read(data, offset+rc1, length-rc1); + return ( rc2==-1 ) ? rc1 : rc1+rc2; + } + + } + + /** + * @see org.activeio.Packet#write(int) + */ + public boolean write(int data) { + if( first.hasRemaining() ) { + return first.write(data); + } else if( last.hasRemaining() ) { + return last.write(data); + } else { + return false; + } + } + + /** + * @see org.activeio.Packet#write(byte[], int, int) + */ + public int write(byte[] data, int offset, int length) { + int rc1 = first.write(data, offset, length); + if( rc1==-1 ) { + int rc2 = last.write(data, offset, length); + return ( rc2==-1 ) ? -1 : rc2; + } else { + int rc2 = last.write(data, offset+rc1, length-rc1); + return ( rc2==-1 ) ? rc1 : rc1+rc2; + } + } + + public int read(Packet dest) { + int rc = first.read(dest); + rc += last.read(dest); + return rc; + } + + public String toString() { + return "{position="+position()+",limit="+limit()+",capacity="+capacity()+"}"; + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + Object object = first.getAdapter(target); + if( object == null ) + object = last.getAdapter(target); + return object; + } + + public ByteSequence asByteSequence() { + // TODO: implement me + return null; + } + + public byte[] sliceAsBytes() { + // TODO: implement me + return null; + } + + public void dispose() { + first.dispose(); + last.dispose(); + } + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/packet/ByteArrayPacket.java b/activeio/src/java/org/activeio/packet/ByteArrayPacket.java new file mode 100644 index 0000000000..c3803ec44d --- /dev/null +++ b/activeio/src/java/org/activeio/packet/ByteArrayPacket.java @@ -0,0 +1,241 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.packet; + +import java.io.DataOutput; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Constructor; + +import org.activeio.ByteSequence; +import org.activeio.Packet; + +/** + * Provides a Packet implementation that is directly backed by a byte[]. + * + * @version $Revision$ + */ +final public class ByteArrayPacket implements Packet { + + private final byte buffer[]; + + private final int offset; + private final int capacity; + private int position; + private int limit; + private int remaining; + + + public ByteArrayPacket(byte buffer[]) { + this(buffer,0, buffer.length); + } + + public ByteArrayPacket(ByteSequence sequence) { + this(sequence.getData(), sequence.getOffset(), sequence.getLength()); + } + + public ByteArrayPacket(byte buffer[], int offset, int capacity) { + this.buffer = buffer; + this.offset=offset; + this.capacity=capacity; + this.position = 0; + this.limit = capacity; + this.remaining = limit-position; + } + + public int position() { + return position; + } + + public void position(int position) { + this.position = position; + remaining = limit-position; + } + + public int limit() { + return limit; + } + + public void limit(int limit) { + this.limit = limit; + remaining = limit-position; + } + + public void flip() { + limit = position; + position = 0; + remaining = limit - position; + } + + public int remaining() { + return remaining; + } + + public void rewind() { + position = 0; + remaining = limit - position; + } + + public boolean hasRemaining() { + return remaining > 0; + } + + public void clear() { + position = 0; + limit = capacity; + remaining = limit - position; + } + + public int capacity() { + return capacity; + } + + public Packet slice() { + return new ByteArrayPacket(buffer, offset+position, remaining); + } + + public Packet duplicate() { + return new ByteArrayPacket(buffer, offset, capacity); + } + + public Object duplicate(ClassLoader cl) throws IOException { + try{ + Class clazz = cl.loadClass(ByteArrayPacket.class.getName()); + Constructor constructor = clazz.getConstructor(new Class[]{byte[].class, int.class, int.class}); + return constructor.newInstance(new Object[]{buffer, new Integer(offset), new Integer(capacity())}); + } catch (Throwable e) { + throw (IOException)new IOException("Could not duplicate packet in a different classloader: "+e).initCause(e); + } + } + + public void writeTo(OutputStream out) throws IOException { + out.write(buffer, offset+position, remaining); + position=limit; + remaining = limit-position; + } + + public void writeTo(DataOutput out) throws IOException { + out.write(buffer, offset+position, remaining); + position=limit; + remaining = limit-position; + } + + /** + * @see org.activeio.Packet#read() + */ + public int read() { + if( !(remaining > 0) ) + return -1; + int rc = buffer[offset+position]; + position++; + remaining = limit-position; + return rc & 0xff; + } + + /** + * @see org.activeio.Packet#read(byte[], int, int) + */ + public int read(byte[] data, int offset, int length) { + if( !(remaining > 0) ) + return -1; + + int copyLength = ((length <= remaining) ? length : remaining); + System.arraycopy(buffer, this.offset+position, data, offset, copyLength); + position += copyLength; + remaining = limit-position; + return copyLength; + } + + /** + * @see org.activeio.Packet#write(int) + */ + public boolean write(int data) { + if( !(remaining > 0) ) + return false; + buffer[offset+position]=(byte) data; + position++; + remaining = limit-position; + return true; + } + + /** + * @see org.activeio.Packet#write(byte[], int, int) + */ + public int write(byte[] data, int offset, int length) { + if( !(remaining > 0) ) + return -1; + + int copyLength = ((length <= remaining) ? length : remaining); + System.arraycopy(data, offset, buffer, this.offset+position, copyLength); + position+=copyLength; + remaining = limit-position; + return copyLength; + } + + public ByteSequence asByteSequence() { + return new ByteSequence(buffer, offset+position, remaining); + } + + /** + * @see org.activeio.Packet#sliceAsBytes() + */ + public byte[] sliceAsBytes() { + if( buffer.length == remaining ) { + return buffer; + } else { + byte rc[] = new byte[remaining]; + int op = position; + read(rc,0,remaining); + position=op; + remaining = limit-position; + return rc; + } + } + + /** + * @param dest + * @return the number of bytes read into the dest. + */ + public int read(Packet dest) { + int a = dest.remaining(); + int rc = ((a <= remaining) ? a : remaining); + if( rc > 0 ) { + dest.write( buffer, offset+position, rc); + position = position+rc; + remaining = limit-position; + } + return rc; + } + + public String toString() { + return "{position="+position+",limit="+limit+",capacity="+capacity+"}"; + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return null; + } + + public byte[] getBuffer() { + return buffer; + } + + public void dispose() { + } +} diff --git a/activeio/src/java/org/activeio/packet/ByteBufferPacket.java b/activeio/src/java/org/activeio/packet/ByteBufferPacket.java new file mode 100644 index 0000000000..ff7db0d9f4 --- /dev/null +++ b/activeio/src/java/org/activeio/packet/ByteBufferPacket.java @@ -0,0 +1,282 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.packet; + +import java.io.DataOutput; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Constructor; +import java.nio.ByteBuffer; + +import org.activeio.ByteSequence; +import org.activeio.Packet; + +/** + * Provides a Packet implementation that is backed by a {@see java.nio.ByteBuffer} + * + * @version $Revision$ + */ +final public class ByteBufferPacket implements Packet { + + public static final int DEFAULT_BUFFER_SIZE = Integer.parseInt(System.getProperty("org.activeio.DefaultByteBufferSize", ""+(64*1024))); + public static final int DEFAULT_DIRECT_BUFFER_SIZE = Integer.parseInt(System.getProperty("org.activeio.DefaultDirectByteBufferSize", ""+(8*1024))); + + private final ByteBuffer buffer; + private static final int TEMP_BUFFER_SIZE = 64*1024; + + public ByteBufferPacket(ByteBuffer buffer) { + this.buffer = buffer; + clear(); + } + + public ByteBuffer getByteBuffer() { + return buffer; + } + + public static ByteBufferPacket createDefaultBuffer(boolean direct) { + if( direct ) + return new ByteBufferPacket( ByteBuffer.allocateDirect(DEFAULT_DIRECT_BUFFER_SIZE) ); + return new ByteBufferPacket( ByteBuffer.allocate(DEFAULT_BUFFER_SIZE) ); + } + + public void writeTo(OutputStream out) throws IOException { + if( buffer.hasArray() ) { + + // If the buffer is backed by an array.. then use it directly. + out.write(buffer.array(), position(), remaining()); + position(limit()); + + } else { + + // It's not backed by a buffer.. We can only dump it to a OutputStream via a byte[] so, + // create a temp buffer that we can use to chunk it out. + byte temp[] = new byte[TEMP_BUFFER_SIZE]; + while( buffer.hasRemaining() ) { + int maxWrite = buffer.remaining() > temp.length ? temp.length : buffer.remaining(); + buffer.get(temp, 0, maxWrite); + out.write(temp,0, maxWrite); + } + + } + } + + public void writeTo(DataOutput out) throws IOException { + if( buffer.hasArray() ) { + + // If the buffer is backed by an array.. then use it directly. + out.write(buffer.array(), position(), remaining()); + position(limit()); + + } else { + + // It's not backed by a buffer.. We can only dump it to a OutputStream via a byte[] so, + // create a temp buffer that we can use to chunk it out. + byte temp[] = new byte[TEMP_BUFFER_SIZE]; + while( buffer.hasRemaining() ) { + int maxWrite = buffer.remaining() > temp.length ? temp.length : buffer.remaining(); + buffer.get(temp, 0, maxWrite); + out.write(temp,0, maxWrite); + } + + } + } + + public int capacity() { + return buffer.capacity(); + } + + public void clear() { + buffer.clear(); + } + + public Packet compact() { + buffer.compact(); + return this; + } + + public void flip() { + buffer.flip(); + } + + public boolean hasRemaining() { + return buffer.hasRemaining(); + } + + public boolean isDirect() { + return buffer.isDirect(); + } + + public boolean isReadOnly() { + return buffer.isReadOnly(); + } + + public int limit() { + return buffer.limit(); + } + + public void limit(int arg0) { + buffer.limit(arg0); + } + + public Packet mark() { + buffer.mark(); + return this; + } + + public int position() { + return buffer.position(); + } + + public void position(int arg0) { + buffer.position(arg0); + } + + public int remaining() { + return buffer.remaining(); + } + + public void rewind() { + buffer.rewind(); + } + + public Packet slice() { + return new ByteBufferPacket(buffer.slice()); + } + + public Packet duplicate() { + return new ByteBufferPacket(buffer.duplicate()); + } + + public Object duplicate(ClassLoader cl) throws IOException { + try { + Class clazz = cl.loadClass(ByteBufferPacket.class.getName()); + Constructor constructor = clazz.getConstructor(new Class[]{ByteBuffer.class}); + return constructor.newInstance(new Object[]{buffer.duplicate()}); + } catch (Throwable e) { + throw (IOException)new IOException("Could not duplicate packet in a different classloader: "+e).initCause(e); + } + + } + + + /** + * @see org.activeio.Packet#read() + */ + public int read() { + if( !buffer.hasRemaining() ) + return -1; + return buffer.get() & 0xff; + } + + /** + * @see org.activeio.Packet#read(byte[], int, int) + */ + public int read(byte[] data, int offset, int length) { + if( !hasRemaining() ) + return -1; + + int copyLength = Math.min(length, remaining()); + buffer.get(data, offset, copyLength); + return copyLength; + } + + /** + * @see org.activeio.Packet#write(int) + */ + public boolean write(int data) { + if( !buffer.hasRemaining() ) + return false; + buffer.put((byte)data); + return true; + } + + /** + * @see org.activeio.Packet#write(byte[], int, int) + */ + public int write(byte[] data, int offset, int length) { + if( !hasRemaining() ) + return -1; + + int copyLength = Math.min(length, remaining()); + buffer.put(data, offset, copyLength); + return copyLength; + } + + /** + * @see org.activeio.Packet#asByteSequence() + */ + public ByteSequence asByteSequence() { + if( buffer.hasArray() ) { + byte[] bs = buffer.array(); + return new ByteSequence(bs, buffer.position(), buffer.remaining()); + } + // TODO: implement the direct case. + return null; + } + + /** + * @see org.activeio.Packet#sliceAsBytes() + */ + public byte[] sliceAsBytes() { + // TODO Auto-generated method stub + return null; + } + + /** + * @param dest + * @return the number of bytes read into the dest. + */ + public int read(Packet dest) { + + int rc = Math.min(dest.remaining(), remaining()); + if( rc > 0 ) { + + if( dest.getClass() == ByteBufferPacket.class ) { + + // Adjust our limit so that we don't overflow the dest buffer. + int limit = limit(); + limit(position()+rc); + + ((ByteBufferPacket)dest).buffer.put(buffer); + + // restore the limit. + limit(limit); + + return 0; + } else { + ByteSequence sequence = dest.asByteSequence(); + rc = read(sequence.getData(), sequence.getOffset(), sequence.getLength()); + dest.position(dest.position()+rc); + } + } + return rc; + } + + public String toString() { + return "{position="+position()+",limit="+limit()+",capacity="+capacity()+"}"; + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return null; + } + + public void dispose() { + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/packet/ByteBufferPacketPool.java b/activeio/src/java/org/activeio/packet/ByteBufferPacketPool.java new file mode 100644 index 0000000000..05539eda4f --- /dev/null +++ b/activeio/src/java/org/activeio/packet/ByteBufferPacketPool.java @@ -0,0 +1,48 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.packet; + +import org.activeio.Packet; + +import java.nio.ByteBuffer; + +/** + * Provides a simple pool of ByteBuffer objects. + * + * @version $Revision: 1.1 $ + */ +final public class ByteBufferPacketPool extends PacketPool { + + private final int packetSize; + + /** + * Creates a pool of bufferCount ByteBuffers that are + * directly allocated being bufferSize big. + * + * @param packetCount the number of buffers that will be in the pool. + * @param packetSize the size of the buffers that are in the pool. + */ + public ByteBufferPacketPool(int packetCount,int packetSize) { + super(packetCount); + this.packetSize = packetSize; + } + + protected Packet allocateNewPacket() { + return new ByteBufferPacket(ByteBuffer.allocateDirect(packetSize)); + } +} diff --git a/activeio/src/java/org/activeio/packet/BytePacket.java b/activeio/src/java/org/activeio/packet/BytePacket.java new file mode 100644 index 0000000000..ed03e7eb9b --- /dev/null +++ b/activeio/src/java/org/activeio/packet/BytePacket.java @@ -0,0 +1,211 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.packet; + +import java.io.DataOutput; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Constructor; + +import org.activeio.ByteSequence; +import org.activeio.Packet; + +/** + * Provides a Packet implementation that is directly backed by a byte. + * + * @version $Revision$ + */ +final public class BytePacket implements Packet { + + private byte data; + private byte position; + private byte limit; + + public BytePacket(byte data) { + this.data = data; + clear(); + } + + public int position() { + return position; + } + + public void position(int position) { + this.position = (byte) position; + } + + public int limit() { + return limit; + } + + public void limit(int limit) { + this.limit = (byte) limit; + } + + public void flip() { + limit(position()); + position(0); + } + + public int remaining() { + return limit() - position(); + } + + public void rewind() { + position(0); + } + + public boolean hasRemaining() { + return remaining() > 0; + } + + public void clear() { + position(0); + limit(capacity()); + } + + public int capacity() { + return 1; + } + + public Packet slice() { + if( hasRemaining() ) + return new BytePacket(data); + return EmptyPacket.EMPTY_PACKET; + } + + public Packet duplicate() { + BytePacket packet = new BytePacket(data); + packet.limit(limit()); + packet.position(position()); + return packet; + } + + public Object duplicate(ClassLoader cl) throws IOException { + try { + Class clazz = cl.loadClass(BytePacket.class.getName()); + Constructor constructor = clazz.getConstructor(new Class[]{byte.class}); + return constructor.newInstance(new Object[]{new Byte(data)}); + } catch (Throwable e) { + throw (IOException)new IOException("Could not duplicate packet in a different classloader: "+e).initCause(e); + } + } + + public void writeTo(OutputStream out) throws IOException { + if( hasRemaining() ) { + out.write(data); + position(1); + } + } + + public void writeTo(DataOutput out) throws IOException { + if( hasRemaining() ) { + out.write(data); + position(1); + } + } + + /** + * @see org.activeio.Packet#read() + */ + public int read() { + if( !hasRemaining() ) + return -1; + position(1); + return data & 0xff; + } + + /** + * @see org.activeio.Packet#read(byte[], int, int) + */ + public int read(byte[] data, int offset, int length) { + if( !hasRemaining() ) + return -1; + + if( length > 0 ) { + data[offset] = this.data; + position(1); + return 1; + } + return 0; + } + + /** + * @see org.activeio.Packet#write(int) + */ + public boolean write(int data) { + if( !hasRemaining() ) + return false; + + this.data = (byte) data; + position(1); + return true; + } + + /** + * @see org.activeio.Packet#write(byte[], int, int) + */ + public int write(byte[] data, int offset, int length) { + if( !hasRemaining() ) + return -1; + + if( length > 0 ) { + this.data = data[offset] ; + position(1); + return 1; + } + return 0; + } + + public ByteSequence asByteSequence() { + return null; + } + + /** + * @see org.activeio.Packet#sliceAsBytes() + */ + public byte[] sliceAsBytes() { + return null; + } + + /** + * @param dest + * @return the number of bytes read into the dest. + */ + public int read(Packet dest) { + if( hasRemaining() ) { + dest.write(data); + position(1); + return 1; + } + return 0; + } + + public String toString() { + return "{position="+position()+",limit="+limit()+",capacity="+capacity()+"}"; + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return null; + } + + public void dispose() { + } +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/packet/EOSPacket.java b/activeio/src/java/org/activeio/packet/EOSPacket.java new file mode 100644 index 0000000000..6d84626996 --- /dev/null +++ b/activeio/src/java/org/activeio/packet/EOSPacket.java @@ -0,0 +1,153 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.packet; + +import java.io.DataOutput; +import java.io.IOException; +import java.io.OutputStream; + +import org.activeio.ByteSequence; +import org.activeio.Packet; + +/** + * Provides a Packet implementation that is used to represent the end of a stream. + * + * @version $Revision$ + */ +final public class EOSPacket implements Packet { + + static final public EOSPacket EOS_PACKET = new EOSPacket(); + + private EOSPacket() { + } + + public void writeTo(OutputStream out) throws IOException { + } + public void writeTo(DataOutput out) throws IOException { + } + + public int position() { + return 1; + } + + public void position(int position) { + } + + public int limit() { + return 0; + } + + public void limit(int limit) { + } + + public void flip() { + } + + public int remaining() { + return -1; + } + + public void rewind() { + } + + public boolean hasRemaining() { + return false; + } + + public void clear() { + } + + public int capacity() { + return 0; + } + + public Packet slice() { + return this; + } + + public Packet duplicate() { + return this; + } + + public Object duplicate(ClassLoader cl) throws IOException { + try { + Class clazz = cl.loadClass(EOSPacket.class.getName()); + return clazz.getField("EOS_PACKET").get(null); + } catch (Throwable e) { + throw (IOException)new IOException("Could not duplicate packet in a different classloader: "+e).initCause(e); + } + } + + /** + * @see org.activeio.Packet#read() + */ + public int read() { + return -1; + } + + /** + * @see org.activeio.Packet#read(byte[], int, int) + */ + public int read(byte[] data, int offset, int length) { + return -1; + } + + /** + * @see org.activeio.Packet#write(int) + */ + public boolean write(int data) { + return false; + } + + /** + * @see org.activeio.Packet#write(byte[], int, int) + */ + public int write(byte[] data, int offset, int length) { + return -1; + } + + public ByteSequence asByteSequence() { + return EmptyPacket.EMPTY_BYTE_SEQUENCE; + } + + public byte[] sliceAsBytes() { + return EmptyPacket.EMPTY_BYTE_ARRAY; + } + + /** + * @param dest + * @return the number of bytes read into the dest. + */ + public int read(Packet dest) { + return 0; + } + + public String toString() { + return "{position="+position()+",limit="+limit()+",capacity="+capacity()+"}"; + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return null; + } + + public void dispose() { + } + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/packet/EmptyPacket.java b/activeio/src/java/org/activeio/packet/EmptyPacket.java new file mode 100644 index 0000000000..aaedd4458f --- /dev/null +++ b/activeio/src/java/org/activeio/packet/EmptyPacket.java @@ -0,0 +1,155 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.packet; + +import java.io.DataOutput; +import java.io.IOException; +import java.io.OutputStream; + +import org.activeio.ByteSequence; +import org.activeio.Packet; + +/** + * Provides a Packet implementation that is directly backed by a byte[0]. + * + * @version $Revision$ + */ +final public class EmptyPacket implements Packet { + + static final public EmptyPacket EMPTY_PACKET = new EmptyPacket(); + static final byte EMPTY_BYTE_ARRAY[] = new byte[]{}; + static final ByteSequence EMPTY_BYTE_SEQUENCE = new ByteSequence(EMPTY_BYTE_ARRAY,0,0); + + private EmptyPacket() { + } + + public void writeTo(OutputStream out) throws IOException { + } + public void writeTo(DataOutput out) throws IOException { + } + + public int position() { + return 0; + } + + public void position(int position) { + } + + public int limit() { + return 0; + } + + public void limit(int limit) { + } + + public void flip() { + } + + public int remaining() { + return 0; + } + + public void rewind() { + } + + public boolean hasRemaining() { + return false; + } + + public void clear() { + } + + public int capacity() { + return 0; + } + + public Packet slice() { + return this; + } + + public Packet duplicate() { + return this; + } + + public Object duplicate(ClassLoader cl) throws IOException { + try { + Class clazz = cl.loadClass(EmptyPacket.class.getName()); + return clazz.getField("EMPTY_PACKET").get(null); + } catch (Throwable e) { + throw (IOException)new IOException("Could not duplicate packet in a different classloader: "+e).initCause(e); + } + } + + /** + * @see org.activeio.Packet#read() + */ + public int read() { + return -1; + } + + /** + * @see org.activeio.Packet#read(byte[], int, int) + */ + public int read(byte[] data, int offset, int length) { + return -1; + } + + /** + * @see org.activeio.Packet#write(int) + */ + public boolean write(int data) { + return false; + } + + /** + * @see org.activeio.Packet#write(byte[], int, int) + */ + public int write(byte[] data, int offset, int length) { + return -1; + } + + public ByteSequence asByteSequence() { + return EMPTY_BYTE_SEQUENCE; + } + + public byte[] sliceAsBytes() { + return EMPTY_BYTE_ARRAY; + } + + /** + * @param dest + * @return the number of bytes read into the dest. + */ + public int read(Packet dest) { + return -1; + } + + public String toString() { + return "{position="+position()+",limit="+limit()+",capacity="+capacity()+"}"; + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return null; + } + + public void dispose() { + } + +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/packet/FilterPacket.java b/activeio/src/java/org/activeio/packet/FilterPacket.java new file mode 100644 index 0000000000..d1dd9bc866 --- /dev/null +++ b/activeio/src/java/org/activeio/packet/FilterPacket.java @@ -0,0 +1,139 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.packet; + +import java.io.DataOutput; +import java.io.IOException; +import java.io.OutputStream; + +import org.activeio.ByteSequence; +import org.activeio.Packet; + +/** + * Provides a Packet implementation that filters operations to another packet. + * + * Used to make it easier to augment the {@see #narrow(Class)}method. + * + * @version $Revision$ + */ +public abstract class FilterPacket implements Packet { + final protected Packet next; + + public FilterPacket(Packet next) { + this.next = next; + } + + public ByteSequence asByteSequence() { + return next.asByteSequence(); + } + + public int capacity() { + return next.capacity(); + } + + public void clear() { + next.clear(); + } + + public void flip() { + next.flip(); + } + + public boolean hasRemaining() { + return next.hasRemaining(); + } + + public int limit() { + return next.limit(); + } + + public void limit(int limit) { + next.limit(limit); + } + + public Object getAdapter(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return next.getAdapter(target); + } + + public int position() { + return next.position(); + } + + public void position(int position) { + next.position(position); + } + + public int read() { + return next.read(); + } + + public int read(byte[] data, int offset, int length) { + return next.read(data, offset, length); + } + + public int read(Packet dest) { + return next.read(dest); + } + + public int remaining() { + return next.remaining(); + } + + public void rewind() { + next.rewind(); + } + + public byte[] sliceAsBytes() { + return next.sliceAsBytes(); + } + + public int write(byte[] data, int offset, int length) { + return next.write(data, offset, length); + } + + public boolean write(int data) { + return next.write(data); + } + + public void writeTo(OutputStream out) throws IOException { + next.writeTo(out); + } + public void writeTo(DataOutput out) throws IOException { + next.writeTo(out); + } + + public Object duplicate(ClassLoader cl) throws IOException { + return next.duplicate(cl); + } + + public Packet duplicate() { + return filter(next.duplicate()); + } + + public Packet slice() { + return filter(next.slice()); + } + + public void dispose() { + next.dispose(); + } + + abstract public Packet filter(Packet packet); +} \ No newline at end of file diff --git a/activeio/src/java/org/activeio/packet/PacketPool.java b/activeio/src/java/org/activeio/packet/PacketPool.java new file mode 100644 index 0000000000..606b83a25f --- /dev/null +++ b/activeio/src/java/org/activeio/packet/PacketPool.java @@ -0,0 +1,148 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.packet; + +import java.util.ArrayList; + +import org.activeio.Disposable; +import org.activeio.Packet; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +/** + * Provides a simple pool of Packet objects. When the packets that this pool produces are disposed, + * they are returned to the pool. + * + * @version $Revision: 1.1 $ + */ +abstract public class PacketPool implements Disposable { + + public static final int DEFAULT_POOL_SIZE = Integer.parseInt(System.getProperty("org.activeio.journal.active.DefaultPoolSize", ""+(5))); + public static final int DEFAULT_PACKET_SIZE = Integer.parseInt(System.getProperty("org.activeio.journal.active.DefaultPacketSize", ""+(1024*1024*4))); + + private final ArrayList pool = new ArrayList(); + private final int maxPackets; + private int currentPoolSize; + private boolean disposed; + + public class PooledPacket extends FilterPacket { + private final AtomicInteger referenceCounter; + + public PooledPacket(Packet next) { + this(next, new AtomicInteger(0)); + } + + private PooledPacket(Packet next, AtomicInteger referenceCounter) { + super(next); + this.referenceCounter=referenceCounter; + this.referenceCounter.incrementAndGet(); + } + + public Packet filter(Packet packet) { + return new PooledPacket(next, referenceCounter); + } + + int getReferenceCounter() { + return referenceCounter.get(); + } + + public void dispose() { + if( referenceCounter.decrementAndGet()==0 ) { + returnPacket(next); + } + } + } + + /** + * @param maxPackets the number of buffers that will be in the pool. + */ + public PacketPool(int maxPackets) { + this.maxPackets = maxPackets; + } + + /** + * Blocks until a ByteBuffer can be retreived from the pool. + * + * @return + * @throws InterruptedException + */ + public Packet getPacket() throws InterruptedException { + Packet answer=null; + synchronized(this) { + while(answer==null) { + if( disposed ) + return null; + if( pool.size()>0) { + answer = (Packet) pool.remove(pool.size()-1); + } else if( currentPoolSize < maxPackets ) { + answer = allocateNewPacket(); + currentPoolSize++; + } + if( answer==null ) { + this.wait(); + } + } + } + return new PooledPacket(answer); + } + + /** + * Returns a ByteBuffer to the pool. + * + * @param packet + */ + private void returnPacket(Packet packet) { + packet.clear(); + synchronized(this) { + pool.add(packet); + this.notify(); + } + } + + synchronized public void dispose() { + disposed = true; + while( currentPoolSize>0 ) { + if( pool.size()>0) { + currentPoolSize -= pool.size(); + pool.clear(); + } else { + try { + this.wait(); + } catch (InterruptedException e) { + return; + } + } + } + } + + synchronized public void waitForPacketsToReturn() { + while( currentPoolSize!=pool.size() ) { + try { + this.wait(); + } catch (InterruptedException e) { + return; + } + } + } + + /** + * @return + */ + abstract protected Packet allocateNewPacket(); + +} diff --git a/activeio/src/java/org/activeio/packet/package.html b/activeio/src/java/org/activeio/packet/package.html new file mode 100644 index 0000000000..eea71880ef --- /dev/null +++ b/activeio/src/java/org/activeio/packet/package.html @@ -0,0 +1,11 @@ + + + + + +

+Implementations of the Packet class. +

+ + + diff --git a/activeio/src/java/org/activeio/xnet/ServerService.java b/activeio/src/java/org/activeio/xnet/ServerService.java new file mode 100644 index 0000000000..517116633b --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/ServerService.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio.xnet; + +import java.util.Properties; + + +/** + * The Server will call the following methods. + *

+ * newInstance() + * init( port, properties) + * start() + * stop() + *

+ * All ServerService implementations must have a no argument + * constructor. + */ +public interface ServerService extends SocketService { + + public void init(Properties props) throws Exception; + + public void start() throws ServiceException; + + public void stop() throws ServiceException; + + + /** + * Gets the ip number that the + * daemon is listening on. + */ + public String getIP(); + + /** + * Gets the port number that the + * daemon is listening on. + */ + public int getPort(); + + +} diff --git a/activeio/src/java/org/activeio/xnet/ServiceDaemon.java b/activeio/src/java/org/activeio/xnet/ServiceDaemon.java new file mode 100644 index 0000000000..e07c9387c2 --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/ServiceDaemon.java @@ -0,0 +1,194 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio.xnet; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.util.Properties; + +public class ServiceDaemon implements ServerService { + private static final Log log = LogFactory.getLog(ServiceDaemon.class); + + private final SocketService socketService; + private final InetAddress address; + private int port; + + private SocketListener socketListener; + private int timeout; + private String name; + + public ServiceDaemon(SocketService socketService, InetAddress address, int port) { + this(null, socketService, address, port); + } + + public ServiceDaemon(String name, SocketService socketService, InetAddress address, int port) { + this.name = name; + if (socketService == null) { + throw new IllegalArgumentException("socketService is null"); + } + this.socketService = socketService; + this.address = address; + this.port = port; + } + + public void setSoTimeout(int timeout) throws SocketException { + this.timeout = timeout; + if (socketListener != null) { + socketListener.setSoTimeout(timeout); + } + } + + public int getSoTimeout() throws IOException { + if (socketListener == null) return 0; + return socketListener.getSoTimeout(); + } + + public String getServiceName() { + return socketService.getName(); + } + + /** + * Gets the inetAddress number that the + * daemon is listening on. + */ + public InetAddress getAddress() { + return address; + } + + public void init(Properties props) throws Exception { + } + + public void start() throws ServiceException { + synchronized (this) { + // Don't bother if we are already started/starting + if (socketListener != null) { + return; + } + + ServerSocket serverSocket; + try { + serverSocket = new ServerSocket(port, 20, address); + port = serverSocket.getLocalPort(); + serverSocket.setSoTimeout(timeout); + } catch (Exception e) { + throw new ServiceException("Service failed to open socket", e); + } + + socketListener = new SocketListener(socketService, serverSocket); + Thread thread = new Thread(socketListener); + thread.setName("service." + name + "@" + socketListener.hashCode()); + thread.setDaemon(true); + thread.start(); + } + } + + public void stop() throws ServiceException { + synchronized (this) { + if (socketListener != null) { + socketListener.stop(); + socketListener = null; + } + } + } + + public String getIP() { + return null; + } + + /** + * Gets the port number that the + * daemon is listening on. + */ + public int getPort() { + return port; + } + + public void service(Socket socket) throws ServiceException, IOException { + } + + public String getName() { + return null; + } + + private static class SocketListener implements Runnable { + private SocketService serverService; + private ServerSocket serverSocket; + private boolean stopped; + + public SocketListener(SocketService serverService, ServerSocket serverSocket) { + this.serverService = serverService; + this.serverSocket = serverSocket; + stopped = false; + } + + public synchronized void stop() { + stopped = true; + } + + private synchronized boolean shouldStop() { + return stopped; + } + + public void run() { + while (!shouldStop()) { + Socket socket = null; + try { + socket = serverSocket.accept(); + socket.setTcpNoDelay(true); + if (!shouldStop()) { + // the server service is responsible + // for closing the socket. + serverService.service(socket); + } + } catch (SocketTimeoutException e) { + // we don't really care + // log.debug("Socket timed-out",e); + } catch (Throwable e) { + log.error("Unexpected error", e); + } + } + + if (serverSocket != null) { + try { + serverSocket.close(); + } catch (IOException ioException) { + log.debug("Error cleaning up socked", ioException); + } + serverSocket = null; + } + serverService = null; + } + + public void setSoTimeout(int timeout) throws SocketException { + serverSocket.setSoTimeout(timeout); + } + + public int getSoTimeout() throws IOException { + return serverSocket.getSoTimeout(); + } + } + + +} + diff --git a/activeio/src/java/org/activeio/xnet/ServiceException.java b/activeio/src/java/org/activeio/xnet/ServiceException.java new file mode 100644 index 0000000000..99d31457ff --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/ServiceException.java @@ -0,0 +1,72 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio.xnet; + +/** + * + */ +public class ServiceException extends Exception { + + /** + *

+ * Default constructor, which simply delegates exception + * handling up the inheritance chain to Exception. + *

+ */ + public ServiceException() { + super(); + } + + /** + *

+ * This constructor allows a message to be supplied indicating the source + * of the problem that occurred. + *

+ * + * @param message String identifying the cause of the problem. + */ + public ServiceException(String message) { + super(message); + } + + /** + *

+ * This constructor allows a "root cause" exception to be supplied, + * which may later be used by the wrapping application. + *

+ * + * @param rootCause Throwable that triggered the problem. + */ + public ServiceException(Throwable rootCause) { + super(rootCause); + } + + /** + * This constructor allows both a message identifying the + * problem that occurred as well as a "root cause" exception + * to be supplied, which may later be used by the wrapping + * application. + * + * @param message String identifying the cause of the problem. + * @param rootCause Throwable that triggered this problem. + */ + public ServiceException(String message, Throwable rootCause) { + super(message, rootCause); + } + +} + diff --git a/activeio/src/java/org/activeio/xnet/ServiceLogger.java b/activeio/src/java/org/activeio/xnet/ServiceLogger.java new file mode 100644 index 0000000000..4ba29c73a8 --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/ServiceLogger.java @@ -0,0 +1,127 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio.xnet; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.util.Properties; + +public class ServiceLogger implements ServerService { + private final Log log; + private final ServerService next; + private final String[] logOnSuccess; + private final String[] logOnFailure; + private final String name; + + + public ServiceLogger(String name, ServerService next, String[] logOnSuccess, String[] logOnFailure) { + this.log = LogFactory.getLog("OpenEJB.server.service." + name); + this.next = next; + this.logOnSuccess = logOnSuccess; + this.logOnFailure = logOnFailure; + this.name = name; + } + + /** + * log_on_success + * ----------------- + * Different information can be logged when a server starts: + *

+ * PID : the server's PID (if it's an internal xinetd service, the PID has then a value of 0) ; + * HOST : the client address ; + * USERID : the identity of the remote user, according to RFC1413 defining identification protocol; + * EXIT : the process exit status; + * DURATION : the session duration. + *

+ * log_on_failure + * ------------------ + * Here again, xinetd can log a lot of information when a server can't start, either by lack of resources or because of access rules: + * HOST, USERID : like above mentioned ; + * ATTEMPT : logs an access attempt. This an automatic option as soon as another value is provided; + * RECORD : logs every information available on the client. + * + * @param socket + * @throws org.activeio.xnet.ServiceException + * + * @throws IOException + */ + public void service(Socket socket) throws ServiceException, IOException { + // Fill this in more deeply later. + InetAddress client = socket.getInetAddress(); +// MDC.put("HOST", client.getHostName()); +// MDC.put("SERVER", getName()); + + try { + logIncoming(); + next.service(socket); + logSuccess(); + } catch (Exception e) { + logFailure(e); + e.printStackTrace(); + } + } + + public String[] getLogOnSuccess() { + return logOnSuccess; + } + + public String[] getLogOnFailure() { + return logOnFailure; + } + + private void logIncoming() { + log.trace("incomming request"); + } + + private void logSuccess() { + log.trace("successful request"); + } + + private void logFailure(Exception e) { + log.error(e.getMessage()); + } + + + public void init(Properties props) throws Exception { + next.init(props); + } + + public void start() throws ServiceException { + next.start(); + } + + public void stop() throws ServiceException { + next.stop(); + } + + public String getName() { + return next.getName(); + } + + public String getIP() { + return next.getIP(); + } + + public int getPort() { + return next.getPort(); + } + +} diff --git a/activeio/src/java/org/activeio/xnet/ServicePool.java b/activeio/src/java/org/activeio/xnet/ServicePool.java new file mode 100644 index 0000000000..0ebd099c03 --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/ServicePool.java @@ -0,0 +1,152 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio.xnet; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.net.Socket; +import java.util.Properties; + +public class ServicePool implements ServerService { + private static final Log log = LogFactory.getLog(ServicePool.class); + + private final ServerService next; + private final Executor executor; + + public ServicePool(ServerService next, final String name, final int threads, final long keepAliveTime) { + this.next = next; + + ThreadPoolExecutor p = new ThreadPoolExecutor(threads, threads, keepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); + p.setThreadFactory(new ThreadFactory() { + private volatile int id = 0; + + public Thread newThread(Runnable arg0) { + Thread thread = new Thread(arg0, name + " " + getNextID()); + return thread; + } + + private int getNextID() { + return id++; + } + + }); + executor = p; + } + + public ServicePool(ServerService next, Executor executor) { + this.next = next; + this.executor = executor; + } + + public void service(final Socket socket) throws ServiceException, IOException { + final Runnable service = new Runnable() { + public void run() { + try { + next.service(socket); + } catch (SecurityException e) { + log.error("Security error: " + e.getMessage(), e); + } catch (Throwable e) { + log.error("Unexpected error", e); + } finally { + try { + if (socket != null) { + socket.close(); + } + } catch (Throwable t) { + log.warn("Error while closing connection with client", t); + } + } + } + }; + + final ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + Runnable ctxCL = new Runnable() { + public void run() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(tccl); + try { + service.run(); + } finally { + Thread.currentThread().setContextClassLoader(cl); + } + } + }; + + executor.execute(ctxCL); + } + + /** + * Pulls out the access log information + * + * @param props + * @throws ServiceException + */ + public void init(Properties props) throws Exception { + // Do our stuff + + // Then call the next guy + next.init(props); + } + + public void start() throws ServiceException { + // Do our stuff + + // Then call the next guy + next.start(); + } + + public void stop() throws ServiceException { + // Do our stuff + + // Then call the next guy + next.stop(); + } + + + /** + * Gets the name of the service. + * Used for display purposes only + */ + public String getName() { + return next.getName(); + } + + /** + * Gets the ip number that the + * daemon is listening on. + */ + public String getIP() { + return next.getIP(); + } + + /** + * Gets the port number that the + * daemon is listening on. + */ + public int getPort() { + return next.getPort(); + } + +} diff --git a/activeio/src/java/org/activeio/xnet/SocketService.java b/activeio/src/java/org/activeio/xnet/SocketService.java new file mode 100644 index 0000000000..cb7e6d9220 --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/SocketService.java @@ -0,0 +1,33 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio.xnet; + +import java.io.IOException; +import java.net.Socket; + +/** + * @version $Revision: 1.1 $ $Date: 2004/04/09 19:04:01 $ + */ +public interface SocketService { + void service(Socket socket) throws ServiceException, IOException; + + /** + * Gets the name of the service. + * Used for display purposes only + */ + String getName(); +} diff --git a/activeio/src/java/org/activeio/xnet/StandardServiceStack.java b/activeio/src/java/org/activeio/xnet/StandardServiceStack.java new file mode 100644 index 0000000000..381a33928a --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/StandardServiceStack.java @@ -0,0 +1,110 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio.xnet; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import org.activeio.xnet.hba.ServiceAccessController; +import org.activeio.xnet.hba.IPAddressPermission; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketException; +import java.net.UnknownHostException; + +public class StandardServiceStack { + + private String name; + + private ServiceDaemon daemon; + private ServiceLogger logger; + private ServiceAccessController hba; + private ServicePool pool; + private ServerService server; + private String host; + + public StandardServiceStack(String name, int port, String host, IPAddressPermission[] allowHosts, String[] logOnSuccess, String[] logOnFailure, Executor executor, ServerService server) throws UnknownHostException { + this.server = server; + this.name = name; + this.host = host; + InetAddress address = InetAddress.getByName(host); + this.pool = new ServicePool(server, executor); + this.hba = new ServiceAccessController(name, pool, allowHosts); + this.logger = new ServiceLogger(name, hba, logOnSuccess, logOnFailure); + this.daemon = new ServiceDaemon(name, logger, address, port); + + } + + public String getName() { + return name; + } + + public InetAddress getAddress() { + return daemon.getAddress(); + } + + public InetSocketAddress getFullAddress() { + return new InetSocketAddress(getAddress(), getPort()); + } + + public String getHost() { + return host; + } + + public int getPort() { + return daemon.getPort(); + } + + public int getSoTimeout() throws IOException { + return daemon.getSoTimeout(); + } + + public void setSoTimeout(int timeout) throws SocketException { + daemon.setSoTimeout(timeout); + } + + public String[] getLogOnSuccess() { + return logger.getLogOnSuccess(); + } + + public String[] getLogOnFailure() { + return logger.getLogOnFailure(); + } + + public IPAddressPermission[] getAllowHosts() { + return hba.getAllowHosts(); + } + + public void setAllowHosts(IPAddressPermission[] allowHosts) { + hba.setAllowHosts(allowHosts); + } + + public void doStart() throws Exception { + daemon.start(); + } + + public void doStop() throws Exception { + daemon.stop(); + } + + public void doFail() { + try { + daemon.stop(); + } catch (ServiceException dontCare) { + } + } +} diff --git a/activeio/src/java/org/activeio/xnet/StandardServiceStackGBean.java b/activeio/src/java/org/activeio/xnet/StandardServiceStackGBean.java new file mode 100644 index 0000000000..abf2099303 --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/StandardServiceStackGBean.java @@ -0,0 +1,155 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio.xnet; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import org.activeio.xnet.hba.IPAddressPermission; +import org.apache.geronimo.gbean.GBeanData; +import org.apache.geronimo.gbean.GBeanInfo; +import org.apache.geronimo.gbean.GBeanInfoBuilder; +import org.apache.geronimo.gbean.GBeanLifecycle; +import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory; +import org.apache.geronimo.kernel.GBeanAlreadyExistsException; +import org.apache.geronimo.kernel.GBeanNotFoundException; +import org.apache.geronimo.kernel.Kernel; +import org.apache.geronimo.kernel.jmx.JMXUtil; + +import javax.management.ObjectName; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.net.SocketException; +import java.io.IOException; + +public class StandardServiceStackGBean implements GBeanLifecycle { + private final StandardServiceStack standardServiceStack; + + public StandardServiceStackGBean(String name, int port, String host, IPAddressPermission[] allowHosts, String[] logOnSuccess, String[] logOnFailure, Executor executor, ServerService server) throws UnknownHostException { + standardServiceStack = new StandardServiceStack(name, port, host, allowHosts, logOnSuccess, logOnFailure, executor, server); + } + + public String getName() { + return standardServiceStack.getName(); + } + + public InetAddress getAddress() { + return standardServiceStack.getAddress(); + } + + public InetSocketAddress getFullAddress() { + return standardServiceStack.getFullAddress(); + } + + public String getHost() { + return standardServiceStack.getHost(); + } + + public int getPort() { + return standardServiceStack.getPort(); + } + + public int getSoTimeout() throws IOException { + return standardServiceStack.getSoTimeout(); + } + + public void setSoTimeout(int timeout) throws SocketException { + standardServiceStack.setSoTimeout(timeout); + } + + public String[] getLogOnSuccess() { + return standardServiceStack.getLogOnSuccess(); + } + + public String[] getLogOnFailure() { + return standardServiceStack.getLogOnFailure(); + } + + public IPAddressPermission[] getAllowHosts() { + return standardServiceStack.getAllowHosts(); + } + + public void setAllowHosts(IPAddressPermission[] allowHosts) { + standardServiceStack.setAllowHosts(allowHosts); + } + + public void doStart() throws Exception { + standardServiceStack.doStart(); + } + + public void doStop() throws Exception { + standardServiceStack.doStop(); + } + + public void doFail() { + standardServiceStack.doFail(); + } + + public static final GBeanInfo GBEAN_INFO; + + static { + GBeanInfoBuilder infoFactory = new GBeanInfoBuilder("ActiveIO Connector", StandardServiceStackGBean.class); + + infoFactory.addAttribute("name", String.class, true); + infoFactory.addAttribute("port", int.class, true, true); + infoFactory.addAttribute("soTimeout", int.class, true); + infoFactory.addAttribute("host", String.class, true, true); + infoFactory.addAttribute("fullAddress", InetSocketAddress.class, false); + infoFactory.addAttribute("allowHosts", IPAddressPermission[].class, true); + infoFactory.addAttribute("logOnSuccess", String[].class, true); + infoFactory.addAttribute("logOnFailure", String[].class, true); + + infoFactory.addReference("Executor", Executor.class, NameFactory.GERONIMO_SERVICE); + infoFactory.addReference("Server", ServerService.class, NameFactory.GERONIMO_SERVICE); + + infoFactory.setConstructor(new String[]{ + "name", + "port", + "host", + "allowHosts", + "logOnSuccess", + "logOnFailure", + "Executor", + "Server"}); + + GBEAN_INFO = infoFactory.getBeanInfo(); + } + + public static GBeanInfo getGBeanInfo() { + return GBEAN_INFO; + } + + public static ObjectName addGBean(Kernel kernel, String name, int port, String host, InetAddress[] allowHosts, String[] logOnSuccess, String[] logOnFailure, ObjectName executor, ObjectName server) throws GBeanAlreadyExistsException, GBeanNotFoundException { + ClassLoader classLoader = StandardServiceStack.class.getClassLoader(); + ObjectName SERVICE_NAME = JMXUtil.getObjectName("activeio:type=StandardServiceStack,name=" + name); + + GBeanData gbean = new GBeanData(SERVICE_NAME, StandardServiceStackGBean.GBEAN_INFO); + + gbean.setAttribute("name", name); + gbean.setAttribute("port", new Integer(port)); + gbean.setAttribute("host", host); + gbean.setAttribute("allowHosts", allowHosts); + gbean.setAttribute("logOnSuccess", logOnSuccess); + gbean.setAttribute("logOnFailure", logOnFailure); + + gbean.setReferencePattern("Executor", executor); + gbean.setReferencePattern("Server", server); + + kernel.loadGBean(gbean, classLoader); + kernel.startGBean(SERVICE_NAME); + return SERVICE_NAME; + } +} diff --git a/activeio/src/java/org/activeio/xnet/SyncChannelServerDaemon.java b/activeio/src/java/org/activeio/xnet/SyncChannelServerDaemon.java new file mode 100644 index 0000000000..077d0e5bca --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/SyncChannelServerDaemon.java @@ -0,0 +1,161 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio.xnet; + +import org.activeio.SyncChannel; +import org.activeio.SyncChannelServer; +import org.activeio.adapter.SyncChannelToSocket; +import org.activeio.net.SocketSyncChannelFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.net.URISyntaxException; + +public class SyncChannelServerDaemon implements Runnable { + private static final Log log = LogFactory.getLog(SyncChannelServerDaemon.class); + + private final SocketService socketService; + private InetAddress address; + private int port; + + private int timeout; + private String name; + private URI bindURI; + private SyncChannelServer server; + + public SyncChannelServerDaemon(SocketService socketService, InetAddress address, int port) { + this(null, socketService, address, port); + } + + public SyncChannelServerDaemon(String name, SocketService socketService, InetAddress address, int port) { + this.name = name; + if (socketService == null) { + throw new IllegalArgumentException("socketService is null"); + } + this.socketService = socketService; + this.address = address; + this.port = port; + try { + this.bindURI = new URI("uri", null, address.getHostName(), port, null, null, null); + } catch (URISyntaxException e) { + throw (IllegalArgumentException) new IllegalArgumentException().initCause(e); + } + } + + public void doStart() throws Exception { + SocketSyncChannelFactory factory = new SocketSyncChannelFactory(); + server = null; + + try { + server = factory.bindSyncChannel(bindURI); + port = server.getConnectURI().getPort(); + address = InetAddress.getByName(server.getConnectURI().getHost()); + stopped = false; +// server.setSoTimeout(timeout); + } catch (Exception e) { + throw new ServiceException("Service failed to open socket", e); + } + Thread thread = new Thread(this); + thread.setName("service." + name + "@" + hashCode()); + thread.setDaemon(true); + thread.start(); + } + + public synchronized void doStop() { + stopped = true; + } + + public synchronized void doFail() { + doStop(); + if (server != null) { + server.dispose(); + } + } + + public void setSoTimeout(int timeout) throws SocketException { + this.timeout = timeout; + } + + public int getSoTimeout() throws IOException { + return timeout; + } + + /** + * Gets the inetAddress number that the + * daemon is listening on. + */ + public InetAddress getAddress() { + return address; + } + + /** + * Gets the port number that the + * daemon is listening on. + */ + public int getPort() { + return port; + } + + public void run() { + while (!shouldStop()) { + Socket socket = null; + try { + SyncChannel channel = (SyncChannel) server.accept(timeout); + socket = new SyncChannelToSocket(channel); + socket.setTcpNoDelay(true); + if (!shouldStop()) { + // the server service is responsible + // for closing the socket. + this.socketService.service(socket); + } + } catch (SocketTimeoutException e) { + // we don't really care + log.debug("Socket timed-out", e); + } catch (Throwable e) { + log.error("Unexpected error", e); + } finally { + log.info("Processed"); + } + } + + if (server != null) { + try { + server.dispose(); + } catch (Exception ioException) { + log.debug("Error cleaning up socked", ioException); + } + server = null; + } + } + + private boolean stopped; + + public synchronized void stop() { + stopped = true; + } + + private synchronized boolean shouldStop() { + return stopped; + } +} + diff --git a/activeio/src/java/org/activeio/xnet/hba/ExactIPAddressPermission.java b/activeio/src/java/org/activeio/xnet/hba/ExactIPAddressPermission.java new file mode 100644 index 0000000000..1194c90576 --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/hba/ExactIPAddressPermission.java @@ -0,0 +1,71 @@ +/** + * + * Copyright 2005 Gianny Damour. + * + * Licensed 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.activeio.xnet.hba; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.net.InetAddress; +import java.net.Inet4Address; + +/** + * @version $Revision$ $Date$ + */ +public class ExactIPAddressPermission implements IPAddressPermission { + private static final Pattern MASK_VALIDATOR = Pattern.compile("^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$"); + + public static boolean canSupport(String mask) { + Matcher matcher = MASK_VALIDATOR.matcher(mask); + return matcher.matches(); + } + + private final byte[] bytes; + + public ExactIPAddressPermission(byte[] bytes) { + this.bytes = bytes; + } + + public ExactIPAddressPermission(String mask) { + Matcher matcher = MASK_VALIDATOR.matcher(mask); + if (false == matcher.matches()) { + throw new IllegalArgumentException("Mask " + mask + " does not match pattern " + MASK_VALIDATOR.pattern()); + } + + bytes = new byte[4]; + for (int i = 0; i < 4; i++) { + String group = matcher.group(i + 1); + int value = Integer.parseInt(group); + if (value < 0 || 255 < value) { + throw new IllegalArgumentException("byte #" + i + " is not valid."); + } + bytes[i] = (byte) value; + } + } + + public boolean implies(InetAddress address) { + if (false == address instanceof Inet4Address) { + return false; + } + + byte[] byteAddress = address.getAddress(); + for (int i = 0; i < 4; i++) { + if (byteAddress[i] != bytes[i]) { + return false; + } + } + return true; + } +} diff --git a/activeio/src/java/org/activeio/xnet/hba/ExactIPv6AddressPermission.java b/activeio/src/java/org/activeio/xnet/hba/ExactIPv6AddressPermission.java new file mode 100644 index 0000000000..fdd687dfcb --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/hba/ExactIPv6AddressPermission.java @@ -0,0 +1,72 @@ +/** + * + * Copyright 2005 Gianny Damour. + * + * Licensed 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.activeio.xnet.hba; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.StringTokenizer; +import java.net.InetAddress; +import java.net.Inet6Address; + +/** + * @version $Revision$ $Date$ + */ +public class ExactIPv6AddressPermission implements IPAddressPermission { + private static final Pattern MASK_VALIDATOR = Pattern.compile("^(([a-fA-F0-9]{1,4}:){7})([a-fA-F0-9]{1,4})$"); + + public static boolean canSupport(String mask) { + Matcher matcher = MASK_VALIDATOR.matcher(mask); + return matcher.matches(); + } + + private final byte[] bytes; + + public ExactIPv6AddressPermission(byte[] bytes) { + this.bytes = bytes; + } + + public ExactIPv6AddressPermission(String mask) { + Matcher matcher = MASK_VALIDATOR.matcher(mask); + if (false == matcher.matches()) { + throw new IllegalArgumentException("Mask " + mask + " does not match pattern " + MASK_VALIDATOR.pattern()); + } + + bytes = new byte[16]; + int pos = 0; + StringTokenizer tokenizer = new StringTokenizer(mask, ":"); + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + int value = Integer.parseInt(token, 16); + bytes[pos++] = (byte) ((value & 0xff00) >> 8); + bytes[pos++] = (byte) value; + } + } + + public boolean implies(InetAddress address) { + if (false == address instanceof Inet6Address) { + return false; + } + + byte[] byteAddress = address.getAddress(); + for (int i = 0; i < 16; i++) { + if (byteAddress[i] != bytes[i]) { + return false; + } + } + return true; + } +} diff --git a/activeio/src/java/org/activeio/xnet/hba/FactorizedIPAddressPermission.java b/activeio/src/java/org/activeio/xnet/hba/FactorizedIPAddressPermission.java new file mode 100644 index 0000000000..453a7449d7 --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/hba/FactorizedIPAddressPermission.java @@ -0,0 +1,103 @@ +/** + * + * Copyright 2005 Gianny Damour. + * + * Licensed 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.activeio.xnet.hba; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.StringTokenizer; +import java.net.InetAddress; +import java.net.Inet4Address; + +/** + * @version $Revision$ $Date$ + */ +public class FactorizedIPAddressPermission implements IPAddressPermission { + private static final Pattern MASK_VALIDATOR = Pattern.compile("^((\\d{1,3}){1}(\\.\\d{1,3}){0,2}\\.)?\\{(\\d{1,3}){1}((,\\d{1,3})*)\\}$"); + + public static boolean canSupport(String mask) { + Matcher matcher = MASK_VALIDATOR.matcher(mask); + return matcher.matches(); + } + + private final byte[] prefixBytes; + private final byte[] suffixBytes; + + public FactorizedIPAddressPermission(String mask) { + Matcher matcher = MASK_VALIDATOR.matcher(mask); + if (false == matcher.matches()) { + throw new IllegalArgumentException("Mask " + mask + " does not match pattern " + MASK_VALIDATOR.pattern()); + } + + // group 1 is the factorized IP part. + // e.g. group 1 in "1.2.3.{4,5,6}" is "1.2.3." + String prefix = matcher.group(1); + StringTokenizer tokenizer = new StringTokenizer(prefix, "."); + prefixBytes = new byte[tokenizer.countTokens()]; + for (int i = 0; i < prefixBytes.length; i++) { + String token = tokenizer.nextToken(); + int value = Integer.parseInt(token); + if (value < 0 || 255 < value) { + throw new IllegalArgumentException("byte #" + i + " is not valid."); + } + prefixBytes[i] = (byte) value; + } + + // group 5 is a comma separated list of optional suffixes. + // e.g. group 5 in "1.2.3.{4,5,6}" is ",5,6" + String suffix = matcher.group(5); + tokenizer = new StringTokenizer(suffix, ","); + suffixBytes = new byte[1 + tokenizer.countTokens()]; + + // group 4 is the compulsory and first suffix. + // e.g. group 4 in "1.2.3.{4,5,6}" is "4" + int value = Integer.parseInt(matcher.group(4)); + int i = 0; + if (value < 0 || 255 < value) { + throw new IllegalArgumentException("suffix " + i + " is not valid."); + } + suffixBytes[i++] = (byte) value; + + for (; i < suffixBytes.length; i++) { + String token = tokenizer.nextToken(); + value = Integer.parseInt(token); + if (value < 0 || 255 < value) { + throw new IllegalArgumentException("byte #" + i + " is not valid."); + } + suffixBytes[i] = (byte) value; + } + } + + public boolean implies(InetAddress address) { + if (false == address instanceof Inet4Address) { + return false; + } + + byte[] byteAddress = address.getAddress(); + for (int i = 0; i < prefixBytes.length; i++) { + if (byteAddress[i] != prefixBytes[i]) { + return false; + } + } + byte lastByte = byteAddress[prefixBytes.length]; + for (int i = 0; i < suffixBytes.length; i++) { + if (lastByte == suffixBytes[i]) { + return true; + } + } + return false; + } +} diff --git a/activeio/src/java/org/activeio/xnet/hba/IPAddressPermission.java b/activeio/src/java/org/activeio/xnet/hba/IPAddressPermission.java new file mode 100644 index 0000000000..96f6d6daae --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/hba/IPAddressPermission.java @@ -0,0 +1,27 @@ +/** + * + * Copyright 2005 Gianny Damour. + * + * Licensed 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.activeio.xnet.hba; + +import java.io.Serializable; +import java.net.InetAddress; + +/** + * @version $Revision$ $Date$ + */ +public interface IPAddressPermission extends Serializable { + public boolean implies(InetAddress address); +} diff --git a/activeio/src/java/org/activeio/xnet/hba/IPAddressPermissionEditor.java b/activeio/src/java/org/activeio/xnet/hba/IPAddressPermissionEditor.java new file mode 100644 index 0000000000..cd38d61c84 --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/hba/IPAddressPermissionEditor.java @@ -0,0 +1,34 @@ +/** + * + * Copyright 2005 Gianny Damour. + * + * Licensed 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.activeio.xnet.hba; + +import java.beans.PropertyEditorSupport; + +/** + * @version $Revision$ $Date$ + */ +public class IPAddressPermissionEditor extends PropertyEditorSupport { + private IPAddressPermission addressMask; + + public void setAsText(String text) throws IllegalArgumentException { + addressMask = IPAddressPermissionFactory.getIPAddressMask(text); + } + + public Object getValue() { + return addressMask; + } +} diff --git a/activeio/src/java/org/activeio/xnet/hba/IPAddressPermissionFactory.java b/activeio/src/java/org/activeio/xnet/hba/IPAddressPermissionFactory.java new file mode 100644 index 0000000000..6c2e07f445 --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/hba/IPAddressPermissionFactory.java @@ -0,0 +1,40 @@ +/** + * + * Copyright 2005 Gianny Damour. + * + * Licensed 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.activeio.xnet.hba; + +/** + * @version $Revision$ $Date$ + */ +public class IPAddressPermissionFactory { + + public static IPAddressPermission getIPAddressMask(String mask) { + if (StartWithIPAddressPermission.canSupport(mask)) { + return new StartWithIPAddressPermission(mask); + } else if (ExactIPAddressPermission.canSupport(mask)) { + return new ExactIPAddressPermission(mask); + } else if (FactorizedIPAddressPermission.canSupport(mask)) { + return new FactorizedIPAddressPermission(mask); + } else if (NetmaskIPAddressPermission.canSupport(mask)) { + return new NetmaskIPAddressPermission(mask); + } else if (ExactIPv6AddressPermission.canSupport(mask)) { + return new ExactIPv6AddressPermission(mask); + } else if (NetmaskIPv6AddressPermission.canSupport(mask)) { + return new NetmaskIPv6AddressPermission(mask); + } + throw new IllegalArgumentException("Mask " + mask + " is not supported."); + } +} diff --git a/activeio/src/java/org/activeio/xnet/hba/NetmaskIPAddressPermission.java b/activeio/src/java/org/activeio/xnet/hba/NetmaskIPAddressPermission.java new file mode 100644 index 0000000000..735776ce37 --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/hba/NetmaskIPAddressPermission.java @@ -0,0 +1,89 @@ +/** + * + * Copyright 2005 Gianny Damour. + * + * Licensed 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.activeio.xnet.hba; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.net.InetAddress; +import java.net.Inet4Address; + +/** + * @version $Revision$ $Date$ + */ +public class NetmaskIPAddressPermission implements IPAddressPermission { + private static final Pattern MASK_VALIDATOR = Pattern.compile("^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/((\\d{1,2})|(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3}))$"); + + public static boolean canSupport(String mask) { + Matcher matcher = MASK_VALIDATOR.matcher(mask); + return matcher.matches(); + } + + private final byte[] networkAddressBytes; + private final byte[] netmaskBytes; + + public NetmaskIPAddressPermission(String mask) { + Matcher matcher = MASK_VALIDATOR.matcher(mask); + if (false == matcher.matches()) { + throw new IllegalArgumentException("Mask " + mask + " does not match pattern " + MASK_VALIDATOR.pattern()); + } + + networkAddressBytes = new byte[4]; + for (int i = 0; i < 4; i++) { + String group = matcher.group(i + 1); + int value = Integer.parseInt(group); + if (value < 0 || 255 < value) { + throw new IllegalArgumentException("byte #" + i + " is not valid."); + } + networkAddressBytes[i] = (byte) value; + } + + netmaskBytes = new byte[4]; + String netmask = matcher.group(6); + if (null != netmask) { + int value = Integer.parseInt(netmask); + int pos = value / 8; + int shift = 8 - value % 8; + for (int i = 0; i < pos; i++) { + netmaskBytes[i] = (byte) 0xff; + } + netmaskBytes[pos] = (byte) (0xff << shift); + } else { + for (int i = 0; i < 4; i++) { + String group = matcher.group(i + 7); + int value = Integer.parseInt(group); + if (value < 0 || 255 < value) { + throw new IllegalArgumentException("byte #" + i + " is not valid."); + } + netmaskBytes[i] = (byte) value; + } + } + } + + public boolean implies(InetAddress address) { + if (false == address instanceof Inet4Address) { + return false; + } + + byte[] byteAddress = address.getAddress(); + for (int i = 0; i < 4; i++) { + if ((netmaskBytes[i] & byteAddress[i]) != networkAddressBytes[i]) { + return false; + } + } + return true; + } +} diff --git a/activeio/src/java/org/activeio/xnet/hba/NetmaskIPv6AddressPermission.java b/activeio/src/java/org/activeio/xnet/hba/NetmaskIPv6AddressPermission.java new file mode 100644 index 0000000000..b19e6eb57d --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/hba/NetmaskIPv6AddressPermission.java @@ -0,0 +1,90 @@ +/** + * + * Copyright 2005 Gianny Damour. + * + * Licensed 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.activeio.xnet.hba; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.StringTokenizer; +import java.net.InetAddress; +import java.net.Inet6Address; + +/** + * @version $Revision$ $Date$ + */ +public class NetmaskIPv6AddressPermission implements IPAddressPermission { + private static final Pattern MASK_VALIDATOR = Pattern.compile("^(([a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4})/((\\d{1,3})|(([a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}))$"); + + public static boolean canSupport(String mask) { + Matcher matcher = MASK_VALIDATOR.matcher(mask); + return matcher.matches(); + } + + private final byte[] networkAddressBytes; + private final byte[] netmaskBytes; + + public NetmaskIPv6AddressPermission(String mask) { + Matcher matcher = MASK_VALIDATOR.matcher(mask); + if (false == matcher.matches()) { + throw new IllegalArgumentException("Mask " + mask + " does not match pattern " + MASK_VALIDATOR.pattern()); + } + + networkAddressBytes = new byte[16]; + int pos = 0; + StringTokenizer tokenizer = new StringTokenizer(matcher.group(1), ":"); + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + int value = Integer.parseInt(token, 16); + networkAddressBytes[pos++] = (byte) ((value & 0xff00) >> 8); + networkAddressBytes[pos++] = (byte) value; + } + + netmaskBytes = new byte[16]; + String netmask = matcher.group(4); + if (null != netmask) { + int value = Integer.parseInt(netmask); + pos = value / 8; + int shift = 8 - value % 8; + for (int i = 0; i < pos; i++) { + netmaskBytes[i] = (byte) 0xff; + } + netmaskBytes[pos] = (byte) (0xff << shift); + } else { + pos = 0; + tokenizer = new StringTokenizer(matcher.group(5), ":"); + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + int value = Integer.parseInt(token, 16); + netmaskBytes[pos++] = (byte) ((value & 0xff00) >> 8); + netmaskBytes[pos++] = (byte) value; + } + } + } + + public boolean implies(InetAddress address) { + if (false == address instanceof Inet6Address) { + return false; + } + + byte[] byteAddress = address.getAddress(); + for (int i = 0; i < 16; i++) { + if ((netmaskBytes[i] & byteAddress[i]) != networkAddressBytes[i]) { + return false; + } + } + return true; + } +} diff --git a/activeio/src/java/org/activeio/xnet/hba/ServiceAccessController.java b/activeio/src/java/org/activeio/xnet/hba/ServiceAccessController.java new file mode 100644 index 0000000000..856da52344 --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/hba/ServiceAccessController.java @@ -0,0 +1,129 @@ +/** + * + * Copyright 2005 the original author or authors. + * + * Licensed 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.activeio.xnet.hba; + +import org.activeio.xnet.ServerService; +import org.activeio.xnet.ServiceException; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.LinkedList; +import java.util.Properties; +import java.util.StringTokenizer; + +public class ServiceAccessController implements ServerService { + private final ServerService next; + private IPAddressPermission[] allowHosts; + + public ServiceAccessController(ServerService next) { + this.next = next; + } + + public ServiceAccessController(String name, ServerService next, IPAddressPermission[] ipAddressMasks) { + this.next = next; + this.allowHosts = ipAddressMasks; + } + + public void service(Socket socket) throws ServiceException, IOException { + // Check authorization + checkHostsAuthorization(socket.getInetAddress(), socket.getLocalAddress()); + + next.service(socket); + } + + public IPAddressPermission[] getAllowHosts() { + return allowHosts; + } + + public void setAllowHosts(IPAddressPermission[] ipAddressMasks) { + this.allowHosts = ipAddressMasks; + } + + public void checkHostsAuthorization(InetAddress clientAddress, InetAddress serverAddress) throws SecurityException { + // Check the client ip against the server ip. Hosts are + // allowed to access themselves, so if these ips + // match, the following for loop will be skipped. + if (clientAddress.equals(serverAddress)) { + return; + } + + for (int i = 0; i < allowHosts.length; i++) { + if (allowHosts[i].implies(clientAddress)) { + return; + } + } + + throw new SecurityException("Host " + clientAddress.getHostAddress() + " is not authorized to access this service."); + } + + private void parseAdminIPs(Properties props) throws ServiceException { + LinkedList ipAddressMasksList = new LinkedList(); + + try { + InetAddress[] localIps = InetAddress.getAllByName("localhost"); + for (int i = 0; i < localIps.length; i++) { + if (localIps[i] instanceof Inet4Address) { + ipAddressMasksList.add(new ExactIPAddressPermission(localIps[i].getAddress())); + } else { + ipAddressMasksList.add(new ExactIPv6AddressPermission(localIps[i].getAddress())); + } + } + } catch (UnknownHostException e) { + throw new ServiceException("Could not get localhost inet address", e); + } + + String ipString = props.getProperty("only_from"); + if (ipString != null) { + StringTokenizer st = new StringTokenizer(ipString, " "); + while (st.hasMoreTokens()) { + String mask = st.nextToken(); + ipAddressMasksList.add(IPAddressPermissionFactory.getIPAddressMask(mask)); + } + } + + allowHosts = (IPAddressPermission[]) ipAddressMasksList.toArray(new IPAddressPermission[ipAddressMasksList.size()]); + } + + public void init(Properties props) throws Exception { + parseAdminIPs(props); + next.init(props); + } + + public void start() throws ServiceException { + next.start(); + } + + public void stop() throws ServiceException { + next.stop(); + } + + public String getName() { + return next.getName(); + } + + public String getIP() { + return next.getIP(); + } + + public int getPort() { + return next.getPort(); + } + +} diff --git a/activeio/src/java/org/activeio/xnet/hba/StartWithIPAddressPermission.java b/activeio/src/java/org/activeio/xnet/hba/StartWithIPAddressPermission.java new file mode 100644 index 0000000000..f95a78a2ab --- /dev/null +++ b/activeio/src/java/org/activeio/xnet/hba/StartWithIPAddressPermission.java @@ -0,0 +1,81 @@ +/** + * + * Copyright 2005 Gianny Damour. + * + * Licensed 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.activeio.xnet.hba; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.net.InetAddress; +import java.net.Inet4Address; + +/** + * @version $Revision$ $Date$ + */ +public class StartWithIPAddressPermission implements IPAddressPermission { + private static final Pattern MASK_VALIDATOR = Pattern.compile("^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.0$"); + + public static boolean canSupport(String mask) { + Matcher matcher = MASK_VALIDATOR.matcher(mask); + return matcher.matches(); + } + + private final byte[] bytes; + + public StartWithIPAddressPermission(String mask) { + Matcher matcher = MASK_VALIDATOR.matcher(mask); + if (false == matcher.matches()) { + throw new IllegalArgumentException("Mask " + mask + " does not match pattern " + MASK_VALIDATOR.pattern()); + } + + Byte[] tmpBytes = new Byte[4]; + boolean isWildCard = false; + int size = 0; + for (int i = 0; i < 3; i++) { + String group = matcher.group(i + 1); + if (group.equals("0")) { + isWildCard = true; + } else if (isWildCard) { + throw new IllegalArgumentException("0 at position " + size + " in mask"); + } else { + int value = Integer.parseInt(group); + if (value < 0 || 255 < value) { + throw new IllegalArgumentException("byte #" + i + " is not valid."); + } + tmpBytes[i] = new Byte((byte) value); + size++; + } + } + + bytes = new byte[size]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = tmpBytes[i].byteValue(); + } + } + + public boolean implies(InetAddress address) { + if (false == address instanceof Inet4Address) { + return false; + } + + byte[] byteAddress = address.getAddress(); + for (int i = 0; i < bytes.length; i++) { + if (byteAddress[i] != bytes[i]) { + return false; + } + } + return true; + } +} diff --git a/activeio/src/resources/META-INF/org.activeio.ChannelFactory/aio b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/aio new file mode 100644 index 0000000000..085a18ab15 --- /dev/null +++ b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/aio @@ -0,0 +1 @@ +AsyncChannelFactory.class=org.activeio.net.AIOAsyncChannelFactory diff --git a/activeio/src/resources/META-INF/org.activeio.ChannelFactory/datagram b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/datagram new file mode 100644 index 0000000000..51865e4eba --- /dev/null +++ b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/datagram @@ -0,0 +1 @@ +SyncChannelFactory.class=org.activeio.net.DatagramSocketSyncChannelFactory \ No newline at end of file diff --git a/activeio/src/resources/META-INF/org.activeio.ChannelFactory/jxta b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/jxta new file mode 100644 index 0000000000..aad0a8fbdc --- /dev/null +++ b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/jxta @@ -0,0 +1 @@ +SyncChannelFactory.class=org.activeio.net.JxtaSocketSyncChannelFactory \ No newline at end of file diff --git a/activeio/src/resources/META-INF/org.activeio.ChannelFactory/multicast b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/multicast new file mode 100644 index 0000000000..b8766e7b8f --- /dev/null +++ b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/multicast @@ -0,0 +1 @@ +SyncChannelFactory.class=org.activeio.net.MulticastSocketSyncChannelFactory \ No newline at end of file diff --git a/activeio/src/resources/META-INF/org.activeio.ChannelFactory/nio b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/nio new file mode 100644 index 0000000000..8e9daf7e20 --- /dev/null +++ b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/nio @@ -0,0 +1,2 @@ +SyncChannelFactory.class=org.activeio.net.NIOSyncChannelFactory +AsyncChannelFactory.class=org.activeio.net.NIOAsyncChannelFactory \ No newline at end of file diff --git a/activeio/src/resources/META-INF/org.activeio.ChannelFactory/nio-async b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/nio-async new file mode 100644 index 0000000000..9efad3bf4e --- /dev/null +++ b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/nio-async @@ -0,0 +1 @@ +AsyncChannelFactory.class=org.activeio.net.NIOAsyncChannelFactory \ No newline at end of file diff --git a/activeio/src/resources/META-INF/org.activeio.ChannelFactory/nio-sync b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/nio-sync new file mode 100644 index 0000000000..82b7d11a6f --- /dev/null +++ b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/nio-sync @@ -0,0 +1 @@ +SyncChannelFactory.class=org.activeio.net.NIOSyncChannelFactory \ No newline at end of file diff --git a/activeio/src/resources/META-INF/org.activeio.ChannelFactory/socket b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/socket new file mode 100644 index 0000000000..2fc06b1b94 --- /dev/null +++ b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/socket @@ -0,0 +1 @@ +SyncChannelFactory.class=org.activeio.net.SocketSyncChannelFactory \ No newline at end of file diff --git a/activeio/src/resources/META-INF/org.activeio.ChannelFactory/ssl b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/ssl new file mode 100644 index 0000000000..0e20e20baa --- /dev/null +++ b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/ssl @@ -0,0 +1 @@ +SyncChannelFactory.class=org.activeio.net.SslSocketSyncChannelFactory \ No newline at end of file diff --git a/activeio/src/resources/META-INF/org.activeio.ChannelFactory/vmpipe b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/vmpipe new file mode 100644 index 0000000000..6a069d65cf --- /dev/null +++ b/activeio/src/resources/META-INF/org.activeio.ChannelFactory/vmpipe @@ -0,0 +1 @@ +AsyncChannelFactory.class=org.activeio.net.VMPipeAsyncChannelFactory diff --git a/activeio/src/test/client.keystore b/activeio/src/test/client.keystore new file mode 100644 index 0000000000..8580672777 Binary files /dev/null and b/activeio/src/test/client.keystore differ diff --git a/activeio/src/test/log4j.properties b/activeio/src/test/log4j.properties new file mode 100644 index 0000000000..9aee5f86fe --- /dev/null +++ b/activeio/src/test/log4j.properties @@ -0,0 +1,16 @@ +# +# The logging properties used during tests.. +# +log4j.rootLogger=DEBUG, out + +# CONSOLE appender not used by default +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n + +# File appender +log4j.appender.out=org.apache.log4j.FileAppender +log4j.appender.out.layout=org.apache.log4j.PatternLayout +log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +log4j.appender.out.file=target/test-reports/test.log +log4j.appender.out.append=true diff --git a/activeio/src/test/org/activeio/ChannelFactoryTest.java b/activeio/src/test/org/activeio/ChannelFactoryTest.java new file mode 100644 index 0000000000..c046127ee7 --- /dev/null +++ b/activeio/src/test/org/activeio/ChannelFactoryTest.java @@ -0,0 +1,155 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import junit.framework.TestCase; + +import org.activeio.adapter.AsyncToSyncChannel; +import org.activeio.adapter.SyncToAsyncChannel; +import org.activeio.net.AIOAsyncChannel; +import org.activeio.net.AIOSyncChannelServer; +import org.activeio.net.NIOAsyncChannel; +import org.activeio.net.NIOAsyncChannelServer; +import org.activeio.net.NIOSyncChannel; +import org.activeio.net.NIOSyncChannelServer; +import org.activeio.net.SocketSyncChannel; +import org.activeio.net.SocketSyncChannelServer; +import org.activeio.net.VMPipeAsyncChannelPipe; +import org.activeio.net.VMPipeAsyncChannelServer; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +/** + */ +public class ChannelFactoryTest extends TestCase { + + static final Log log = LogFactory.getLog(ChannelFactoryTest.class); + static boolean aioDisabled = System.getProperty("disable.aio.tests", "false").equals("true"); + + ChannelFactory factory = new ChannelFactory(); + + private SyncChannelServer syncChannelServer; + private SyncChannel clientSynchChannel; + private SyncChannel serverSynchChannel; + + private AsyncChannelServer asyncChannelServer; + private AsyncChannel clientAsyncChannel; + private AsyncChannel serverAsyncChannel; + + protected void setUp() throws Exception { + log.info("Running: "+getName()); + } + + public void testSocket() throws IOException, URISyntaxException, InterruptedException { + + createSynchObjects("socket://localhost:0"); + assertNotNull( syncChannelServer.getAdapter(SocketSyncChannelServer.class) ); + assertNotNull( clientSynchChannel.getAdapter(SocketSyncChannel.class) ); + assertNotNull( serverSynchChannel.getAdapter(SocketSyncChannel.class) ); + + createAsynchObjects("socket://localhost:0"); + assertNotNull( asyncChannelServer.getAdapter(SocketSyncChannelServer.class) ); + assertNotNull( clientAsyncChannel.getAdapter(SocketSyncChannel.class) ); + assertNotNull( serverAsyncChannel.getAdapter(SocketSyncChannel.class) ); + + } + + public void testAIO() throws IOException, URISyntaxException, InterruptedException { + + if( aioDisabled ) { + return; + } + + createSynchObjects("aio://localhost:0"); + assertNotNull( syncChannelServer.getAdapter(AIOSyncChannelServer.class) ); + assertNotNull( clientSynchChannel.getAdapter(AIOAsyncChannel.class) ); + assertNotNull( serverSynchChannel.getAdapter(AIOAsyncChannel.class) ); + + createAsynchObjects("aio://localhost:0"); + assertNotNull( asyncChannelServer.getAdapter(AIOSyncChannelServer.class) ); + assertNotNull( clientAsyncChannel.getAdapter(AIOAsyncChannel.class) ); + assertNotNull( serverAsyncChannel.getAdapter(AIOAsyncChannel.class) ); + + } + + public void testNIO() throws IOException, URISyntaxException, InterruptedException { + + createSynchObjects("nio://localhost:0"); + assertNotNull( syncChannelServer.getAdapter(NIOSyncChannelServer.class) ); + assertNotNull( clientSynchChannel.getAdapter(NIOSyncChannel.class) ); + assertNotNull( serverSynchChannel.getAdapter(NIOSyncChannel.class) ); + + createAsynchObjects("nio://localhost:0"); + assertNotNull( asyncChannelServer.getAdapter(NIOAsyncChannelServer.class) ); + assertNotNull( clientAsyncChannel.getAdapter(NIOAsyncChannel.class) ); + assertNotNull( serverAsyncChannel.getAdapter(NIOAsyncChannel.class) ); + + } + + public void testVMPipe() throws IOException, URISyntaxException, InterruptedException { + + createSynchObjects("vmpipe://localhost"); + assertNotNull( syncChannelServer.getAdapter(VMPipeAsyncChannelServer.class) ); + assertNotNull( clientSynchChannel.getAdapter(VMPipeAsyncChannelPipe.PipeChannel.class) ); + assertNotNull( serverSynchChannel.getAdapter(VMPipeAsyncChannelPipe.PipeChannel.class) ); + + createAsynchObjects("vmpipe://localhost"); + assertNotNull( asyncChannelServer.getAdapter(VMPipeAsyncChannelServer.class) ); + assertNotNull( clientAsyncChannel.getAdapter(VMPipeAsyncChannelPipe.PipeChannel.class) ); + assertNotNull( serverAsyncChannel.getAdapter(VMPipeAsyncChannelPipe.PipeChannel.class) ); + + } + + private void createSynchObjects(String bindURI) throws IOException, URISyntaxException { + syncChannelServer = factory.bindSyncChannel(new URI(bindURI)); + syncChannelServer.start(); + clientSynchChannel = factory.openSyncChannel(syncChannelServer.getConnectURI()); + serverSynchChannel = AsyncToSyncChannel.adapt( syncChannelServer.accept(1000*5) ); + serverSynchChannel.dispose(); + clientSynchChannel.dispose(); + syncChannelServer.dispose(); + } + + private void createAsynchObjects(String bindURI) throws IOException, URISyntaxException, InterruptedException { + asyncChannelServer = factory.bindAsyncChannel(new URI(bindURI)); + final CountDownLatch accepted = new CountDownLatch(1); + asyncChannelServer.setAcceptListener(new AcceptListener() { + public void onAccept(Channel channel) { + serverAsyncChannel = SyncToAsyncChannel.adapt(channel); + channel.dispose(); + accepted.countDown(); + } + public void onAcceptError(IOException error) { + error.printStackTrace(); + } + }); + asyncChannelServer.start(); + clientAsyncChannel = factory.openAsyncChannel(asyncChannelServer.getConnectURI()); + accepted.await(1000*10, TimeUnit.MILLISECONDS); + clientAsyncChannel.dispose(); + asyncChannelServer.dispose(); + } + +} diff --git a/activeio/src/test/org/activeio/ChannelRequestTestSupport.java b/activeio/src/test/org/activeio/ChannelRequestTestSupport.java new file mode 100644 index 0000000000..ba801fd1c8 --- /dev/null +++ b/activeio/src/test/org/activeio/ChannelRequestTestSupport.java @@ -0,0 +1,79 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import org.activeio.packet.ByteArrayPacket; + +import java.io.IOException; +import java.net.URISyntaxException; + +import junit.framework.TestCase; + +/** + */ +abstract public class ChannelRequestTestSupport extends TestCase implements RequestListener { + + private ChannelServer server; + + public void test() throws IOException { + final RequestChannel channel = createClientRequestChannel(); + try { + channel.start(); + sendRequest(channel, 1001); + sendRequest(channel, 1002); + sendRequest(channel, 1003); + sendRequest(channel, 1004); + sendRequest(channel, 1005); + } finally { + channel.dispose(); + } + } + + private void sendRequest(final RequestChannel channel, int packetSize) throws IOException { + Packet request = new ByteArrayPacket(fill(new byte[packetSize],(byte)1)); + Packet response = channel.request(request, 1000 * 30*1000); + assertNotNull(response); + assertEquals(packetSize, response.remaining()); + } + + private byte[] fill(byte[] bs, byte b) { + for (int i = 0; i < bs.length; i++) { + bs[i] = b; + } + return bs; + } + + public Packet onRequest(Packet request) { + return new ByteArrayPacket(fill(new byte[request.remaining()],(byte)2)); + } + + public void onRquestError(IOException error) { + error.printStackTrace(); + } + + protected void setUp() throws Exception { + server = createChannelServer(this); + server.start(); + } + + protected void tearDown() throws Exception { + server.dispose(); + } + + abstract protected RequestChannel createClientRequestChannel() throws IOException; + abstract protected ChannelServer createChannelServer(final RequestListener requestListener) throws IOException, URISyntaxException;} diff --git a/activeio/src/test/org/activeio/PacketDataTest.java b/activeio/src/test/org/activeio/PacketDataTest.java new file mode 100644 index 0000000000..e7ae39f38e --- /dev/null +++ b/activeio/src/test/org/activeio/PacketDataTest.java @@ -0,0 +1,44 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio; + +import java.io.IOException; + +import org.activeio.packet.ByteArrayPacket; + +import junit.framework.TestCase; + +/** + */ +public class PacketDataTest extends TestCase { + + ByteArrayPacket packet = new ByteArrayPacket(new byte[200]); + PacketData data = new PacketData(packet); + + public void testInteger() throws IOException { + data.writeInt(Integer.MAX_VALUE); + data.writeInt(Integer.MIN_VALUE); + data.writeInt(551); + + packet.flip(); + assertEquals(Integer.MAX_VALUE, data.readInt()); + assertEquals(Integer.MIN_VALUE, data.readInt()); + assertEquals(551, data.readInt()); + } + +} diff --git a/activeio/src/test/org/activeio/filter/PacketAggregatingChannelFilterTest.java b/activeio/src/test/org/activeio/filter/PacketAggregatingChannelFilterTest.java new file mode 100644 index 0000000000..aeaeb3e650 --- /dev/null +++ b/activeio/src/test/org/activeio/filter/PacketAggregatingChannelFilterTest.java @@ -0,0 +1,92 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.filter; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activeio.AcceptListener; +import org.activeio.AsyncChannelServer; +import org.activeio.Channel; +import org.activeio.ChannelRequestTestSupport; +import org.activeio.ChannelServer; +import org.activeio.RequestChannel; +import org.activeio.RequestListener; +import org.activeio.SyncChannelFactory; +import org.activeio.adapter.AsyncChannelToClientRequestChannel; +import org.activeio.adapter.AsyncChannelToServerRequestChannel; +import org.activeio.adapter.SyncToAsyncChannel; +import org.activeio.adapter.SyncToAsyncChannelServer; +import org.activeio.net.SocketMetadata; +import org.activeio.net.SocketSyncChannelFactory; + +/** + */ +public class PacketAggregatingChannelFilterTest extends ChannelRequestTestSupport { + + private URI serverURI; + + protected ChannelServer createChannelServer(final RequestListener requestListener) throws IOException, URISyntaxException { + + SyncChannelFactory factory = new SocketSyncChannelFactory(); + + AsyncChannelServer server = new SyncToAsyncChannelServer(factory.bindSyncChannel(new URI("tcp://localhost:0"))); + + server.setAcceptListener(new AcceptListener() { + public void onAccept(Channel channel) { + + RequestChannel requestChannel = null; + try { + ((SocketMetadata)channel.getAdapter(SocketMetadata.class)).setTcpNoDelay(true); + requestChannel = + new AsyncChannelToServerRequestChannel( + new PacketAggregatingAsyncChannel( + SyncToAsyncChannel.adapt(channel))); + + requestChannel.setRequestListener(requestListener); + requestChannel.start(); + + } catch (IOException e) { + if (requestChannel != null) + requestChannel.dispose(); + else + channel.dispose(); + } + } + + public void onAcceptError(IOException error) { + error.printStackTrace(); + } + }); + serverURI = server.getConnectURI(); + return server; + } + + /** + * @return + * @throws IOException + */ + protected RequestChannel createClientRequestChannel() throws IOException { + SyncChannelFactory factory = new SocketSyncChannelFactory(); + PacketAggregatingSyncChannel channel = new PacketAggregatingSyncChannel(factory.openSyncChannel(serverURI)); + ((SocketMetadata)channel.getAdapter(SocketMetadata.class)).setTcpNoDelay(true); + return new AsyncChannelToClientRequestChannel(channel); + } + +} diff --git a/activeio/src/test/org/activeio/journal/JournalPerfToolSupport.java b/activeio/src/test/org/activeio/journal/JournalPerfToolSupport.java new file mode 100644 index 0000000000..a4e6d0b0bf --- /dev/null +++ b/activeio/src/test/org/activeio/journal/JournalPerfToolSupport.java @@ -0,0 +1,161 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Random; + +import org.activeio.packet.ByteArrayPacket; + +/** + * Provides the base class uses to run performance tests against a Journal. + * Should be subclassed to customize for specific journal implementation. + * + * @version $Revision: 1.1 $ + */ +abstract public class JournalPerfToolSupport implements JournalEventListener { + + private JournalStatsFilter journal; + private Random random = new Random(); + private byte data[]; + private int workerCount=0; + private PrintWriter statWriter; + // Performance test Options + + // The output goes here: + protected File journalDirectory = new File("journal-logs"); + protected File statCSVFile = new File("stats.csv");; + + // Controls how often we start a new batch of workers. + protected int workerIncrement=20; + protected long incrementDelay=1000*20; + protected boolean verbose=true; + + // Worker configuration. + protected int recordSize=1024; + protected int syncFrequency=15; + protected int workerThinkTime=100; + + private final class Worker implements Runnable { + public void run() { + int i=random.nextInt()%syncFrequency; + while(true) { + boolean sync=false; + + if( syncFrequency>=0 && (i%syncFrequency)==0 ) { + sync=true; + } + try { + journal.write(new ByteArrayPacket(data), sync); + Thread.sleep(workerThinkTime); + } catch (Exception e) { + e.printStackTrace(); + return; + } + i++; + } + } + } + + /** + * @throws IOException + * + */ + protected void exec() throws Exception { + + System.out.println("Client threads write records using: Record Size: "+recordSize+", Sync Frequency: "+syncFrequency+", Worker Think Time: "+workerThinkTime); + + // Create the record and fill it with some values. + data = new byte[recordSize]; + for (int i = 0; i < data.length; i++) { + data[i] = (byte)i; + } + + if( statCSVFile!=null ) { + statWriter = new PrintWriter(new FileOutputStream(statCSVFile)); + statWriter.println("Threads,Throughput (k/s),Forcd write latency (ms),Throughput (records/s)"); + } + + if( journalDirectory.exists() ) { + deleteDir(journalDirectory); + } + journal = new JournalStatsFilter(createJournal()).enableDetailedStats(verbose); + journal.setJournalEventListener(this); + + try { + + // Wait a little to see the worker affect the stats. + // Increment the number of workers every few seconds. + while(true) { + System.out.println("Starting "+workerIncrement+" Workers..."); + for(int i=0;i next. + * @param next + */ + public JournalStatsFilter(Journal next) { + this.next = next; + } + + /** + * @see org.codehaus.activemq.journal.Journal#write(byte[], boolean) + */ + public RecordLocation write(Packet data, boolean sync) throws IOException { + //writeWaitTimeStat + long start = System.currentTimeMillis(); + RecordLocation answer = next.write(data, sync); + long end = System.currentTimeMillis(); + + writeRecordsCounter.increment(); + writeBytesCounter.add(data.remaining()); + if( sync ) + synchedWriteLatency.addTime(end-start); + else + unsynchedWriteLatency.addTime(end-start); + return answer; + } + + /** + * @see org.codehaus.activemq.journal.Journal#read(org.codehaus.activemq.journal.RecordLocation) + */ + public Packet read(RecordLocation location) + throws InvalidRecordLocationException, IOException { + + long start = System.currentTimeMillis(); + Packet answer = next.read(location); + long end = System.currentTimeMillis(); + + readBytesCounter.add(answer.remaining()); + readLatency.addTime(end-start); + return answer; + } + + /** + * @see org.codehaus.activemq.journal.Journal#setMark(org.codehaus.activemq.journal.RecordLocation, boolean) + */ + public void setMark(RecordLocation recordLocator, boolean force) + throws InvalidRecordLocationException, IOException { + next.setMark(recordLocator, force); + } + + /** + * @see org.codehaus.activemq.journal.Journal#getMark() + */ + public RecordLocation getMark() { + return next.getMark(); + } + + /** + * @see org.codehaus.activemq.journal.Journal#close() + */ + public void close() throws IOException { + next.close(); + } + + /** + * @see org.codehaus.activemq.journal.Journal#setJournalEventListener(org.codehaus.activemq.journal.JournalEventListener) + */ + public void setJournalEventListener(JournalEventListener eventListener) { + next.setJournalEventListener(eventListener); + } + + /** + * @see org.codehaus.activemq.journal.Journal#getNextRecordLocation(org.codehaus.activemq.journal.RecordLocation) + */ + public RecordLocation getNextRecordLocation(RecordLocation lastLocation) + throws IOException, InvalidRecordLocationException { + return next.getNextRecordLocation(lastLocation); + } + + /** + * Writes the gathered statistics to the out object. + * + * @param out + */ + public void dump(IndentPrinter out) { + out.printIndent(); + out.println("Journal Stats {"); + out.incrementIndent(); + out.printIndent(); + out.println("Throughput : "+ getThroughputKps() +" k/s and " + getThroughputRps() +" records/s" ); + out.printIndent(); + out.println("Latency with force : "+ getAvgSyncedLatencyMs() +" ms" ); + out.printIndent(); + out.println("Latency without force: "+ getAvgUnSyncedLatencyMs() +" ms" ); + + out.printIndent(); + out.println("Raw Stats {"); + out.incrementIndent(); + + out.printIndent(); + out.println(writeRecordsCounter); + out.printIndent(); + out.println(writeBytesCounter); + out.printIndent(); + out.println(writeLatency); + out.incrementIndent(); + out.printIndent(); + out.println(synchedWriteLatency); + out.printIndent(); + out.println(unsynchedWriteLatency); + out.decrementIndent(); + + out.printIndent(); + out.println(readBytesCounter); + + out.printIndent(); + out.println(readLatency); + out.decrementIndent(); + out.printIndent(); + out.println("}"); + + out.decrementIndent(); + out.printIndent(); + out.println("}"); + + } + + /** + * Dumps the stats to a String. + * + * @see java.lang.Object#toString() + */ + public String toString() { + if( detailedStats ) { + StringWriter w = new StringWriter(); + PrintWriter pw = new PrintWriter(w); + dump(new IndentPrinter(pw, " ")); + return w.getBuffer().toString(); + } else { + StringWriter w = new StringWriter(); + PrintWriter pw = new PrintWriter(w); + IndentPrinter out = new IndentPrinter(pw, " "); + out.println("Throughput : "+ getThroughputKps() +" k/s and " + getThroughputRps() +" records/s"); + out.printIndent(); + out.println("Latency with force : "+getAvgSyncedLatencyMs()+" ms" ); + out.printIndent(); + out.println("Latency without force: "+getAvgUnSyncedLatencyMs()+" ms" ); + return w.getBuffer().toString(); + } + } + + /** + * @param detailedStats true if details stats should be displayed by toString() and dump + * @return + */ + public JournalStatsFilter enableDetailedStats(boolean detailedStats) { + this.detailedStats = detailedStats; + return this; + } + + /** + * Gets the average throughput in k/s. + * + * @return the average throughput in k/s. + */ + public double getThroughputKps() { + long totalTime = writeBytesCounter.getLastSampleTime()-writeBytesCounter.getStartTime(); + return (((double)writeBytesCounter.getCount()/(double)totalTime)/(double)1024)*1000; + } + + /** + * Gets the average throughput in records/s. + * + * @return the average throughput in records/s. + */ + public double getThroughputRps() { + long totalTime = writeRecordsCounter.getLastSampleTime()-writeRecordsCounter.getStartTime(); + return (((double)writeRecordsCounter.getCount()/(double)totalTime))*1000; + } + + /** + * Gets the average number of writes done per second + * + * @return the average number of writes in w/s. + */ + public double getWritesPerSecond() { + return writeLatency.getAveragePerSecond(); + } + + /** + * Gets the average sync write latency in ms. + * + * @return the average sync write latency in ms. + */ + public double getAvgSyncedLatencyMs() { + return synchedWriteLatency.getAverageTime(); + } + + /** + * Gets the average non sync write latency in ms. + * + * @return the average non sync write latency in ms. + */ + public double getAvgUnSyncedLatencyMs() { + return unsynchedWriteLatency.getAverageTime(); + } + + /** + * Resets the stats sample. + */ + public void reset() { + writeLatency.reset(); + writeBytesCounter.reset(); + writeRecordsCounter.reset(); + synchedWriteLatency.reset(); + unsynchedWriteLatency.reset(); + readLatency.reset(); + readBytesCounter.reset(); + } +} \ No newline at end of file diff --git a/activeio/src/test/org/activeio/journal/active/DataStruturesTest.java b/activeio/src/test/org/activeio/journal/active/DataStruturesTest.java new file mode 100644 index 0000000000..fc1b2e66a2 --- /dev/null +++ b/activeio/src/test/org/activeio/journal/active/DataStruturesTest.java @@ -0,0 +1,52 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.active; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; + +import junit.framework.TestCase; + +/** + * Tests the data structures used JournalImpl + * + * @version $Revision: 1.1 $ + */ +public class DataStruturesTest extends TestCase { + + synchronized public void testRecordLocationImplComparison() throws IOException { + Location l1 = new Location(0, 1); + Location l2 = new Location(0, 2); + Location l3 = new Location(0, 3); + + assertTrue( l1.compareTo(l2)<0 ); + + // Sort them using a list. Put them in the wrong order. + ArrayList l = new ArrayList(); + l.add(l2); + l.add(l3); + l.add(l1); + Collections.sort(l); + + // Did they get sorted to the correct order? + assertSame( l.get(0), l1 ); + assertSame( l.get(1), l2 ); + assertSame( l.get(2), l3 ); + } +} diff --git a/activeio/src/test/org/activeio/journal/active/JournalImplTest.java b/activeio/src/test/org/activeio/journal/active/JournalImplTest.java new file mode 100644 index 0000000000..c535099947 --- /dev/null +++ b/activeio/src/test/org/activeio/journal/active/JournalImplTest.java @@ -0,0 +1,167 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.active; + +import java.io.File; +import java.io.IOException; + +import junit.framework.TestCase; + +import org.activeio.Packet; +import org.activeio.journal.InvalidRecordLocationException; +import org.activeio.journal.Journal; +import org.activeio.journal.RecordLocation; +import org.activeio.packet.ByteArrayPacket; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Tests the JournalImpl + * + * @version $Revision: 1.1 $ + */ +public class JournalImplTest extends TestCase { + + Log log = LogFactory.getLog(JournalImplTest.class); + + int size = 1024*10; + int logFileCount=2; + File logDirectory = new File("test-logfile"); + private Journal journal; + + /** + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + if( logDirectory.exists() ) { + deleteDir(logDirectory); + } + assertTrue("Could not delete directory: "+logDirectory.getCanonicalPath(), !logDirectory.exists() ); + journal = new JournalImpl(logDirectory,logFileCount, size, logDirectory); + } + + /** + */ + private void deleteDir(File f) { + File[] files = f.listFiles(); + for (int i = 0; i < files.length; i++) { + File file = files[i]; + file.delete(); + } + f.delete(); + } + + protected void tearDown() throws Exception { + journal.close(); + if( logDirectory.exists() ) + deleteDir(logDirectory); + //assertTrue( !logDirectory.exists() ); + } + + public void testLogFileCreation() throws IOException { + RecordLocation mark = journal.getMark(); + assertNull(mark); + } + + public void testAppendAndRead() throws InvalidRecordLocationException, InterruptedException, IOException { + + Packet data1 = createPacket("Hello World 1"); + RecordLocation location1 = journal.write( data1, false); + Packet data2 = createPacket("Hello World 2"); + RecordLocation location2 = journal.write( data2, false); + Packet data3 = createPacket("Hello World 3"); + RecordLocation location3 = journal.write( data3, false); + + // Now see if we can read that data. + Packet data; + data = journal.read(location2); + assertEquals( data2, data); + data = journal.read(location1); + assertEquals( data1, data); + data = journal.read(location3); + assertEquals( data3, data); + + // Can we cursor the data? + RecordLocation l=journal.getNextRecordLocation(null); + assertEquals(0, l.compareTo(location1)); + data = journal.read(l); + assertEquals( data1, data); + + l=journal.getNextRecordLocation(l); + assertEquals(0, l.compareTo(location2)); + data = journal.read(l); + assertEquals( data2, data); + + l=journal.getNextRecordLocation(l); + assertEquals(0, l.compareTo(location3)); + data = journal.read(l); + assertEquals( data3, data); + + l=journal.getNextRecordLocation(l); + assertNull(l); + + log.info(journal); + } + + public void testCanReadFromArchivedLogFile() throws InvalidRecordLocationException, InterruptedException, IOException { + + Packet data1 = createPacket("Hello World 1"); + RecordLocation location1 = journal.write( data1, false); + + Location pos; + do { + + Packet p = createPacket("<<>>"); + pos = (Location) journal.write( p, false); + journal.setMark(pos, false); + + } while( pos.getLogFileId() < 5 ); + + // Now see if we can read that first packet. + Packet data; + data = journal.read(location1); + assertEquals( data1, data); + + } + + /** + * @param string + * @return + */ + private Packet createPacket(String string) { + return new ByteArrayPacket(string.getBytes()); + } + + public static void assertEquals(Packet arg0, Packet arg1) { + assertEquals(arg0.sliceAsBytes(), arg1.sliceAsBytes()); + } + + public static void assertEquals(byte[] arg0, byte[] arg1) { + if( arg0==null ^ arg1==null ) + fail("Not equal: "+arg0+" != "+arg1); + if( arg0==null ) + return; + if( arg0.length!=arg1.length) + fail("Array lenght not equal: "+arg0.length+" != "+arg1.length); + for( int i=0; i 0 ) { + tool.journalDirectory = new File(args[0]); + } + if( args.length > 1 ) { + tool.workerIncrement = Integer.parseInt(args[1]); + } + if( args.length > 2 ) { + tool.incrementDelay = Long.parseLong(args[2]); + } + if( args.length > 3 ) { + tool.verbose = Boolean.getBoolean(args[3]); + } + if( args.length > 4 ) { + tool.recordSize = Integer.parseInt(args[4]); + } + if( args.length > 5 ) { + tool.syncFrequency = Integer.parseInt(args[5]); + } + if( args.length > 6 ) { + tool.workerThinkTime = Integer.parseInt(args[6]); + } + if( args.length > 7 ) { + tool.logFileCount = Integer.parseInt(args[7]); + } + if( args.length > 8 ) { + tool.logFileSize = Integer.parseInt(args[8]); + } + tool.exec(); + } + + /** + * @throws IOException + * @see org.activeio.journal.JournalPerfToolSupport#createJournal() + */ + public Journal createJournal() throws IOException { + return new JournalImpl( this.journalDirectory, logFileCount, logFileSize); + } + +} diff --git a/activeio/src/test/org/activeio/journal/active/LogFileManagerTest.java b/activeio/src/test/org/activeio/journal/active/LogFileManagerTest.java new file mode 100644 index 0000000000..32662f8a23 --- /dev/null +++ b/activeio/src/test/org/activeio/journal/active/LogFileManagerTest.java @@ -0,0 +1,131 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.active; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; + +import junit.framework.TestCase; + +import org.activeio.journal.InvalidRecordLocationException; +import org.activeio.packet.ByteArrayPacket; +import org.activeio.packet.ByteBufferPacket; + +/** + * Tests the LogFile used by JournalImpl + * + * @version $Revision: 1.1 $ + */ +public class LogFileManagerTest extends TestCase { + + int size = 1024 * 512; + + int logFileCount = 4; + + File logDirectory = new File("test-logfile"); + + private LogFileManager logFile; + + /** + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + if (logDirectory.exists()) { + deleteDir(logDirectory); + } + assertTrue(!logDirectory.exists()); + logFile = new LogFileManager(logDirectory, logFileCount, size, null); + } + + /** + */ + private void deleteDir(File f) { + File[] files = f.listFiles(); + for (int i = 0; i < files.length; i++) { + File file = files[i]; + file.delete(); + } + f.delete(); + } + + protected void tearDown() throws Exception { + logFile.dispose(); + if (logDirectory.exists()) + deleteDir(logDirectory); + assertTrue(!logDirectory.exists()); + } + + public void testLogFileCreation() throws IOException { + assertTrue(logFile.canActivateNextLogFile()); + assertEquals(null,logFile.getFirstActiveLogLocation()); + assertNull(logFile.getLastMarkedRecordLocation()); + assertEquals(new Location(0, 0),logFile.getNextAppendLocation()); + } + + public void testAppendAndRead() throws IOException, InvalidRecordLocationException, InterruptedException { + + System.out.println("Initial:"+logFile.getNextAppendLocation()); + appendHelloRecord(1001); + Location loc2 = logFile.getNextAppendLocation(); + appendHelloRecord(2002); + appendHelloRecord(3003); + appendHelloRecord(3004); + + Location loc3 = logFile.getNextDataRecordLocation(loc2); + assertTrue(loc3.getLogFileOffset() > loc2.getLogFileOffset()); + Location loc4 = logFile.getNextDataRecordLocation(loc3); + assertTrue(loc4.getLogFileOffset() > loc3.getLogFileOffset()); + + } + + public void testRollOver() throws IOException, InvalidRecordLocationException, InterruptedException { + + int lastId = logFile.getNextAppendLocation().getLogFileId(); + int counter = 0; + for (int i = 0; i < logFileCount; i++) { + counter += 500; + appendHelloRecord(counter); + if (i + 1 == logFileCount) { + assertFalse(logFile.canActivateNextLogFile()); + } else { + assertTrue(logFile.canActivateNextLogFile()); + logFile.activateNextLogFile(); + assertEquals(lastId + 1, logFile.getNextAppendLocation().getLogFileId()); + lastId = logFile.getNextAppendLocation().getLogFileId(); + } + } + + } + + /** + * @param i + * @throws IOException + * @throws InterruptedException + */ + private void appendHelloRecord(int i) throws IOException, InterruptedException { + byte data[] = ("Hello World: " + i).getBytes(); + Record batchedRecord = new Record(LogFileManager.DATA_RECORD_TYPE, new ByteArrayPacket(data), null); + batchedRecord.setLocation(logFile.getNextAppendLocation()); + + BatchedWrite write = new BatchedWrite(new ByteBufferPacket(ByteBuffer.allocate(1024))); + write.append(batchedRecord,null, true); + write.flip(); + logFile.append(write); + } +} diff --git a/activeio/src/test/org/activeio/journal/howl/JournalPerfTool.java b/activeio/src/test/org/activeio/journal/howl/JournalPerfTool.java new file mode 100644 index 0000000000..c49e979a21 --- /dev/null +++ b/activeio/src/test/org/activeio/journal/howl/JournalPerfTool.java @@ -0,0 +1,92 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.journal.howl; + +import java.io.File; + +import org.activeio.journal.Journal; +import org.activeio.journal.JournalPerfToolSupport; +import org.objectweb.howl.log.Configuration; + +/** + * A Performance statistics gathering tool for the HOWL based Journal. + * + * @version $Revision: 1.1 $ + */ +public class JournalPerfTool extends JournalPerfToolSupport { + + private int maxLogFiles= 2; + private int bufferSize = 1024*4; + private int maxBuffers = 20; + private int maxBlocksPerFile = 100; + + public static void main(String[] args) throws Exception { + + try { + JournalPerfTool tool = new JournalPerfTool(); + if( args.length > 0 ) { + tool.journalDirectory = new File(args[0]); + } + if( args.length > 1 ) { + tool.workerIncrement = Integer.parseInt(args[1]); + } + if( args.length > 2 ) { + tool.incrementDelay = Long.parseLong(args[2]); + } + if( args.length > 3 ) { + tool.verbose = Boolean.getBoolean(args[3]); + } + if( args.length > 4 ) { + tool.recordSize = Integer.parseInt(args[4]); + } + if( args.length > 5 ) { + tool.syncFrequency = Integer.parseInt(args[5]); + } + if( args.length > 6 ) { + tool.workerThinkTime = Integer.parseInt(args[6]); + } + + if( args.length > 7 ) { + tool.maxLogFiles = Integer.parseInt(args[7]); + } + if( args.length > 8 ) { + tool.bufferSize = Integer.parseInt(args[8]); + } + if( args.length > 9 ) { + tool.maxBuffers = Integer.parseInt(args[9]); + } + if( args.length > 10 ) { + tool.maxBlocksPerFile = Integer.parseInt(args[10]); + } + tool.exec(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + public Journal createJournal() throws Exception { + Configuration c = new Configuration(); + c.setLogFileDir(journalDirectory.getPath()); + c.setMaxLogFiles(maxLogFiles); + c.setBufferSize(bufferSize); + c.setMaxBuffers(maxBuffers); + c.setMaxBlocksPerFile(maxBlocksPerFile); + return new HowlJournal( c ); + } + +} diff --git a/activeio/src/test/org/activeio/net/AIOAsyncChannelTest.java b/activeio/src/test/org/activeio/net/AIOAsyncChannelTest.java new file mode 100644 index 0000000000..c5a35aeb70 --- /dev/null +++ b/activeio/src/test/org/activeio/net/AIOAsyncChannelTest.java @@ -0,0 +1,45 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activeio.Channel; +import org.activeio.ChannelServer; + +/** + * @version $Revision$ + */ +public class AIOAsyncChannelTest extends SyncChannelTestSupport { + + static boolean disabled = System.getProperty("disable.aio.tests", "false").equals("true"); + AIOAsyncChannelFactory factory = new AIOAsyncChannelFactory(); + + protected Channel openChannel(URI connectURI) throws IOException { + return factory.openAsyncChannel(connectURI); + } + + protected ChannelServer bindChannel() throws IOException, URISyntaxException { + return factory.bindAsyncChannel(new URI("tcp://localhost:0")); + } + + protected boolean isDisabled() { + return disabled; + } +} diff --git a/activeio/src/test/org/activeio/net/ConnectionlessSyncChannelTestSupport.java b/activeio/src/test/org/activeio/net/ConnectionlessSyncChannelTestSupport.java new file mode 100644 index 0000000000..2039545d01 --- /dev/null +++ b/activeio/src/test/org/activeio/net/ConnectionlessSyncChannelTestSupport.java @@ -0,0 +1,181 @@ +/** + * + * Copyright 2005 (C) The original author or authors. + * + * Licensed 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.activeio.net; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor; + +import org.activeio.Channel; +import org.activeio.Packet; +import org.activeio.SyncChannel; +import org.activeio.adapter.AsyncToSyncChannel; +import org.activeio.packet.ByteArrayPacket; +import org.activeio.packet.FilterPacket; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.URI; +import java.net.URISyntaxException; + +import junit.framework.TestCase; + + +/** + * @version $Revision$ + */ +abstract public class ConnectionlessSyncChannelTestSupport extends TestCase { + + private final Log log = LogFactory.getLog(ConnectionlessSyncChannelTestSupport.class); + private SyncChannel clientChannel; + private SyncChannel serverChannel; + private final Executor sendExecutor = new ScheduledThreadPoolExecutor(1); + + public void testSmallSendReceive() throws IOException, URISyntaxException, InterruptedException { + if (isDisabled()) { + log.info("test disabled: " + getName()); + return; + } + UDPFilterPacket fp = new UDPFilterPacket("Hello World".getBytes(), new InetSocketAddress(getAddress(), 4444)); + doSendReceive(fp.duplicate()); + } + + public void testManySmallSendReceives() throws IOException, URISyntaxException, InterruptedException { + if (isDisabled()) { + log.info("test disabled: " + getName()); + return; + } + log.info("Start of testManySmallSendReceives"); + UDPFilterPacket fp = new UDPFilterPacket("Hello World".getBytes(), new InetSocketAddress(getAddress(), 4444)); + long start = System.currentTimeMillis(); + for (int i = 0; i < getTestIterations(); i++) { + doSendReceive(fp.duplicate()); + } + long end = System.currentTimeMillis(); + log.info("done. Duration: " + duration(start, end) + "s, duration per send: " + (unitDuration(start, end, getTestIterations()) * 1000.0) + "ms"); + } + + private float unitDuration(long start, long end, int testIterations) { + return duration(start, end) / testIterations; + } + + private float duration(long start, long end) { + return (float) (((float) (end - start)) / 1000.0f); + } + + protected int getTestIterations() { + return 1000; + } + + protected void setUp() throws Exception { + + log.info("Running: " + getName()); + + if (isDisabled()) { + return; + } + + log.info("Client connecting to: " + getAddress() + ":4444"); + + clientChannel = AsyncToSyncChannel.adapt(openClientChannel(new URI("test://" + getAddress() + ":4444"))); + clientChannel.start(); + + serverChannel = AsyncToSyncChannel.adapt(openServerChannel(new URI("test://" + getAddress() + ":4444"))); + serverChannel.start(); + } + + private void doSendReceive(final Packet outboundPacket) throws IOException, InterruptedException { + ByteArrayPacket ip = new ByteArrayPacket(new byte[outboundPacket.remaining()]); + + // Do the send async. + sendExecutor.execute(new Runnable() { + public void run() { + try { + clientChannel.write(outboundPacket); + clientChannel.flush(); + } + catch (Exception e) { + int i = 0; + } + } + }); + + while (ip.hasRemaining()) { + Packet packet = serverChannel.read(1000 * 5); + assertNotNull(packet); + packet.read(ip); + } + outboundPacket.clear(); + ip.clear(); + assertEquals(outboundPacket.sliceAsBytes(), ip.sliceAsBytes()); + } + + protected void tearDown() throws Exception { + if (isDisabled()) return; + + log.info("Closing down the channels."); + + serverChannel.dispose(); + clientChannel.dispose(); + } + + protected boolean isDisabled() { + return false; + } + + public void assertEquals(byte []b1, byte[] b2) { + assertEquals(b1.length, b2.length); + for (int i = 0; i < b2.length; i++) { + assertEquals(b1[i], b2[i]); + } + } + + abstract protected Channel openClientChannel(URI connectURI) throws IOException; + + abstract protected Channel openServerChannel(URI connectURI) throws IOException; + + abstract protected String getAddress(); + + private final class UDPFilterPacket extends FilterPacket { + private final DatagramPacket packet; + + private UDPFilterPacket(byte[] buf, SocketAddress address) throws SocketException { + super(new ByteArrayPacket(buf)); + this.packet = new DatagramPacket(buf, buf.length, address); + } + + private UDPFilterPacket(Packet op, DatagramPacket packet) { + super(op); + this.packet = packet; + } + + public Object getAdapter(Class target) { + if (target == DatagramContext.class) { + return new DatagramContext(packet); + } + return super.getAdapter(target); + } + + public Packet filter(Packet packet) { + return new UDPFilterPacket(packet, this.packet); + } + } +} diff --git a/activeio/src/test/org/activeio/net/DatagramSyncChannelTest.java b/activeio/src/test/org/activeio/net/DatagramSyncChannelTest.java new file mode 100644 index 0000000000..3ad5f918bf --- /dev/null +++ b/activeio/src/test/org/activeio/net/DatagramSyncChannelTest.java @@ -0,0 +1,43 @@ +/** + * + * Copyright 2005 (C) The original author or authors. + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; + +import org.activeio.Channel; + + +/** + * @version $Revision$ + */ +public class DatagramSyncChannelTest extends ConnectionlessSyncChannelTestSupport { + + DatagramSocketSyncChannelFactory factory = new DatagramSocketSyncChannelFactory(); + + protected Channel openClientChannel(URI connectURI) throws IOException { + return factory.openSyncChannel(null); + } + + protected Channel openServerChannel(URI connectURI) throws IOException { + return factory.openSyncChannel(null, connectURI); + } + + protected String getAddress() { + return "127.0.0.1"; + } +} diff --git a/activeio/src/test/org/activeio/net/MulticastSyncChannelTest.java b/activeio/src/test/org/activeio/net/MulticastSyncChannelTest.java new file mode 100644 index 0000000000..abaa827924 --- /dev/null +++ b/activeio/src/test/org/activeio/net/MulticastSyncChannelTest.java @@ -0,0 +1,43 @@ +/** + * + * Copyright 2005 (C) The original author or authors. + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; + +import org.activeio.Channel; + + +/** + * @version $Revision$ + */ +public class MulticastSyncChannelTest extends ConnectionlessSyncChannelTestSupport { + + MulticastSocketSyncChannelFactory factory = new MulticastSocketSyncChannelFactory(); + + protected Channel openClientChannel(URI connectURI) throws IOException { + return factory.openSyncChannel(connectURI); + } + + protected Channel openServerChannel(URI connectURI) throws IOException { + return factory.openSyncChannel(connectURI, null); + } + + protected String getAddress() { + return "224.2.2.2"; + } +} diff --git a/activeio/src/test/org/activeio/net/NIOAsyncChannelTest.java b/activeio/src/test/org/activeio/net/NIOAsyncChannelTest.java new file mode 100644 index 0000000000..664e994d2f --- /dev/null +++ b/activeio/src/test/org/activeio/net/NIOAsyncChannelTest.java @@ -0,0 +1,41 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activeio.Channel; +import org.activeio.ChannelServer; + +/** + * @version $Revision$ + */ +public class NIOAsyncChannelTest extends SyncChannelTestSupport { + + NIOAsyncChannelFactory factory = new NIOAsyncChannelFactory(true); + + protected Channel openChannel(URI connectURI) throws IOException { + return factory.openAsyncChannel(connectURI); + } + + protected ChannelServer bindChannel() throws IOException, URISyntaxException { + return factory.bindAsyncChannel(new URI("tcp://localhost:0")); + } + +} diff --git a/activeio/src/test/org/activeio/net/NIOSyncChannelTest.java b/activeio/src/test/org/activeio/net/NIOSyncChannelTest.java new file mode 100644 index 0000000000..c4e90e100c --- /dev/null +++ b/activeio/src/test/org/activeio/net/NIOSyncChannelTest.java @@ -0,0 +1,42 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activeio.Channel; +import org.activeio.ChannelServer; + +/** + * @version $Revision$ + */ +public class NIOSyncChannelTest extends SyncChannelTestSupport { + + NIOSyncChannelFactory factory = new NIOSyncChannelFactory(true); + + protected Channel openChannel(URI connectURI) throws IOException { + return factory.openSyncChannel(connectURI); + } + + protected ChannelServer bindChannel() throws IOException, URISyntaxException { + return factory.bindSyncChannel(new URI("tcp://localhost:0")); + } + + +} diff --git a/activeio/src/test/org/activeio/net/SlowSocketChannelSyncChannelTest.java b/activeio/src/test/org/activeio/net/SlowSocketChannelSyncChannelTest.java new file mode 100644 index 0000000000..761eab7544 --- /dev/null +++ b/activeio/src/test/org/activeio/net/SlowSocketChannelSyncChannelTest.java @@ -0,0 +1,47 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activeio.Channel; +import org.activeio.ChannelServer; + +/** + * @version $Revision$ + */ +public class SlowSocketChannelSyncChannelTest extends SyncChannelTestSupport { + + SlowWriteSyncChannelFactory factory = new SlowWriteSyncChannelFactory(new NIOSyncChannelFactory(true), 1, 100); + + protected Channel openChannel(URI connectURI) throws IOException { + return factory.openSyncChannel(connectURI); + } + + protected ChannelServer bindChannel() throws IOException, URISyntaxException { + return factory.bindSyncChannel(new URI("tcp://localhost:0")); + } + + /** + * Reduce the number if iterations since we are running slower than normal. + */ + protected int getTestIterations() { + return 25; + } +} diff --git a/activeio/src/test/org/activeio/net/SlowSocketSyncChannelTest.java b/activeio/src/test/org/activeio/net/SlowSocketSyncChannelTest.java new file mode 100644 index 0000000000..991ed6643f --- /dev/null +++ b/activeio/src/test/org/activeio/net/SlowSocketSyncChannelTest.java @@ -0,0 +1,47 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activeio.Channel; +import org.activeio.ChannelServer; + +/** + * @version $Revision$ + */ +public class SlowSocketSyncChannelTest extends SyncChannelTestSupport { + + SlowWriteSyncChannelFactory factory = new SlowWriteSyncChannelFactory(new SocketSyncChannelFactory(), 4, 1); + + protected Channel openChannel(URI connectURI) throws IOException { + return factory.openSyncChannel(connectURI); + } + + protected ChannelServer bindChannel() throws IOException, URISyntaxException { + return factory.bindSyncChannel(new URI("tcp://localhost:0")); + } + + /** + * Reduce the number if iterations since we are running slower than normal. + */ + protected int getTestIterations() { + return 25; + } +} diff --git a/activeio/src/test/org/activeio/net/SlowWriteSyncChannelFactory.java b/activeio/src/test/org/activeio/net/SlowWriteSyncChannelFactory.java new file mode 100644 index 0000000000..e366cea9e8 --- /dev/null +++ b/activeio/src/test/org/activeio/net/SlowWriteSyncChannelFactory.java @@ -0,0 +1,94 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.URI; + +import org.activeio.Channel; +import org.activeio.FilterSyncChannel; +import org.activeio.FilterSyncChannelServer; +import org.activeio.Packet; +import org.activeio.SyncChannel; +import org.activeio.SyncChannelFactory; +import org.activeio.SyncChannelServer; + +/** + * Makes all the channels produced by another [@see org.activeio.SyncChannelFactory} + * have write operations that have built in delays for testing. + * + * @version $Revision$ + */ +public class SlowWriteSyncChannelFactory implements SyncChannelFactory { + + final SyncChannelFactory next; + private final int maxPacketSize; + private final long packetDelay; + + public SlowWriteSyncChannelFactory(final SyncChannelFactory next, int maxPacketSize, long packetDelay) { + this.next = next; + this.maxPacketSize = maxPacketSize; + this.packetDelay = packetDelay; + } + + class SlowWriteSyncChannel extends FilterSyncChannel { + public SlowWriteSyncChannel(SyncChannel next) { + super(next); + } + public void write(Packet packet) throws IOException { + packet = packet.slice(); + while(packet.hasRemaining()) { + int size = Math.max(maxPacketSize, packet.remaining()); + packet.position(size); + Packet remaining = packet.slice(); + packet.flip(); + Packet data = packet.slice(); + super.write(data); + packet = remaining; + try { + Thread.sleep(packetDelay); + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + } + } + } + + class SlowWriteSyncChannelServer extends FilterSyncChannelServer { + public SlowWriteSyncChannelServer(SyncChannelServer next) { + super(next); + } + public Channel accept(long timeout) throws IOException { + Channel channel = super.accept(timeout); + if( channel != null ) { + channel = new SlowWriteSyncChannel((SyncChannel) channel); + } + return channel; + } + } + + public SyncChannelServer bindSyncChannel(URI location) throws IOException { + return next.bindSyncChannel(location); + } + + public SyncChannel openSyncChannel(URI location) throws IOException { + return new SlowWriteSyncChannel(next.openSyncChannel(location)); + } + +} diff --git a/activeio/src/test/org/activeio/net/SocketSyncChannelTest.java b/activeio/src/test/org/activeio/net/SocketSyncChannelTest.java new file mode 100644 index 0000000000..e255dce9a7 --- /dev/null +++ b/activeio/src/test/org/activeio/net/SocketSyncChannelTest.java @@ -0,0 +1,41 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activeio.Channel; +import org.activeio.ChannelServer; + +/** + * @version $Revision$ + */ +public class SocketSyncChannelTest extends SyncChannelTestSupport { + + SocketSyncChannelFactory factory = new SocketSyncChannelFactory(); + + protected Channel openChannel(URI connectURI) throws IOException { + return factory.openSyncChannel(connectURI); + } + + protected ChannelServer bindChannel() throws IOException, URISyntaxException { + return factory.bindSyncChannel(new URI("tcp://localhost:0")); + } + +} diff --git a/activeio/src/test/org/activeio/net/SslSocketSynchChannelTest.java b/activeio/src/test/org/activeio/net/SslSocketSynchChannelTest.java new file mode 100644 index 0000000000..d0a98ec34e --- /dev/null +++ b/activeio/src/test/org/activeio/net/SslSocketSynchChannelTest.java @@ -0,0 +1,50 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activeio.Channel; +import org.activeio.ChannelServer; + +/** + * @version $Revision$ + */ +public class SslSocketSynchChannelTest extends SyncChannelTestSupport { + + static { + System.setProperty("javax.net.ssl.trustStore", "src/test/client.keystore"); + System.setProperty("javax.net.ssl.trustStorePassword", "password"); + System.setProperty("javax.net.ssl.trustStoreType", "jks"); + System.setProperty("javax.net.ssl.keyStore", "src/test/server.keystore"); + System.setProperty("javax.net.ssl.keyStorePassword", "password"); + System.setProperty("javax.net.ssl.keyStoreType", "jks"); + //System.setProperty("javax.net.debug", "ssl,handshake,data,trustmanager"); + } + + SslSocketSyncChannelFactory factory = new SslSocketSyncChannelFactory(); + + protected Channel openChannel(URI connectURI) throws IOException { + return factory.openSyncChannel(connectURI); + } + + protected ChannelServer bindChannel() throws IOException, URISyntaxException { + return factory.bindSyncChannel(new URI("ssl://localhost:0")); + } +} diff --git a/activeio/src/test/org/activeio/net/SyncChannelTestSupport.java b/activeio/src/test/org/activeio/net/SyncChannelTestSupport.java new file mode 100644 index 0000000000..6a0e7b6181 --- /dev/null +++ b/activeio/src/test/org/activeio/net/SyncChannelTestSupport.java @@ -0,0 +1,200 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.Semaphore; + +import org.activeio.Channel; +import org.activeio.ChannelServer; +import org.activeio.Packet; +import org.activeio.SyncChannel; +import org.activeio.SyncChannelServer; +import org.activeio.adapter.AsyncToSyncChannel; +import org.activeio.adapter.AsyncToSyncChannelServer; +import org.activeio.packet.ByteArrayPacket; +import org.activeio.packet.EOSPacket; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import junit.framework.TestCase; + + +/** + * Used to test the {@see org.activeio.net.TcpSynchChannel} + * + * @version $Revision$ + */ +abstract public class SyncChannelTestSupport extends TestCase { + + Log log = LogFactory.getLog(SyncChannelTestSupport.class); + private SyncChannelServer server; + private SyncChannel clientChannel; + private SyncChannel serverChannel; + Executor sendExecutor = new ScheduledThreadPoolExecutor(1); + + public void testSmallSendReceive() throws IOException, URISyntaxException, InterruptedException { + if( isDisabled() ) { + log.info("test disabled: "+getName()); + return; + } + Packet outboundPacket = new ByteArrayPacket("Hello World".getBytes()); + doSendReceive(outboundPacket.duplicate()); + } + + public void testPeerDisconnect() throws IOException, URISyntaxException, InterruptedException { + if( isDisabled() ) { + log.info("test disabled: "+getName()); + return; + } + + Packet outboundPacket = new ByteArrayPacket("Hello World".getBytes()); + doSendReceive(outboundPacket.duplicate()); + // disconnect the client. + clientChannel.dispose(); + + // The server should get an EOS packet. + Packet packet = serverChannel.read(1000); + assertEquals(EOSPacket.EOS_PACKET, packet); + } + + public void testManySmallSendReceives() throws IOException, URISyntaxException, InterruptedException { + if( isDisabled() ) { + log.info("test disabled: "+getName()); + return; + } + log.info("Start of testManySmallSendReceives"); + Packet outboundPacket = new ByteArrayPacket("Hello World".getBytes()); + long start = System.currentTimeMillis(); + for( int i=0; i < getTestIterations(); i++ ) { + doSendReceive(outboundPacket.duplicate()); + } + long end = System.currentTimeMillis(); + log.info("done. Duration: "+duration(start,end)+", duration per send: "+unitDuration(start, end, getTestIterations())); + } + + private float unitDuration(long start, long end, int testIterations) { + return duration(start,end)/testIterations; + } + + private float duration(long start, long end) { + return (float) (((float)(end-start))/1000.0f); + } + + protected int getTestIterations() { + return 1000; + } + + protected void setUp() throws Exception { + + log.info("Running: "+getName()); + + if( isDisabled() ) { + return; + } + + log.info("Bind to an annonymous tcp port."); + server = AsyncToSyncChannelServer.adapt(bindChannel()); + server.start(); + log.info("Server Bound at URI: "+server.getBindURI()); + + log.info("Client connecting to: "+server.getConnectURI()); + clientChannel = AsyncToSyncChannel.adapt( openChannel(server.getConnectURI())); + clientChannel.start(); + SocketMetadata socket = (SocketMetadata) clientChannel.getAdapter(SocketMetadata.class); + if( socket != null ) + socket.setTcpNoDelay(true); + log.info("Get connection that was accepted on the server side."); + + Channel c = server.accept(1000*5); + assertNotNull(c); + + serverChannel = AsyncToSyncChannel.adapt(c); + serverChannel.start(); + socket = (SocketMetadata) serverChannel.getAdapter(SocketMetadata.class); + if( socket != null ) { + socket.setTcpNoDelay(true); + log.info("Server Channel's Remote addreess: "+socket.getRemoteSocketAddress()); + log.info("Server Channel's Local addreess: "+socket.getLocalSocketAddress()); + } + } + + /** + * @param outboundPacket + * @throws IOException + * @throws URISyntaxException + * @throws InterruptedException + */ + private void doSendReceive(final Packet outboundPacket) throws IOException, URISyntaxException, InterruptedException { + ByteArrayPacket inboundPacket = new ByteArrayPacket(new byte[outboundPacket.remaining()]); + final Semaphore runMutext = new Semaphore(0); + + // Do the send async. + sendExecutor.execute( new Runnable() { + public void run() { + try { + clientChannel.write(outboundPacket); + clientChannel.flush(); + runMutext.release(); + } catch (IOException e) { + } + } + }); + + while( inboundPacket.hasRemaining() ) { + Packet packet = serverChannel.read(1000*5); + assertNotNull(packet); + packet.read(inboundPacket); + } + outboundPacket.clear(); + inboundPacket.clear(); + assertEquals(outboundPacket.sliceAsBytes(), inboundPacket.sliceAsBytes()); + + runMutext.acquire(); + } + + protected void tearDown() throws Exception { + if( isDisabled() ) { + return; + } + log.info("Closing down the channels."); + serverChannel.dispose(); + clientChannel.dispose(); + server.dispose(); + } + + protected boolean isDisabled() { + return false; + } + + public void assertEquals(byte []b1, byte[] b2 ) { + assertEquals(b1.length, b2.length); + for (int i = 0; i < b2.length; i++) { + assertEquals(b1[i], b2[i]); + } + } + + abstract protected Channel openChannel(URI connectURI) throws IOException ; + abstract protected ChannelServer bindChannel() throws IOException, URISyntaxException; + + +} diff --git a/activeio/src/test/org/activeio/net/VMPipeAsyncChannelTest.java b/activeio/src/test/org/activeio/net/VMPipeAsyncChannelTest.java new file mode 100644 index 0000000000..8a4c079b64 --- /dev/null +++ b/activeio/src/test/org/activeio/net/VMPipeAsyncChannelTest.java @@ -0,0 +1,41 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activeio.Channel; +import org.activeio.ChannelServer; + +/** + * @version $Revision$ + */ +public class VMPipeAsyncChannelTest extends SyncChannelTestSupport { + + VMPipeAsyncChannelFactory factory = new VMPipeAsyncChannelFactory(); + + protected Channel openChannel(URI connectURI) throws IOException { + return factory.openAsyncChannel(connectURI); + } + + protected ChannelServer bindChannel() throws IOException, URISyntaxException { + return factory.bindAsyncChannel(new URI("vmpipe://testpipe")); + } + +} diff --git a/activeio/src/test/org/activeio/net/VMPipeReflectionAsyncChannelTest.java b/activeio/src/test/org/activeio/net/VMPipeReflectionAsyncChannelTest.java new file mode 100644 index 0000000000..dcff958b8b --- /dev/null +++ b/activeio/src/test/org/activeio/net/VMPipeReflectionAsyncChannelTest.java @@ -0,0 +1,43 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activeio.Channel; +import org.activeio.ChannelServer; + +/** + * @version $Revision$ + */ +public class VMPipeReflectionAsyncChannelTest extends SyncChannelTestSupport { + + VMPipeAsyncChannelFactory factory = new VMPipeAsyncChannelFactory(); + + protected Channel openChannel(URI connectURI) throws IOException { + factory.setForceRefelection(true); + return factory.openAsyncChannel(connectURI); + } + + protected ChannelServer bindChannel() throws IOException, URISyntaxException { + factory.setForceRefelection(true); + return factory.bindAsyncChannel(new URI("vmpipe://testpipe")); + } + +} diff --git a/activeio/src/test/org/activeio/net/benchmark/ClientLoadSimulator.java b/activeio/src/test/org/activeio/net/benchmark/ClientLoadSimulator.java new file mode 100644 index 0000000000..52d1d941ae --- /dev/null +++ b/activeio/src/test/org/activeio/net/benchmark/ClientLoadSimulator.java @@ -0,0 +1,344 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net.benchmark; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import org.activeio.ChannelFactory; +import org.activeio.Packet; +import org.activeio.SyncChannel; +import org.activeio.packet.ByteArrayPacket; +import org.activeio.packet.EOSPacket; +import org.activeio.stats.CountStatisticImpl; +import org.activeio.stats.TimeStatisticImpl; +import org.apache.commons.beanutils.BeanUtils; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.SynchronousQueue; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +/** + * Simulates multiple a simple tcp echo clients for use in benchmarking activeio + * channel implementations. + * + * @version $Revision$ + */ +public class ClientLoadSimulator implements Runnable { + + private URI url; + + // Afects how clients are created + private int concurrentClients = 10; + private long rampUpTime = 1000 * concurrentClients; + + // Afects how clients behave + private long requestDelay = 500; + private int requestIterations = Integer.MAX_VALUE; + private int requestSize = 1024; + + ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, Integer.MAX_VALUE, 5, TimeUnit.SECONDS, new SynchronousQueue()); + + // The packet the clients send to the server. + private Packet requestPacket; + private long sampleInterval = 1000; + private ChannelFactory factory = new ChannelFactory(); + private CountDownLatch shutdownCountDownLatch; + + private final CountStatisticImpl activeConnectionsCounter = new CountStatisticImpl("activeConnectionsCounter", + "The number of active connection attached to the server."); + private final CountStatisticImpl echoedBytesCounter = new CountStatisticImpl("echoedBytesCounter", + "The number of bytes that have been echoed by the server."); + private final TimeStatisticImpl requestLatency = new TimeStatisticImpl("requestLatency", + "The amount of time that is spent waiting for a request to be serviced"); + + public static void main(String[] args) throws URISyntaxException, IllegalAccessException, InvocationTargetException { + + ClientLoadSimulator client = new ClientLoadSimulator(); + + HashMap options = new HashMap(); + for (int i = 0; i < args.length; i++) { + + String option = args[i]; + if (!option.startsWith("-") || option.length() < 2 || i + 1 >= args.length) { + System.out.println("Invalid usage"); + return; + } + + option = option.substring(1); + options.put(option, args[++i]); + } + + BeanUtils.populate(client, options); + + System.out.println(); + System.out.println("Server starting with the following options: "); + System.out.println(" url="+client.getUrl()); + System.out.println(" sampleInterval="+client.getSampleInterval()); + System.out.println(" concurrentClients="+client.getConcurrentClients()); + System.out.println(" rampUpTime="+client.getRampUpTime()); + System.out.println(" requestIterations="+client.getRequestIterations()); + System.out.println(" requestSize="+client.getRequestSize()); + System.out.println(" requestDelay="+client.getRequestDelay()); + System.out.println(); + client.run(); + + } + private void printSampleData() { + long now = System.currentTimeMillis(); + float runDuration = (now - activeConnectionsCounter.getStartTime()) / 1000f; + System.out.println("Active connections: " + activeConnectionsCounter.getCount()); + System.out.println("Echoed bytes: " + (echoedBytesCounter.getCount()/1024f) + " kb" + + ", Request latency: " + requestLatency.getAverageTime()+" ms"); + echoedBytesCounter.reset(); + requestLatency.reset(); + } + + public void run() { + ArrayList clients = new ArrayList(); + try { + + shutdownCountDownLatch = new CountDownLatch(1); + activeConnectionsCounter.reset(); + echoedBytesCounter.reset(); + + new Thread("Sampler") { + public void run() { + System.out.println("Sampler started."); + try { + while (!shutdownCountDownLatch.await(sampleInterval, TimeUnit.MILLISECONDS)) { + printSampleData(); + } + } catch (InterruptedException e) { + } + System.out.println("Sampler stopped."); + } + }.start(); + + byte data[] = new byte[requestSize]; + for (int i = 0; i < data.length; i++) { + data[i] = (byte) i; + } + requestPacket = new ByteArrayPacket(data); + + // Loop to ramp up the clients. + + long clientActivationDelay = rampUpTime / concurrentClients; + for (int i = 0; i < concurrentClients && !shutdownCountDownLatch.await(clientActivationDelay, TimeUnit.MILLISECONDS); i++) { + System.out.println("Adding Client: " + i); + Client client = new Client(); + clients.add(client); + new Thread(client, "Client: " + i).start(); + } + + shutdownCountDownLatch.await(); + + } catch (InterruptedException e) { + } finally { + System.out.println("Shutting down clients."); + for (Iterator iter = clients.iterator(); iter.hasNext();) { + Client client = (Client) iter.next(); + client.dispose(); + } + } + } + + public String getUrl() { + return url.toString(); + } + + public void setUrl(String url) throws URISyntaxException { + this.url = new URI(url); + } + + class Client implements Runnable { + + private CountDownLatch shutdownCountDownLatch = new CountDownLatch(1); + + Packet packet = requestPacket.duplicate(); + + private SyncChannel syncChannel; + + public void run() { + try { + System.out.println("Client started."); + + activeConnectionsCounter.increment(); + syncChannel = factory.openSyncChannel(url); + for (int i = 0; i < requestIterations && !shutdownCountDownLatch.await(1, TimeUnit.MILLISECONDS) ; i++) { + + long start = System.currentTimeMillis(); + sendRequest(); + long end = System.currentTimeMillis(); + + requestLatency.addTime(end - start); + echoedBytesCounter.add(packet.remaining()); + + if( requestDelay > 0 ) { + Thread.sleep(requestDelay); + } + } + + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + System.out.println("Client stopped."); + + activeConnectionsCounter.decrement(); + if( syncChannel!=null ) { + syncChannel.dispose(); + syncChannel = null; + } + } + } + + private void sendRequest() throws IOException, InterruptedException { + + final CountDownLatch done = new CountDownLatch(1); + + // Read the data async to avoid dead locks due buffers being to small for + // data being sent. + threadPool.execute(new Runnable() { + public void run() { + try { + int c = 0; + while (c < packet.remaining()) { + Packet p = syncChannel.read(1000*5); + if( p==null ) { + continue; + } + if( p == EOSPacket.EOS_PACKET ) { + System.out.println("Peer disconnected."); + dispose(); + } + c += p.remaining(); + } + done.countDown(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + + syncChannel.write(packet.duplicate()); + syncChannel.flush(); + done.await(); + + } + + public void dispose() { + shutdownCountDownLatch.countDown(); + } + } + + /** + * @return Returns the concurrentClients. + */ + public int getConcurrentClients() { + return concurrentClients; + } + + /** + * @param concurrentClients + * The concurrentClients to set. + */ + public void setConcurrentClients(int concurrentClients) { + this.concurrentClients = concurrentClients; + } + + /** + * @return Returns the rampUpTime. + */ + public long getRampUpTime() { + return rampUpTime; + } + + /** + * @param rampUpTime + * The rampUpTime to set. + */ + public void setRampUpTime(long rampUpTime) { + this.rampUpTime = rampUpTime; + } + + /** + * @return Returns the requestDelay. + */ + public long getRequestDelay() { + return requestDelay; + } + + /** + * @param requestDelay + * The requestDelay to set. + */ + public void setRequestDelay(long requestDelay) { + this.requestDelay = requestDelay; + } + + /** + * @return Returns the requestIterations. + */ + public int getRequestIterations() { + return requestIterations; + } + + /** + * @param requestIterations + * The requestIterations to set. + */ + public void setRequestIterations(int requestIterations) { + this.requestIterations = requestIterations; + } + + /** + * @return Returns the requestSize. + */ + public int getRequestSize() { + return requestSize; + } + + /** + * @param requestSize + * The requestSize to set. + */ + public void setRequestSize(int requestSize) { + this.requestSize = requestSize; + } + + /** + * @return Returns the sampleInterval. + */ + public long getSampleInterval() { + return sampleInterval; + } + /** + * @param sampleInterval The sampleInterval to set. + */ + public void setSampleInterval(long sampleInterval) { + this.sampleInterval = sampleInterval; + } +} diff --git a/activeio/src/test/org/activeio/net/benchmark/Server.java b/activeio/src/test/org/activeio/net/benchmark/Server.java new file mode 100644 index 0000000000..fc2b662d1d --- /dev/null +++ b/activeio/src/test/org/activeio/net/benchmark/Server.java @@ -0,0 +1,219 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.net.benchmark; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; + +import org.activeio.AcceptListener; +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelListener; +import org.activeio.AsyncChannelServer; +import org.activeio.Channel; +import org.activeio.ChannelFactory; +import org.activeio.Packet; +import org.activeio.adapter.SyncToAsyncChannel; +import org.activeio.packet.EOSPacket; +import org.activeio.stats.CountStatisticImpl; +import org.apache.commons.beanutils.BeanUtils; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +/** + * Implements a simple tcp echo server for use in benchmarking + * activeio channel implementations. + * + * @version $Revision$ + */ +public class Server implements Runnable, AcceptListener { + + private URI url; + private CountDownLatch shutdownCountDownLatch; + private long requestDelay = 0; + private long sampleInterval = 1000; + + private final CountStatisticImpl activeConnectionsCounter = new CountStatisticImpl("activeConnectionsCounter","The number of active connection attached to the server."); + private final CountStatisticImpl echoedBytesCounter = new CountStatisticImpl("echoedBytesCounter","The number of bytes that have been echoed by the server."); + + public static void main(String[] args) throws URISyntaxException, IllegalAccessException, InvocationTargetException { + + Server server = new Server(); + + HashMap options = new HashMap(); + for( int i=0; i < args.length; i++ ) { + + String option = args[i]; + if( !option.startsWith("-") || option.length()<2 || i+1 >= args.length ) { + System.out.println("Invalid usage."); + return; + } + + option = option.substring(1); + options.put(option, args[++i]); + } + BeanUtils.populate(server, options); + + System.out.println(); + System.out.println("Server starting with the following options: "); + System.out.println(" url="+server.getUrl()); + System.out.println(" sampleInterval="+server.getSampleInterval()); + System.out.println(" requestDelay="+server.getRequestDelay()); + System.out.println(); + server.run(); + + } + + private void printSampleData() { + long now = System.currentTimeMillis(); + float runDuration = (now - activeConnectionsCounter.getStartTime())/1000f; + System.out.println("Active connections: "+activeConnectionsCounter.getCount()); + System.out.println("Echoed bytes: " + (echoedBytesCounter.getCount()/1024f) + " kb"); + echoedBytesCounter.reset(); + } + + + public void run() { + try { + + activeConnectionsCounter.reset(); + echoedBytesCounter.reset(); + + shutdownCountDownLatch = new CountDownLatch(1); + + ChannelFactory factory = new ChannelFactory(); + AsyncChannelServer server = factory.bindAsyncChannel(url); + System.out.println("Server accepting connections on: "+server.getConnectURI()); + server.setAcceptListener(this); + server.start(); + + while(!shutdownCountDownLatch.await(sampleInterval, TimeUnit.MILLISECONDS)) { + printSampleData(); + } + + System.out.println("Stopping server."); + server.stop(1000*5); + server.dispose(); + + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + } + } + + public String getUrl() { + return url.toString(); + } + + public void setUrl(String url) throws URISyntaxException { + this.url = new URI(url); + } + + class ServerConnectionHandler implements AsyncChannelListener { + + private final AsyncChannel asyncChannel; + private boolean disposed; + + public ServerConnectionHandler(AsyncChannel asyncChannel) { + this.asyncChannel = asyncChannel; + activeConnectionsCounter.increment(); + } + + public void onPacket(Packet packet) { + + if( packet == EOSPacket.EOS_PACKET ) { + System.out.println("Peer disconnected."); + dispose(); + return; + } + + try { + if( requestDelay > 0 ) { + Thread.sleep(requestDelay); + } + + echoedBytesCounter.add(packet.remaining()); + asyncChannel.write(packet); + asyncChannel.flush(); + + } catch (IOException e) { + onPacketError(e); + } catch (InterruptedException e) { + System.out.println("Interrupted... Shutting down."); + dispose(); + } + } + + public void onPacketError(IOException error) { + error.printStackTrace(); + dispose(); + } + + private void dispose() { + if( !disposed ) { + asyncChannel.dispose(); + activeConnectionsCounter.decrement(); + disposed=true; + } + } + } + + public void onAccept(Channel channel) { + try { + + AsyncChannel asyncChannel = SyncToAsyncChannel.adapt(channel); + asyncChannel.setAsyncChannelListener(new ServerConnectionHandler(asyncChannel)); + asyncChannel.start(); + + } catch (IOException e) { + onAcceptError(e); + } + } + + public void onAcceptError(IOException error) { + error.printStackTrace(); + shutdownCountDownLatch.countDown(); + } + + /** + * @return Returns the requestDelay. + */ + public long getRequestDelay() { + return requestDelay; + } + /** + * @param requestDelay The requestDelay to set. + */ + public void setRequestDelay(long requestDelay) { + this.requestDelay = requestDelay; + } + /** + * @return Returns the sampleInterval. + */ + public long getSampleInterval() { + return sampleInterval; + } + /** + * @param sampleInterval The sampleInterval to set. + */ + public void setSampleInterval(long sampleInterval) { + this.sampleInterval = sampleInterval; + } +} diff --git a/activeio/src/test/org/activeio/oneport/JettyOnePortSocketListenerTest.java b/activeio/src/test/org/activeio/oneport/JettyOnePortSocketListenerTest.java new file mode 100644 index 0000000000..820bf129da --- /dev/null +++ b/activeio/src/test/org/activeio/oneport/JettyOnePortSocketListenerTest.java @@ -0,0 +1,65 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.oneport; + +import org.activeio.oneport.JettyOnePortSocketListener; +import org.mortbay.http.HttpContext; +import org.mortbay.http.HttpServer; +import org.mortbay.jetty.servlet.ServletHandler; + +import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue; + +/** + * + */ +public class JettyOnePortSocketListenerTest extends OnePortAsyncChannelServerTest { + + static public BlockingQueue staticResultSlot; + + private HttpServer jetty; + + protected void startHTTPServer() throws Exception { + staticResultSlot = resultSlot; + + // Create the server + jetty = new HttpServer(); + + // Create a port listener + JettyOnePortSocketListener listener = new JettyOnePortSocketListener(server); + jetty.addListener(listener); + + // Create a context + HttpContext context = new HttpContext(); + context.setContextPath("/"); + jetty.addContext(context); + + // Create a servlet container + ServletHandler servlets = new ServletHandler(); + context.addHandler(servlets); + + // Map a servlet onto the container + servlets.addServlet("Test", "*", TestServlet.class.getName()); + + // Start the http server + jetty.start(); + } + + protected void stopHTTPServer() throws InterruptedException { + jetty.stop(); + } +} diff --git a/activeio/src/test/org/activeio/oneport/OnePortAsyncChannelServerTest.java b/activeio/src/test/org/activeio/oneport/OnePortAsyncChannelServerTest.java new file mode 100644 index 0000000000..a9e11f0612 --- /dev/null +++ b/activeio/src/test/org/activeio/oneport/OnePortAsyncChannelServerTest.java @@ -0,0 +1,228 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.oneport; + +import edu.emory.mathcs.backport.java.util.concurrent.ArrayBlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +import org.activeio.AcceptListener; +import org.activeio.AsyncChannel; +import org.activeio.AsyncChannelFactory; +import org.activeio.AsyncChannelServer; +import org.activeio.Channel; +import org.activeio.FilterAsyncChannelServer; +import org.activeio.FilterSyncChannel; +import org.activeio.Packet; +import org.activeio.SyncChannel; +import org.activeio.adapter.AsyncToSyncChannel; +import org.activeio.adapter.SyncToAsyncChannelFactory; +import org.activeio.net.SocketMetadata; +import org.activeio.net.SocketSyncChannelFactory; +import org.activeio.packet.ByteArrayPacket; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.naming.NamingException; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +import junit.framework.TestCase; + +/** + */ +public class OnePortAsyncChannelServerTest extends TestCase { + + static private Log log = LogFactory.getLog(OnePortAsyncChannelServerTest.class); + static public AtomicInteger serverPacketCounter = new AtomicInteger(0); + + public OnePortAsyncChannelServer server; + public AsyncChannelServer httpServer; + public AsyncChannelServer iiopServer; + public SocketSyncChannelFactory channelFactory; + public BlockingQueue resultSlot = new ArrayBlockingQueue(1); + + public void testIIOPAccept() throws Exception { + serverPacketCounter.set(0); + hitIIOPServer(); + String type = (String) resultSlot.poll(1000 * 5, TimeUnit.MILLISECONDS); + assertEquals("IIOP", type); + // Verify that a request when through the one port. + assertTrue(serverPacketCounter.get()>0); + } + + public void testHttpAccept() throws IOException, URISyntaxException, InterruptedException { + serverPacketCounter.set(0); + hitHttpServer(); + String type = (String) resultSlot.poll(1000 * 5 * 10000, TimeUnit.MILLISECONDS); + assertEquals("HTTP", type); + // Verify that a request when through the one port. + assertTrue(serverPacketCounter.get()>0); + } + + protected void hitHttpServer() throws IOException, MalformedURLException { + URI connectURI = server.getConnectURI(); + String url = "http://" + connectURI.getHost() + ":" + connectURI.getPort() + "/index.action"; + log.info(url); + InputStream is = new URL(url).openStream(); + StringBuffer b = new StringBuffer(); + int c; + while ((c = is.read()) >= 0) { + b.append((char) c); + } + + log.info("HTTP response: " + b); + } + + protected void hitIIOPServer() throws Exception { + SyncChannel channel = channelFactory.openSyncChannel(server.getConnectURI()); + ((SocketMetadata)channel.getAdapter(SocketMetadata.class)).setTcpNoDelay(true); + channel.write(new ByteArrayPacket("GIOPcrapcrap".getBytes("UTF-8"))); + channel.flush(); + channel.dispose(); + } + + public void testUnknownAccept() throws IOException, URISyntaxException, InterruptedException { + SyncChannel channel = channelFactory.openSyncChannel(server.getConnectURI()); + ((SocketMetadata)channel.getAdapter(SocketMetadata.class)).setTcpNoDelay(true); + channel + .write(new ByteArrayPacket("Licensed under the Apache License, Version 2.0 (the \"License\")" + .getBytes("UTF-8"))); + channel.flush(); + String type = (String) resultSlot.poll(1000 * 5, TimeUnit.MILLISECONDS); + assertNull(type); + channel.dispose(); + } + + protected void setUp() throws Exception { + channelFactory = new SocketSyncChannelFactory(); + ThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, new ThreadFactory(){ + int count=0; + public Thread newThread(Runnable arg0) { + return new Thread(arg0, "activeio:"+(count++)); + }}); + AsyncChannelFactory factory = SyncToAsyncChannelFactory.adapt(channelFactory,executor); + + AsyncChannelServer cs = factory.bindAsyncChannel(new URI("tcp://localhost:0")); + cs = new FilterAsyncChannelServer(cs) { + public void onAccept(Channel channel) { + SyncChannel syncChannel = AsyncToSyncChannel.adapt(channel); + super.onAccept(new FilterSyncChannel(syncChannel) { + public org.activeio.Packet read(long timeout) throws IOException { + Packet packet = super.read(timeout); + if( packet!=null && packet.hasRemaining() ) + serverPacketCounter.incrementAndGet(); + return packet; + } + }); + } + }; + + server = new OnePortAsyncChannelServer(cs); + server.start(); + + startHTTPServer(); + startIIOPServer(); + + log.info("Running on: "+server.getConnectURI()); + } + + /** + * @throws IOException + * @throws NamingException + */ + protected void startIIOPServer() throws Exception { + iiopServer = server.bindAsyncChannel(IIOPRecognizer.IIOP_RECOGNIZER); + iiopServer.setAcceptListener(new AcceptListener() { + public void onAccept(Channel channel) { + try { + log.info("Got a IIOP connection."); + resultSlot.offer("IIOP", 1, TimeUnit.MILLISECONDS); + channel.dispose(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public void onAcceptError(IOException error) { + } + }); + iiopServer.start(); + } + + /** + * @throws IOException + * @throws Exception + */ + protected void startHTTPServer() throws Exception { + httpServer = server.bindAsyncChannel(HttpRecognizer.HTTP_RECOGNIZER); + httpServer.setAcceptListener(new AcceptListener() { + public void onAccept(Channel channel) { + try { + log.info("Got a HTTP connection."); + resultSlot.offer("HTTP", 1, TimeUnit.MILLISECONDS); + + byte data[] = ("HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=UTF-8\r\n" + "\r\n" + + "Hello World").getBytes("UTF-8"); + + ((SocketMetadata)channel.getAdapter(SocketMetadata.class)).setTcpNoDelay(true); + ((AsyncChannel) channel).write(new ByteArrayPacket(data)); + + channel.dispose(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + public void onAcceptError(IOException error) { + } + }); + httpServer.start(); + } + + protected void tearDown() throws Exception { + stopIIOPServer(); + stopHTTPServer(); + server.dispose(); + } + + /** + * @throws InterruptedException + * + */ + protected void stopHTTPServer() throws InterruptedException { + httpServer.dispose(); + } + + /** + * @throws Exception + * + */ + protected void stopIIOPServer() throws Exception { + iiopServer.dispose(); + } +} diff --git a/activeio/src/test/org/activeio/oneport/OpenORBOnePortSocketFactoryTest.java b/activeio/src/test/org/activeio/oneport/OpenORBOnePortSocketFactoryTest.java new file mode 100644 index 0000000000..ccb2a74801 --- /dev/null +++ b/activeio/src/test/org/activeio/oneport/OpenORBOnePortSocketFactoryTest.java @@ -0,0 +1,90 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.oneport; + +import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue; + +import org.omg.CORBA.ORB; +import org.omg.CORBA.Object; +import org.omg.PortableServer.POA; +import org.omg.PortableServer.POAHelper; + +import javax.naming.NamingException; + +import java.rmi.RemoteException; +import java.util.Properties; + +/** + * + */ +public class OpenORBOnePortSocketFactoryTest extends OnePortAsyncChannelServerTest { + + static public BlockingQueue staticResultSlot; + private ORB orb; + private String serverRef; + private TestIIOPServerImpl testIIOPServer; + private POA rootPOA; + + protected void startIIOPServer() throws Exception { + staticResultSlot = resultSlot; + + Properties props = new Properties(); + props.setProperty("org.omg.PortableInterceptor.ORBInitializerClass.org.activeio.oneport.OpenORBOpenPortFeatureInitializer", ""); + props.setProperty("org.omg.CORBA.ORBClass", "org.openorb.orb.core.ORB"); + props.setProperty("org.omg.CORBA.ORBSingletonClass", "org.openorb.orb.core.ORBSingleton"); + + OpenORBOpenPortFeatureInitializer.setContextSocketFactory(new OpenORBOpenPortSocketFactory(server)); + orb = ORB.init( new String[]{}, props ); + OpenORBOpenPortFeatureInitializer.setContextSocketFactory(null); + + rootPOA = POAHelper.narrow( orb.resolve_initial_references( "RootPOA" ) ); + + TestIIOPServerImpl srv = new TestIIOPServerImpl(); + serverRef = orb.object_to_string( srv._this( orb ) ); + rootPOA.the_POAManager().activate(); + new Thread(){ + public void run() { + orb.run(); + } + }.start(); + } + + protected void stopIIOPServer() throws Exception { + orb.shutdown(true); + } + + protected void hitIIOPServer( ) throws NamingException, RemoteException + { + // Create a client side orb. + Properties props = new Properties(); + props.setProperty("org.omg.CORBA.ORBClass", "org.openorb.orb.core.ORB"); + props.setProperty("org.omg.CORBA.ORBSingletonClass", "org.openorb.orb.core.ORBSingleton"); + ORB orb = ORB.init( new String[]{}, props ); + + Object obj = orb.string_to_object( serverRef ); + TestIIOPServer srv = TestIIOPServerHelper.narrow( obj ); + try { + srv.test(); + } catch (Throwable e) { + e.printStackTrace(); + } finally { + orb.shutdown(true); + } + } + +} diff --git a/activeio/src/test/org/activeio/oneport/TestIIOPServer.idl b/activeio/src/test/org/activeio/oneport/TestIIOPServer.idl new file mode 100644 index 0000000000..cbf900bee8 --- /dev/null +++ b/activeio/src/test/org/activeio/oneport/TestIIOPServer.idl @@ -0,0 +1,32 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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. + * + **/ + +module org +{ + module activeio + { + module oneport + { + interface TestIIOPServer + { + void test(); + }; + }; + }; +}; + diff --git a/activeio/src/test/org/activeio/oneport/TestIIOPServer.java b/activeio/src/test/org/activeio/oneport/TestIIOPServer.java new file mode 100644 index 0000000000..f9a9172e8e --- /dev/null +++ b/activeio/src/test/org/activeio/oneport/TestIIOPServer.java @@ -0,0 +1,10 @@ +package org.activeio.oneport; + +/** + * Interface definition: TestIIOPServer. + * + * @author OpenORB Compiler + */ +public interface TestIIOPServer extends TestIIOPServerOperations, org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity +{ +} diff --git a/activeio/src/test/org/activeio/oneport/TestIIOPServerHelper.java b/activeio/src/test/org/activeio/oneport/TestIIOPServerHelper.java new file mode 100644 index 0000000000..104becf214 --- /dev/null +++ b/activeio/src/test/org/activeio/oneport/TestIIOPServerHelper.java @@ -0,0 +1,131 @@ +package org.activeio.oneport; + +/** + * Helper class for : TestIIOPServer + * + * @author OpenORB Compiler + */ +public class TestIIOPServerHelper +{ + /** + * Insert TestIIOPServer into an any + * @param a an any + * @param t TestIIOPServer value + */ + public static void insert(org.omg.CORBA.Any a, org.activeio.oneport.TestIIOPServer t) + { + a.insert_Object(t , type()); + } + + /** + * Extract TestIIOPServer from an any + * + * @param a an any + * @return the extracted TestIIOPServer value + */ + public static org.activeio.oneport.TestIIOPServer extract( org.omg.CORBA.Any a ) + { + if ( !a.type().equivalent( type() ) ) + { + throw new org.omg.CORBA.MARSHAL(); + } + try + { + return org.activeio.oneport.TestIIOPServerHelper.narrow( a.extract_Object() ); + } + catch ( final org.omg.CORBA.BAD_PARAM e ) + { + throw new org.omg.CORBA.MARSHAL(e.getMessage()); + } + } + + // + // Internal TypeCode value + // + private static org.omg.CORBA.TypeCode _tc = null; + + /** + * Return the TestIIOPServer TypeCode + * @return a TypeCode + */ + public static org.omg.CORBA.TypeCode type() + { + if (_tc == null) { + org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(); + _tc = orb.create_interface_tc( id(), "TestIIOPServer" ); + } + return _tc; + } + + /** + * Return the TestIIOPServer IDL ID + * @return an ID + */ + public static String id() + { + return _id; + } + + private final static String _id = "IDL:org/activeio/oneport/TestIIOPServer:1.0"; + + /** + * Read TestIIOPServer from a marshalled stream + * @param istream the input stream + * @return the readed TestIIOPServer value + */ + public static org.activeio.oneport.TestIIOPServer read(org.omg.CORBA.portable.InputStream istream) + { + return(org.activeio.oneport.TestIIOPServer)istream.read_Object(org.activeio.oneport._TestIIOPServerStub.class); + } + + /** + * Write TestIIOPServer into a marshalled stream + * @param ostream the output stream + * @param value TestIIOPServer value + */ + public static void write(org.omg.CORBA.portable.OutputStream ostream, org.activeio.oneport.TestIIOPServer value) + { + ostream.write_Object((org.omg.CORBA.portable.ObjectImpl)value); + } + + /** + * Narrow CORBA::Object to TestIIOPServer + * @param obj the CORBA Object + * @return TestIIOPServer Object + */ + public static TestIIOPServer narrow(org.omg.CORBA.Object obj) + { + if (obj == null) + return null; + if (obj instanceof TestIIOPServer) + return (TestIIOPServer)obj; + + if (obj._is_a(id())) + { + _TestIIOPServerStub stub = new _TestIIOPServerStub(); + stub._set_delegate(((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate()); + return stub; + } + + throw new org.omg.CORBA.BAD_PARAM(); + } + + /** + * Unchecked Narrow CORBA::Object to TestIIOPServer + * @param obj the CORBA Object + * @return TestIIOPServer Object + */ + public static TestIIOPServer unchecked_narrow(org.omg.CORBA.Object obj) + { + if (obj == null) + return null; + if (obj instanceof TestIIOPServer) + return (TestIIOPServer)obj; + + _TestIIOPServerStub stub = new _TestIIOPServerStub(); + stub._set_delegate(((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate()); + return stub; + + } + +} diff --git a/activeio/src/test/org/activeio/oneport/TestIIOPServerHolder.java b/activeio/src/test/org/activeio/oneport/TestIIOPServerHolder.java new file mode 100644 index 0000000000..8f4d4ddbb6 --- /dev/null +++ b/activeio/src/test/org/activeio/oneport/TestIIOPServerHolder.java @@ -0,0 +1,58 @@ +package org.activeio.oneport; + +/** + * Holder class for : TestIIOPServer + * + * @author OpenORB Compiler + */ +final public class TestIIOPServerHolder + implements org.omg.CORBA.portable.Streamable +{ + /** + * Internal TestIIOPServer value + */ + public org.activeio.oneport.TestIIOPServer value; + + /** + * Default constructor + */ + public TestIIOPServerHolder() + { } + + /** + * Constructor with value initialisation + * @param initial the initial value + */ + public TestIIOPServerHolder(org.activeio.oneport.TestIIOPServer initial) + { + value = initial; + } + + /** + * Read TestIIOPServer from a marshalled stream + * @param istream the input stream + */ + public void _read(org.omg.CORBA.portable.InputStream istream) + { + value = TestIIOPServerHelper.read(istream); + } + + /** + * Write TestIIOPServer into a marshalled stream + * @param ostream the output stream + */ + public void _write(org.omg.CORBA.portable.OutputStream ostream) + { + TestIIOPServerHelper.write(ostream,value); + } + + /** + * Return the TestIIOPServer TypeCode + * @return a TypeCode + */ + public org.omg.CORBA.TypeCode _type() + { + return TestIIOPServerHelper.type(); + } + +} diff --git a/activeio/src/test/org/activeio/oneport/TestIIOPServerImpl.java b/activeio/src/test/org/activeio/oneport/TestIIOPServerImpl.java new file mode 100644 index 0000000000..bc0a530929 --- /dev/null +++ b/activeio/src/test/org/activeio/oneport/TestIIOPServerImpl.java @@ -0,0 +1,32 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.oneport; + +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +/** + * + */ +public class TestIIOPServerImpl extends TestIIOPServerPOA { + public void test() { + try { + OpenORBOnePortSocketFactoryTest.staticResultSlot.offer("IIOP", 1, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + } + } +} diff --git a/activeio/src/test/org/activeio/oneport/TestIIOPServerOperations.java b/activeio/src/test/org/activeio/oneport/TestIIOPServerOperations.java new file mode 100644 index 0000000000..6f001c2957 --- /dev/null +++ b/activeio/src/test/org/activeio/oneport/TestIIOPServerOperations.java @@ -0,0 +1,15 @@ +package org.activeio.oneport; + +/** + * Interface definition: TestIIOPServer. + * + * @author OpenORB Compiler + */ +public interface TestIIOPServerOperations +{ + /** + * Operation test + */ + public void test(); + +} diff --git a/activeio/src/test/org/activeio/oneport/TestIIOPServerPOA.java b/activeio/src/test/org/activeio/oneport/TestIIOPServerPOA.java new file mode 100644 index 0000000000..948017fdc8 --- /dev/null +++ b/activeio/src/test/org/activeio/oneport/TestIIOPServerPOA.java @@ -0,0 +1,56 @@ +package org.activeio.oneport; + +/** + * Interface definition: TestIIOPServer. + * + * @author OpenORB Compiler + */ +public abstract class TestIIOPServerPOA extends org.omg.PortableServer.Servant + implements TestIIOPServerOperations, org.omg.CORBA.portable.InvokeHandler +{ + public TestIIOPServer _this() + { + return TestIIOPServerHelper.narrow(_this_object()); + } + + public TestIIOPServer _this(org.omg.CORBA.ORB orb) + { + return TestIIOPServerHelper.narrow(_this_object(orb)); + } + + private static String [] _ids_list = + { + "IDL:org/activeio/oneport/TestIIOPServer:1.0" + }; + + public String[] _all_interfaces(org.omg.PortableServer.POA poa, byte [] objectId) + { + return _ids_list; + } + + public final org.omg.CORBA.portable.OutputStream _invoke(final String opName, + final org.omg.CORBA.portable.InputStream _is, + final org.omg.CORBA.portable.ResponseHandler handler) + { + + if (opName.equals("test")) { + return _invoke_test(_is, handler); + } else { + throw new org.omg.CORBA.BAD_OPERATION(opName); + } + } + + // helper methods + private org.omg.CORBA.portable.OutputStream _invoke_test( + final org.omg.CORBA.portable.InputStream _is, + final org.omg.CORBA.portable.ResponseHandler handler) { + org.omg.CORBA.portable.OutputStream _output; + + test(); + + _output = handler.createReply(); + + return _output; + } + +} diff --git a/activeio/src/test/org/activeio/oneport/TestIIOPServerPOATie.java b/activeio/src/test/org/activeio/oneport/TestIIOPServerPOATie.java new file mode 100644 index 0000000000..ab10f5ccb0 --- /dev/null +++ b/activeio/src/test/org/activeio/oneport/TestIIOPServerPOATie.java @@ -0,0 +1,73 @@ +package org.activeio.oneport; + +/** + * Interface definition: TestIIOPServer. + * + * @author OpenORB Compiler + */ +public class TestIIOPServerPOATie extends TestIIOPServerPOA +{ + + // + // Private reference to implementation object + // + private TestIIOPServerOperations _tie; + + // + // Private reference to POA + // + private org.omg.PortableServer.POA _poa; + + /** + * Constructor + */ + public TestIIOPServerPOATie(TestIIOPServerOperations tieObject) + { + _tie = tieObject; + } + + /** + * Constructor + */ + public TestIIOPServerPOATie(TestIIOPServerOperations tieObject, org.omg.PortableServer.POA poa) + { + _tie = tieObject; + _poa = poa; + } + + /** + * Get the delegate + */ + public TestIIOPServerOperations _delegate() + { + return _tie; + } + + /** + * Set the delegate + */ + public void _delegate(TestIIOPServerOperations delegate_) + { + _tie = delegate_; + } + + /** + * _default_POA method + */ + public org.omg.PortableServer.POA _default_POA() + { + if (_poa != null) + return _poa; + else + return super._default_POA(); + } + + /** + * Operation test + */ + public void test() + { + _tie.test(); + } + +} diff --git a/activeio/src/test/org/activeio/oneport/TestServlet.java b/activeio/src/test/org/activeio/oneport/TestServlet.java new file mode 100644 index 0000000000..1670f225c7 --- /dev/null +++ b/activeio/src/test/org/activeio/oneport/TestServlet.java @@ -0,0 +1,43 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.oneport; + +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + + +public class TestServlet extends HttpServlet { + + private static final long serialVersionUID = 3257286933137733686L; + + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + JettyOnePortSocketListenerTest.staticResultSlot.offer("HTTP", 1, TimeUnit.MILLISECONDS); + resp.getOutputStream().print("Hello World!"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/activeio/src/test/org/activeio/oneport/_TestIIOPServerStub.java b/activeio/src/test/org/activeio/oneport/_TestIIOPServerStub.java new file mode 100644 index 0000000000..6e39ad25b6 --- /dev/null +++ b/activeio/src/test/org/activeio/oneport/_TestIIOPServerStub.java @@ -0,0 +1,72 @@ +package org.activeio.oneport; + +/** + * Interface definition: TestIIOPServer. + * + * @author OpenORB Compiler + */ +public class _TestIIOPServerStub extends org.omg.CORBA.portable.ObjectImpl + implements TestIIOPServer +{ + static final String[] _ids_list = + { + "IDL:org/activeio/oneport/TestIIOPServer:1.0" + }; + + public String[] _ids() + { + return _ids_list; + } + + private final static Class _opsClass = org.activeio.oneport.TestIIOPServerOperations.class; + + /** + * Operation test + */ + public void test() + { + while(true) + { + if (!this._is_local()) + { + org.omg.CORBA.portable.InputStream _input = null; + try + { + org.omg.CORBA.portable.OutputStream _output = this._request("test",true); + _input = this._invoke(_output); + return; + } + catch(org.omg.CORBA.portable.RemarshalException _exception) + { + continue; + } + catch(org.omg.CORBA.portable.ApplicationException _exception) + { + String _exception_id = _exception.getId(); + throw new org.omg.CORBA.UNKNOWN("Unexpected User Exception: "+ _exception_id); + } + finally + { + this._releaseReply(_input); + } + } + else + { + org.omg.CORBA.portable.ServantObject _so = _servant_preinvoke("test",_opsClass); + if (_so == null) + continue; + org.activeio.oneport.TestIIOPServerOperations _self = (org.activeio.oneport.TestIIOPServerOperations) _so.servant; + try + { + _self.test(); + return; + } + finally + { + _servant_postinvoke(_so); + } + } + } + } + +} diff --git a/activeio/src/test/org/activeio/packet/AppendedPacketTest.java b/activeio/src/test/org/activeio/packet/AppendedPacketTest.java new file mode 100644 index 0000000000..e6f726b388 --- /dev/null +++ b/activeio/src/test/org/activeio/packet/AppendedPacketTest.java @@ -0,0 +1,34 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.packet; + +import org.activeio.Packet; + +/** + */ +public class AppendedPacketTest extends PacketTestSupport { + + Packet createTestPacket(int capacity) { + int c1 = capacity/2; + int c2 = capacity-c1; + + return AppendedPacket.join( + new ByteArrayPacket(new byte[c1]), + new ByteArrayPacket(new byte[c2])); + } + +} diff --git a/activeio/src/test/org/activeio/packet/ByteArrayPacketTest.java b/activeio/src/test/org/activeio/packet/ByteArrayPacketTest.java new file mode 100644 index 0000000000..5d1e6a60b9 --- /dev/null +++ b/activeio/src/test/org/activeio/packet/ByteArrayPacketTest.java @@ -0,0 +1,29 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.packet; + +import org.activeio.Packet; + +/** + */ +public class ByteArrayPacketTest extends PacketTestSupport { + + Packet createTestPacket(int capacity) { + return new ByteArrayPacket(new byte[capacity]); + } + +} diff --git a/activeio/src/test/org/activeio/packet/ByteBufferPacketTest.java b/activeio/src/test/org/activeio/packet/ByteBufferPacketTest.java new file mode 100644 index 0000000000..81e81cddd5 --- /dev/null +++ b/activeio/src/test/org/activeio/packet/ByteBufferPacketTest.java @@ -0,0 +1,31 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.packet; + +import java.nio.ByteBuffer; + +import org.activeio.Packet; + +/** + */ +public class ByteBufferPacketTest extends PacketTestSupport { + + Packet createTestPacket(int capacity) { + return new ByteBufferPacket(ByteBuffer.allocate(capacity)); + } + +} diff --git a/activeio/src/test/org/activeio/packet/PacketTestSupport.java b/activeio/src/test/org/activeio/packet/PacketTestSupport.java new file mode 100644 index 0000000000..0f80252a30 --- /dev/null +++ b/activeio/src/test/org/activeio/packet/PacketTestSupport.java @@ -0,0 +1,157 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activeio.packet; + +import java.util.Arrays; + +import junit.framework.TestCase; + +import org.activeio.Packet; + +/** + */ +abstract public class PacketTestSupport extends TestCase { + abstract Packet createTestPacket(int capacity); + + public void testInit() { + Packet packet = createTestPacket(100); + assertEquals( 100, packet.capacity() ); + assertEquals( 0, packet.position()); + assertEquals( 100, packet.limit() ); + assertEquals( 100, packet.remaining() ); + assertTrue( packet.hasRemaining() ); + } + + public void testPosition() { + Packet packet = createTestPacket(100); + packet.position(10); + assertEquals( 10, packet.position() ); + } + + public void testLimit() { + Packet packet = createTestPacket(100); + packet.limit(10); + assertEquals( 10, packet.limit() ); + } + + public void testRemaining() { + Packet packet = createTestPacket(100); + packet.position(5); + packet.limit(95); + assertEquals(90, packet.remaining()); + assertTrue(packet.hasRemaining()); + + packet.position(5); + packet.limit(5); + assertEquals(0, packet.remaining()); + assertFalse(packet.hasRemaining()); + } + + public void testFlip() { + Packet packet = createTestPacket(100); + packet.position(95); + packet.flip(); + assertEquals(0, packet.position()); + assertEquals(95, packet.limit()); + } + + public void testClear() { + Packet packet = createTestPacket(100); + packet.position(5); + packet.limit(95); + packet.clear(); + assertEquals(0, packet.position()); + assertEquals(100, packet.limit()); + } + + public void testDuplicate() { + Packet packet = createTestPacket(100); + packet.position(5); + packet.limit(95); + Packet packet2 = packet.duplicate(); + packet2.position(10); + packet2.limit(20); + + assertEquals(5, packet.position()); + assertEquals(95, packet.limit()); + assertEquals(10, packet2.position()); + assertEquals(20, packet2.limit()); + } + + public void testRewind() { + Packet packet = createTestPacket(100); + packet.position(5); + packet.limit(95); + packet.rewind(); + + assertEquals(0, packet.position()); + assertEquals(95, packet.limit()); + } + + public void testSlice() { + Packet packet = createTestPacket(100); + packet.position(5); + packet.limit(95); + Packet packet2 = packet.slice(); + + assertEquals(0, packet2.position()); + assertEquals(90, packet2.capacity()); + assertEquals(90, packet2.limit()); + } + + public void testWriteAndReadByte() { + + Packet packet = createTestPacket(256); + for(int i=0; i < 256; i++) { + assertTrue(packet.write(i)); + } + assertFalse(packet.write(0)); + + packet.flip(); + for(int i=0; i < 256; i++) { + assertEquals(i, packet.read()); + } + assertEquals(-1, packet.read()); + } + + public void testWriteAndReadBulkByte() { + + byte data[] = new byte[10]; + Packet packet = createTestPacket(data.length*10); + for(int i=0; i < 10; i++) { + Arrays.fill(data,(byte)i); + assertEquals(data.length, packet.write(data,0,data.length)); + } + assertEquals(-1, packet.write(data,0,data.length)); + + byte buffer[] = new byte[data.length]; + packet.flip(); + for(int i=0; i < 10; i++) { + assertEquals(buffer.length, packet.read(buffer,0,buffer.length)); + Arrays.fill(data,(byte)i); + assertEquals(buffer, data); + } + assertEquals(-1, packet.read(buffer,0,buffer.length)); + } + + public void assertEquals(byte buffer[], byte data[]) { + assertEquals(buffer.length, data.length); + for (int i = 0; i < data.length; i++) { + assertEquals(buffer[i], data[i]); + } + } +} diff --git a/activeio/src/test/org/activeio/stats/CountStatisticImpl.java b/activeio/src/test/org/activeio/stats/CountStatisticImpl.java new file mode 100644 index 0000000000..7f254dbed5 --- /dev/null +++ b/activeio/src/test/org/activeio/stats/CountStatisticImpl.java @@ -0,0 +1,106 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activeio.stats; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicLong; + +import javax.management.j2ee.statistics.CountStatistic; + +/** + * Shamelessly taken from the ActiveMQ project ( http://activemq.com ) + * A count statistic implementation + * + * @version $Revision: 1.1 $ + */ +public class CountStatisticImpl extends StatisticImpl implements CountStatistic { + + private final AtomicLong counter = new AtomicLong(0); + private CountStatisticImpl parent; + + public CountStatisticImpl(CountStatisticImpl parent, String name, String description) { + this(name, description); + this.parent = parent; + } + + public CountStatisticImpl(String name, String description) { + this(name, "count", description); + } + + public CountStatisticImpl(String name, String unit, String description) { + super(name, unit, description); + } + + public void reset() { + super.reset(); + counter.set(0); + } + + public long getCount() { + return counter.get(); + } + + public void setCount(long count) { + counter.set(count); + } + + public void add(long amount) { + counter.addAndGet(amount); + updateSampleTime(); + if (parent != null) { + parent.add(amount); + } + } + + public void increment() { + counter.incrementAndGet(); + updateSampleTime(); + if (parent != null) { + parent.increment(); + } + } + + public void subtract(long amount) { + counter.addAndGet(-amount); + updateSampleTime(); + if (parent != null) { + parent.subtract(amount); + } + } + + public void decrement() { + counter.decrementAndGet(); + updateSampleTime(); + if (parent != null) { + parent.decrement(); + } + } + + public CountStatisticImpl getParent() { + return parent; + } + + public void setParent(CountStatisticImpl parent) { + this.parent = parent; + } + + protected void appendFieldDescription(StringBuffer buffer) { + buffer.append(" count: "); + buffer.append(Long.toString(counter.get())); + super.appendFieldDescription(buffer); + } +} diff --git a/activeio/src/test/org/activeio/stats/IndentPrinter.java b/activeio/src/test/org/activeio/stats/IndentPrinter.java new file mode 100644 index 0000000000..dc830bfb4c --- /dev/null +++ b/activeio/src/test/org/activeio/stats/IndentPrinter.java @@ -0,0 +1,89 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activeio.stats; + +import java.io.PrintWriter; + +/** + * A helper class for printing indented text + * + * @version $Revision: 1.1 $ + */ +public class IndentPrinter { + + private int indentLevel; + private String indent; + private PrintWriter out; + + public IndentPrinter() { + this(new PrintWriter(System.out), " "); + } + + public IndentPrinter(PrintWriter out) { + this(out, " "); + } + + public IndentPrinter(PrintWriter out, String indent) { + this.out = out; + this.indent = indent; + } + + public void println(Object value) { + out.print(value.toString()); + out.println(); + } + + public void println(String text) { + out.print(text); + out.println(); + } + + public void print(String text) { + out.print(text); + } + + public void printIndent() { + for (int i = 0; i < indentLevel; i++) { + out.print(indent); + } + } + + public void println() { + out.println(); + } + + public void incrementIndent() { + ++indentLevel; + } + + public void decrementIndent() { + --indentLevel; + } + + public int getIndentLevel() { + return indentLevel; + } + + public void setIndentLevel(int indentLevel) { + this.indentLevel = indentLevel; + } + + public void flush() { + out.flush(); + } +} diff --git a/activeio/src/test/org/activeio/stats/StatisticImpl.java b/activeio/src/test/org/activeio/stats/StatisticImpl.java new file mode 100644 index 0000000000..a6f4aeadb5 --- /dev/null +++ b/activeio/src/test/org/activeio/stats/StatisticImpl.java @@ -0,0 +1,93 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activeio.stats; + +import javax.management.j2ee.statistics.Statistic; + +/** + * Shamelessly taken from the ActiveMQ project ( http://activemq.com ) + * + * Base class for a Statistic implementation + * @version $Revision: 1.1 $ + */ +public class StatisticImpl implements Statistic { + private String name; + private String unit; + private String description; + private long startTime; + private long lastSampleTime; + + public StatisticImpl(String name, String unit, String description) { + this.name = name; + this.unit = unit; + this.description = description; + startTime = System.currentTimeMillis(); + lastSampleTime = startTime; + } + + public synchronized void reset() { + startTime = System.currentTimeMillis(); + lastSampleTime = startTime; + } + + protected synchronized void updateSampleTime() { + lastSampleTime = System.currentTimeMillis(); + } + + public synchronized String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(name); + buffer.append("{"); + appendFieldDescription(buffer); + buffer.append(" }"); + return buffer.toString(); + } + + public String getName() { + return name; + } + + public String getUnit() { + return unit; + } + + public String getDescription() { + return description; + } + + public synchronized long getStartTime() { + return startTime; + } + + public synchronized long getLastSampleTime() { + return lastSampleTime; + } + + protected synchronized void appendFieldDescription(StringBuffer buffer) { + buffer.append(" unit: "); + buffer.append(unit); + buffer.append(" startTime: "); + //buffer.append(new Date(startTime)); + buffer.append(startTime); + buffer.append(" lastSampleTime: "); + //buffer.append(new Date(lastSampleTime)); + buffer.append(lastSampleTime); + buffer.append(" description: "); + buffer.append(description); + } +} diff --git a/activeio/src/test/org/activeio/stats/TimeStatisticImpl.java b/activeio/src/test/org/activeio/stats/TimeStatisticImpl.java new file mode 100644 index 0000000000..7f5f2e6dbb --- /dev/null +++ b/activeio/src/test/org/activeio/stats/TimeStatisticImpl.java @@ -0,0 +1,175 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activeio.stats; + +import javax.management.j2ee.statistics.TimeStatistic; + +/** + * Shamelessly taken from the ActiveMQ project ( http://activemq.com ) + * A time statistic implementation + * + * @version $Revision: 1.1 $ + */ +public class TimeStatisticImpl extends StatisticImpl implements TimeStatistic { + private long count; + private long maxTime; + private long minTime; + private long totalTime; + private TimeStatisticImpl parent; + + public TimeStatisticImpl(String name, String description) { + this(name, "millis", description); + } + + public TimeStatisticImpl(TimeStatisticImpl parent, String name, String description) { + this(name, description); + this.parent = parent; + } + + public TimeStatisticImpl(String name, String unit, String description) { + super(name, unit, description); + } + + public synchronized void reset() { + super.reset(); + count = 0; + maxTime = 0; + minTime = 0; + totalTime = 0; + } + + public synchronized long getCount() { + return count; + } + + public synchronized void addTime(long time) { + count++; + totalTime += time; + if (time > maxTime) { + maxTime = time; + } + if (time < minTime || minTime == 0) { + minTime = time; + } + updateSampleTime(); + if (parent != null) { + parent.addTime(time); + } + } + + /** + * @return the maximum time of any step + */ + public long getMaxTime() { + return maxTime; + } + + /** + * @return the minimum time of any step + */ + public synchronized long getMinTime() { + return minTime; + } + + /** + * @return the total time of all the steps added together + */ + public synchronized long getTotalTime() { + return totalTime; + } + + /** + * @return the average time calculated by dividing the + * total time by the number of counts + */ + public synchronized double getAverageTime() { + if (count == 0) { + return 0; + } + double d = totalTime; + return d / count; + } + + + /** + * @return the average time calculated by dividing the + * total time by the number of counts but excluding the + * minimum and maximum times. + */ + public synchronized double getAverageTimeExcludingMinMax() { + if (count <= 2) { + return 0; + } + double d = totalTime - minTime - maxTime; + return d / (count - 2); + } + + + /** + * @return the average number of steps per second + */ + public double getAveragePerSecond() { + double d = 1000; + double averageTime = getAverageTime(); + if (averageTime == 0) { + return 0; + } + return d / averageTime; + } + + /** + * @return the average number of steps per second excluding the min & max values + */ + public double getAveragePerSecondExcludingMinMax() { + double d = 1000; + double average = getAverageTimeExcludingMinMax(); + if (average == 0) { + return 0; + } + return d / average; + } + + public TimeStatisticImpl getParent() { + return parent; + } + + public void setParent(TimeStatisticImpl parent) { + this.parent = parent; + } + + protected synchronized void appendFieldDescription(StringBuffer buffer) { + buffer.append(" count: "); + buffer.append(Long.toString(count)); + buffer.append(" maxTime: "); + buffer.append(Long.toString(maxTime)); + buffer.append(" minTime: "); + buffer.append(Long.toString(minTime)); + buffer.append(" totalTime: "); + buffer.append(Long.toString(totalTime)); + buffer.append(" averageTime: "); + buffer.append(Double.toString(getAverageTime())); + buffer.append(" averageTimeExMinMax: "); + buffer.append(Double.toString(getAverageTimeExcludingMinMax())); + buffer.append(" averagePerSecond: "); + buffer.append(Double.toString(getAveragePerSecond())); + buffer.append(" averagePerSecondExMinMax: "); + buffer.append(Double.toString(getAveragePerSecondExcludingMinMax())); + super.appendFieldDescription(buffer); + } + +} diff --git a/activeio/src/test/org/activeio/stats/package.html b/activeio/src/test/org/activeio/stats/package.html new file mode 100644 index 0000000000..0ce41587e0 --- /dev/null +++ b/activeio/src/test/org/activeio/stats/package.html @@ -0,0 +1,11 @@ + + + + + +

+ Some Statistic helper classes. +

+ + + diff --git a/activeio/src/test/org/activeio/xnet/hba/ServiceAccessControllerTest.java b/activeio/src/test/org/activeio/xnet/hba/ServiceAccessControllerTest.java new file mode 100755 index 0000000000..f76c5f14e8 --- /dev/null +++ b/activeio/src/test/org/activeio/xnet/hba/ServiceAccessControllerTest.java @@ -0,0 +1,216 @@ +/** + * + * Copyright 2005 Gianny Damour. + * + * Licensed 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.activeio.xnet.hba; + +import junit.framework.TestCase; +import org.activeio.xnet.ServerService; +import org.activeio.xnet.ServiceException; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.Properties; + +public class ServiceAccessControllerTest extends TestCase { + + public void testWrongExactIPAddressPermission1() throws Exception { + try { + IPAddressPermissionFactory.getIPAddressMask("121.122.123.a"); + fail(); + } catch (IllegalArgumentException e) { + } + } + + public void testWrongExactIPAddressPermission2() throws Exception { + try { + IPAddressPermissionFactory.getIPAddressMask("121.122.123.256"); + fail(); + } catch (IllegalArgumentException e) { + } + } + + public void testExactIPAddressPermission() throws Exception { + IPAddressPermission permission = IPAddressPermissionFactory.getIPAddressMask("121.122.123.124"); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 123, 124}))); + assertFalse(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 123, 125}))); + } + + public void testWrongStartWithIPAddressPermission1() throws Exception { + try { + IPAddressPermissionFactory.getIPAddressMask("121.0.123.0"); + fail(); + } catch (IllegalArgumentException e) { + } + } + + public void testStartWithIPAddressPermission() throws Exception { + IPAddressPermission permission = IPAddressPermissionFactory.getIPAddressMask("121.122.0.0"); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 123, 124}))); + assertFalse(permission.implies(InetAddress.getByAddress(new byte[]{121, 123, 123, 124}))); + } + + public void testFactorizedIPAddressPermission() throws Exception { + IPAddressPermission permission = IPAddressPermissionFactory.getIPAddressMask("121.122.123.{1,2,3}"); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 123, 1}))); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 123, 2}))); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 123, 3}))); + assertFalse(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 123, 4}))); + + permission = IPAddressPermissionFactory.getIPAddressMask("121.122.{1,2,3}"); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 1, 1}))); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 2, 2}))); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 3, 3}))); + assertFalse(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 4, 3}))); + } + + public void testNetmaskIPAddressPermission() throws Exception { + IPAddressPermission permission = IPAddressPermissionFactory.getIPAddressMask("121.122.123.254/31"); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 123, (byte) 254}))); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 123, (byte) 255}))); + assertFalse(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 123, (byte) 253}))); + + permission = IPAddressPermissionFactory.getIPAddressMask("121.122.123.254/255.255.255.254"); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 123, (byte) 254}))); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 123, (byte) 255}))); + assertFalse(permission.implies(InetAddress.getByAddress(new byte[]{121, 122, 123, (byte) 253}))); + } + + public void testExactIPv6AddressPermission() throws Exception { + IPAddressPermission permission = IPAddressPermissionFactory.getIPAddressMask("101:102:103:104:105:106:107:108"); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1, 8}))); + assertFalse(permission.implies(InetAddress.getByAddress(new byte[]{1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1, 9}))); + } + + public void testNetmaskIPv6AddressPermission() throws Exception { + IPAddressPermission permission = IPAddressPermissionFactory.getIPAddressMask("101:102:103:104:105:106:107:FFFE/127"); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, (byte) 255, (byte) 254}))); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, (byte) 255, (byte) 255}))); + assertFalse(permission.implies(InetAddress.getByAddress(new byte[]{1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, (byte) 255, (byte) 253}))); + + permission = IPAddressPermissionFactory.getIPAddressMask("101:102:103:104:105:106:107:FFFE/FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFE"); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, (byte) 255, (byte) 254}))); + assertTrue(permission.implies(InetAddress.getByAddress(new byte[]{1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, (byte) 255, (byte) 255}))); + assertFalse(permission.implies(InetAddress.getByAddress(new byte[]{1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, (byte) 255, (byte) 253}))); + } + + public void testServiceOKWithConstructor() throws Exception { + IPAddressPermission[] masks = new IPAddressPermission[]{ + IPAddressPermissionFactory.getIPAddressMask("121.122.{56,57}") + }; + + MockServerService mockServerService = new MockServerService(); + ServiceAccessController controller = new ServiceAccessController(null, mockServerService, masks); + + executeTestServiceOK(mockServerService, controller); + } + + public void testServiceNOK() throws Exception { + IPAddressPermission[] masks = new IPAddressPermission[]{ + IPAddressPermissionFactory.getIPAddressMask("121.122.{56,57}") + }; + + MockServerService mockServerService = new MockServerService(); + ServiceAccessController controller = new ServiceAccessController(null, mockServerService, masks); + + executeTestServiceNOK(controller); + } + + public void testServiceOKWithInit() throws Exception { + Properties properties = new Properties(); + properties.put("only_from", "121.122.{56,57}"); + + MockServerService mockServerService = new MockServerService(); + ServiceAccessController controller = new ServiceAccessController(mockServerService); + controller.init(properties); + + executeTestServiceOK(mockServerService, controller); + } + + public void testServiceNOKWithInit() throws Exception { + Properties properties = new Properties(); + properties.put("only_from", "121.122.{56,57}"); + + MockServerService mockServerService = new MockServerService(); + ServiceAccessController controller = new ServiceAccessController(mockServerService); + controller.init(properties); + + executeTestServiceOK(mockServerService, controller); + } + + private void executeTestServiceOK(MockServerService mockServerService, ServiceAccessController controller) throws UnknownHostException, ServiceException, IOException { + MockSocket mockSocket = new MockSocket(InetAddress.getByAddress(new byte[]{121, 122, 56, 123})); + controller.service(mockSocket); + assertSame(mockSocket, mockServerService.socket); + + mockSocket = new MockSocket(InetAddress.getByAddress(new byte[]{121, 122, 57, 123})); + controller.service(mockSocket); + assertSame(mockSocket, mockServerService.socket); + } + + private void executeTestServiceNOK(ServiceAccessController controller) throws UnknownHostException, ServiceException, IOException { + MockSocket mockSocket = new MockSocket(InetAddress.getByAddress(new byte[]{121, 122, 58, 123})); + try { + controller.service(mockSocket); + fail(); + } catch (SecurityException e) { + } + } + + private static class MockSocket extends Socket { + private final InetAddress address; + + private MockSocket(InetAddress address) { + this.address = address; + } + + public InetAddress getInetAddress() { + return address; + } + } + + private static class MockServerService implements ServerService { + private Socket socket; + + public void init(Properties props) throws Exception { + } + + public void start() throws ServiceException { + throw new AssertionError(); + } + + public void stop() throws ServiceException { + throw new AssertionError(); + } + + public String getIP() { + throw new AssertionError(); + } + + public int getPort() { + throw new AssertionError(); + } + + public void service(Socket socket) throws ServiceException, IOException { + this.socket = socket; + } + + public String getName() { + throw new AssertionError(); + } + } +} \ No newline at end of file diff --git a/activeio/src/test/server.keystore b/activeio/src/test/server.keystore new file mode 100644 index 0000000000..d9223d80a8 Binary files /dev/null and b/activeio/src/test/server.keystore differ diff --git a/activemq-core/.cvsignore b/activemq-core/.cvsignore new file mode 100755 index 0000000000..04a541a150 --- /dev/null +++ b/activemq-core/.cvsignore @@ -0,0 +1,11 @@ +target +.project +.classpath +bin +velocity.log +test-data +activemq-data +activemq-core.iws +activemq-core.ipr +activemq-core.iml +junit*.properties diff --git a/activemq-core/maven.xml b/activemq-core/maven.xml new file mode 100755 index 0000000000..14d2f799aa --- /dev/null +++ b/activemq-core/maven.xml @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Running the ActiveMQ broker for the URI ${uri} + + + + + + + + + + + + + + + Running the ActiveMQ consumer for the URI ${uri} + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/pom.xml b/activemq-core/pom.xml new file mode 100755 index 0000000000..547540dcbf --- /dev/null +++ b/activemq-core/pom.xml @@ -0,0 +1,218 @@ + + 4.0.0 + + + activemq + activemq + 4.0-SNAPSHOT + + + activemq-core + jar + ActiveMQ JMS Broker + + + + + + + maven-surefire-plugin + 2.1-SNAPSHOT + + + + **/DefaultStoreBrokerTest.* + **/ActiveIOTransportBrokerTest.* + + **/SpringTest.* + + + **/*Test.* + + pertest + + + + + + + + + + + + + + activeio + activeio + + + + backport-util-concurrent + backport-util-concurrent + + + + commons-logging + commons-logging + + + + log4j + log4j + + + + geronimo-spec + geronimo-spec-jms + + + geronimo-spec + geronimo-spec-jta + + + geronimo-spec + geronimo-spec-j2ee-management + + + geronimo-spec + geronimo-spec-j2ee-jacc + + + + springframework + spring + + + + activecluster + activecluster + + + + org.apache.derby + derby + + + + axion + axion + + + commons-collections + commons-collections + + + commons-primitives + commons-primitives + + + regexp + regexp + + + + activemq + jmdns + + + + activemq + activemq-jaas + + + + activemq + smack + + + activemq + smackx + + + + xmlbeans + xbean + + + xmlbeans + xmlpublic + + + xmlbeans + xbean_xpath + + + stax + stax-api + + + stax + stax + + + activesoap + jaxp-api + + + xalan + xalan + + + + mx4j + mx4j + + + mx4j + mx4j-jmx + + + mx4j + mx4j-remote + + + mx4j + mx4j-tools + + + mx4j + mx4j-impl + + + + commons-pool + commons-pool + + + + xbean + xbean-spring + + + + + diff --git a/activemq-core/project.properties b/activemq-core/project.properties new file mode 100644 index 0000000000..3248dc8240 --- /dev/null +++ b/activemq-core/project.properties @@ -0,0 +1,13 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=jar +maven.eclipse.classpath.include=src/main/resources,src/test/resources,src/gram/java,target/generated + +openwire.version=1 + +maven.changelog.range=730 + + + + diff --git a/activemq-core/project.xml b/activemq-core/project.xml new file mode 100755 index 0000000000..4d19326b18 --- /dev/null +++ b/activemq-core/project.xml @@ -0,0 +1,456 @@ + + + + + + 3 + ${basedir}/../etc/project.xml + + ActiveMQ :: Core + activemq-core + + ActiveMQ is an open source message broker and JMS 1.1 provider + ActiveMQ JMS Message Broker + + org.activemq + + + Core JMS Client API + org.activemq:org.activemq.message + + + JMS Broker and Region implementations + org.activemq.broker:org.activemq.broker.* + + + Management Statistics + org.activemq.management + + + JNDI support + org.activemq.jndi + + + Message filter and router + org.activemq.filter:org.activemq.filter.*:org.activemq.selector + + + Security strategies and implementations + org.activemq.security + + + Transport, Discovery and WireFormat strategies and implementations + org.activemq.transport:org.activemq.transport.* + + + OpenWire support + org.activemq.command:org.activemq.openwire:org.activemq.openwire.* + + + Support for Networks + org.activemq.network:org.activemq.network.* + + + Message persistence strategies and implementations + org.activemq.store:org.activemq.store.* + + + Core router services + org.activemq.service:org.activemq.service.* + + + Utilities + org.activemq.capacity:org.activemq.io.util:org.activemq.util + + + + + + + + activemq + activemq-jaas + ${pom.currentVersion} + + true + + + + + + commons-beanutils + commons-beanutils + ${commons_beanutils_version} + + true + true + + + + + javacc + javacc + ${javacc_version} + JavaCC.zip + + + + activecluster + activecluster + ${activecluster_version} + + + activemq + jmdns + ${jmdns_version} + + + activeio + activeio + ${activeio_version} + + + jmock + jmock + ${jmock_version} + http://jmock.codehaus.org/ + + + jmock + jmock-cglib + ${jmock_cglib_version} + http://jmock.codehaus.org/ + + + cglib + cglib-full + ${cglib_full_version} + http://cglib.sourceforge.net/ + + + + org.apache.derby + derby + ${derby_version} + + + + + howl + howl-logger + ${howl_logger_version} + http://forge.objectweb.org/projects/howl + + + + springframework + spring + ${spring_version} + http://www.springframework.org + + true + + + + + xbean + xbean-spring + ${xbean_spring_version} + http://www.gbean.org + + true + + + + xmlbeans + xbean + 2.0.0-beta1 + + + xmlbeans + xmlpublic + 2.0.0-beta1 + + + xmlbeans + xbean_xpath + 2.0.0-beta1 + + + stax + stax-api + 1.0 + + + stax + stax + 1.1.1-dev + + + activesoap + jaxp-api + 1.3 + + + xalan + xalan + 2.6.0 + + + qdox + qdox + 1.5 + + + activemq + smack + 1.5.0 + + + activemq + smackx + 1.5.0 + + + mx4j + mx4j + 2.1.1 + + + mx4j + mx4j-jmx + 2.1.1 + + + mx4j + mx4j-remote + 2.1.1 + + + mx4j + mx4j-tools + 2.1.1 + + + mx4j + mx4j-impl + 2.1.1 + + + + + groovy + gram + 1.1-SNAPSHOT + + + groovy + groovy-all + 1.0-jsr-01 + jar + + + annogen + annogen + 0.1.0 + + + + + axion + axion + ${axion_version} + + + commons-primitives + commons-primitives + ${commons_primitives_version} + + + regexp + regexp + ${regexp_version} + + + + hsqldb + hsqldb + ${hsqldb_version} + + + commons-dbcp + commons-dbcp + ${commons_dbcp_version} + + + commons-pool + commons-pool + ${commons_pool_version} + + + + + activemq + activemq-jaas + ${pom.currentVersion} + + + + + + dev@activemq.codehaus.org + src/main/java + src/test/java + + + + src/test/resources + + **/*.properties + **/*.xml + + false + + + + **/*Test.* + + + + **/DefaultStoreBrokerTest.* + **/TcpTransportBrokerTest.* + **/activeio/* + + + **/perf/* + + + **/ProxyConnectorTest.* + + + **/ItStillMarshallsTheSameTest.* + + + + + src/main/resources + + **/* + + false + + + target/generated + + **/* + + false + + + + + + + James Strachan + jstrachan + jstrachan@logicblaze.com + + + Hiram Chirino + chirino + hiram@logicblaze.com + + + Rob Davies + Rob + rajdavies@gmail.com + + + Jonas Lim + jlim + jlim@exist.com + + + Frederick Oconer + foconer + foconer@exist.com + + + Joseph Gapuz + jgapuz + jgapuz@exist.com + + + Patrick Villacorta + pvillacorta + pvillacorta@exist.com + + + Darwin Flores + dflores + dflores@exist.com + + + Merwin Yap + myap + myap@exist.com + + + Adrian Co + aco + aco@exist.com + + + Dennis Cook + dcook + dennis@bevocal.com + + + Dag Liodden + daggerrz + dag.liodden@giantleap.no + + + Peter Brooke + pbrooke + + + Ramzi Saba + rsaba + rsaba@optaros.com + + + Brian McCallister + brianm + brianm@apache.org + + + + + maven-javadoc-plugin + maven-junit-report-plugin + + + + scm:svn:svn://svn.activemq.org/activemq/scm/branches/activemq-4-0/activemq + scm:svn:svn://svn.activemq.org/activemq/branches/activemq-4-0/activemq + https://svn.activemq.org/activemq/branches/activemq-4-0/activemq/ + + + diff --git a/activemq-core/src/gram/java/org/activemq/openwire/tool/OpenWireScript.java b/activemq-core/src/gram/java/org/activemq/openwire/tool/OpenWireScript.java new file mode 100755 index 0000000000..5ed15bf0c7 --- /dev/null +++ b/activemq-core/src/gram/java/org/activemq/openwire/tool/OpenWireScript.java @@ -0,0 +1,75 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.openwire.tool; + +import org.codehaus.gram.GramSupport; +import org.codehaus.jam.JAnnotationValue; +import org.codehaus.jam.JClass; +import org.codehaus.jam.JField; +import org.codehaus.jam.JMethod; +import org.codehaus.jam.JProperty; + +/** + * @version $Revision$ + */ +public abstract class OpenWireScript extends GramSupport { + + public boolean isValidProperty(JProperty it) { + JMethod getter = it.getGetter(); + return getter != null && it.getSetter() != null && getter.isStatic() == false + && getter.getAnnotation("openwire:property") != null; + } + + public boolean isCachedProperty(JProperty it) { + JMethod getter = it.getGetter(); + if( !isValidProperty(it) ) + return false; + JAnnotationValue value = getter.getAnnotation("openwire:property").getValue("cache"); + return value!=null && value.asBoolean(); + } + + public boolean isAbstract(JClass j) { + JField[] fields = j.getFields(); + for (int i = 0; i < fields.length; i++) { + JField field = fields[i]; + if (field.isStatic() && field.isPublic() && field.isFinal() + && field.getSimpleName().equals("DATA_STRUCTURE_TYPE")) { + return false; + } + } + return true; + } + + public boolean isThrowable(JClass j) { + if (j.getQualifiedName().equals(Throwable.class.getName())) { + return true; + } + return j.getSuperclass()!=null && isThrowable(j.getSuperclass()); + } + + public boolean isMarshallAware(JClass j) { + JClass[] interfaces = j.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + if( interfaces[i].getQualifiedName().equals("org.activemq.command.MarshallAware") ) { + return true; + } + } + return false; //j.getSuperclass()!=null && isMarshallAware(j.getSuperclass()); + } +} \ No newline at end of file diff --git a/activemq-core/src/gram/script/GenerateCMarshalling.groovy b/activemq-core/src/gram/script/GenerateCMarshalling.groovy new file mode 100755 index 0000000000..94d70bdbe4 --- /dev/null +++ b/activemq-core/src/gram/script/GenerateCMarshalling.groovy @@ -0,0 +1,631 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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. +* +**/ +import org.activemq.openwire.tool.OpenWireScript + +/** + * Generates the Java marshalling code for the Open Wire Format + * + * @version $Revision$ + */ +class GenerateCMarshalling extends OpenWireScript { + + String changeCase(String value) { + StringBuffer b = new StringBuffer(); + value.each { c | + if( Character.isUpperCase((char)c) ) { + b.append('_'); + b.append(Character.toLowerCase((char)c)); + } else { + b.append(c); + } + } + return b.toString(); + } + + String toPropertyCase(String value) { + return value.substring(0,1).toLowerCase()+value.substring(1); + } + + /** + * Sort the class list so that base classes come up first. + */ + def sort(classes) { + + classes = (java.util.List)classes; + def rc = []; + def objectClass; + + // Prime with the Object class + def x = classes[0]; + while( !x.simpleName.equals("Object") ) { + x = x.superclass; + } + rc[0]=x; + + // Add all classes that have a parent in the rc list + while( classes.size() > 0 ) { + for (c in classes) { + for( i in rc ) { + if( c.superclass.equals(i) ) { + rc.add(c); + break; + } + } + } + classes.removeAll(rc); + } + + // Get rid of that primed Object class + rc.remove(0); + return rc; + } + + + def generateFields(out, jclass) { + + println("getting fields for: ${jclass.simpleName}"); + if( jclass.superclass.simpleName.equals("Object") ) { +out << """ + ow_byte structType; +"""; + } else { + generateFields(out, jclass.superclass); + } + + def properties = jclass.declaredProperties.findAll { isValidProperty(it) } + for (property in properties) { + def annotation = property.getter.getAnnotation("openwire:property"); + def size = annotation.getValue("size"); + def name = toPropertyCase(property.simpleName); + def cached = isCachedProperty(property); + + out << " " + def type = property.type.qualifiedName + switch (type) { + case "boolean": + out << "ow_$type $name;"; break; + break; + case "byte": + out << "ow_$type $name;"; break; + break; + case "char": + out << "ow_$type $name;"; break; + break; + case "short": + out << "ow_$type $name;"; break; + break; + case "int": + out << "ow_$type $name;"; break; + break; + case "long": + out << "ow_$type $name;"; break; + break; + case "byte[]": + out << "ow_byte_array *$name;"; break; + break; + case "org.activeio.ByteSequence": + out << "ow_byte_array *$name;"; break; + break; + case "org.activeio.ByteSequence": + out << "ow_byte_array *$name;"; break; + break; + case "java.lang.String": + out << "ow_string *$name;"; break; + break; + default: + if( property.type.arrayType ) { + out << "ow_DataStructure_array *$name;"; break; + } else if( isThrowable(property.type) ) { + out << "ow_throwable *$name;"; break; + } else { + out << "struct ow_"+property.type.simpleName+" *$name;"; break; + } + } +out << """ +""" + } + + } + + Object run() { + + def openwireVersion = System.getProperty("openwire.version"); + + def destDir = new File("../openwire-c/src/libopenwire") + println "Generating C marshalling code to directory ${destDir}" + + def openwireClasses = classes.findAll { + it.getAnnotation("openwire:marshaller")!=null + } + + println "Sorting classes..." + openwireClasses = sort(openwireClasses); + + def concreteClasses = new ArrayList() + int counter = 0 + Map map = [:] + + destDir.mkdirs() + + ////////////////////////////////////////////////////////////////////// + // + // Start generateing the ow_commands_v1.h File + // + ////////////////////////////////////////////////////////////////////// + def file = new File(destDir, "ow_commands_v${openwireVersion}.h") + file.withWriter { out | + +out << """/** + * + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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. + */ + +/***************************************************************************************** + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + *****************************************************************************************/ + +#ifndef OW_COMMANDS_V${openwireVersion}_H +#define OW_COMMANDS_V${openwireVersion}_H + +#include "ow.h" +#include "ow_command_types_v${openwireVersion}.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define OW_WIREFORMAT_VERSION ${openwireVersion} + +apr_status_t ow_bitmarshall(ow_bit_buffer *buffer, ow_DataStructure *object); +apr_status_t ow_marshall(ow_byte_buffer *buffer, ow_DataStructure *object); +""" + + for (jclass in openwireClasses) { + + println "Processing ${jclass.simpleName}" + + def structName = jclass.simpleName; + def type = "OW_"+structName.toUpperCase()+"_TYPE" + + counter++; +out << """ +typedef struct ow_${structName} { +""" + // This recusivly generates the field definitions of the class and it's supper classes. + generateFields(out, jclass); + +out << """ +} ow_${structName}; +ow_${structName} *ow_${structName}_create(apr_pool_t *pool); +ow_boolean ow_is_a_${structName}(ow_DataStructure *object); +""" + } + +out << """ +#ifdef __cplusplus +} +#endif + +#endif /* ! OW_COMMANDS_V${openwireVersion}_H */ +""" + + } + + ////////////////////////////////////////////////////////////////////// + // + // Start generateing the ow_commands_v1.c File + // + ////////////////////////////////////////////////////////////////////// + + file = new File(destDir, "ow_commands_v${openwireVersion}.c") + file.withWriter { out | + +out << """/** + * + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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. + */ + +/***************************************************************************************** + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + *****************************************************************************************/ + + +#include "ow_commands_v${openwireVersion}.h" + +#define SUCCESS_CHECK( f ) { apr_status_t rc=f; if(rc!=APR_SUCCESS) return rc; } + +""" + for (jclass in openwireClasses) { + + def properties = jclass.declaredProperties.findAll { isValidProperty(it) } + def name = jclass.simpleName; + def type = ("ow_"+name).toUpperCase()+"_TYPE" + + def baseName="DataStructure"; + if( !jclass.superclass.simpleName.equals("Object") ) { + baseName = jclass.superclass.simpleName; + } + +out << """ +ow_boolean ow_is_a_${name}(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) {""" + for (sub in openwireClasses) { + def subtype = "OW_"+sub.simpleName.toUpperCase()+"_TYPE" + if( jclass.isAssignableFrom(sub) && !isAbstract(sub) ) { +out << """ + case $subtype:""" + + } + } +out << """ + return 1; + } + return 0; +} +""" + + + if( !isAbstract(jclass) ) { +out << """ + +ow_${name} *ow_${name}_create(apr_pool_t *pool) +{ + ow_${name} *value = apr_pcalloc(pool,sizeof(ow_${name})); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = ${type}; + } + return value; +} +""" + } + +out << """ + +apr_status_t ow_marshal1_${name}(ow_bit_buffer *buffer, ow_${name} *object) +{ + ow_marshal1_${baseName}(buffer, (ow_${baseName}*)object); +""" + + def properties = jclass.declaredProperties.findAll { isValidProperty(it) } + for (property in properties) { + def propname = toPropertyCase(property.simpleName); + def cached = isCachedProperty(property); + def annotation = property.getter.getAnnotation("openwire:property"); + def size = annotation.getValue("size"); + + out << " "; + def type = property.type.qualifiedName + switch (type) { + + case "boolean": + out << "ow_bit_buffer_append(buffer, object->$propname);"; break; + break; + case "byte":break; + case "char":break; + case "short":break; + case "int":break; + case "long": + out << "ow_marshal1_long(buffer, object->$propname);"; break; + break; + case "byte[]": + if( size ==null ) { + out << """ + ow_bit_buffer_append(buffer, object->$propname!=0 ); + """; + } + break; + case "org.activeio.ByteSequence": + if( size ==null ) { + out << """ + ow_bit_buffer_append(buffer, object->$propname!=0 ); + """; + } + break; + case "java.lang.String": + out << "ow_marshal1_string(buffer, object->$propname);"; break; + default: + if( property.type.arrayType ) { + if( size!=null ) { + out << "SUCCESS_CHECK(ow_marshal1_DataStructure_array_const_size(buffer, object->$propname, ${size.asInt()}));"; break; + } else { + out << "SUCCESS_CHECK(ow_marshal1_DataStructure_array(buffer, object->$propname));"; break; + } + } else if( isThrowable(property.type) ) { + out << "SUCCESS_CHECK(ow_marshal1_throwable(buffer, object->$propname));"; break; + } else { + if( cached ) { + out << "SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->$propname));"; break; + } else { + out << "SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->$propname));"; break; + } + } + } +out << """ +""" + } + + +out << """ + return APR_SUCCESS; +} +apr_status_t ow_marshal2_${name}(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_${name} *object) +{ + ow_marshal2_${baseName}(buffer, bitbuffer, (ow_${baseName}*)object); +""" + + def properties = jclass.declaredProperties.findAll { isValidProperty(it) } + for (property in properties) { + def annotation = property.getter.getAnnotation("openwire:property"); + def size = annotation.getValue("size"); + def propname = toPropertyCase(property.simpleName); + def cached = isCachedProperty(property); + + out << " " + def type = property.type.qualifiedName + switch (type) { + case "boolean": + out << "ow_bit_buffer_read(bitbuffer);"; break; + case "byte": + out << "SUCCESS_CHECK(ow_byte_buffer_append_${type}(buffer, object->$propname));"; break; + break; + case "char": + out << "SUCCESS_CHECK(ow_byte_buffer_append_${type}(buffer, object->$propname));"; break; + break; + case "short": + out << "SUCCESS_CHECK(ow_byte_buffer_append_${type}(buffer, object->$propname));"; break; + break; + case "int": + out << "SUCCESS_CHECK(ow_byte_buffer_append_${type}(buffer, object->$propname));"; break; + break; + case "long": + out << "SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->$propname));"; break; + break; + case "byte[]": + if( size!=null ) { + out << "SUCCESS_CHECK(ow_marshal2_byte_array_const_size(buffer, object->$propname, ${size.asInt()}));"; break; + } else { + out << "SUCCESS_CHECK(ow_marshal2_byte_array(buffer, bitbuffer, object->$propname));"; break; + } + break; + case "org.activeio.ByteSequence": + if( size!=null ) { + out << "SUCCESS_CHECK(ow_marshal2_byte_array_const_size(buffer, object->$propname, ${size.asInt()}));"; break; + } else { + out << "SUCCESS_CHECK(ow_marshal2_byte_array(buffer, bitbuffer, object->$propname));"; break; + } + break; + case "java.lang.String": + out << "SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->$propname));"; break; + break; + default: + if( property.type.arrayType ) { + if( size!=null ) { + out << "SUCCESS_CHECK(ow_marshal2_DataStructure_array_const_size(buffer, bitbuffer, object->$propname, ${size.asInt()}));"; break; + } else { + out << "SUCCESS_CHECK(ow_marshal2_DataStructure_array(buffer, bitbuffer, object->$propname));"; break; + } + } else if( isThrowable(property.type) ) { + out << "SUCCESS_CHECK(ow_marshal2_throwable(buffer, bitbuffer, object->$propname));"; break; + } else { + if( cached ) { + out << "SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->$propname));"; break; + } else { + out << "SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->$propname));"; break; + } + } + } +out << """ +""" + } + +out << """ + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_${name}(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_${name} *object, apr_pool_t *pool) +{ + ow_unmarshal_${baseName}(buffer, bitbuffer, (ow_${baseName}*)object, pool); +""" + + def properties = jclass.declaredProperties.findAll { isValidProperty(it) } + for (property in properties) { + def annotation = property.getter.getAnnotation("openwire:property"); + def size = annotation.getValue("size"); + def propname = toPropertyCase(property.simpleName); + def cached = isCachedProperty(property); + + out << " " + def type = property.type.qualifiedName + switch (type) { + case "boolean": + out << "object->$propname = ow_bit_buffer_read(bitbuffer);"; break; + break; + case "byte": + out << "SUCCESS_CHECK(ow_byte_array_read_${type}(buffer, &object->$propname));"; break; + break; + case "char": + out << "SUCCESS_CHECK(ow_byte_array_read_${type}(buffer, &object->$propname));"; break; + break; + case "short": + out << "SUCCESS_CHECK(ow_byte_array_read_${type}(buffer, &object->$propname));"; break; + break; + case "int": + out << "SUCCESS_CHECK(ow_byte_array_read_${type}(buffer, &object->$propname));"; break; + break; + case "long": + out << "SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->$propname, pool));"; break; + break; + case "byte[]": + if( size!=null ) { + out << "SUCCESS_CHECK(ow_unmarshal_byte_array_const_size(buffer, &object->$propname, ${size.asInt()}, pool));"; break; + } else { + out << "SUCCESS_CHECK(ow_unmarshal_byte_array(buffer, bitbuffer, &object->$propname, pool));"; break; + } + break; + case "org.activeio.ByteSequence": + if( size!=null ) { + out << "SUCCESS_CHECK(ow_unmarshal_byte_array_const_size(buffer, &object->$propname, ${size.asInt()}, pool));"; break; + } else { + out << "SUCCESS_CHECK(ow_unmarshal_byte_array(buffer, bitbuffer, &object->$propname, pool));"; break; + } + break; + case "java.lang.String": + out << "SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->$propname, pool));"; break; + break; + default: + if( property.type.arrayType ) { + if( size!=null ) { + out << "SUCCESS_CHECK(ow_unmarshal_DataStructure_array_const_size(buffer, bitbuffer, &object->$propname, ${size.asInt()}, pool));"; break; + } else { + out << "SUCCESS_CHECK(ow_unmarshal_DataStructure_array(buffer, bitbuffer, &object->$propname, pool));"; break; + } + } else if( isThrowable(property.type) ) { + out << "SUCCESS_CHECK(ow_unmarshal_throwable(buffer, bitbuffer, &object->$propname, pool));"; break; + } else { + if( cached ) { + out << "SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->$propname, pool));"; break; + } else { + out << "SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->$propname, pool));"; break; + } + } + } +out << """ +""" + } + +out << """ + return APR_SUCCESS; +} +""" + } + + +out << """ +ow_DataStructure *ow_create_object(ow_byte type, apr_pool_t *pool) +{ + switch( type ) { +""" + for (jclass in openwireClasses) { + def name = jclass.simpleName; + def type = ("ow_"+name).toUpperCase()+"_TYPE"; + if( !isAbstract(jclass) ) { +out << """ + case ${type}: return (ow_DataStructure *)ow_${name}_create(pool);""" + } + } + +out << """ + } + return 0; +} + +apr_status_t ow_marshal1_object(ow_bit_buffer *buffer, ow_DataStructure *object) +{ + switch( object->structType ) { +""" + for (jclass in openwireClasses) { + def name = jclass.simpleName; + def type = ("ow_"+name).toUpperCase()+"_TYPE"; + if( !isAbstract(jclass) ) { +out << """ + case ${type}: return ow_marshal1_${name}(buffer, (ow_${name}*)object);""" + } + } + +out << """ + } + return APR_EGENERAL; +} + +apr_status_t ow_marshal2_object(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure *object) +{ + switch( object->structType ) { +""" + for (jclass in openwireClasses) { + def name = jclass.simpleName; + def type = ("ow_"+name).toUpperCase()+"_TYPE"; + if( !isAbstract(jclass) ) { +out << """ + case ${type}: return ow_marshal2_${name}(buffer, bitbuffer, (ow_${name}*)object);""" + } + } + +out << """ + } + return APR_EGENERAL; +} + +apr_status_t ow_unmarshal_object(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure *object, apr_pool_t *pool) +{ + switch( object->structType ) { +""" + for (jclass in openwireClasses) { + def name = jclass.simpleName; + def type = ("ow_"+name).toUpperCase()+"_TYPE"; + if( !isAbstract(jclass) ) { +out << """ + case ${type}: return ow_unmarshal_${name}(buffer, bitbuffer, (ow_${name}*)object, pool);""" + } + } + +out << """ + } + return APR_EGENERAL; +} +""" + } + } +} diff --git a/activemq-core/src/gram/script/GenerateCSharpClasses.groovy b/activemq-core/src/gram/script/GenerateCSharpClasses.groovy new file mode 100755 index 0000000000..863612d7be --- /dev/null +++ b/activemq-core/src/gram/script/GenerateCSharpClasses.groovy @@ -0,0 +1,141 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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. +* +**/ +import org.activemq.openwire.tool.OpenWireScript + +/** + * Generates the C# marshalling code for the Open Wire Format + * + * @version $Revision$ + */ +class GenerateCSharpClasses extends OpenWireScript { + + Object run() { + def destDir = new File("target/generated/dotnet/cs/org/activemq/openwire") + destDir.mkdirs() + + def messageClasses = classes.findAll { isMessageType(it) } + + println "Generating Java marshalling code to directory ${destDir}" + + def buffer = new StringBuffer() + + int counter = 0 + Map map = [:] + + for (jclass in messageClasses) { + + println "Processing $jclass.simpleName" + + def properties = jclass.declaredProperties.findAll { isValidProperty(it) } + def file = new File(destDir, jclass.simpleName + ".cs") + + String baseClass = "AbstractPacket" + if (jclass.superclass?.simpleName == "ActiveMQMessage") { + baseClass = "ActiveMQMessage" + } + + buffer << """ +${jclass.simpleName}.class +""" + + file.withWriter { out | + out << """/** + * Marshalling code for Open Wire Format for ${jclass.simpleName} + * + * + * NOTE!: This file is autogenerated - do not modify! + * if you need to make a change, please see the Groovy scripts in the + * activemq-openwire module + */ + +using System; +using System.Collections; + +namespace ActiveMQ +{ + public class ${jclass.simpleName} : $baseClass + { +""" + for (property in properties) { + + def type = toCSharpType(property.type) + def name = decapitalize(property.simpleName) + out << """ $type $name; +""" + } + + out << """ + + + // TODO generate Equals method + // TODO generate HashCode method + // TODO generate ToString method + + + public overide int getPacketType() { + return ${getEnum(jclass)}; + } + + + // Properties + +""" + for (property in properties) { + def type = toCSharpType(property.type) + def name = decapitalize(property.simpleName) + def getter = property.getter.simpleName + def setter = property.setter.simpleName + + + out << """ + public $type $getter() + { + return this.$name; + } + + public void $setter($type $name) + { + this.$name = $name; + } + +""" + } + + out << """ + } +} +""" + } + } + } + + def toCSharpType(type) { + def name = type.qualifiedName + switch (type) { + case "java.lang.String": + return "string" + case "org.activemq.message.ActiveMQDestination": + return "ActiveMQDestination" + case "org.activemq.message.ActiveMQXid": + return "ActiveMQXid" + default: + return name + } + } +} \ No newline at end of file diff --git a/activemq-core/src/gram/script/GenerateCSharpMarshalling.groovy b/activemq-core/src/gram/script/GenerateCSharpMarshalling.groovy new file mode 100755 index 0000000000..ff4f276737 --- /dev/null +++ b/activemq-core/src/gram/script/GenerateCSharpMarshalling.groovy @@ -0,0 +1,207 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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. +* +**/ +import org.activemq.openwire.tool.OpenWireScript + +/** + * Generates the Java marshalling code for the Open Wire Format + * + * @version $Revision$ + */ +class GenerateCSharpMarshalling extends OpenWireScript { + + Object run() { + def destDir = new File("target/generated/dotnet/cs/org/activemq/openwire/io") + destDir.mkdirs() + + def messageClasses = classes.findAll { isMessageType(it) } + + println "Generating Java marshalling code to directory ${destDir}" + + def buffer = new StringBuffer() + + int counter = 0 + Map map = [:] + + for (jclass in messageClasses) { + + println "Processing $jclass.simpleName" + + def properties = jclass.declaredProperties.findAll { isValidProperty(it) } + def file = new File(destDir, jclass.simpleName + "Marshaller.cs") + + String baseClass = "AbstractPacketMarshaller" + if (jclass.superclass?.simpleName == "ActiveMQMessage") { + baseClass = "ActiveMQMessageMarshaller" + } + + buffer << """ +${jclass.simpleName}Marshaller.class +""" + + file.withWriter { out | + out << """/** + * Marshalling code for Open Wire Format for ${jclass.simpleName} + * + * + * NOTE!: This file is autogenerated - do not modify! + * if you need to make a change, please see the Groovy scripts in the + * activemq-openwire module + */ + +using System; +using System.Collections; + +namespace ActiveMQ +{ + public class ${jclass.simpleName} : $baseClass + { + + public override int GetPacketType() { + return ${getEnum(jclass)}; + } + + public override Packet CreatePacket() { + return new ${jclass.simpleName}(); + } + + public override void BuildPacket(Packet packet, DataInput dataIn) throws IOException { + super.buildPacket(packet, dataIn); + ${jclass.simpleName} info = (${jclass.simpleName}) packet; +""" + for (property in properties) { + out << " info.${property.setter.simpleName}(" + + def type = property.type.qualifiedName + switch (type) { + case "java.lang.String": + out << "dataIn.readUTF()" + break; + + case "org.activemq.message.ActiveMQDestination": + out << "readDestination(dataIn)" + break; + + case "boolean": + out << "dataIn.readBoolean()" + break; + + case "byte": + out << "dataIn.readByte()" + break; + + case "char": + out << "dataIn.readChar()" + break; + + case "short": + out << "dataIn.readShort()" + break; + + case "int": + out << "dataIn.readInt()" + break; + + case "long": + out << "dataIn.readLong()" + break; + + case "float": + out << "dataIn.readFloat()" + break; + + case "double": + out << "dataIn.readDouble()" + break; + + default: + out << "(${type}) readObject(dataIn)" + } + out << """); +""" + } + + out << """ + } + + public override void WritePacket(Packet packet, DataOutput dataOut) throws IOException { + super.writePacket(packet, dataOut); + ${jclass.simpleName} info = (${jclass.simpleName}) packet; +""" + for (property in properties) { + def getter = "info." + property.getter.simpleName + "()" + out << " " + + def type = property.type.qualifiedName + switch (type) { + case "java.lang.String": + out << "writeUTF($getter, dataOut);" + break; + + case "org.activemq.message.ActiveMQDestination": + out << "writeDestination($getter, dataOut);" + break; + + case "boolean": + out << "dataOut.writeBoolean($getter);" + break; + + case "byte": + out << "dataOut.writeByte($getter);" + break; + + case "char": + out << "dataOut.writeChar($getter);" + break; + + case "short": + out << "dataOut.writeShort($getter);" + break; + + case "int": + out << "dataOut.writeInt($getter);" + break; + + case "long": + out << "dataOut.writeLong($getter);" + break; + + case "float": + out << "dataOut.writeFloat($getter);" + break; + + case "double": + out << "dataOut.writeDouble($getter);" + break; + + default: + out << "writeObject($getter, dataOut);" + } + out << """ +""" + } + + out << """ + } + } +} +""" + } + } + } +} \ No newline at end of file diff --git a/activemq-core/src/gram/script/GenerateJavaMarshalling.groovy b/activemq-core/src/gram/script/GenerateJavaMarshalling.groovy new file mode 100755 index 0000000000..3f5a92d308 --- /dev/null +++ b/activemq-core/src/gram/script/GenerateJavaMarshalling.groovy @@ -0,0 +1,497 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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. +* +**/ +import org.activemq.openwire.tool.OpenWireScript + +/** + * Generates the Java marshalling code for the Open Wire Format + * + * @version $Revision$ + */ +class GenerateJavaMarshalling extends OpenWireScript { + + Object run() { + + def openwireVersion = System.getProperty("openwire.version"); + + def destDir = new File("src/main/java/org/activemq/openwire/v${openwireVersion}") + println "Generating Java marshalling code to directory ${destDir}" + + def openwireClasses = classes.findAll { + it.getAnnotation("openwire:marshaller")!=null + } + + def concreteClasses = new ArrayList() + def buffer = new StringBuffer() + int counter = 0 + Map map = [:] + + destDir.mkdirs() + for (jclass in openwireClasses) { + + println "Processing ${jclass.simpleName}" + def isAbstract = isAbstract(jclass); + if( !isAbstract ) { + concreteClasses.add(jclass) + } + + def properties = jclass.declaredProperties.findAll { isValidProperty(it) } + + def file = new File(destDir, jclass.simpleName + "Marshaller.java") + + buffer << """ +${jclass.simpleName}Marshaller.class +""" + + file.withWriter { out | +out << """/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v${openwireVersion}; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; +""" +for (pkg in jclass.importedPackages) { + for (clazz in pkg.classes) { + out << "import "+clazz.qualifiedName+";" + } +} + +def baseClass = "org.activemq.openwire.DataStreamMarshaller" +if( !jclass.superclass.simpleName.equals("Object") ) { + baseClass = jclass.superclass.simpleName + "Marshaller"; +} + +def marshallerAware = isMarshallAware(jclass); + +out << """ + +/** + * Marshalling code for Open Wire Format for ${jclass.simpleName} + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version \$Revision\$ + */ +public """+ (isAbstract?"abstract ":"") + """class ${jclass.simpleName}Marshaller extends ${baseClass} { +""" +if( !isAbstract ) { +out << """ + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ${jclass.simpleName}.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ${jclass.simpleName}(); + } +""" +} +out << """ + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); +""" +if( !properties.isEmpty() ) { +out << """ + ${jclass.simpleName} info = (${jclass.simpleName})o; +""" +} +if( marshallerAware ) { +out << """ + info.beforeUnmarshall(wireFormat); + +""" +} +for (property in properties) { + def annotation = property.getter.getAnnotation("openwire:property"); + def size = annotation.getValue("size"); + def type = property.type.qualifiedName + def cached = isCachedProperty(property); + + out << " " + switch (type) { + case "boolean": + out << "info.${property.setter.simpleName}(bs.readBoolean());"; break; + case "byte": + out << "info.${property.setter.simpleName}(dataIn.readByte());"; break; + case "char": + out << "info.${property.setter.simpleName}(dataIn.readChar());"; break; + case "short": + out << "info.${property.setter.simpleName}(dataIn.readShort());"; break; + case "int": + out << "info.${property.setter.simpleName}(dataIn.readInt());"; break; + case "long": + out << "info.${property.setter.simpleName}(unmarshalLong(wireFormat, dataIn, bs));"; break; + case "byte[]": + if( size != null ) { + out << """{ + byte data[] = new byte[${size.asInt()}]; + dataIn.readFully(data); + info.${property.setter.simpleName}(data); + } + """; + } else { + out << """{ + if( bs.readBoolean() ) { + int size = dataIn.readInt(); + byte data[] = new byte[size]; + dataIn.readFully(data); + info.${property.setter.simpleName}(data); + } else { + info.${property.setter.simpleName}(null); + } + } + """; + } + break; + case "org.activeio.ByteSequence": + out << """ + if( bs.readBoolean() ) { + int size = dataIn.readInt(); + byte data[] = new byte[size]; + dataIn.readFully(data); + info.${property.setter.simpleName}(new org.activeio.ByteSequence(data,0,size)); + } else { + info.${property.setter.simpleName}(null); + } + """; + break; + case "java.lang.String": + out << "info.${property.setter.simpleName}(readString(dataIn, bs));"; break; + default: + if( property.type.arrayType ) { + def arrayType = property.type.arrayComponentType.qualifiedName; + if( size!=null ) { + out << """ + ${arrayType} value[] = new ${arrayType}[${size}]; + for( int i=0; i < ${size}; i++ ) { + value[i] = (${arrayType})unmarsalNestedObject(wireFormat,dataIn, bs); + } + info.${property.setter.simpleName}(value); + """ + } else { + out << """ + if( bs.readBoolean() ) { + short size = dataIn.readShort(); + ${arrayType} value[] = new ${arrayType}[size]; + for( int i=0; i < size; i++ ) { + value[i] = (${arrayType})unmarsalNestedObject(wireFormat,dataIn, bs); + } + info.${property.setter.simpleName}(value); + } else { + info.${property.setter.simpleName}(null); + } + """ + } + } else if( isThrowable(property.type) ) { + out << "info.${property.setter.simpleName}((${type}) unmarsalThrowable(wireFormat, dataIn, bs));" + } else { + if ( cached ) { + out << "info.${property.setter.simpleName}((${type}) unmarsalCachedObject(wireFormat, dataIn, bs));" + } else { + out << "info.${property.setter.simpleName}((${type}) unmarsalNestedObject(wireFormat, dataIn, bs));" + } + } + } + out << """ +""" + } +if( marshallerAware ) { +out << """ + info.afterUnmarshall(wireFormat); +""" +} + out << """ + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { +""" +if( !properties.isEmpty() ) { +out << """ + ${jclass.simpleName} info = (${jclass.simpleName})o; +""" +} +if( marshallerAware ) { +out << """ + info.beforeMarshall(wireFormat); +""" +} + def baseSize=0; +out << """ + int rc = super.marshal1(wireFormat, o, bs); +""" +for (property in properties) { + def annotation = property.getter.getAnnotation("openwire:property"); + def size = annotation.getValue("size"); + def getter = "info." + property.getter.simpleName + "()" + def cached = isCachedProperty(property); + + out << " " + + def type = property.type.qualifiedName + switch (type) { + case "boolean": + out << "bs.writeBoolean($getter);"; break; + case "byte": baseSize+=1; break; + case "char": baseSize+=2; break; + case "short": baseSize+=2; break; + case "int": baseSize+=4; break; + case "long": + out << "rc+=marshal1Long(wireFormat, $getter, bs);"; break; + case "byte[]": + if( size ==null ) { + out << """ + bs.writeBoolean($getter!=null); + rc += ${getter}==null ? 0 : ${getter}.length+4; + """; + } else { + baseSize += size.asInt(); + } + break; + case "org.activeio.ByteSequence": + out << """ + bs.writeBoolean($getter!=null); + rc += ${getter}==null ? 0 : ${getter}.getLength()+4; + """; + break; + case "java.lang.String": + out << "rc += writeString($getter, bs);"; break; + default: + if( property.type.arrayType ) { + if( size!=null ) { + out << "rc += marshalObjectArrayConstSize(wireFormat, $getter, bs, $size);"; break; + } else { + out << "rc += marshalObjectArray(wireFormat, $getter, bs);"; break; + } + } else if( isThrowable(property.type) ) { + out << "rc += marshalThrowable(wireFormat, $getter, bs);"; break; + } else { + if( cached ) { + out << "rc += marshal1CachedObject(wireFormat, $getter, bs);"; break; + } else { + out << "rc += marshal1NestedObject(wireFormat, $getter, bs);"; break; + } + } + } + out << """ +""" + } + out << """ + return rc+${baseSize}; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); +""" +if( !properties.isEmpty() ) { +out << """ + ${jclass.simpleName} info = (${jclass.simpleName})o; +""" +} +for (property in properties) { + def annotation = property.getter.getAnnotation("openwire:property"); + def size = annotation.getValue("size"); + def getter = "info." + property.getter.simpleName + "()" + def cached = isCachedProperty(property); + + out << " " + + def type = property.type.qualifiedName + switch (type) { + case "boolean": + out << "bs.readBoolean();"; break; + case "byte": + out << "dataOut.writeByte($getter);"; break; + case "char": + out << "dataOut.writeChar($getter);"; break; + case "short": + out << "dataOut.writeShort($getter);"; break; + case "int": + out << "dataOut.writeInt($getter);"; break; + case "long": + out << "marshal2Long(wireFormat, $getter, dataOut, bs);"; break; + case "byte[]": + if( size !=null ) { + out << "dataOut.write($getter, 0, ${size.asInt()});"; + } else { + out << """ + if(bs.readBoolean()) { + dataOut.writeInt(${getter}.length); + dataOut.write(${getter}); + } + """; + } + break; + case "org.activeio.ByteSequence": + out << """ + if(bs.readBoolean()) { + org.activeio.ByteSequence data = ${getter}; + dataOut.writeInt(data.getLength()); + dataOut.write(data.getData(), data.getOffset(), data.getLength()); + } + """; + break; + case "java.lang.String": + out << "writeString($getter, dataOut, bs);"; break; + default: + if( property.type.arrayType ) { + if( size!=null ) { + out << "marshalObjectArrayConstSize(wireFormat, $getter, dataOut, bs, $size);"; break; + } else { + out << "marshalObjectArray(wireFormat, $getter, dataOut, bs);"; break; + } + } else if( isThrowable(property.type) ) { + out << "marshalThrowable(wireFormat, $getter, dataOut, bs);"; break; + } else { + if( cached ) { + out << "marshal2CachedObject(wireFormat, $getter, dataOut, bs);"; break; + } else { + out << "marshal2NestedObject(wireFormat, $getter, dataOut, bs);"; break; + } + } + } + out << """ +""" + } +if( marshallerAware ) { +out << """ + info.afterMarshall(wireFormat); +""" +} + out << """ + } +} +""" + } + } + + def file = new File(destDir, "MarshallerFactory.java") + file.withWriter { out | +out << """/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v${openwireVersion}; + +import org.activemq.openwire.DataStreamMarshaller; +import org.activemq.openwire.OpenWireFormat; + +/** + * MarshallerFactory for Open Wire Format. + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version \$Revision\$ + */ +public class MarshallerFactory { + + /** + * Creates a Map of command type -> Marshallers + */ + static final private DataStreamMarshaller marshaller[] = new DataStreamMarshaller[256]; + static { +""" +for (jclass in concreteClasses) { +out << """ + add(new ${jclass.simpleName}Marshaller());""" +} +out << """ + + } + + static private void add(DataStreamMarshaller dsm) { + marshaller[dsm.getDataStructureType()] = dsm; + } + + static public DataStreamMarshaller[] createMarshallerMap(OpenWireFormat wireFormat) { + return marshaller; + } +} +""" + } + + } +} \ No newline at end of file diff --git a/activemq-core/src/main/grammar/SelectorParser.jj b/activemq-core/src/main/grammar/SelectorParser.jj new file mode 100755 index 0000000000..2cca7de1ca --- /dev/null +++ b/activemq-core/src/main/grammar/SelectorParser.jj @@ -0,0 +1,572 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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. +* +**/ + +// ---------------------------------------------------------------------------- +// OPTIONS +// ---------------------------------------------------------------------------- +options { + STATIC = false; + UNICODE_INPUT = true; + + // some performance optimizations + OPTIMIZE_TOKEN_MANAGER = true; + ERROR_REPORTING = false; +} + +// ---------------------------------------------------------------------------- +// PARSER +// ---------------------------------------------------------------------------- + +PARSER_BEGIN(SelectorParser) + +package org.activemq.selector; + +import java.io.*; +import java.util.*; + +import javax.jms.InvalidSelectorException; + +import org.activemq.filter.*; + +/** + * JMS Selector Parser generated by JavaCC + * + * Do not edit this .java file directly - it is autogenerated from SelectorParser.jj + */ +public class SelectorParser { + + public SelectorParser() { + this(new StringReader("")); + } + + public BooleanExpression parse(String sql) throws InvalidSelectorException { + this.ReInit(new StringReader(sql)); + + try { + return this.JmsSelector(); + } + catch (Throwable e) { + throw (InvalidSelectorException)new InvalidSelectorException(sql).initCause(e); + } + + } + + private BooleanExpression asBooleanExpression(Expression value) throws ParseException { + if (value instanceof BooleanExpression) { + return (BooleanExpression) value; + } + if (value instanceof PropertyExpression) { + return UnaryExpression.createBooleanCast( value ); + } + throw new ParseException("Expression will not result in a boolean value: " + value); + } + + +} + +PARSER_END(SelectorParser) + +// ---------------------------------------------------------------------------- +// Tokens +// ---------------------------------------------------------------------------- + +/* White Space */ +SPECIAL_TOKEN : +{ + " " | "\t" | "\n" | "\r" | "\f" +} + +/* Comments */ +SKIP: +{ + +} + +SKIP: +{ + +} + +/* Reserved Words */ +TOKEN [IGNORE_CASE] : +{ + < NOT : "NOT"> + | < AND : "AND"> + | < OR : "OR"> + | < BETWEEN : "BETWEEN"> + | < LIKE : "LIKE"> + | < ESCAPE : "ESCAPE"> + | < IN : "IN"> + | < IS : "IS"> + | < TRUE : "TRUE" > + | < FALSE : "FALSE" > + | < NULL : "NULL" > + | < XPATH : "XPATH" > + | < XQUERY : "XQUERY" > +} + +/* Literals */ +TOKEN [IGNORE_CASE] : +{ + + < DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* (["l","L"])? > + | < HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ > + | < OCTAL_LITERAL: "0" (["0"-"7"])* > + | < FLOATING_POINT_LITERAL: + (["0"-"9"])+ "." (["0"-"9"])* ()? // matches: 5.5 or 5. or 5.5E10 or 5.E10 + | "." (["0"-"9"])+ ()? // matches: .5 or .5E10 + | (["0"-"9"])+ // matches: 5E10 + > + | < #EXPONENT: "E" (["+","-"])? (["0"-"9"])+ > + | < STRING_LITERAL: "'" ( ("''") | ~["'"] )* "'" > +} + +TOKEN [IGNORE_CASE] : +{ + < ID : ["a"-"z", "_", "$"] (["a"-"z","0"-"9","_", "$"])* > +} + +// ---------------------------------------------------------------------------- +// Grammer +// ---------------------------------------------------------------------------- +BooleanExpression JmsSelector() : +{ + Expression left=null; +} +{ + ( + left = orExpression() + ) + { + return asBooleanExpression(left); + } + +} + +Expression orExpression() : +{ + Expression left; + Expression right; +} +{ + ( + left = andExpression() + ( + right = andExpression() + { + left = LogicExpression.createOR(asBooleanExpression(left), asBooleanExpression(right)); + } + )* + ) + { + return left; + } + +} + + +Expression andExpression() : +{ + Expression left; + Expression right; +} +{ + ( + left = equalityExpression() + ( + right = equalityExpression() + { + left = LogicExpression.createAND(asBooleanExpression(left), asBooleanExpression(right)); + } + )* + ) + { + return left; + } +} + +Expression equalityExpression() : +{ + Expression left; + Expression right; +} +{ + ( + left = comparisonExpression() + ( + + "=" right = comparisonExpression() + { + left = ComparisonExpression.createEqual(left, right); + } + | + "<>" right = comparisonExpression() + { + left = ComparisonExpression.createNotEqual(left, right); + } + | + LOOKAHEAD(2) + + { + left = ComparisonExpression.createIsNull(left); + } + | + + { + left = ComparisonExpression.createIsNotNull(left); + } + )* + ) + { + return left; + } +} + +Expression comparisonExpression() : +{ + Expression left; + Expression right; + Expression low; + Expression high; + String t, u; + boolean not; + ArrayList list; +} +{ + ( + left = addExpression() + ( + + ">" right = addExpression() + { + left = ComparisonExpression.createGreaterThan(left, right); + } + | + ">=" right = addExpression() + { + left = ComparisonExpression.createGreaterThanEqual(left, right); + } + | + "<" right = addExpression() + { + left = ComparisonExpression.createLessThan(left, right); + } + | + "<=" right = addExpression() + { + left = ComparisonExpression.createLessThanEqual(left, right); + } + | + { + u=null; + } + t = stringLitteral() + [ u = stringLitteral() ] + { + left = ComparisonExpression.createLike(left, t, u); + } + | + LOOKAHEAD(2) + { + u=null; + } + t = stringLitteral() [ u = stringLitteral() ] + { + left = ComparisonExpression.createNotLike(left, t, u); + } + | + low = addExpression() high = addExpression() + { + left = ComparisonExpression.createBetween(left, low, high); + } + | + LOOKAHEAD(2) + low = addExpression() high = addExpression() + { + left = ComparisonExpression.createNotBetween(left, low, high); + } + | + + "(" + t = stringLitteral() + { + list = new ArrayList(); + list.add( t ); + } + ( + "," + t = stringLitteral() + { + list.add( t ); + } + + )* + ")" + { + left = ComparisonExpression.createInFilter(left, list); + } + | + LOOKAHEAD(2) + + "(" + t = stringLitteral() + { + list = new ArrayList(); + list.add( t ); + } + ( + "," + t = stringLitteral() + { + list.add( t ); + } + + )* + ")" + { + left = ComparisonExpression.createNotInFilter(left, list); + } + + )* + ) + { + return left; + } +} + +Expression addExpression() : +{ + Expression left; + Expression right; +} +{ + left = multExpr() + ( + LOOKAHEAD( ("+"|"-") multExpr()) + ( + "+" right = multExpr() + { + left = ArithmeticExpression.createPlus(left, right); + } + | + "-" right = multExpr() + { + left = ArithmeticExpression.createMinus(left, right); + } + ) + + )* + { + return left; + } +} + +Expression multExpr() : +{ + Expression left; + Expression right; +} +{ + left = unaryExpr() + ( + "*" right = unaryExpr() + { + left = ArithmeticExpression.createMultiply(left, right); + } + | + "/" right = unaryExpr() + { + left = ArithmeticExpression.createDivide(left, right); + } + | + "%" right = unaryExpr() + { + left = ArithmeticExpression.createMod(left, right); + } + + )* + { + return left; + } +} + + +Expression unaryExpr() : +{ + String s=null; + Expression left=null; +} +{ + ( + LOOKAHEAD( "+" unaryExpr() ) + "+" left=unaryExpr() + | + "-" left=unaryExpr() + { + left = UnaryExpression.createNegate(left); + } + | + left=unaryExpr() + { + left = UnaryExpression.createNOT( asBooleanExpression(left) ); + } + | + s=stringLitteral() + { + left = UnaryExpression.createXPath( s ); + } + | + s=stringLitteral() + { + left = UnaryExpression.createXQuery( s ); + } + | + left = primaryExpr() + ) + { + return left; + } + +} + +Expression primaryExpr() : +{ + Expression left=null; +} +{ + ( + left = literal() + | + left = variable() + | + "(" left = orExpression() ")" + ) + { + return left; + } +} + + + +ConstantExpression literal() : +{ + Token t; + String s; + ConstantExpression left=null; +} +{ + ( + ( + s = stringLitteral() + { + left = new ConstantExpression(s); + } + ) + | + ( + t = + { + left = ConstantExpression.createFromDecimal(t.image); + } + ) + | + ( + t = + { + left = ConstantExpression.createFromHex(t.image); + } + ) + | + ( + t = + { + left = ConstantExpression.createFromOctal(t.image); + } + ) + | + ( + t = + { + left = ConstantExpression.createFloat(t.image); + } + ) + | + ( + + { + left = ConstantExpression.TRUE; + } + ) + | + ( + + { + left = ConstantExpression.FALSE; + } + ) + | + ( + + { + left = ConstantExpression.NULL; + } + ) + ) + { + return left; + } +} + +String stringLitteral() : +{ + Token t; + StringBuffer rc = new StringBuffer(); + boolean first=true; +} +{ + t = + { + // Decode the sting value. + String image = t.image; + for( int i=1; i < image.length()-1; i++ ) { + char c = image.charAt(i); + if( c == '\'' ) + i++; + rc.append(c); + } + return rc.toString(); + } +} + +PropertyExpression variable() : +{ + Token t; + PropertyExpression left=null; +} +{ + ( + t = + { + left = new PropertyExpression(t.image); + } + ) + { + return left; + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQConnection.java b/activemq-core/src/main/java/org/activemq/ActiveMQConnection.java new file mode 100755 index 0000000000..494f3797d3 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQConnection.java @@ -0,0 +1,1545 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.jms.Connection; +import javax.jms.ConnectionConsumer; +import javax.jms.ConnectionMetaData; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.IllegalStateException; +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueSession; +import javax.jms.ServerSessionPool; +import javax.jms.Session; +import javax.jms.Topic; +import javax.jms.TopicConnection; +import javax.jms.TopicSession; +import javax.jms.XAConnection; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ActiveMQTempDestination; +import org.activemq.command.ActiveMQTempQueue; +import org.activemq.command.ActiveMQTempTopic; +import org.activemq.command.BrokerInfo; +import org.activemq.command.Command; +import org.activemq.command.ConnectionId; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerId; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.ControlCommand; +import org.activemq.command.DestinationInfo; +import org.activemq.command.ExceptionResponse; +import org.activemq.command.Message; +import org.activemq.command.MessageDispatch; +import org.activemq.command.MessageId; +import org.activemq.command.ProducerId; +import org.activemq.command.RedeliveryPolicy; +import org.activemq.command.RemoveSubscriptionInfo; +import org.activemq.command.Response; +import org.activemq.command.SessionId; +import org.activemq.command.ShutdownInfo; +import org.activemq.management.JMSConnectionStatsImpl; +import org.activemq.management.JMSStatsImpl; +import org.activemq.management.StatsCapable; +import org.activemq.management.StatsImpl; +import org.activemq.thread.TaskRunnerFactory; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportListener; +import org.activemq.util.IdGenerator; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.JMSExceptionSupport; +import org.activemq.util.LongSequenceGenerator; +import org.activemq.util.ServiceSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +public class ActiveMQConnection implements Connection, TopicConnection, QueueConnection, StatsCapable, Closeable, TransportListener, StreamConnection { + + public static final TaskRunnerFactory SESSION_TASK_RUNNER = new TaskRunnerFactory("session Task",ThreadPriorities.INBOUND_CLIENT_SESSION,true,1000); + + private static final Log log = LogFactory.getLog(ActiveMQConnection.class); + private static final IdGenerator connectionIdGenerator = new IdGenerator(); + private static final IdGenerator clientIdGenerator = new IdGenerator(); + + public static final String DEFAULT_USER = ActiveMQConnectionFactory.DEFAULT_USER; + public static final String DEFAULT_PASSWORD = ActiveMQConnectionFactory.DEFAULT_PASSWORD; + public static final String DEFAULT_BROKER_URL = ActiveMQConnectionFactory.DEFAULT_BROKER_URL; + + // Connection state variables + private final ConnectionInfo info; + private ExceptionListener exceptionListener; + private String resourceManagerId; + private boolean clientIDSet; + private boolean isConnectionInfoSentToBroker; + private boolean userSpecifiedClientID; + + // Configuration options variables + private ActiveMQPrefetchPolicy prefetchPolicy = new ActiveMQPrefetchPolicy(); + private RedeliveryPolicy redeliveryPolicy; + private boolean disableTimeStampsByDefault = false; + private boolean onSendPrepareMessageBody = true; + private boolean optimizedMessageDispatch = true; + private boolean copyMessageOnSend = true; + private boolean useCompression = false; + private boolean objectMessageSerializationDefered = false; + protected boolean asyncDispatch = true; + private boolean useAsyncSend = false; + private boolean useRetroactiveConsumer; + + private long flowControlSleepTime = 0; + private final JMSConnectionStatsImpl stats; + private final JMSStatsImpl factoryStats; + private final Transport transport; + private final AtomicBoolean started = new AtomicBoolean(false); + private final AtomicBoolean closing = new AtomicBoolean(false); + private final AtomicBoolean closed = new AtomicBoolean(false); + private final CopyOnWriteArrayList sessions = new CopyOnWriteArrayList(); + private final CopyOnWriteArrayList connectionConsumers = new CopyOnWriteArrayList(); + private final CopyOnWriteArrayList inputStreams = new CopyOnWriteArrayList(); + private final CopyOnWriteArrayList outputStreams = new CopyOnWriteArrayList(); + + // Maps ConsumerIds to ActiveMQConsumer objects + private final ConcurrentHashMap dispatchers = new ConcurrentHashMap(); + private final LongSequenceGenerator sessionIdGenerator = new LongSequenceGenerator(); + private final SessionId connectionSessionId; + private final LongSequenceGenerator consumerIdGenerator = new LongSequenceGenerator(); + private final LongSequenceGenerator producerIdGenerator = new LongSequenceGenerator(); + private final LongSequenceGenerator tempDestinationIdGenerator = new LongSequenceGenerator(); + private final LongSequenceGenerator localTransactionIdGenerator = new LongSequenceGenerator(); + final ConcurrentHashMap activeTempDestinations = new ConcurrentHashMap(); + + private AdvisoryConsumer advisoryConsumer; + private final CountDownLatch brokerInfoReceived = new CountDownLatch(1); + + + + /** + * + * @param factoryStats + * @param userName + * @param password + * @throws Exception + */ + protected ActiveMQConnection(Transport transport, String userName, String password, JMSStatsImpl factoryStats) + throws Exception { + this.transport = transport; + this.info = new ConnectionInfo(new ConnectionId(connectionIdGenerator.generateId())); + this.info.setUserName(userName); + this.info.setPassword(password); + + this.connectionSessionId = new SessionId(info.getConnectionId(), -1); + + this.factoryStats = factoryStats; + this.factoryStats.addConnection(this); + this.stats = new JMSConnectionStatsImpl(sessions, this instanceof XAConnection); + this.transport.setTransportListener(this); + + transport.start(); + } + + /** + * A static helper method to create a new connection + * + * @return an ActiveMQConnection + * @throws JMSException + */ + public static ActiveMQConnection makeConnection() throws JMSException { + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(); + return (ActiveMQConnection) factory.createConnection(); + } + + /** + * A static helper method to create a new connection + * + * @param uri + * @return and ActiveMQConnection + * @throws JMSException + */ + public static ActiveMQConnection makeConnection(String uri) throws JMSException, URISyntaxException { + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(uri); + return (ActiveMQConnection) factory.createConnection(); + } + + /** + * A static helper method to create a new connection + * + * @param user + * @param password + * @param uri + * @return an ActiveMQConnection + * @throws JMSException + */ + public static ActiveMQConnection makeConnection(String user, String password, String uri) + throws JMSException, URISyntaxException { + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(user, password, new URI(uri)); + return (ActiveMQConnection) factory.createConnection(); + } + + /** + * @return a number unique for this connection + */ + public JMSConnectionStatsImpl getConnectionStats() { + return stats; + } + + /** + * Creates a Session object. + * + * @param transacted + * indicates whether the session is transacted + * @param acknowledgeMode + * indicates whether the consumer or the client will acknowledge + * any messages it receives; ignored if the session is + * transacted. Legal values are + * Session.AUTO_ACKNOWLEDGE, + * Session.CLIENT_ACKNOWLEDGE, and + * Session.DUPS_OK_ACKNOWLEDGE. + * @return a newly created session + * @throws JMSException + * if the Connection object fails to create a + * session due to some internal error or lack of support for the + * specific transaction and acknowledgement mode. + * @see Session#AUTO_ACKNOWLEDGE + * @see Session#CLIENT_ACKNOWLEDGE + * @see Session#DUPS_OK_ACKNOWLEDGE + * @since 1.1 + */ + public Session createSession(boolean transacted, int acknowledgeMode) throws JMSException { + checkClosed(); + ensureConnectionInfoSent(); + return new ActiveMQSession(this, getNextSessionId(), (transacted ? Session.SESSION_TRANSACTED + : (acknowledgeMode == Session.SESSION_TRANSACTED ? Session.AUTO_ACKNOWLEDGE : acknowledgeMode)), asyncDispatch); + } + + /** + * @return + */ + protected SessionId getNextSessionId() { + return new SessionId(info.getConnectionId(), sessionIdGenerator.getNextSequenceId()); + } + + /** + * Gets the client identifier for this connection. + *

+ * This value is specific to the JMS provider. It is either preconfigured by + * an administrator in a ConnectionFactory object or assigned + * dynamically by the application by calling the setClientID + * method. + * + * @return the unique client identifier + * @throws JMSException + * if the JMS provider fails to return the client ID for this + * connection due to some internal error. + */ + public String getClientID() throws JMSException { + checkClosed(); + return this.info.getClientId(); + } + + /** + * Sets the client identifier for this connection. + *

+ * The preferred way to assign a JMS client's client identifier is for it to + * be configured in a client-specific ConnectionFactory + * object and transparently assigned to the Connection object + * it creates. + *

+ * Alternatively, a client can set a connection's client identifier using a + * provider-specific value. The facility to set a connection's client + * identifier explicitly is not a mechanism for overriding the identifier + * that has been administratively configured. It is provided for the case + * where no administratively specified identifier exists. If one does exist, + * an attempt to change it by setting it must throw an IllegalStateException. + * If a client sets the client identifier explicitly, it must do so + * immediately after it creates the connection and before any other action + * on the connection is taken. After this point, setting the client + * identifier is a programming error that should throw an IllegalStateException. + *

+ * The purpose of the client identifier is to associate a connection and its + * objects with a state maintained on behalf of the client by a provider. + * The only such state identified by the JMS API is that required to support + * durable subscriptions. + *

+ * If another connection with the same clientID is already + * running when this method is called, the JMS provider should detect the + * duplicate ID and throw an InvalidClientIDException. + * + * @param newClientID + * the unique client identifier + * @throws JMSException + * if the JMS provider fails to set the client ID for this + * connection due to some internal error. + * @throws javax.jms.InvalidClientIDException + * if the JMS client specifies an invalid or duplicate client + * ID. + * @throws javax.jms.IllegalStateException + * if the JMS client attempts to set a connection's client ID at + * the wrong time or when it has been administratively + * configured. + */ + public void setClientID(String newClientID) throws JMSException { + checkClosed(); + + if (this.clientIDSet) { + throw new IllegalStateException("The clientID has already been set"); + } + + if (this.isConnectionInfoSentToBroker) { + throw new IllegalStateException("Setting clientID on a used Connection is not allowed"); + } + + this.info.setClientId(newClientID); + this.userSpecifiedClientID = true; + ensureConnectionInfoSent(); + } + + /** + * Gets the metadata for this connection. + * + * @return the connection metadata + * @throws JMSException + * if the JMS provider fails to get the connection metadata for + * this connection. + * @see javax.jms.ConnectionMetaData + */ + public ConnectionMetaData getMetaData() throws JMSException { + checkClosed(); + return ActiveMQConnectionMetaData.INSTANCE; + } + + /** + * Gets the ExceptionListener object for this connection. Not + * every Connection has an ExceptionListener + * associated with it. + * + * @return the ExceptionListener for this connection, or + * null. if no ExceptionListener is associated with + * this connection. + * @throws JMSException + * if the JMS provider fails to get the ExceptionListener + * for this connection. + * @see javax.jms.Connection#setExceptionListener(ExceptionListener) + */ + public ExceptionListener getExceptionListener() throws JMSException { + checkClosed(); + return this.exceptionListener; + } + + /** + * Sets an exception listener for this connection. + *

+ * If a JMS provider detects a serious problem with a connection, it informs + * the connection's ExceptionListener, if one has been + * registered. It does this by calling the listener's onException + * method, passing it a JMSException object + * describing the problem. + *

+ * An exception listener allows a client to be notified of a problem + * asynchronously. Some connections only consume messages, so they would + * have no other way to learn their connection has failed. + *

+ * A connection serializes execution of its ExceptionListener. + *

+ * A JMS provider should attempt to resolve connection problems itself + * before it notifies the client of them. + * + * @param listener + * the exception listener + * @throws JMSException + * if the JMS provider fails to set the exception listener for + * this connection. + */ + public void setExceptionListener(ExceptionListener listener) throws JMSException { + checkClosed(); + this.exceptionListener = listener; + } + + /** + * Starts (or restarts) a connection's delivery of incoming messages. A call + * to start on a connection that has already been started is + * ignored. + * + * @throws JMSException + * if the JMS provider fails to start message delivery due to + * some internal error. + * @see javax.jms.Connection#stop() + */ + public void start() throws JMSException { + checkClosed(); + ensureConnectionInfoSent(); + if (started.compareAndSet(false, true)) { + for (Iterator i = sessions.iterator(); i.hasNext();) { + ActiveMQSession session = (ActiveMQSession) i.next(); + session.start(); + } + } + } + + /** + * @return true if this Connection is started + */ + protected boolean isStarted() { + return started.get(); + } + + /** + * Temporarily stops a connection's delivery of incoming messages. Delivery + * can be restarted using the connection's start method. When + * the connection is stopped, delivery to all the connection's message + * consumers is inhibited: synchronous receives block, and messages are not + * delivered to message listeners. + *

+ * This call blocks until receives and/or message listeners in progress have + * completed. + *

+ * Stopping a connection has no effect on its ability to send messages. A + * call to stop on a connection that has already been stopped + * is ignored. + *

+ * A call to stop must not return until delivery of messages + * has paused. This means that a client can rely on the fact that none of + * its message listeners will be called and that all threads of control + * waiting for receive calls to return will not return with a + * message until the connection is restarted. The receive timers for a + * stopped connection continue to advance, so receives may time out while + * the connection is stopped. + *

+ * If message listeners are running when stop is invoked, the + * stop call must wait until all of them have returned before + * it may return. While these message listeners are completing, they must + * have the full services of the connection available to them. + * + * @throws JMSException + * if the JMS provider fails to stop message delivery due to + * some internal error. + * @see javax.jms.Connection#start() + */ + public void stop() throws JMSException { + checkClosed(); + if (started.compareAndSet(true, false)) { + for (Iterator i = sessions.iterator(); i.hasNext();) { + ActiveMQSession s = (ActiveMQSession) i.next(); + s.stop(); + } + } + } + + /** + * Closes the connection. + *

+ * Since a provider typically allocates significant resources outside the + * JVM on behalf of a connection, clients should close these resources when + * they are not needed. Relying on garbage collection to eventually reclaim + * these resources may not be timely enough. + *

+ * There is no need to close the sessions, producers, and consumers of a + * closed connection. + *

+ * Closing a connection causes all temporary destinations to be deleted. + *

+ * When this method is invoked, it should not return until message + * processing has been shut down in an orderly fashion. This means that all + * message listeners that may have been running have returned, and that all + * pending receives have returned. A close terminates all pending message + * receives on the connection's sessions' consumers. The receives may return + * with a message or with null, depending on whether there was a message + * available at the time of the close. If one or more of the connection's + * sessions' message listeners is processing a message at the time when + * connection close is invoked, all the facilities of the + * connection and its sessions must remain available to those listeners + * until they return control to the JMS provider. + *

+ * Closing a connection causes any of its sessions' transactions in progress + * to be rolled back. In the case where a session's work is coordinated by + * an external transaction manager, a session's commit and + * rollback methods are not used and the result of a closed + * session's work is determined later by the transaction manager. Closing a + * connection does NOT force an acknowledgment of client-acknowledged + * sessions. + *

+ * Invoking the acknowledge method of a received message from + * a closed connection's session must throw an IllegalStateException. + * Closing a closed connection must NOT throw an exception. + * + * @throws JMSException + * if the JMS provider fails to close the connection due to some + * internal error. For example, a failure to release resources + * or to close a socket connection can cause this exception to + * be thrown. + */ + public void close() throws JMSException { + checkClosed(); + + // If we were running, lets stop first. + stop(); + + synchronized (this) { + if (!closed.get()) { + closing.set(true); + + if( advisoryConsumer!=null ) { + advisoryConsumer.dispose(); + advisoryConsumer=null; + } + + for (Iterator i = this.sessions.iterator(); i.hasNext();) { + ActiveMQSession s = (ActiveMQSession) i.next(); + s.dispose(); + } + for (Iterator i = this.connectionConsumers.iterator(); i.hasNext();) { + ActiveMQConnectionConsumer c = (ActiveMQConnectionConsumer) i.next(); + c.dispose(); + } + for (Iterator i = this.inputStreams.iterator(); i.hasNext();) { + ActiveMQInputStream c = (ActiveMQInputStream) i.next(); + c.dispose(); + } + for (Iterator i = this.outputStreams.iterator(); i.hasNext();) { + ActiveMQOutputStream c = (ActiveMQOutputStream) i.next(); + c.dispose(); + } + + + if (isConnectionInfoSentToBroker) { + syncSendPacket(info.createRemoveCommand()); + } + + asyncSendPacket(new ShutdownInfo()); + ServiceSupport.dispose(this.transport); + + started.set(false); + + // TODO : ActiveMQConnectionFactory.onConnectionClose() not + // yet implemented. + // factory.onConnectionClose(this); + + closed.set(true); + closing.set(false); + } + } + } + + /** + * Tells the broker to terminate its VM. This can be used to cleanly + * terminate a broker running in a standalone java process. Server must have + * property enable.vm.shutdown=true defined to allow this to work. + */ + // TODO : org.activemq.message.BrokerAdminCommand not yet implemented. + /* + * public void terminateBrokerVM() throws JMSException { BrokerAdminCommand + * command = new BrokerAdminCommand(); + * command.setCommand(BrokerAdminCommand.SHUTDOWN_SERVER_VM); + * asyncSendPacket(command); } + */ + + + /** + * Create a durable connection consumer for this connection (optional + * operation). This is an expert facility not used by regular JMS clients. + * + * @param topic + * topic to access + * @param subscriptionName + * durable subscription name + * @param messageSelector + * only messages with properties matching the message selector + * expression are delivered. A value of null or an empty string + * indicates that there is no message selector for the message + * consumer. + * @param sessionPool + * the server session pool to associate with this durable + * connection consumer + * @param maxMessages + * the maximum number of messages that can be assigned to a + * server session at one time + * @return the durable connection consumer + * @throws JMSException + * if the Connection object fails to create a + * connection consumer due to some internal error or invalid + * arguments for sessionPool and messageSelector. + * @throws javax.jms.InvalidDestinationException + * if an invalid destination is specified. + * @throws javax.jms.InvalidSelectorException + * if the message selector is invalid. + * @see javax.jms.ConnectionConsumer + * @since 1.1 + */ + public ConnectionConsumer createDurableConnectionConsumer(Topic topic, String subscriptionName, + String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException { + return this.createDurableConnectionConsumer(topic, subscriptionName, messageSelector, sessionPool, maxMessages, false); + } + + /** + * Create a durable connection consumer for this connection (optional + * operation). This is an expert facility not used by regular JMS clients. + * + * @param topic + * topic to access + * @param subscriptionName + * durable subscription name + * @param messageSelector + * only messages with properties matching the message selector + * expression are delivered. A value of null or an empty string + * indicates that there is no message selector for the message + * consumer. + * @param sessionPool + * the server session pool to associate with this durable + * connection consumer + * @param maxMessages + * the maximum number of messages that can be assigned to a + * server session at one time + * @param noLocal + * set true if you want to filter out messages published locally + * + * @return the durable connection consumer + * @throws JMSException + * if the Connection object fails to create a + * connection consumer due to some internal error or invalid + * arguments for sessionPool and messageSelector. + * @throws javax.jms.InvalidDestinationException + * if an invalid destination is specified. + * @throws javax.jms.InvalidSelectorException + * if the message selector is invalid. + * @see javax.jms.ConnectionConsumer + * @since 1.1 + */ + public ConnectionConsumer createDurableConnectionConsumer(Topic topic, String subscriptionName, + String messageSelector, ServerSessionPool sessionPool, int maxMessages, boolean noLocal) + throws JMSException { + checkClosed(); + ensureConnectionInfoSent(); + SessionId sessionId = new SessionId(info.getConnectionId(), -1); + ConsumerInfo info = new ConsumerInfo(new ConsumerId(sessionId, consumerIdGenerator + .getNextSequenceId())); + info.setDestination(ActiveMQMessageTransformation.transformDestination(topic)); + info.setSubcriptionName(subscriptionName); + info.setSelector(messageSelector); + info.setPrefetchSize(maxMessages); + + // Allows the options on the destination to configure the consumerInfo + if( info.getDestination().getOptions()!=null ) { + HashMap options = new HashMap(info.getDestination().getOptions()); + IntrospectionSupport.setProperties(this.info, options, "consumer."); + } + + return new ActiveMQConnectionConsumer(this, sessionPool, info); + } + + + // Properties + // ------------------------------------------------------------------------- + + /** + * @return Returns the prefetchPolicy. + */ + public ActiveMQPrefetchPolicy getPrefetchPolicy() { + return prefetchPolicy; + } + + /** + * @param prefetchPolicy + * The prefetchPolicy to set. + */ + public void setPrefetchPolicy(ActiveMQPrefetchPolicy prefetchPolicy) { + this.prefetchPolicy = prefetchPolicy; + } + + /** + */ + public Transport getTransportChannel() { + return transport; + } + + /** + * + * @return Returns the clientID of the connection, forcing one to be + * generated if one has not yet been configured. + */ + public String getInitializedClientID() throws JMSException { + ensureConnectionInfoSent(); + return info.getClientId(); + } + + /** + * + * @return Returns the timeStampsDisableByDefault. + */ + public boolean isDisableTimeStampsByDefault() { + return disableTimeStampsByDefault; + } + + /** + * + * @param timeStampsDisableByDefault + * The timeStampsDisableByDefault to set. + */ + public void setDisableTimeStampsByDefault(boolean timeStampsDisableByDefault) { + this.disableTimeStampsByDefault = timeStampsDisableByDefault; + } + + /** + * + * @return Returns the dispatchOptimizedMessage. + */ + public boolean isOptimizedMessageDispatch() { + return optimizedMessageDispatch; + } + + /** + * + * @param dispatchOptimizedMessage + * The dispatchOptimizedMessage to set. + */ + public void setOptimizedMessageDispatch(boolean dispatchOptimizedMessage) { + this.optimizedMessageDispatch = dispatchOptimizedMessage; + } + + /** + * + * @return Returns the onSendPrepareMessageBody. + */ + public boolean isOnSendPrepareMessageBody() { + return onSendPrepareMessageBody; + } + + /** + * + * @param onSendPrepareMessageBody + * The onSendPrepareMessageBody to set. + */ + public void setOnSendPrepareMessageBody(boolean onSendPrepareMessageBody) { + this.onSendPrepareMessageBody = onSendPrepareMessageBody; + } + + /** + * + * @return ConnectionInfo + */ + public ConnectionInfo getConnectionInfo() { + return this.info; + } + + public boolean isUseRetroactiveConsumer() { + return useRetroactiveConsumer; + } + + /** + * Sets whether or not retroactive consumers are enabled. Retroactive consumers allow + * non-durable topic subscribers to receive old messages that were published before the + * non-durable subscriber started. + */ + public void setUseRetroactiveConsumer(boolean useRetroactiveConsumer) { + this.useRetroactiveConsumer = useRetroactiveConsumer; + } + + + // Implementation methods + // ------------------------------------------------------------------------- + + /** + * Used internally for adding Sessions to the Connection + * + * @param session + * @throws JMSException + * @throws JMSException + */ + protected void addSession(ActiveMQSession session) throws JMSException { + this.sessions.add(session); + if (sessions.size()>1 || session.isTransacted()){ + optimizedMessageDispatch = false; + } + } + + /** + * Used interanlly for removing Sessions from a Connection + * + * @param session + */ + protected void removeSession(ActiveMQSession session) { + this.sessions.remove(session); + } + + /** + * Add a ConnectionConsumer + * + * @param connectionConsumer + * @throws JMSException + */ + protected void addConnectionConsumer(ActiveMQConnectionConsumer connectionConsumer) throws JMSException { + this.connectionConsumers.add(connectionConsumer); + } + + /** + * Remove a ConnectionConsumer + * + * @param connectionConsumer + */ + protected void removeConnectionConsumer(ActiveMQConnectionConsumer connectionConsumer) { + this.connectionConsumers.remove(connectionConsumer); + } + + /** + * Creates a TopicSession object. + * + * @param transacted + * indicates whether the session is transacted + * @param acknowledgeMode + * indicates whether the consumer or the client will acknowledge + * any messages it receives; ignored if the session is + * transacted. Legal values are + * Session.AUTO_ACKNOWLEDGE, + * Session.CLIENT_ACKNOWLEDGE, and + * Session.DUPS_OK_ACKNOWLEDGE. + * @return a newly created topic session + * @throws JMSException + * if the TopicConnection object fails to create + * a session due to some internal error or lack of support for + * the specific transaction and acknowledgement mode. + * @see Session#AUTO_ACKNOWLEDGE + * @see Session#CLIENT_ACKNOWLEDGE + * @see Session#DUPS_OK_ACKNOWLEDGE + */ + public TopicSession createTopicSession(boolean transacted, int acknowledgeMode) throws JMSException { + return new ActiveMQTopicSession((ActiveMQSession) createSession(transacted, acknowledgeMode)); + } + + /** + * Creates a connection consumer for this connection (optional operation). + * This is an expert facility not used by regular JMS clients. + * + * @param topic + * the topic to access + * @param messageSelector + * only messages with properties matching the message selector + * expression are delivered. A value of null or an empty string + * indicates that there is no message selector for the message + * consumer. + * @param sessionPool + * the server session pool to associate with this connection + * consumer + * @param maxMessages + * the maximum number of messages that can be assigned to a + * server session at one time + * @return the connection consumer + * @throws JMSException + * if the TopicConnection object fails to create + * a connection consumer due to some internal error or invalid + * arguments for sessionPool and messageSelector. + * @throws javax.jms.InvalidDestinationException + * if an invalid topic is specified. + * @throws javax.jms.InvalidSelectorException + * if the message selector is invalid. + * @see javax.jms.ConnectionConsumer + */ + public ConnectionConsumer createConnectionConsumer(Topic topic, String messageSelector, ServerSessionPool sessionPool, int maxMessages) throws JMSException { + return createConnectionConsumer(topic, messageSelector, sessionPool, maxMessages, false); + } + + /** + * Creates a connection consumer for this connection (optional operation). + * This is an expert facility not used by regular JMS clients. + * + * @param queue + * the queue to access + * @param messageSelector + * only messages with properties matching the message selector + * expression are delivered. A value of null or an empty string + * indicates that there is no message selector for the message + * consumer. + * @param sessionPool + * the server session pool to associate with this connection + * consumer + * @param maxMessages + * the maximum number of messages that can be assigned to a + * server session at one time + * @return the connection consumer + * @throws JMSException + * if the QueueConnection object fails to create + * a connection consumer due to some internal error or invalid + * arguments for sessionPool and messageSelector. + * @throws javax.jms.InvalidDestinationException + * if an invalid queue is specified. + * @throws javax.jms.InvalidSelectorException + * if the message selector is invalid. + * @see javax.jms.ConnectionConsumer + */ + public ConnectionConsumer createConnectionConsumer(Queue queue, String messageSelector, + ServerSessionPool sessionPool, int maxMessages) throws JMSException { + return createConnectionConsumer(queue, messageSelector, sessionPool, maxMessages, false); + } + + /** + * Creates a connection consumer for this connection (optional operation). + * This is an expert facility not used by regular JMS clients. + * + * @param destination + * the destination to access + * @param messageSelector + * only messages with properties matching the message selector + * expression are delivered. A value of null or an empty string + * indicates that there is no message selector for the message + * consumer. + * @param sessionPool + * the server session pool to associate with this connection + * consumer + * @param maxMessages + * the maximum number of messages that can be assigned to a + * server session at one time + * @return the connection consumer + * @throws JMSException + * if the Connection object fails to create a + * connection consumer due to some internal error or invalid + * arguments for sessionPool and messageSelector. + * @throws javax.jms.InvalidDestinationException + * if an invalid destination is specified. + * @throws javax.jms.InvalidSelectorException + * if the message selector is invalid. + * @see javax.jms.ConnectionConsumer + * @since 1.1 + */ + public ConnectionConsumer createConnectionConsumer(Destination destination, String messageSelector, + ServerSessionPool sessionPool, int maxMessages) throws JMSException { + return createConnectionConsumer(destination, messageSelector, sessionPool, maxMessages, false); + } + + public ConnectionConsumer createConnectionConsumer(Destination destination, String messageSelector, ServerSessionPool sessionPool, int maxMessages, boolean noLocal) throws JMSException { + + checkClosed(); + ensureConnectionInfoSent(); + ConsumerId consumerId = createConsumerId(); + ConsumerInfo info = new ConsumerInfo(consumerId); + info.setDestination(ActiveMQMessageTransformation.transformDestination(destination)); + info.setSelector(messageSelector); + info.setPrefetchSize(maxMessages); + info.setNoLocal(noLocal); + + // Allows the options on the destination to configure the consumerInfo + if( info.getDestination().getOptions()!=null ) { + HashMap options = new HashMap(info.getDestination().getOptions()); + IntrospectionSupport.setProperties(this.info, options, "consumer."); + } + + return new ActiveMQConnectionConsumer(this, sessionPool, info); + } + + /** + * @return + */ + private ConsumerId createConsumerId() { + return new ConsumerId(connectionSessionId, consumerIdGenerator.getNextSequenceId()); + } + + /** + * @return + */ + private ProducerId createProducerId() { + return new ProducerId(connectionSessionId, producerIdGenerator.getNextSequenceId()); + } + + + /** + * Creates a QueueSession object. + * + * @param transacted + * indicates whether the session is transacted + * @param acknowledgeMode + * indicates whether the consumer or the client will acknowledge + * any messages it receives; ignored if the session is + * transacted. Legal values are + * Session.AUTO_ACKNOWLEDGE, + * Session.CLIENT_ACKNOWLEDGE, and + * Session.DUPS_OK_ACKNOWLEDGE. + * @return a newly created queue session + * @throws JMSException + * if the QueueConnection object fails to create + * a session due to some internal error or lack of support for + * the specific transaction and acknowledgement mode. + * @see Session#AUTO_ACKNOWLEDGE + * @see Session#CLIENT_ACKNOWLEDGE + * @see Session#DUPS_OK_ACKNOWLEDGE + */ + public QueueSession createQueueSession(boolean transacted, int acknowledgeMode) throws JMSException { + return new ActiveMQQueueSession((ActiveMQSession) createSession(transacted, acknowledgeMode)); + } + + /** + * Ensures that the clientID was manually specified and not auto-generated. + * If the clientID was not specified this method will throw an exception. + * This method is used to ensure that the clientID + durableSubscriber name + * are used correctly. + * + * @throws JMSException + */ + public void checkClientIDWasManuallySpecified() throws JMSException { + if (!userSpecifiedClientID) { + throw new JMSException( + "You cannot create a durable subscriber without specifying a unique clientID on a Connection"); + } + } + + /** + * send a Packet through the Connection - for internal use only + * + * @param command + * @throws JMSException + */ + public void asyncSendPacket(Command command) throws JMSException { + if (isClosed()) { + throw new ConnectionClosedException(); + } else { + + if (command.isMessage() && flowControlSleepTime > 0) { + try { + Thread.sleep(flowControlSleepTime); + } catch (InterruptedException e) { + } + } + + try { + this.transport.oneway(command); + } catch (IOException e) { + throw JMSExceptionSupport.create(e); + } + } + } + + /** + * Send a packet through a Connection - for internal use only + * + * @param command + * @return + * @throws JMSException + */ + public Response syncSendPacket(Command command) throws JMSException { + if (isClosed()) { + throw new ConnectionClosedException(); + } else { + + if (command.isMessage() && flowControlSleepTime > 0) { + try { + Thread.sleep(flowControlSleepTime); + } catch (InterruptedException e) { + } + } + + try { + Response response = this.transport.request(command); + if (response.isException()) { + ExceptionResponse er = (ExceptionResponse) response; + if (er.getException() instanceof JMSException) + throw (JMSException) er.getException(); + else + throw JMSExceptionSupport.create(er.getException()); + } + return response; + } catch (IOException e) { + throw JMSExceptionSupport.create(e); + } + } + } + + public boolean isClosed() { + return closed.get(); + } + + /** + * @return statistics for this Connection + */ + public StatsImpl getStats() { + return stats; + } + + /** + * simply throws an exception if the Connection is already closed + * + * @throws JMSException + */ + protected synchronized void checkClosed() throws JMSException { + if (closed.get()) { + throw new ConnectionClosedException(); + } + } + + /** + * Send the ConnectionInfo to the Broker + * + * @throws JMSException + */ + protected void ensureConnectionInfoSent() throws JMSException { + // Can we skip sending the ConnectionInfo packet?? + if (isConnectionInfoSentToBroker) { + return; + } + + if (info.getClientId() == null || info.getClientId().trim().length() == 0) { + info.setClientId(clientIdGenerator.generateId()); + } + syncSendPacket(info); + + this.isConnectionInfoSentToBroker = true; + // Add a temp destination advisory consumer so that + // We know what the valid temporary destinations are on the + // broker without having to do an RPC to the broker. + + ConsumerId consumerId = new ConsumerId(new SessionId(info.getConnectionId(), -1),consumerIdGenerator.getNextSequenceId()); + advisoryConsumer = new AdvisoryConsumer(this, consumerId); + + } + + /** + * @return Returns the useAsyncSend. + */ + public boolean isUseAsyncSend() { + return useAsyncSend; + } + + /** + * @param useAsyncSend + * The useAsyncSend to set. + */ + public void setUseAsyncSend(boolean useAsyncSend) { + this.useAsyncSend = useAsyncSend; + } + + /** + * Cleans up this connection so that it's state is as if the connection was + * just created. This allows the Resource Adapter to clean up a connection + * so that it can be reused without having to close and recreate the + * connection. + * + */ + public void cleanup() throws JMSException { + + if( advisoryConsumer!=null ) { + advisoryConsumer.dispose(); + advisoryConsumer=null; + } + + for (Iterator i = this.sessions.iterator(); i.hasNext();) { + ActiveMQSession s = (ActiveMQSession) i.next(); + s.dispose(); + } + for (Iterator i = this.connectionConsumers.iterator(); i.hasNext();) { + ActiveMQConnectionConsumer c = (ActiveMQConnectionConsumer) i.next(); + c.dispose(); + } + for (Iterator i = this.inputStreams.iterator(); i.hasNext();) { + ActiveMQInputStream c = (ActiveMQInputStream) i.next(); + c.dispose(); + } + for (Iterator i = this.outputStreams.iterator(); i.hasNext();) { + ActiveMQOutputStream c = (ActiveMQOutputStream) i.next(); + c.dispose(); + } + + if( isConnectionInfoSentToBroker ) { + asyncSendPacket(info.createRemoveCommand()); + isConnectionInfoSentToBroker = false; + } + if( userSpecifiedClientID ) { + info.setClientId(null); + userSpecifiedClientID=false; + } + clientIDSet = false; + + started.set(false); + } + + /** + * Changes the associated username/password that is associated with this + * connection. If the connection has been used, you must called cleanup() + * before calling this method. + * + * @throws IllegalStateException + * if the connection is in used. + * + */ + public void changeUserInfo(String userName, String password) throws JMSException { + if (isConnectionInfoSentToBroker) + throw new IllegalStateException("changeUserInfo used Connection is not allowed"); + + this.info.setUserName(userName); + this.info.setPassword(password); + } + + /** + * @return Returns the resourceManagerId. + * @throws JMSException + */ + public String getResourceManagerId() throws JMSException { + waitForBrokerInfo(); + if( resourceManagerId==null ) + throw new JMSException("Resource manager id could not be determined."); + return resourceManagerId; + } + + /** + * @return Returns the RedeliveryPolicy. + * @throws JMSException + */ + public RedeliveryPolicy getRedeliveryPolicy() throws JMSException { + waitForBrokerInfo(); + return redeliveryPolicy; + } + + private void waitForBrokerInfo() throws JMSException { + try { + brokerInfoReceived.await(); + } catch (InterruptedException e) { + throw JMSExceptionSupport.create(e); + } + } + + // Package protected so that it can be used in unit tests + Transport getTransport() { + return transport; + } + + public void addDispatcher(ConsumerId consumerId, ActiveMQDispatcher dispatcher) { + dispatchers.put(consumerId, dispatcher); + } + public void removeDispatcher(ConsumerId consumerId) { + dispatchers.remove(consumerId); + } + + /** + * @param command - the command to consume + */ + public void onCommand(Command command) { + if (!closed.get() && command != null) { + if (command.isMessageDispatch()) { + MessageDispatch md = (MessageDispatch) command; + ActiveMQDispatcher dispatcher = (ActiveMQDispatcher) dispatchers.get(md.getConsumerId()); + if (dispatcher != null) { + // Copy in case a embedded broker is dispatching via vm:// + // md.getMessage() == null to signal end of queue browse. + Message msg = md.getMessage(); + if( msg!=null ) { + msg = msg.copy(); + msg.setReadOnlyBody(true); + msg.setReadOnlyProperties(true); + msg.setRedeliveryCounter(md.getRedeliveryCounter()); + msg.setConnection(this); + md.setMessage( msg ); + } + dispatcher.dispatch(md); + } + } else if ( command.isBrokerInfo() ) { + BrokerInfo brokerInfo = (BrokerInfo)command; + resourceManagerId = brokerInfo.getBrokerId().getBrokerId(); + if( redeliveryPolicy == null ) { + if( brokerInfo.getRedeliveryPolicy()!=null ) { + redeliveryPolicy = brokerInfo.getRedeliveryPolicy(); + } else { + redeliveryPolicy = new RedeliveryPolicy(); + } + } + brokerInfoReceived.countDown(); + } + else if (command instanceof ControlCommand) { + onControlCommand((ControlCommand) command); + } + } + } + + /** + * Used for handling async exceptions + * + * @param error + */ + public void onAsyncException(Throwable error) { + if (!closed.get() && !closing.get()) { + if (this.exceptionListener != null) { + if (!(error instanceof JMSException)) + error = JMSExceptionSupport.create(error); + this.exceptionListener.onException((JMSException) error); + } else { + log.warn("Async exception with no exception listener: " + error, error); + } + } + } + + public void onException(IOException error) { + onAsyncException(error); + ServiceSupport.dispose(this.transport); + brokerInfoReceived.countDown(); + } + + /** + * Create the DestinationInfo object for the temporary destination. + * + * @param topic - if its true topic, else queue. + * @return DestinationInfo + * @throws JMSException + */ + protected ActiveMQTempDestination createTempDestination(boolean topic) throws JMSException { + + // Check if Destination info is of temporary type. + ActiveMQTempDestination dest; + if( topic ) { + dest = new ActiveMQTempTopic(info.getConnectionId(), tempDestinationIdGenerator.getNextSequenceId()); + } else { + dest = new ActiveMQTempQueue(info.getConnectionId(), tempDestinationIdGenerator.getNextSequenceId()); + } + + DestinationInfo info = new DestinationInfo(); + info.setConnectionId(this.info.getConnectionId()); + info.setOperationType(DestinationInfo.ADD_OPERATION_TYPE); + info.setDestination(dest); + syncSendPacket(info); + + dest.setConnection(this); + activeTempDestinations.put(dest,dest); + return dest; + } + + /** + * + * @param destination + * @throws JMSException + */ + public void deleteTempDestination(ActiveMQTempDestination destination) throws JMSException { + + checkClosed(); + activeTempDestinations.remove(destination); + + DestinationInfo info = new DestinationInfo(); + info.setConnectionId(this.info.getConnectionId()); + info.setOperationType(DestinationInfo.REMOVE_OPERATION_TYPE); + info.setDestination(destination); + info.setTimeout(1000*5); + syncSendPacket(info); + } + + + + public boolean isDeleted(ActiveMQDestination dest) { + return !activeTempDestinations.contains(dest); + } + + public boolean isCopyMessageOnSend() { + return copyMessageOnSend; + } + + public LongSequenceGenerator getLocalTransactionIdGenerator() { + return localTransactionIdGenerator; + } + + public boolean isUseCompression() { + return useCompression; + } + + public void setUseCompression(boolean useCompression) { + this.useCompression = useCompression; + } + + public void destroyDestination(ActiveMQDestination destination) throws JMSException { + + checkClosed(); + ensureConnectionInfoSent(); + + DestinationInfo info = new DestinationInfo(); + info.setConnectionId(this.info.getConnectionId()); + info.setOperationType(DestinationInfo.REMOVE_OPERATION_TYPE); + info.setDestination(destination); + info.setTimeout(1000*5); + syncSendPacket(info); + + } + + public boolean isAsyncDispatch() { + return asyncDispatch; + } + + public void setAsyncDispatch(boolean asyncDispatch) { + this.asyncDispatch = asyncDispatch; + } + + public boolean isObjectMessageSerializationDefered() { + return objectMessageSerializationDefered; + } + + public void setObjectMessageSerializationDefered(boolean objectMessageSerializationDefered) { + this.objectMessageSerializationDefered = objectMessageSerializationDefered; + } + + public InputStream createInputStream(Destination dest) throws JMSException { + return createInputStream(dest, null); + } + + public InputStream createInputStream(Destination dest, String messageSelector) throws JMSException { + return createInputStream(dest, messageSelector, false); + } + + public InputStream createInputStream(Destination dest, String messageSelector, boolean noLocal) throws JMSException { + return doCreateInputStream(dest, messageSelector, noLocal, null); + } + + public InputStream createDurableInputStream(Topic dest, String name) throws JMSException { + return createInputStream(dest, null, false); + } + + public InputStream createDurableInputStream(Topic dest, String name, String messageSelector) throws JMSException { + return createDurableInputStream(dest, name, messageSelector, false); + } + + public InputStream createDurableInputStream(Topic dest, String name, String messageSelector, boolean noLocal) throws JMSException { + return doCreateInputStream(dest, messageSelector, noLocal, name); + } + + private InputStream doCreateInputStream(Destination dest, String messageSelector, boolean noLocal, String subName) throws JMSException { + checkClosed(); + ensureConnectionInfoSent(); + return new ActiveMQInputStream(this, createConsumerId(), ActiveMQDestination.transform(dest), messageSelector, noLocal, subName, prefetchPolicy.getInputStreamPrefetch()); + } + + + public OutputStream createOutputStream(Destination dest) throws JMSException { + return createOutputStream(dest, null, ActiveMQMessage.DEFAULT_DELIVERY_MODE, ActiveMQMessage.DEFAULT_PRIORITY, ActiveMQMessage.DEFAULT_TIME_TO_LIVE); + } + + public OutputStream createOutputStream(Destination dest, Map streamProperties, int deliveryMode, int priority, long timeToLive) throws JMSException { + checkClosed(); + ensureConnectionInfoSent(); + return new ActiveMQOutputStream(this, createProducerId(), ActiveMQDestination.transform(dest), streamProperties, deliveryMode, priority, timeToLive); + } + + /** + * Unsubscribes a durable subscription that has been created by a client. + *

+ * This method deletes the state being maintained on behalf of the + * subscriber by its provider. + *

+ * It is erroneous for a client to delete a durable subscription while there + * is an active MessageConsumer or TopicSubscriber + * for the subscription, or while a consumed message is part of a pending + * transaction or has not been acknowledged in the session. + * + * @param name + * the name used to identify this subscription + * @throws JMSException + * if the session fails to unsubscribe to the durable + * subscription due to some internal error. + * @throws InvalidDestinationException + * if an invalid subscription name is specified. + * @since 1.1 + */ + public void unsubscribe(String name) throws JMSException { + checkClosed(); + RemoveSubscriptionInfo rsi = new RemoveSubscriptionInfo(); + rsi.setConnectionId(getConnectionInfo().getConnectionId()); + rsi.setSubcriptionName(name); + rsi.setClientId(getConnectionInfo().getClientId()); + syncSendPacket(rsi); + } + + /** + * Internal send method optimized: + * - It does not copy the message + * - It can only handle ActiveMQ messages. + * - You can specify if the send is async or sync + * - Does not allow you to send /w a transaction. + */ + void send(ActiveMQDestination destination, ActiveMQMessage msg, MessageId messageId, int deliveryMode, int priority, long timeToLive, boolean async) throws JMSException { + checkClosed(); + + if( destination.isTemporary() && isDeleted(destination) ) { + throw new JMSException("Cannot publish to a deleted Destination: "+destination); + } + + msg.setJMSDestination(destination); + msg.setJMSDeliveryMode(deliveryMode); + long expiration = 0L; + + if (!isDisableTimeStampsByDefault()) { + long timeStamp = System.currentTimeMillis(); + msg.setJMSTimestamp(timeStamp); + if (timeToLive > 0) { + expiration = timeToLive + timeStamp; + } + } + + msg.setJMSExpiration(expiration); + msg.setJMSPriority(priority); + + msg.setJMSRedelivered(false); + msg.setMessageId( messageId ); + + msg.onSend(); + + msg.setProducerId(msg.getMessageId().getProducerId()); + + if (log.isDebugEnabled()) { + log.debug("Sending message: " + msg); + } + + if( async) { + asyncSendPacket(msg); + } else { + syncSendPacket(msg); + } + + } + + public void addOutputStream(ActiveMQOutputStream stream) { + outputStreams.add(stream); + } + public void removeOutputStream(ActiveMQOutputStream stream) { + outputStreams.remove(stream); + } + public void addInputStream(ActiveMQInputStream stream) { + inputStreams.add(stream); + } + public void removeInputStream(ActiveMQInputStream stream) { + inputStreams.remove(stream); + } + + protected void onControlCommand(ControlCommand command) { + String text = command.getCommand(); + if (text != null) { + if (text.equals("shutdown")) { + log.info("JVM told to shutdown"); + System.exit(0); + } + } + } + + public void setCopyMessageOnSend(boolean copyMessageOnSend) { + this.copyMessageOnSend = copyMessageOnSend; + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQConnectionConsumer.java b/activemq-core/src/main/java/org/activemq/ActiveMQConnectionConsumer.java new file mode 100755 index 0000000000..16197a1b85 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQConnectionConsumer.java @@ -0,0 +1,151 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import javax.jms.ConnectionConsumer; +import javax.jms.IllegalStateException; +import javax.jms.JMSException; +import javax.jms.ServerSession; +import javax.jms.ServerSessionPool; +import javax.jms.Session; + +import org.activemq.command.ConsumerInfo; +import org.activemq.command.MessageDispatch; + +/** + * For application servers, Connection objects provide a special + * facility for creating a ConnectionConsumer (optional). The + * messages it is to consume are specified by a Destination and + * a message selector. In addition, a ConnectionConsumer must be + * given a ServerSessionPool to use for processing its messages. + *

+ *

+ * Normally, when traffic is light, a ConnectionConsumer gets a + * ServerSession from its pool, loads it with a single message, + * and starts it. As traffic picks up, messages can back up. If this happens, a + * ConnectionConsumer can load each ServerSession + * with more than one message. This reduces the thread context switches and + * minimizes resource use at the expense of some serialization of message + * processing. + * + * @see javax.jms.Connection#createConnectionConsumer + * @see javax.jms.Connection#createDurableConnectionConsumer + * @see javax.jms.QueueConnection#createConnectionConsumer + * @see javax.jms.TopicConnection#createConnectionConsumer + * @see javax.jms.TopicConnection#createDurableConnectionConsumer + */ + +public class ActiveMQConnectionConsumer implements ConnectionConsumer, ActiveMQDispatcher { + + private ActiveMQConnection connection; + private ServerSessionPool sessionPool; + private ConsumerInfo consumerInfo; + private boolean closed; + + protected final List messageQueue = Collections.synchronizedList(new LinkedList()); + + + /** + * Create a ConnectionConsumer + * + * @param theConnection + * @param theSessionPool + * @param theConsumerInfo + * @param theMaximumMessages + * @throws JMSException + */ + protected ActiveMQConnectionConsumer(ActiveMQConnection theConnection, + ServerSessionPool theSessionPool, + ConsumerInfo theConsumerInfo) throws JMSException { + this.connection = theConnection; + this.sessionPool = theSessionPool; + this.consumerInfo = theConsumerInfo; + + this.connection.addConnectionConsumer(this); + this.connection.addDispatcher(consumerInfo.getConsumerId(), this); + this.connection.syncSendPacket(this.consumerInfo); + } + + /** + * Gets the server session pool associated with this connection consumer. + * + * @return the server session pool used by this connection consumer + * @throws JMSException if the JMS provider fails to get the server session pool + * associated with this consumer due to some internal error. + */ + + public ServerSessionPool getServerSessionPool() throws JMSException { + if (closed) { + throw new IllegalStateException("The Connection Consumer is closed"); + } + return this.sessionPool; + } + + /** + * Closes the connection consumer. + *

+ *

+ * Since a provider may allocate some resources on behalf of a connection + * consumer outside the Java virtual machine, clients should close these + * resources when they are not needed. Relying on garbage collection to + * eventually reclaim these resources may not be timely enough. + * + * @throws JMSException + */ + + public void close() throws JMSException { + if (!closed) { + dispose(); + this.connection.asyncSendPacket(this.consumerInfo.createRemoveCommand()); + } + + } + + public void dispose() { + if (!closed) { + this.connection.removeDispatcher(consumerInfo.getConsumerId()); + this.connection.removeConnectionConsumer(this); + closed = true; + } + } + + public void dispatch(MessageDispatch messageDispatch) { + try { + messageDispatch.setConsumer(this); + + ServerSession serverSession = sessionPool.getServerSession(); + Session s = serverSession.getSession(); + ActiveMQSession session = null; + if( s instanceof ActiveMQSession ) { + session = (ActiveMQSession) s; + } else { + connection.onAsyncException(new JMSException("Session pool provided an invalid session type: "+s.getClass())); + return; + } + session.dispatch(messageDispatch); + serverSession.start(); + } catch (JMSException e) { + connection.onAsyncException(e); + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQConnectionFactory.java b/activemq-core/src/main/java/org/activemq/ActiveMQConnectionFactory.java new file mode 100755 index 0000000000..0239d11cca --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQConnectionFactory.java @@ -0,0 +1,403 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.Properties; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.jms.QueueConnection; +import javax.jms.QueueConnectionFactory; +import javax.jms.TopicConnection; +import javax.jms.TopicConnectionFactory; +import javax.naming.Context; + +import org.activemq.management.JMSStatsImpl; +import org.activemq.management.StatsCapable; +import org.activemq.management.StatsImpl; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.JMSExceptionSupport; +import org.activemq.util.URISupport; +import org.activemq.util.URISupport.CompositeData; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory; + +/** + * A ConnectionFactory is an an Administered object, and is used for creating + * Connections.

This class also implements QueueConnectionFactory and + * TopicConnectionFactory. You can use this connection to create both + * QueueConnections and TopicConnections. + * + * @version $Revision: 1.9 $ + * @see javax.jms.ConnectionFactory + */ +public class ActiveMQConnectionFactory implements ConnectionFactory, QueueConnectionFactory, TopicConnectionFactory, StatsCapable { + + public static final String DEFAULT_BROKER_URL = "tcp://localhost:61616"; + public static final String DEFAULT_USER = null; + public static final String DEFAULT_PASSWORD = null; + + protected URI brokerURL; + protected String userName; + protected String password; + protected String clientID; + + protected boolean useEmbeddedBroker; + + // optimization flags + private ActiveMQPrefetchPolicy prefetchPolicy = new ActiveMQPrefetchPolicy(); + private boolean disableTimeStampsByDefault = false; + private boolean onSendPrepareMessageBody = true; + private boolean optimizedMessageDispatch = true; + private boolean copyMessageOnSend = true; + private boolean useCompression = false; + private boolean objectMessageSerializationDefered = false; + protected boolean asyncDispatch = true; + private boolean useAsyncSend = false; + private boolean useRetroactiveConsumer; + + JMSStatsImpl factoryStats = new JMSStatsImpl(); + + static protected final Executor DEFAULT_CONNECTION_EXECUTOR = new ScheduledThreadPoolExecutor(5, new ThreadFactory() { + public Thread newThread(Runnable run) { + Thread thread = new Thread(run); + thread.setPriority(ThreadPriorities.INBOUND_CLIENT_CONNECTION); + return thread; + } + }); + + // ///////////////////////////////////////////// + // + // ConnectionFactory, QueueConnectionFactory, TopicConnectionFactory Methods + // + // ///////////////////////////////////////////// + + public ActiveMQConnectionFactory() { + this(DEFAULT_BROKER_URL); + } + + public ActiveMQConnectionFactory(String brokerURL) { + this(createURI(brokerURL)); + } + + /** + * @param brokerURL + * @return + * @throws URISyntaxException + */ + private static URI createURI(String brokerURL) { + try { + return new URI(brokerURL); + } + catch (URISyntaxException e) { + throw (IllegalArgumentException) new IllegalArgumentException("Invalid broker URI: " + brokerURL).initCause(e); + } + } + + public ActiveMQConnectionFactory(URI brokerURL) { + setBrokerURL(brokerURL.toString()); + } + + public ActiveMQConnectionFactory(String userName, String password, URI brokerURL) { + setUserName(userName); + setPassword(password); + setBrokerURL(brokerURL.toString()); + } + + public ActiveMQConnectionFactory(String userName, String password, String brokerURL) { + setUserName(userName); + setPassword(password); + setBrokerURL(brokerURL); + } + + /** + * @return Returns the Connection. + */ + public Connection createConnection() throws JMSException { + return createActiveMQConnection(userName, password); + } + + /** + * @return Returns the Connection. + */ + public Connection createConnection(String userName, String password) throws JMSException { + return createActiveMQConnection(userName, password); + } + + /** + * @return Returns the QueueConnection. + * @throws JMSException + */ + public QueueConnection createQueueConnection() throws JMSException { + return createActiveMQConnection(userName, password); + } + + /** + * @return Returns the QueueConnection. + */ + public QueueConnection createQueueConnection(String userName, String password) throws JMSException { + return createActiveMQConnection(userName, password); + } + + /** + * @return Returns the TopicConnection. + * @throws JMSException + */ + public TopicConnection createTopicConnection() throws JMSException { + return createActiveMQConnection(userName, password); + } + + /** + * @return Returns the TopicConnection. + */ + public TopicConnection createTopicConnection(String userName, String password) throws JMSException { + return createActiveMQConnection(userName, password); + } + + public StatsImpl getStats() { + // TODO + return null; + } + + // ///////////////////////////////////////////// + // + // Implementation methods. + // + // ///////////////////////////////////////////// + + /** + * @return Returns the Connection. + */ + private ActiveMQConnection createActiveMQConnection(String userName, String password) throws JMSException { + if (brokerURL == null) { + throw new ConfigurationException("brokerURL not set."); + } + Transport transport; + try { + transport = TransportFactory.connect(brokerURL,DEFAULT_CONNECTION_EXECUTOR); + ActiveMQConnection connection = new ActiveMQConnection(transport, userName, password, factoryStats); + + connection.setPrefetchPolicy(getPrefetchPolicy()); + connection.setDisableTimeStampsByDefault(isDisableTimeStampsByDefault()); + connection.setOnSendPrepareMessageBody(isOnSendPrepareMessageBody()); + connection.setOptimizedMessageDispatch(isOptimizedMessageDispatch()); + connection.setCopyMessageOnSend(isCopyMessageOnSend()); + connection.setUseCompression(isUseCompression()); + connection.setObjectMessageSerializationDefered(isObjectMessageSerializationDefered()); + connection.setAsyncDispatch(isAsyncDispatch()); + connection.setUseAsyncSend(isUseAsyncSend()); + connection.setUseRetroactiveConsumer(isUseRetroactiveConsumer()); + + return connection; + } + catch (JMSException e) { + throw e; + } + catch (Exception e) { + throw JMSExceptionSupport.create("Could not connect to broker URL: " + brokerURL + ". Reason: " + e, e); + } + } + + // ///////////////////////////////////////////// + // + // Property Accessors + // + // ///////////////////////////////////////////// + + public String getBrokerURL() { + return brokerURL==null?null:brokerURL.toString(); + } + public void setBrokerURL(String brokerURL) { + this.brokerURL = createURI(brokerURL); + + // Use all the properties prefixed with 'jms.' to set the connection factory + // options. + if( this.brokerURL.getQuery() !=null ) { + // It might be a standard URI or... + try { + + Map map = URISupport.parseQuery(this.brokerURL.getQuery()); + if( IntrospectionSupport.setProperties(this, map, "jms.") ) { + this.brokerURL = URISupport.createRemainingURI(this.brokerURL, map); + } + + } catch (URISyntaxException e) { + } + + } else { + + // It might be a composite URI. + try { + CompositeData data = URISupport.parseComposite(this.brokerURL); + if( IntrospectionSupport.setProperties(this, data.getParameters(), "jms.") ) { + this.brokerURL = data.toURI(); + } + } catch (URISyntaxException e) { + } + } + } + + public String getClientID() { + return clientID; + } + + public void setClientID(String clientID) { + this.clientID = clientID; + } + + public boolean isCopyMessageOnSend() { + return copyMessageOnSend; + } + + public void setCopyMessageOnSend(boolean copyMessageOnSend) { + this.copyMessageOnSend = copyMessageOnSend; + } + + public boolean isDisableTimeStampsByDefault() { + return disableTimeStampsByDefault; + } + + public void setDisableTimeStampsByDefault(boolean disableTimeStampsByDefault) { + this.disableTimeStampsByDefault = disableTimeStampsByDefault; + } + + public boolean isOptimizedMessageDispatch() { + return optimizedMessageDispatch; + } + + public void setOptimizedMessageDispatch(boolean optimizedMessageDispatch) { + this.optimizedMessageDispatch = optimizedMessageDispatch; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public ActiveMQPrefetchPolicy getPrefetchPolicy() { + return prefetchPolicy; + } + + public void setPrefetchPolicy(ActiveMQPrefetchPolicy prefetchPolicy) { + this.prefetchPolicy = prefetchPolicy; + } + + public boolean isUseAsyncSend() { + return useAsyncSend; + } + + public void setUseAsyncSend(boolean useAsyncSend) { + this.useAsyncSend = useAsyncSend; + } + + public boolean isUseEmbeddedBroker() { + return useEmbeddedBroker; + } + + public void setUseEmbeddedBroker(boolean useEmbeddedBroker) { + this.useEmbeddedBroker = useEmbeddedBroker; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public boolean isUseRetroactiveConsumer() { + return useRetroactiveConsumer; + } + + /** + * Sets whether or not retroactive consumers are enabled. Retroactive consumers allow + * non-durable topic subscribers to receive old messages that were published before the + * non-durable subscriber started. + */ + public void setUseRetroactiveConsumer(boolean useRetroactiveConsumer) { + this.useRetroactiveConsumer = useRetroactiveConsumer; + } + + /** + * set the properties for this instance as retrieved from JNDI + * + * @param properties + */ + public void setProperties(Properties properties) throws URISyntaxException { + + if (properties == null) { + properties = new Properties(); + } + + IntrospectionSupport.setProperties(this, properties); + + String temp = properties.getProperty(Context.PROVIDER_URL); + if (temp == null || temp.length() == 0) { + temp = properties.getProperty("brokerURL"); + } + if (temp != null && temp.length() > 0) { + setBrokerURL(temp); + } + } + + public boolean isOnSendPrepareMessageBody() { + return onSendPrepareMessageBody; + } + + public void setOnSendPrepareMessageBody(boolean onSendPrepareMessageBody) { + this.onSendPrepareMessageBody = onSendPrepareMessageBody; + } + + public boolean isUseCompression() { + return useCompression; + } + + public void setUseCompression(boolean useCompression) { + this.useCompression = useCompression; + } + + public boolean isObjectMessageSerializationDefered() { + return objectMessageSerializationDefered; + } + + public void setObjectMessageSerializationDefered(boolean objectMessageSerializationDefered) { + this.objectMessageSerializationDefered = objectMessageSerializationDefered; + } + + public boolean isAsyncDispatch() { + return asyncDispatch; + } + + public void setAsyncDispatch(boolean asyncDispatch) { + this.asyncDispatch = asyncDispatch; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQConnectionMetaData.java b/activemq-core/src/main/java/org/activemq/ActiveMQConnectionMetaData.java new file mode 100755 index 0000000000..b7b8fe3b08 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQConnectionMetaData.java @@ -0,0 +1,148 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.jms.ConnectionMetaData; + +/** + * A ConnectionMetaData object provides information describing + * the Connection object. + */ + +public class ActiveMQConnectionMetaData implements ConnectionMetaData { + + public static final String PROVIDER_VERSION; + public static final int PROVIDER_MAJOR_VERSION; + public static final int PROVIDER_MINOR_VERSION; + + public static final ActiveMQConnectionMetaData INSTANCE = new ActiveMQConnectionMetaData(); + + static { + String version=null; + int major=0; + int minor=0; + try { + Package p = Package.getPackage("org.activemq"); + if (p != null) { + version = p.getImplementationVersion(); + Pattern pattern = Pattern.compile("(\\d+)\\.(\\d+).*"); + Matcher m = pattern.matcher(version); + if( m.matches() ) { + major = Integer.parseInt(m.group(1)); + minor = Integer.parseInt(m.group(2)); + } + } + } catch ( Throwable e) { + } + PROVIDER_VERSION = version; + PROVIDER_MAJOR_VERSION = major; + PROVIDER_MINOR_VERSION = minor; + } + + private ActiveMQConnectionMetaData() {} + + /** + * Gets the JMS API version. + * + * @return the JMS API version + */ + + public String getJMSVersion() { + return "1.1"; + } + + /** + * Gets the JMS major version number. + * + * @return the JMS API major version number + */ + + public int getJMSMajorVersion() { + return 1; + } + + /** + * Gets the JMS minor version number. + * + * @return the JMS API minor version number + */ + + public int getJMSMinorVersion() { + return 1; + } + + /** + * Gets the JMS provider name. + * + * @return the JMS provider name + */ + + public String getJMSProviderName() { + return "ActiveMQ"; + } + + /** + * Gets the JMS provider version. + * + * @return the JMS provider version + */ + + public String getProviderVersion() { + return PROVIDER_VERSION; + } + + /** + * Gets the JMS provider major version number. + * + * @return the JMS provider major version number + */ + + public int getProviderMajorVersion() { + return PROVIDER_MAJOR_VERSION; + } + + /** + * Gets the JMS provider minor version number. + * + * @return the JMS provider minor version number + */ + + public int getProviderMinorVersion() { + return PROVIDER_MINOR_VERSION; + } + + /** + * Gets an enumeration of the JMSX property names. + * + * @return an Enumeration of JMSX property names + */ + + public Enumeration getJMSXPropertyNames() { + Hashtable jmxProperties = new Hashtable(); + jmxProperties.put("JMSXGroupID", "1"); + jmxProperties.put("JMSXGroupSeq", "1"); + jmxProperties.put("JMSXDeliveryCount","1"); + return jmxProperties.keys(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQDispatcher.java b/activemq-core/src/main/java/org/activemq/ActiveMQDispatcher.java new file mode 100755 index 0000000000..2ceff51a18 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQDispatcher.java @@ -0,0 +1,25 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import org.activemq.command.MessageDispatch; + +public interface ActiveMQDispatcher { + void dispatch(MessageDispatch messageDispatch); +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQInputStream.java b/activemq-core/src/main/java/org/activemq/ActiveMQInputStream.java new file mode 100644 index 0000000000..97e3cac0f5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQInputStream.java @@ -0,0 +1,230 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 5 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq; + +import java.io.IOException; +import java.io.InputStream; + +import javax.jms.IllegalStateException; +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; + +import org.activemq.command.ActiveMQBytesMessage; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.CommandTypes; +import org.activemq.command.ConsumerId; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageDispatch; +import org.activemq.command.ProducerId; +import org.activemq.selector.SelectorParser; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.JMSExceptionSupport; + +/** + * + * @version $Revision$ + */ +public class ActiveMQInputStream extends InputStream implements ActiveMQDispatcher { + + private final ActiveMQConnection connection; + private final ConsumerInfo info; + // These are the messages waiting to be delivered to the client + private final MessageDispatchChannel unconsumedMessages = new MessageDispatchChannel(); + + private int deliveredCounter = 0; + private MessageDispatch lastDelivered; + private boolean eosReached; + private byte buffer[]; + private int pos; + + private ProducerId producerId; + private long nextSequenceId=0; + + public ActiveMQInputStream(ActiveMQConnection connection, ConsumerId consumerId, ActiveMQDestination dest, + String selector, boolean noLocal, String name, int prefetch) throws JMSException { + this.connection = connection; + + if (dest == null) { + throw new InvalidDestinationException("Don't understand null destinations"); + } else if (dest.isTemporary()) { + String physicalName = dest.getPhysicalName(); + + if (physicalName == null) { + throw new IllegalArgumentException("Physical name of Destination should be valid: " + dest); + } + + String connectionID = connection.getConnectionInfo().getConnectionId().getConnectionId(); + + if (physicalName.indexOf(connectionID) < 0) { + throw new InvalidDestinationException("Cannot use a Temporary destination from another Connection"); + } + + if (connection.isDeleted(dest)) { + throw new InvalidDestinationException("Cannot use a Temporary destination that has been deleted"); + } + } + + this.info = new ConsumerInfo(consumerId); + this.info.setDestination(dest); + this.info.setSubcriptionName(name); + + if (selector != null && selector.trim().length() != 0) { + selector = "JMSType='org.activemq.Stream' AND ( "+selector+" ) "; + } else { + selector = "JMSType='org.activemq.Stream'"; + } + + new SelectorParser().parse(selector); + this.info.setSelector(selector); + + this.info.setPrefetchSize(prefetch); + this.info.setNoLocal(noLocal); + this.info.setBrowser(false); + this.info.setDispatchAsync(false); + + this.connection.addInputStream(this); + this.connection.addDispatcher(info.getConsumerId(), this); + this.connection.syncSendPacket(info); + unconsumedMessages.start(); + } + + public void close() throws IOException { + if (!unconsumedMessages.isClosed()) { + try { + if (lastDelivered != null) { + MessageAck ack = new MessageAck(lastDelivered, MessageAck.STANDARD_ACK_TYPE, deliveredCounter); + connection.asyncSendPacket(ack); + } + dispose(); + this.connection.syncSendPacket(info.createRemoveCommand()); + } catch (JMSException e) { + throw IOExceptionSupport.create(e); + } + } + } + + public void dispose() { + if (!unconsumedMessages.isClosed()) { + unconsumedMessages.close(); + this.connection.removeDispatcher(info.getConsumerId()); + this.connection.removeInputStream(this); + } + } + + public ActiveMQMessage receive() throws JMSException { + checkClosed(); + MessageDispatch md; + try { + md = unconsumedMessages.dequeue(-1); + } catch (InterruptedException e) { + throw JMSExceptionSupport.create(e); + } + + if (md == null || unconsumedMessages.isClosed() || md.getMessage().isExpired()) + return null; + + deliveredCounter++; + if ((0.75 * info.getPrefetchSize()) <= deliveredCounter) { + MessageAck ack = new MessageAck(md, MessageAck.STANDARD_ACK_TYPE, deliveredCounter); + connection.asyncSendPacket(ack); + deliveredCounter = 0; + lastDelivered = null; + } else { + lastDelivered = md; + } + + return (ActiveMQMessage) md.getMessage(); + } + + /** + * @throws IllegalStateException + */ + protected void checkClosed() throws IllegalStateException { + if (unconsumedMessages.isClosed()) { + throw new IllegalStateException("The Consumer is closed"); + } + } + + public int read() throws IOException { + fillBuffer(); + if( eosReached ) + return -1; + return buffer[pos++] & 0xff; + } + + public int read(byte[] b, int off, int len) throws IOException { + fillBuffer(); + if( eosReached ) + return -1; + + int max = Math.min(len, buffer.length-pos); + System.arraycopy(buffer, pos, b, off, max); + + pos += max; + return max; + } + + private void fillBuffer() throws IOException { + if( eosReached || (buffer!=null && buffer.length > pos) ) + return; + try { + while(true) { + ActiveMQMessage m = receive(); + if( m!=null && m.getDataStructureType() == CommandTypes.ACTIVEMQ_BYTES_MESSAGE ) { + // First message. + if( producerId == null ) { + // We have to start a stream at sequence id = 0 + if( m.getMessageId().getProducerSequenceId()!=0 ) { + continue; + } + nextSequenceId++; + producerId = m.getMessageId().getProducerId(); + } else { + // Verify it's the next message of the sequence. + if( !m.getMessageId().getProducerId().equals(producerId) ) { + throw new IOException("Received an unexpected message: invalid producer: "+m); + } + if( m.getMessageId().getProducerSequenceId()!=nextSequenceId++ ) { + throw new IOException("Received an unexpected message: invalid sequence id: "+m); + } + } + + // Read the buffer in. + ActiveMQBytesMessage bm = (ActiveMQBytesMessage) m; + buffer = new byte[(int) bm.getBodyLength()]; + bm.readBytes(buffer); + pos=0; + } else { + eosReached=true; + } + return; + } + } catch (JMSException e) { + eosReached = true; + throw IOExceptionSupport.create(e); + } + } + + public void dispatch(MessageDispatch md) { + unconsumedMessages.enqueue(md); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQMessageConsumer.java b/activemq-core/src/main/java/org/activemq/ActiveMQMessageConsumer.java new file mode 100755 index 0000000000..600aa545a2 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQMessageConsumer.java @@ -0,0 +1,731 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; + +import javax.jms.IllegalStateException; +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ConsumerId; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageDispatch; +import org.activemq.command.RedeliveryPolicy; +import org.activemq.management.JMSConsumerStatsImpl; +import org.activemq.management.StatsCapable; +import org.activemq.management.StatsImpl; +import org.activemq.selector.SelectorParser; +import org.activemq.thread.Scheduler; +import org.activemq.transaction.Synchronization; +import org.activemq.util.Callback; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.JMSExceptionSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +/** + * A client uses a MessageConsumer object to receive messages + * from a destination. A MessageConsumer object is created by + * passing a Destination object to a message-consumer creation + * method supplied by a session. + *

+ * MessageConsumer is the parent interface for all message + * consumers. + *

+ * A message consumer can be created with a message selector. A message selector + * allows the client to restrict the messages delivered to the message consumer + * to those that match the selector. + *

+ * A client may either synchronously receive a message consumer's messages or + * have the consumer asynchronously deliver them as they arrive. + *

+ * For synchronous receipt, a client can request the next message from a message + * consumer using one of its receive methods. There are several + * variations of receive that allow a client to poll or wait for + * the next message. + *

+ * For asynchronous delivery, a client can register a MessageListener + * object with a message consumer. As messages arrive at the message consumer, + * it delivers them by calling the MessageListener's + * onMessage method. + *

+ * It is a client programming error for a MessageListener to + * throw an exception. + * + * @version $Revision: 1.22 $ + * @see javax.jms.MessageConsumer + * @see javax.jms.QueueReceiver + * @see javax.jms.TopicSubscriber + * @see javax.jms.Session + */ +public class ActiveMQMessageConsumer implements MessageAvailableConsumer, StatsCapable, ActiveMQDispatcher { + + private static final Log log = LogFactory.getLog(ActiveMQMessageConsumer.class); + + protected final ActiveMQSession session; + protected final ConsumerInfo info; + + // These are the messages waiting to be delivered to the client + private final MessageDispatchChannel unconsumedMessages = new MessageDispatchChannel(); + + // The are the messages that were delivered to the consumer but that have + // not been acknowledged. It's kept in reverse order since we + // Always walk list in reverse order. Only used when session is client ack. + private final LinkedList deliveredMessages = new LinkedList(); + private int deliveredCounter = 0; + private int additionalWindowSize = 0; + private int rollbackCounter = 0; + private long redeliveryDelay = 0; + + private MessageListener messageListener; + private JMSConsumerStatsImpl stats; + + private final String selector; + private boolean synchronizationRegistered = false; + private AtomicBoolean started = new AtomicBoolean(false); + + private MessageAvailableListener availableListener; + + /** + * Create a MessageConsumer + * + * @param session + * @param consumerId + * @param dest + * @param name + * @param selector + * @param prefetch + * @param noLocal + * @param browser + * @param dispatchAsync + * @throws JMSException + */ + public ActiveMQMessageConsumer(ActiveMQSession session, ConsumerId consumerId, ActiveMQDestination dest, + String name, String selector, int prefetch, boolean noLocal, boolean browser, boolean dispatchAsync) + throws JMSException { + if (dest == null) { + throw new InvalidDestinationException("Don't understand null destinations"); + } else if (dest.getPhysicalName() == null) { + throw new InvalidDestinationException("The destination object was not given a physical name."); + } else if (dest.isTemporary()) { + String physicalName = dest.getPhysicalName(); + + if (physicalName == null) { + throw new IllegalArgumentException("Physical name of Destination should be valid: " + dest); + } + + String connectionID = session.connection.getConnectionInfo().getConnectionId().getConnectionId(); + + if (physicalName.indexOf(connectionID) < 0) { + throw new InvalidDestinationException("Cannot use a Temporary destination from another Connection"); + } + + if (session.connection.isDeleted(dest)) { + throw new InvalidDestinationException("Cannot use a Temporary destination that has been deleted"); + } + } + + this.session = session; + this.selector = selector; + + this.info = new ConsumerInfo(consumerId); + this.info.setSubcriptionName(name); + this.info.setPrefetchSize(prefetch); + this.info.setNoLocal(noLocal); + this.info.setDispatchAsync(dispatchAsync); + this.info.setRetroactive(this.session.connection.isUseRetroactiveConsumer()); + + // Allows the options on the destination to configure the consumerInfo + if (dest.getOptions() != null) { + HashMap options = new HashMap(dest.getOptions()); + IntrospectionSupport.setProperties(this.info, options, "consumer."); + } + + this.info.setDestination(dest); + this.info.setBrowser(browser); + if (selector != null && selector.trim().length() != 0) { + // Validate that the selector + new SelectorParser().parse(selector); + this.info.setSelector(selector); + } else { + this.info.setSelector(null); + } + + this.stats = new JMSConsumerStatsImpl(session.getSessionStats(), dest); + try { + this.session.addConsumer(this); + this.session.syncSendPacket(info); + } catch (JMSException e) { + this.session.removeConsumer(this); + throw e; + } + + if (session.connection.isStarted()) + start(); + } + + public StatsImpl getStats() { + return stats; + } + + public JMSConsumerStatsImpl getConsumerStats() { + return stats; + } + + /** + * @return Returns the consumerId. + */ + protected ConsumerId getConsumerId() { + return info.getConsumerId(); + } + + /** + * @return the consumer name - used for durable consumers + */ + protected String getConsumerName() { + return this.info.getSubcriptionName(); + } + + /** + * @return true if this consumer does not accept locally produced messages + */ + protected boolean isNoLocal() { + return info.isNoLocal(); + } + + /** + * Retrieve is a browser + * + * @return true if a browser + */ + protected boolean isBrowser() { + return info.isBrowser(); + } + + /** + * @return ActiveMQDestination + */ + protected ActiveMQDestination getDestination() { + return info.getDestination(); + } + + /** + * @return Returns the prefetchNumber. + */ + public int getPrefetchNumber() { + return info.getPrefetchSize(); + } + + /** + * @return true if this is a durable topic subscriber + */ + public boolean isDurableSubscriber() { + // TODO Add ActiveMQTopicSubscriber + return false; + // return this instanceof ActiveMQTopicSubscriber && consumerName != + // null && consumerName.length() > 0; + } + + /** + * Gets this message consumer's message selector expression. + * + * @return this message consumer's message selector, or null if no message + * selector exists for the message consumer (that is, if the message + * selector was not set or was set to null or the empty string) + * @throws JMSException + * if the JMS provider fails to receive the next message due to + * some internal error. + */ + public String getMessageSelector() throws JMSException { + checkClosed(); + return selector; + } + + /** + * Gets the message consumer's MessageListener. + * + * @return the listener for the message consumer, or null if no listener is + * set + * @throws JMSException + * if the JMS provider fails to get the message listener due to + * some internal error. + * @see javax.jms.MessageConsumer#setMessageListener(javax.jms.MessageListener) + */ + public MessageListener getMessageListener() throws JMSException { + checkClosed(); + return this.messageListener; + } + + /** + * Sets the message consumer's MessageListener. + *

+ * Setting the message listener to null is the equivalent of unsetting the + * message listener for the message consumer. + *

+ * The effect of calling MessageConsumer.setMessageListener + * while messages are being consumed by an existing listener or the consumer + * is being used to consume messages synchronously is undefined. + * + * @param listener + * the listener to which the messages are to be delivered + * @throws JMSException + * if the JMS provider fails to receive the next message due to + * some internal error. + * @see javax.jms.MessageConsumer#getMessageListener + */ + public void setMessageListener(MessageListener listener) throws JMSException { + checkClosed(); + this.messageListener = listener; + if (listener != null) { + boolean wasRunning = session.isRunning(); + if (wasRunning) + session.stop(); + + session.redispatch(unconsumedMessages); + + if (wasRunning) + session.start(); + + } + } + + + public MessageAvailableListener getAvailableListener() { + return availableListener; + } + + /** + * Sets the listener used to notify synchronous consumers that there is a message + * available so that the {@link MessageConsumer#receiveNoWait()} can be called. + */ + public void setAvailableListener(MessageAvailableListener availableListener) { + this.availableListener = availableListener; + } + + /** + * Used to get an enqueued message from the unconsumedMessages list. The + * amount of time this method blocks is based on the timeout value. - if + * timeout==-1 then it blocks until a message is received. - if timeout==0 + * then it it tries to not block at all, it returns a message if it is + * available - if timeout>0 then it blocks up to timeout amount of time. + * + * Expired messages will consumed by this method. + * + * @throws JMSException + * + * @return null if we timeout or if the consumer is closed. + */ + private MessageDispatch dequeue(long timeout) throws JMSException { + try { + long deadline = 0; + if (timeout > 0) { + deadline = System.currentTimeMillis() + timeout; + } + while (true) { + MessageDispatch md = unconsumedMessages.dequeue(timeout); + if (md == null) { + if (timeout > 0 && !unconsumedMessages.isClosed()) { + timeout = Math.max(deadline - System.currentTimeMillis(), 0); + } else { + return null; + } + } else if (md.getMessage().isExpired()) { + if (log.isDebugEnabled()) { + log.debug("Received expired message: " + md); + } + beforeMessageIsConsumed(md); + afterMessageIsConsumed(md, true); + if (timeout > 0) { + timeout = Math.max(deadline - System.currentTimeMillis(), 0); + } + } else { + if (log.isDebugEnabled()) { + log.debug("Received message: " + md); + } + return md; + } + } + } catch (InterruptedException e) { + throw JMSExceptionSupport.create(e); + } + } + + /** + * Receives the next message produced for this message consumer. + *

+ * This call blocks indefinitely until a message is produced or until this + * message consumer is closed. + *

+ * If this receive is done within a transaction, the consumer + * retains the message until the transaction commits. + * + * @return the next message produced for this message consumer, or null if + * this message consumer is concurrently closed + */ + public Message receive() throws JMSException { + checkClosed(); + checkMessageListener(); + MessageDispatch md = dequeue(-1); + if (md == null) + return null; + + beforeMessageIsConsumed(md); + afterMessageIsConsumed(md, false); + + return createActiveMQMessage(md); + } + + /** + * @param md + * @return + */ + private ActiveMQMessage createActiveMQMessage(final MessageDispatch md) { + ActiveMQMessage m = (ActiveMQMessage) md.getMessage(); + if (session.isClientAcknowledge()) { + m.setAcknowledgeCallback(new Callback() { + public void execute() throws Throwable { + session.checkClosed(); + session.acknowledge(); + } + }); + } + return m; + } + + /** + * Receives the next message that arrives within the specified timeout + * interval. + *

+ * This call blocks until a message arrives, the timeout expires, or this + * message consumer is closed. A timeout of zero never + * expires, and the call blocks indefinitely. + * + * @param timeout + * the timeout value (in milliseconds) + * @return the next message produced for this message consumer, or null if + * the timeout expires or this message consumer is concurrently + * closed + */ + public Message receive(long timeout) throws JMSException { + checkClosed(); + checkMessageListener(); + if (timeout == 0) { + return this.receive(); + + } + + while (timeout > 0) { + MessageDispatch md = dequeue(timeout); + if (md == null) + return null; + + beforeMessageIsConsumed(md); + afterMessageIsConsumed(md, false); + return createActiveMQMessage(md); + } + return null; + } + + /** + * Receives the next message if one is immediately available. + * + * @return the next message produced for this message consumer, or null if + * one is not available + * @throws JMSException + * if the JMS provider fails to receive the next message due to + * some internal error. + */ + public Message receiveNoWait() throws JMSException { + checkClosed(); + checkMessageListener(); + MessageDispatch md = dequeue(0); + if (md == null) + return null; + + beforeMessageIsConsumed(md); + afterMessageIsConsumed(md, false); + return createActiveMQMessage(md); + } + + /** + * Closes the message consumer. + *

+ * Since a provider may allocate some resources on behalf of a + * MessageConsumer outside the Java virtual machine, clients should + * close them when they are not needed. Relying on garbage collection to + * eventually reclaim these resources may not be timely enough. + *

+ * This call blocks until a receive or message listener in + * progress has completed. A blocked message consumer receive + * call returns null when this message consumer is closed. + * + * @throws JMSException + * if the JMS provider fails to close the consumer due to some + * internal error. + */ + public void close() throws JMSException { + if (!unconsumedMessages.isClosed()) { + dispose(); + this.session.syncSendPacket(info.createRemoveCommand()); + } + } + + public void dispose() throws JMSException { + if (!unconsumedMessages.isClosed()) { + // Do we have any acks we need to send out before closing? + // Ack any delivered messages now. (session may still + // commit/rollback the acks). + if ((session.isTransacted() || session.isDupsOkAcknowledge())) { + acknowledge(); + } + deliveredMessages.clear(); + unconsumedMessages.close(); + this.session.removeConsumer(this); + } + } + + /** + * @throws IllegalStateException + */ + protected void checkClosed() throws IllegalStateException { + if (unconsumedMessages.isClosed()) { + throw new IllegalStateException("The Consumer is closed"); + } + } + + protected void checkMessageListener() throws IllegalStateException { + if (messageListener != null) { + throw new IllegalStateException("Cannot synchronously receive a message when a MessageListener is set"); + } + } + + private void beforeMessageIsConsumed(MessageDispatch md) { + md.setDeliverySequenceId(session.getNextDeliveryId()); + if (!session.isDupsOkAcknowledge()) + deliveredMessages.addFirst(md); + } + + private void afterMessageIsConsumed(MessageDispatch md, boolean messageExpired) throws JMSException { + if (unconsumedMessages.isClosed()) + return; + + if (messageExpired) { + ackLater(md, MessageAck.DELIVERED_ACK_TYPE); + } else { + stats.onMessage(); + if (session.isTransacted()) { + ackLater(md, MessageAck.DELIVERED_ACK_TYPE); + } else if (session.isAutoAcknowledge()) { + if (!deliveredMessages.isEmpty()) { + MessageAck ack = new MessageAck(md, MessageAck.STANDARD_ACK_TYPE, deliveredMessages.size()); + session.asyncSendPacket(ack); + deliveredMessages.clear(); + } + } else if (session.isDupsOkAcknowledge()) { + ackLater(md, MessageAck.STANDARD_ACK_TYPE); + } else if (session.isClientAcknowledge()) { + ackLater(md, MessageAck.DELIVERED_ACK_TYPE); + } else { + throw new IllegalStateException("Invalid session state."); + } + } + } + + private void ackLater(MessageDispatch md, byte ackType) throws JMSException { + + // Don't acknowledge now, but we may need to let the broker know the + // consumer got the message + // to expand the pre-fetch window + if (session.isTransacted()) { + session.doStartTransaction(); + if (!synchronizationRegistered) { + synchronizationRegistered = true; + session.getTransactionContext().addSynchronization(new Synchronization() { + public void beforeEnd() throws Throwable { + acknowledge(); + synchronizationRegistered = false; + } + + public void afterCommit() throws Throwable { + commit(); + synchronizationRegistered = false; + } + + public void afterRollback() throws Throwable { + rollback(); + synchronizationRegistered = false; + } + }); + } + } + + // The delivered message list is only needed for the recover method + // which is only used with client ack. + deliveredCounter++; + if ((0.5 * info.getPrefetchSize()) <= (deliveredCounter - additionalWindowSize)) { + MessageAck ack = new MessageAck(md, ackType, deliveredCounter); + ack.setTransactionId(session.getTransactionContext().getTransactionId()); + session.asyncSendPacket(ack); + additionalWindowSize = deliveredCounter; + + // When using DUPS ok, we do a real ack. + if (ackType == MessageAck.STANDARD_ACK_TYPE) { + deliveredCounter = additionalWindowSize = 0; + } + } + } + + /** + * Acknowledge all the messages that have been delivered to the client upto + * this point. + * + * @param deliverySequenceId + * @throws JMSException + */ + public void acknowledge() throws JMSException { + if (deliveredMessages.isEmpty()) + return; + + // Acknowledge the last message. + MessageDispatch lastMd = (MessageDispatch) deliveredMessages.get(0); + MessageAck ack = new MessageAck(lastMd, MessageAck.STANDARD_ACK_TYPE, deliveredMessages.size()); + if (session.isTransacted()) { + session.doStartTransaction(); + ack.setTransactionId(session.getTransactionContext().getTransactionId()); + } + session.asyncSendPacket(ack); + + // Adjust the counters + deliveredCounter -= deliveredMessages.size(); + additionalWindowSize = Math.max(0, additionalWindowSize - deliveredMessages.size()); + + if (!session.isTransacted()) { + deliveredMessages.clear(); + } + } + + public void commit() throws JMSException { + deliveredMessages.clear(); + rollbackCounter = 0; + redeliveryDelay = 0; + } + + public void rollback() throws JMSException { + synchronized (unconsumedMessages.getMutex()) { + if (deliveredMessages.isEmpty()) + return; + + rollbackCounter++; + RedeliveryPolicy redeliveryPolicy = session.connection.getRedeliveryPolicy(); + if (rollbackCounter > redeliveryPolicy.getMaximumRedeliveries()) { + + // We need to NACK the messages so that they get sent to the + // DLQ. + + // Acknowledge the last message. + MessageDispatch lastMd = (MessageDispatch) deliveredMessages.get(0); + MessageAck ack = new MessageAck(lastMd, MessageAck.POSION_ACK_TYPE, deliveredMessages.size()); + session.asyncSendPacket(ack); + + // Adjust the counters + deliveredCounter -= deliveredMessages.size(); + additionalWindowSize = Math.max(0, additionalWindowSize - deliveredMessages.size()); + + } else { + + // stop the delivery of messages. + unconsumedMessages.stop(); + + // Start up the delivery again a little later. + if (redeliveryDelay == 0) { + redeliveryDelay = redeliveryPolicy.getInitialRedeliveryDelay(); + } else { + if (redeliveryPolicy.isUseExponentialBackOff()) + redeliveryDelay *= redeliveryPolicy.getBackOffMultiplier(); + } + + Scheduler.executeAfterDelay(new Runnable() { + public void run() { + if (started.get()) + unconsumedMessages.start(); + } + }, redeliveryDelay); + + for (Iterator iter = deliveredMessages.iterator(); iter.hasNext();) { + MessageDispatch md = (MessageDispatch) iter.next(); + md.getMessage().incrementRedeliveryCounter(); + unconsumedMessages.enqueueFirst(md); + } + } + + deliveredMessages.clear(); + } + + if (messageListener != null) { + session.redispatch(unconsumedMessages); + } + } + + public void dispatch(MessageDispatch md) { + MessageListener listener = this.messageListener; + try { + if (!unconsumedMessages.isClosed()) { + if (listener != null) { + ActiveMQMessage message = createActiveMQMessage(md); + beforeMessageIsConsumed(md); + listener.onMessage(message); + afterMessageIsConsumed(md, false); + } else { + if (availableListener != null) { + availableListener.onMessageAvailable(this); + } + unconsumedMessages.enqueue(md); + } + } + } catch (Exception e) { + log.warn("could not process message: " + md, e); + } + } + + public int getMessageSize() { + return unconsumedMessages.size(); + } + + public void start() { + started.set(true); + unconsumedMessages.start(); + } + + public void stop() { + started.set(false); + unconsumedMessages.stop(); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQMessageProducer.java b/activemq-core/src/main/java/org/activemq/ActiveMQMessageProducer.java new file mode 100755 index 0000000000..71b82c8f7f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQMessageProducer.java @@ -0,0 +1,503 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.IllegalStateException; +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageFormatException; +import javax.jms.MessageProducer; + +import org.activeio.Disposable; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ProducerId; +import org.activemq.command.ProducerInfo; +import org.activemq.management.JMSProducerStatsImpl; +import org.activemq.management.StatsCapable; +import org.activemq.management.StatsImpl; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicLong; + +/** + * A client uses a MessageProducer object to send messages to a + * destination. A MessageProducer object is created by passing a + * Destination object to a message-producer creation method + * supplied by a session. + *

+ * MessageProducer is the parent interface for all message + * producers. + *

+ * A client also has the option of creating a message producer without + * supplying a destination. In this case, a destination must be provided with + * every send operation. A typical use for this kind of message producer is to + * send replies to requests using the request's JMSReplyTo + * destination. + *

+ * A client can specify a default delivery mode, priority, and time to live for + * messages sent by a message producer. It can also specify the delivery mode, + * priority, and time to live for an individual message. + *

+ * A client can specify a time-to-live value in milliseconds for each message + * it sends. This value defines a message expiration time that is the sum of + * the message's time-to-live and the GMT when it is sent (for transacted + * sends, this is the time the client sends the message, not the time the + * transaction is committed). + *

+ * A JMS provider should do its best to expire messages accurately; however, + * the JMS API does not define the accuracy provided. + * + * @version $Revision: 1.14 $ + * @see javax.jms.TopicPublisher + * @see javax.jms.QueueSender + * @see javax.jms.Session#createProducer + */ +public class ActiveMQMessageProducer implements MessageProducer, StatsCapable, Closeable, Disposable { + + protected ActiveMQSession session; + protected ProducerInfo info; + private JMSProducerStatsImpl stats; + private AtomicLong messageSequence; + + protected boolean closed; + private boolean disableMessageID; + private boolean disableMessageTimestamp; + private int defaultDeliveryMode; + private int defaultPriority; + private long defaultTimeToLive; + private long startTime; + + protected ActiveMQMessageProducer(ActiveMQSession session, ProducerId producerId, ActiveMQDestination destination) + throws JMSException { + this.session = session; + this.info = new ProducerInfo(producerId); + this.info.setDestination(destination); + this.disableMessageID = false; + this.disableMessageTimestamp = session.connection.isDisableTimeStampsByDefault(); + this.defaultDeliveryMode = Message.DEFAULT_DELIVERY_MODE; + this.defaultPriority = Message.DEFAULT_PRIORITY; + this.defaultTimeToLive = Message.DEFAULT_TIME_TO_LIVE; + this.startTime = System.currentTimeMillis(); + this.messageSequence = new AtomicLong(0); + this.stats = new JMSProducerStatsImpl(session.getSessionStats(), destination); + this.session.addProducer(this); + this.session.asyncSendPacket(info); + } + + public StatsImpl getStats() { + return stats; + } + + public JMSProducerStatsImpl getProducerStats() { + return stats; + } + + /** + * Sets whether message IDs are disabled. + *

+ * Since message IDs take some effort to create and increase a message's + * size, some JMS providers may be able to optimize message overhead if + * they are given a hint that the message ID is not used by an application. + * By calling the setDisableMessageID method on this message + * producer, a JMS client enables this potential optimization for all + * messages sent by this message producer. If the JMS provider accepts this + * hint, these messages must have the message ID set to null; if the + * provider ignores the hint, the message ID must be set to its normal + * unique value. + *

+ * Message IDs are enabled by default. + * + * @param value indicates if message IDs are disabled + * @throws JMSException if the JMS provider fails to close the producer due to + * some internal error. + */ + public void setDisableMessageID(boolean value) throws JMSException { + checkClosed(); + this.disableMessageID = value; + } + + /** + * Gets an indication of whether message IDs are disabled. + * + * @return an indication of whether message IDs are disabled + * @throws JMSException if the JMS provider fails to determine if message IDs are + * disabled due to some internal error. + */ + public boolean getDisableMessageID() throws JMSException { + checkClosed(); + return this.disableMessageID; + } + + /** + * Sets whether message timestamps are disabled. + *

+ * Since timestamps take some effort to create and increase a message's + * size, some JMS providers may be able to optimize message overhead if + * they are given a hint that the timestamp is not used by an application. + * By calling the setDisableMessageTimestamp method on this + * message producer, a JMS client enables this potential optimization for + * all messages sent by this message producer. If the JMS provider accepts + * this hint, these messages must have the timestamp set to zero; if the + * provider ignores the hint, the timestamp must be set to its normal + * value. + *

+ * Message timestamps are enabled by default. + * + * @param value indicates if message timestamps are disabled + * @throws JMSException if the JMS provider fails to close the producer due to + * some internal error. + */ + public void setDisableMessageTimestamp(boolean value) throws JMSException { + checkClosed(); + this.disableMessageTimestamp = value; + } + + /** + * Gets an indication of whether message timestamps are disabled. + * + * @return an indication of whether message timestamps are disabled + * @throws JMSException if the JMS provider fails to close the producer due to + * some internal error. + */ + public boolean getDisableMessageTimestamp() throws JMSException { + checkClosed(); + return this.disableMessageTimestamp; + } + + /** + * Sets the producer's default delivery mode. + *

+ * Delivery mode is set to PERSISTENT by default. + * + * @param newDeliveryMode the message delivery mode for this message producer; legal + * values are DeliveryMode.NON_PERSISTENT and + * DeliveryMode.PERSISTENT + * @throws JMSException if the JMS provider fails to set the delivery mode due to + * some internal error. + * @see javax.jms.MessageProducer#getDeliveryMode + * @see javax.jms.DeliveryMode#NON_PERSISTENT + * @see javax.jms.DeliveryMode#PERSISTENT + * @see javax.jms.Message#DEFAULT_DELIVERY_MODE + */ + public void setDeliveryMode(int newDeliveryMode) throws JMSException { + if (newDeliveryMode != DeliveryMode.PERSISTENT && newDeliveryMode != DeliveryMode.NON_PERSISTENT) { + throw new IllegalStateException("unkown delivery mode: " + newDeliveryMode); + } + checkClosed(); + this.defaultDeliveryMode = newDeliveryMode; + } + + /** + * Gets the producer's default delivery mode. + * + * @return the message delivery mode for this message producer + * @throws JMSException if the JMS provider fails to close the producer due to + * some internal error. + */ + public int getDeliveryMode() throws JMSException { + checkClosed(); + return this.defaultDeliveryMode; + } + + /** + * Sets the producer's default priority. + *

+ * The JMS API defines ten levels of priority value, with 0 as the lowest + * priority and 9 as the highest. Clients should consider priorities 0-4 as + * gradations of normal priority and priorities 5-9 as gradations of + * expedited priority. Priority is set to 4 by default. + * + * @param newDefaultPriority the message priority for this message producer; must be a + * value between 0 and 9 + * @throws JMSException if the JMS provider fails to set the delivery mode due to + * some internal error. + * @see javax.jms.MessageProducer#getPriority + * @see javax.jms.Message#DEFAULT_PRIORITY + */ + public void setPriority(int newDefaultPriority) throws JMSException { + if (newDefaultPriority < 0 || newDefaultPriority > 9) { + throw new IllegalStateException("default priority must be a value between 0 and 9"); + } + checkClosed(); + this.defaultPriority = newDefaultPriority; + } + + /** + * Gets the producer's default priority. + * + * @return the message priority for this message producer + * @throws JMSException if the JMS provider fails to close the producer due to + * some internal error. + * @see javax.jms.MessageProducer#setPriority + */ + public int getPriority() throws JMSException { + checkClosed(); + return this.defaultPriority; + } + + /** + * Sets the default length of time in milliseconds from its dispatch time + * that a produced message should be retained by the message system. + *

+ * Time to live is set to zero by default. + * + * @param timeToLive the message time to live in milliseconds; zero is unlimited + * @throws JMSException if the JMS provider fails to set the time to live due to + * some internal error. + * @see javax.jms.MessageProducer#getTimeToLive + * @see javax.jms.Message#DEFAULT_TIME_TO_LIVE + */ + public void setTimeToLive(long timeToLive) throws JMSException { + if (timeToLive < 0l) { + throw new IllegalStateException("cannot set a negative timeToLive"); + } + checkClosed(); + this.defaultTimeToLive = timeToLive; + } + + /** + * Gets the default length of time in milliseconds from its dispatch time + * that a produced message should be retained by the message system. + * + * @return the message time to live in milliseconds; zero is unlimited + * @throws JMSException if the JMS provider fails to get the time to live due to + * some internal error. + * @see javax.jms.MessageProducer#setTimeToLive + */ + public long getTimeToLive() throws JMSException { + checkClosed(); + return this.defaultTimeToLive; + } + + /** + * Gets the destination associated with this MessageProducer. + * + * @return this producer's Destination/ + * @throws JMSException if the JMS provider fails to close the producer due to + * some internal error. + * @since 1.1 + */ + public Destination getDestination() throws JMSException { + checkClosed(); + return this.info.getDestination(); + } + + /** + * Closes the message producer. + *

+ * Since a provider may allocate some resources on behalf of a + * MessageProducer outside the Java virtual machine, clients should + * close them when they are not needed. Relying on garbage collection to + * eventually reclaim these resources may not be timely enough. + * + * @throws JMSException if the JMS provider fails to close the producer due to + * some internal error. + */ + public void close() throws JMSException { + if( closed==false ) { + dispose(); + this.session.asyncSendPacket(info.createRemoveCommand()); + } + } + + public void dispose() { + if( closed==false ) { + this.session.removeProducer(this); + closed = true; + } + } + + + /** + * Check if the instance of this producer has been closed. + * @throws IllegalStateException + */ + protected void checkClosed() throws IllegalStateException { + if (closed) { + throw new IllegalStateException("The producer is closed"); + } + } + + /** + * Sends a message using the MessageProducer's default + * delivery mode, priority, and time to live. + * + * @param message the message to send + * @throws JMSException if the JMS provider fails to send the message due to some + * internal error. + * @throws MessageFormatException if an invalid message is specified. + * @throws InvalidDestinationException if a client uses this method with a + * MessageProducer with an invalid destination. + * @throws java.lang.UnsupportedOperationException + * if a client uses this method with a + * MessageProducer that did not specify a + * destination at creation time. + * @see javax.jms.Session#createProducer + * @see javax.jms.MessageProducer + * @since 1.1 + */ + public void send(Message message) throws JMSException { + this.send(this.getDestination(), + message, + this.defaultDeliveryMode, + this.defaultPriority, + this.defaultTimeToLive); + } + + /** + * Sends a message to the destination, specifying delivery mode, priority, + * and time to live. + * + * @param message the message to send + * @param deliveryMode the delivery mode to use + * @param priority the priority for this message + * @param timeToLive the message's lifetime (in milliseconds) + * @throws JMSException if the JMS provider fails to send the message due to some + * internal error. + * @throws MessageFormatException if an invalid message is specified. + * @throws InvalidDestinationException if a client uses this method with a + * MessageProducer with an invalid destination. + * @throws java.lang.UnsupportedOperationException + * if a client uses this method with a + * MessageProducer that did not specify a + * destination at creation time. + * @see javax.jms.Session#createProducer + * @since 1.1 + */ + public void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException { + this.send(this.getDestination(), + message, + deliveryMode, + priority, + timeToLive); + } + + /** + * Sends a message to a destination for an unidentified message producer. + * Uses the MessageProducer's default delivery mode, + * priority, and time to live. + *

+ * Typically, a message producer is assigned a destination at creation + * time; however, the JMS API also supports unidentified message producers, + * which require that the destination be supplied every time a message is + * sent. + * + * @param destination the destination to send this message to + * @param message the message to send + * @throws JMSException if the JMS provider fails to send the message due to some + * internal error. + * @throws MessageFormatException if an invalid message is specified. + * @throws InvalidDestinationException if a client uses this method with an invalid destination. + * @throws java.lang.UnsupportedOperationException + * if a client uses this method with a + * MessageProducer that specified a destination at + * creation time. + * @see javax.jms.Session#createProducer + * @see javax.jms.MessageProducer + */ + public void send(Destination destination, Message message) throws JMSException { + this.send(destination, + message, + this.defaultDeliveryMode, + this.defaultPriority, + this.defaultTimeToLive); + } + + /** + * Sends a message to a destination for an unidentified message producer, + * specifying delivery mode, priority and time to live. + *

+ * Typically, a message producer is assigned a destination at creation + * time; however, the JMS API also supports unidentified message producers, + * which require that the destination be supplied every time a message is + * sent. + * + * @param destination the destination to send this message to + * @param message the message to send + * @param deliveryMode the delivery mode to use + * @param priority the priority for this message + * @param timeToLive the message's lifetime (in milliseconds) + * @throws JMSException if the JMS provider fails to send the message due to some + * internal error. + * @throws UnsupportedOperationException if an invalid destination is specified. + * @throws InvalidDestinationException if a client uses this method with an invalid destination. + * @see javax.jms.Session#createProducer + * @since 1.1 + */ + public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive) + throws JMSException { + checkClosed(); + if (destination == null) { + if( info.getDestination() == null ) { + throw new UnsupportedOperationException("A destination must be specified."); + } + throw new InvalidDestinationException("Don't understand null destinations"); + } + + ActiveMQDestination dest; + if( destination == info.getDestination() ) { + dest = (ActiveMQDestination) destination; + } else if ( info.getDestination() == null ) { + dest = ActiveMQDestination.transform(destination); + } else { + throw new UnsupportedOperationException("This producer can only send messages to: " + this.info.getDestination().getPhysicalName()); + } + + this.session.send(this, dest, message, deliveryMode, priority, timeToLive); + stats.onMessage(); + } + + /** + * @return the time in milli second when this object was created. + */ + protected long getStartTime() { + return this.startTime; + } + + /** + * @return Returns the messageSequence. + */ + protected long getMessageSequence() { + return messageSequence.incrementAndGet(); + } + + /** + * @param messageSequence The messageSequence to set. + */ + protected void setMessageSequence(AtomicLong messageSequence) { + this.messageSequence = messageSequence; + } + + /** + * @return Returns the info. + */ + protected ProducerInfo getProducerInfo(){ + return this.info!=null?this.info:null; + } + + /** + * @param info The info to set + */ + protected void setProducerInfo(ProducerInfo info){ + this.info = info; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQMessageTransformation.java b/activemq-core/src/main/java/org/activemq/ActiveMQMessageTransformation.java new file mode 100755 index 0000000000..1cfbba4817 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQMessageTransformation.java @@ -0,0 +1,190 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.util.Enumeration; + +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.MessageEOFException; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.StreamMessage; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.TextMessage; +import javax.jms.Topic; + +import org.activemq.command.ActiveMQDestination; + +import org.activemq.command.ActiveMQBytesMessage; +import org.activemq.command.ActiveMQMapMessage; +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ActiveMQObjectMessage; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQStreamMessage; +import org.activemq.command.ActiveMQTempQueue; +import org.activemq.command.ActiveMQTempTopic; +import org.activemq.command.ActiveMQTextMessage; +import org.activemq.command.ActiveMQTopic; + +class ActiveMQMessageTransformation { + + /** + * Creates a an available JMS message from another provider. + * + * @param destination - Destination to be converted into ActiveMQ's implementation. + * @return ActiveMQDestination - ActiveMQ's implementation of the destination. + * @throws JMSException if an error occurs + */ + public static ActiveMQDestination transformDestination(Destination destination) throws JMSException { + ActiveMQDestination activeMQDestination = null; + + if (destination != null) { + if (destination instanceof ActiveMQDestination) { + return (ActiveMQDestination) destination; + + } + else { + if (destination instanceof TemporaryQueue) { + activeMQDestination = new ActiveMQTempQueue(((Queue) destination).getQueueName()); + } + else if (destination instanceof TemporaryTopic) { + activeMQDestination = new ActiveMQTempTopic(((Topic) destination).getTopicName()); + } + else if (destination instanceof Queue) { + activeMQDestination = new ActiveMQQueue(((Queue) destination).getQueueName()); + } + else if (destination instanceof Topic) { + activeMQDestination = new ActiveMQTopic(((Topic) destination).getTopicName()); + } + } + } + + return activeMQDestination; + } + + + /** + * Creates a fast shallow copy of the current ActiveMQMessage or creates a whole new + * message instance from an available JMS message from another provider. + * + * @param message - Message to be converted into ActiveMQ's implementation. + * @param connection + * @return ActiveMQMessage - ActiveMQ's implementation object of the message. + * @throws JMSException if an error occurs + */ + public static final ActiveMQMessage transformMessage(Message message, ActiveMQConnection connection) throws JMSException { + if (message instanceof ActiveMQMessage) { + return (ActiveMQMessage) message; + + } else { + ActiveMQMessage activeMessage = null; + + if (message instanceof BytesMessage) { + BytesMessage bytesMsg = (BytesMessage) message; + bytesMsg.reset(); + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + msg.setConnection(connection); + try { + for (;;) { + // Reads a byte from the message stream until the stream + // is empty + msg.writeByte(bytesMsg.readByte()); + } + } catch (MessageEOFException e) { + // if an end of message stream as expected + } catch (JMSException e) { + } + + activeMessage = msg; + } else if (message instanceof MapMessage) { + MapMessage mapMsg = (MapMessage) message; + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + msg.setConnection(connection); + Enumeration iter = mapMsg.getMapNames(); + + while (iter.hasMoreElements()) { + String name = iter.nextElement().toString(); + msg.setObject(name, mapMsg.getObject(name)); + } + + activeMessage = msg; + } else if (message instanceof ObjectMessage) { + ObjectMessage objMsg = (ObjectMessage) message; + ActiveMQObjectMessage msg = new ActiveMQObjectMessage(); + msg.setConnection(connection); + msg.setObject(objMsg.getObject()); + msg.storeContent(); + activeMessage = msg; + } else if (message instanceof StreamMessage) { + StreamMessage streamMessage = (StreamMessage) message; + streamMessage.reset(); + ActiveMQStreamMessage msg = new ActiveMQStreamMessage(); + msg.setConnection(connection); + Object obj = null; + + try { + while ((obj = streamMessage.readObject()) != null) { + msg.writeObject(obj); + } + } catch (MessageEOFException e) { + // if an end of message stream as expected + } catch (JMSException e) { + } + + activeMessage = msg; + } else if (message instanceof TextMessage) { + TextMessage textMsg = (TextMessage) message; + ActiveMQTextMessage msg = new ActiveMQTextMessage(); + msg.setConnection(connection); + msg.setText(textMsg.getText()); + activeMessage = msg; + } else { + activeMessage = new ActiveMQMessage(); + activeMessage.setConnection(connection); + } + + activeMessage.setJMSMessageID(message.getJMSMessageID()); + activeMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + activeMessage.setJMSReplyTo(transformDestination(message.getJMSReplyTo())); + activeMessage.setJMSDestination(transformDestination(message.getJMSDestination())); + activeMessage.setJMSDeliveryMode(message.getJMSDeliveryMode()); + activeMessage.setJMSRedelivered(message.getJMSRedelivered()); + activeMessage.setJMSType(message.getJMSType()); + activeMessage.setJMSExpiration(message.getJMSExpiration()); + activeMessage.setJMSPriority(message.getJMSPriority()); + activeMessage.setJMSTimestamp(message.getJMSTimestamp()); + + Enumeration propertyNames = message.getPropertyNames(); + + while (propertyNames.hasMoreElements()) { + String name = propertyNames.nextElement().toString(); + Object obj = message.getObjectProperty(name); + activeMessage.setObjectProperty(name, obj); + } + + return activeMessage; + } + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQOutputStream.java b/activemq-core/src/main/java/org/activemq/ActiveMQOutputStream.java new file mode 100644 index 0000000000..0cc9a3878e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQOutputStream.java @@ -0,0 +1,158 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; + +import org.activeio.Disposable; +import org.activemq.command.ActiveMQBytesMessage; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.MessageId; +import org.activemq.command.ProducerId; +import org.activemq.command.ProducerInfo; +import org.activemq.util.IOExceptionSupport; + +/** + * @version $Revision$ + */ +public class ActiveMQOutputStream extends OutputStream implements Disposable { + + // Send down 64k messages. + final byte buffer[] = new byte[64 * 1024]; + protected int count; + + private final ActiveMQConnection connection; + private final HashMap properties; + private final ProducerInfo info; + + private long messageSequence; + private boolean closed; + private final int deliveryMode; + private final int priority; + private final long timeToLive; + + public ActiveMQOutputStream(ActiveMQConnection connection, ProducerId producerId, ActiveMQDestination destination, + Map properties, int deliveryMode, int priority, long timeToLive) throws JMSException { + this.connection = connection; + this.deliveryMode = deliveryMode; + this.priority = priority; + this.timeToLive = timeToLive; + this.properties = properties==null ? null : new HashMap(properties); + + if (destination == null) { + throw new InvalidDestinationException("Don't understand null destinations"); + } + + this.info = new ProducerInfo(producerId); + this.info.setDestination(destination); + + this.connection.addOutputStream(this); + this.connection.asyncSendPacket(info); + } + + public void close() throws IOException { + if (closed == false) { + flushBuffer(); + try { + // Send an EOS style empty message to signal EOS. + send(new ActiveMQMessage(), true); + dispose(); + this.connection.asyncSendPacket(info.createRemoveCommand()); + } catch (JMSException e) { + IOExceptionSupport.create(e); + } + } + } + + public void dispose() { + if (closed == false) { + this.connection.removeOutputStream(this); + closed = true; + } + } + + public synchronized void write(int b) throws IOException { + buffer[count++] = (byte) b; + if (count == buffer.length) { + flushBuffer(); + } + } + + public synchronized void write(byte b[], int off, int len) throws IOException { + while(len > 0) { + int max = Math.min(len, buffer.length-count); + System.arraycopy(b, off, buffer, count, max); + + len -= max; + count += max; + off += max; + + if (count == buffer.length) { + flushBuffer(); + } + } + } + + synchronized public void flush() throws IOException { + flushBuffer(); + } + + private void flushBuffer() throws IOException { + try { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + msg.writeBytes(buffer, 0, count); + send(msg, false); + } catch (JMSException e) { + throw IOExceptionSupport.create(e); + } + count=0; + } + + /** + * @param msg + * @throws JMSException + */ + private void send(ActiveMQMessage msg, boolean eosMessage) throws JMSException { + if (properties != null) { + for (Iterator iter = properties.keySet().iterator(); iter.hasNext();) { + String key = (String) iter.next(); + Object value = properties.get(key); + msg.setObjectProperty(key, value); + } + } + msg.setType("org.activemq.Stream"); + msg.setGroupID(info.getProducerId().toString()); + if( eosMessage ) { + msg.setGroupSequence(-1); + } else { + msg.setGroupSequence((int) messageSequence); + } + MessageId id = new MessageId(info.getProducerId(), messageSequence++); + connection.send(info.getDestination(), msg, id, deliveryMode, priority, timeToLive, !eosMessage); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQPrefetchPolicy.java b/activemq-core/src/main/java/org/activemq/ActiveMQPrefetchPolicy.java new file mode 100755 index 0000000000..22bf1ec95b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQPrefetchPolicy.java @@ -0,0 +1,130 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * Defines the pretech message policies for different types of consumers + * @version $Revision: 1.3 $ + */ +public class ActiveMQPrefetchPolicy { + private static final Log log = LogFactory.getLog(ActiveMQPrefetchPolicy.class); + private static final int MAX_PREFETCH_SIZE = (Short.MAX_VALUE -1); + private int queuePrefetch; + private int queueBrowserPrefetch; + private int topicPrefetch; + private int durableTopicPrefetch; + private int inputStreamPrefetch; + + + /** + * Initialize default prefetch policies + */ + public ActiveMQPrefetchPolicy() { + this.queuePrefetch = 1000; + this.queueBrowserPrefetch = 500; + this.topicPrefetch = MAX_PREFETCH_SIZE; + this.durableTopicPrefetch = 100; + this.inputStreamPrefetch = 100; + } + + /** + * @return Returns the durableTopicPrefetch. + */ + public int getDurableTopicPrefetch() { + return durableTopicPrefetch; + } + + /** + * @param durableTopicPrefetch The durableTopicPrefetch to set. + */ + public void setDurableTopicPrefetch(int durableTopicPrefetch) { + this.durableTopicPrefetch = getMaxPrefetchLimit(durableTopicPrefetch); + } + + /** + * @return Returns the queuePrefetch. + */ + public int getQueuePrefetch() { + return queuePrefetch; + } + + /** + * @param queuePrefetch The queuePrefetch to set. + */ + public void setQueuePrefetch(int queuePrefetch) { + this.queuePrefetch = getMaxPrefetchLimit(queuePrefetch); + } + + /** + * @return Returns the queueBrowserPrefetch. + */ + public int getQueueBrowserPrefetch() { + return queueBrowserPrefetch; + } + + /** + * @param queueBrowserPrefetch The queueBrowserPrefetch to set. + */ + public void setQueueBrowserPrefetch(int queueBrowserPrefetch) { + this.queueBrowserPrefetch = getMaxPrefetchLimit(queueBrowserPrefetch); + } + + /** + * @return Returns the topicPrefetch. + */ + public int getTopicPrefetch() { + return topicPrefetch; + } + + /** + * @param topicPrefetch The topicPrefetch to set. + */ + public void setTopicPrefetch(int topicPrefetch) { + this.topicPrefetch = getMaxPrefetchLimit(topicPrefetch); + } + + private int getMaxPrefetchLimit(int value) { + int result = Math.min(value, MAX_PREFETCH_SIZE); + if (result < value) { + log.warn("maximum prefetch limit has been reset from " + value + " to " + MAX_PREFETCH_SIZE); + } + return result; + } + + public void setAll(int i) { + this.durableTopicPrefetch=i; + this.queueBrowserPrefetch=i; + this.queuePrefetch=i; + this.topicPrefetch=i; + this.inputStreamPrefetch=1; + } + + public int getInputStreamPrefetch() { + return inputStreamPrefetch; + } + + public void setInputStreamPrefetch(int inputStreamPrefetch) { + this.inputStreamPrefetch = getMaxPrefetchLimit(inputStreamPrefetch); + } +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQQueueBrowser.java b/activemq-core/src/main/java/org/activemq/ActiveMQQueueBrowser.java new file mode 100755 index 0000000000..9d63aa448e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQQueueBrowser.java @@ -0,0 +1,245 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.util.Enumeration; + +import javax.jms.IllegalStateException; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Queue; +import javax.jms.QueueBrowser; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConsumerId; +import org.activemq.command.MessageDispatch; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +/** + * A client uses a QueueBrowser object to look at messages on a + * queue without removing them. + *

+ *

+ * The getEnumeration method returns a + * java.util.Enumeration that is used to scan the queue's messages. It + * may be an enumeration of the entire content of a queue, or it may contain + * only the messages matching a message selector. + *

+ *

+ * Messages may be arriving and expiring while the scan is done. The JMS API + * does not require the content of an enumeration to be a static snapshot of + * queue content. Whether these changes are visible or not depends on the JMS + * provider. + *

+ *

+ * A QueueBrowser can be created from either a Session + * or a QueueSession. + * + * @see javax.jms.Session#createBrowser + * @see javax.jms.QueueSession#createBrowser + * @see javax.jms.QueueBrowser + * @see javax.jms.QueueReceiver + */ + +public class ActiveMQQueueBrowser implements + QueueBrowser, Enumeration { + + private final ActiveMQSession session; + private final ActiveMQDestination destination; + private final String selector; + + private ActiveMQMessageConsumer consumer; + private boolean closed; + private final ConsumerId consumerId; + private final AtomicBoolean browseDone = new AtomicBoolean(true); + private final boolean dispatchAsync; + private Object semaphore = new Object(); + + /** + * Constructor for an ActiveMQQueueBrowser - used internally + * + * @param theSession + * @param dest + * @param selector + * @throws JMSException + */ + protected ActiveMQQueueBrowser(ActiveMQSession session, ConsumerId consumerId, ActiveMQDestination destination, String selector, boolean dispatchAsync) throws JMSException { + this.session = session; + this.consumerId = consumerId; + this.destination = destination; + this.selector = selector; + this.dispatchAsync=dispatchAsync; + this.consumer = createConsumer(); + } + + /** + * @param session + * @param originalDestination + * @param selector + * @param cnum + * @return + * @throws JMSException + */ + private ActiveMQMessageConsumer createConsumer() throws JMSException { + browseDone.set(false); + return new ActiveMQMessageConsumer(session, consumerId, destination, null, selector, session.connection.getPrefetchPolicy().getQueueBrowserPrefetch(), false, true, dispatchAsync) { + public void dispatch(MessageDispatch md) { + if( md.getMessage()==null ) { + browseDone.set(true); + } else { + super.dispatch(md); + } + notifyMessageAvailable(); + } + }; + } + + private void destroyConsumer() { + if( consumer == null ) + return; + try { + consumer.close(); + consumer=null; + } catch (JMSException e) { + e.printStackTrace(); + } + } + + /** + * Gets an enumeration for browsing the current queue messages in the order + * they would be received. + * + * @return an enumeration for browsing the messages + * @throws JMSException if the JMS provider fails to get the enumeration for this + * browser due to some internal error. + */ + + public Enumeration getEnumeration() throws JMSException { + checkClosed(); + if( consumer==null ) + consumer = createConsumer(); + return this; + } + + private void checkClosed() throws IllegalStateException { + if (closed) { + throw new IllegalStateException("The Consumer is closed"); + } + } + + /** + * @return true if more messages to process + */ + public boolean hasMoreElements() { + while( true ) { + + synchronized(this) { + if( consumer==null ) + return false; + } + + if( consumer.getMessageSize() > 0 ) { + return true; + } + + if( browseDone.get() || !session.isRunning() ) { + destroyConsumer(); + return false; + } + + waitForMessage(); + } + } + + + /** + * @return the next message + */ + public Object nextElement() { + while( true ) { + + synchronized(this) { + if( consumer==null ) + return null; + } + + try { + Message answer = consumer.receiveNoWait(); + if( answer!=null ) + return answer; + } catch (JMSException e) { + this.session.connection.onAsyncException(e); + return null; + } + + if( browseDone.get() || !session.isRunning() ) { + destroyConsumer(); + return null; + } + + waitForMessage(); + } + } + + synchronized public void close() throws JMSException { + destroyConsumer(); + closed=true; + } + + /** + * Gets the queue associated with this queue browser. + * + * @return the queue + * @throws JMSException if the JMS provider fails to get the queue associated + * with this browser due to some internal error. + */ + + public Queue getQueue() throws JMSException { + return (Queue) destination; + } + + + public String getMessageSelector() throws JMSException { + return selector; + } + + + // Implementation methods + // ------------------------------------------------------------------------- + + /** + * Wait on a semaphore for a fixed amount of time for a message to come in. + */ + protected void waitForMessage() { + try { + synchronized (semaphore ) { + semaphore.wait(2000); + } + } catch (InterruptedException e) { + } + } + + + protected void notifyMessageAvailable() { + synchronized (semaphore ) { + semaphore.notifyAll(); + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQQueueReceiver.java b/activemq-core/src/main/java/org/activemq/ActiveMQQueueReceiver.java new file mode 100755 index 0000000000..5e73a4d6d6 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQQueueReceiver.java @@ -0,0 +1,89 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConsumerId; + +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.QueueReceiver; + +/** + * A client uses a QueueReceiver object to receive messages that + * have been delivered to a queue. + *

+ *

+ * Although it is possible to have multiple QueueReceiver s for + * the same queue, the JMS API does not define how messages are distributed + * between the QueueReceivers. + *

+ *

+ * If a QueueReceiver specifies a message selector, the messages + * that are not selected remain on the queue. By definition, a message selector + * allows a QueueReceiver to skip messages. This means that when + * the skipped messages are eventually read, the total ordering of the reads + * does not retain the partial order defined by each message producer. Only + * QueueReceiver s without a message selector will read messages + * in message producer order. + *

+ *

+ * Creating a MessageConsumer provides the same features as + * creating a QueueReceiver. A MessageConsumer + * object is recommended for creating new code. The QueueReceiver + * is provided to support existing code. + * + * @see javax.jms.Session#createConsumer(javax.jms.Destination, String) + * @see javax.jms.Session#createConsumer(javax.jms.Destination) + * @see javax.jms.QueueSession#createReceiver(Queue, String) + * @see javax.jms.QueueSession#createReceiver(Queue) + * @see javax.jms.MessageConsumer + */ + +public class ActiveMQQueueReceiver extends ActiveMQMessageConsumer implements + QueueReceiver { + + /** + * @param theSession + * @param consumerId + * @param destination + * @param messageSelector + * @param prefetch + * @param asyncDispatch + * @throws JMSException + */ + protected ActiveMQQueueReceiver(ActiveMQSession theSession, + ConsumerId consumerId, ActiveMQDestination destination, String selector, int prefetch, boolean asyncDispatch) + throws JMSException { + super(theSession, consumerId, destination, null, selector, prefetch, false, false, asyncDispatch); + } + + /** + * Gets the Queue associated with this queue receiver. + * + * @return this receiver's Queue + * @throws JMSException if the JMS provider fails to get the queue for this queue + * receiver due to some internal error. + */ + + public Queue getQueue() throws JMSException { + checkClosed(); + return (Queue) super.getDestination(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQQueueSender.java b/activemq-core/src/main/java/org/activemq/ActiveMQQueueSender.java new file mode 100755 index 0000000000..67eaa2bfc2 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQQueueSender.java @@ -0,0 +1,146 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Queue; +import javax.jms.QueueSender; + +import org.activemq.command.ActiveMQDestination; + +/** + * A client uses a QueueSender object to send messages to a + * queue. + *

+ *

+ * Normally, the Queue is specified when a QueueSender + * is created. In this case, an attempt to use the send + * methods for an unidentified QueueSender will throw a + * java.lang.UnsupportedOperationException. + *

+ *

+ * If the QueueSender is created with an unidentified + * Queue, an attempt to use the send methods that + * assume that the Queue has been identified will throw a + * java.lang.UnsupportedOperationException. + *

+ *

+ * During the execution of its send method, a message must not + * be changed by other threads within the client. If the message is modified, + * the result of the send is undefined. + *

+ *

+ * After sending a message, a client may retain and modify it without affecting + * the message that has been sent. The same message object may be sent multiple + * times. + *

+ *

+ * The following message headers are set as part of sending a message: JMSDestination, + * JMSDeliveryMode,JMSExpiration,JMSPriority, + * JMSMessageID and JMSTimeStamp. When the + * message is sent, the values of these headers are ignored. After the + * completion of the send, the headers hold the values + * specified by the method sending the message. It is possible for the send + * method not to set JMSMessageID and JMSTimeStamp + * if the setting of these headers is explicitly disabled by the MessageProducer.setDisableMessageID + * or MessageProducer.setDisableMessageTimestamp method. + *

+ *

+ * Creating a MessageProducer provides the same features as + * creating a QueueSender. A MessageProducer + * object is recommended when creating new code. The QueueSender + * is provided to support existing code. + * + * @see javax.jms.MessageProducer + * @see javax.jms.QueueSession#createSender(Queue) + */ + +public class ActiveMQQueueSender extends ActiveMQMessageProducer implements QueueSender { + + protected ActiveMQQueueSender(ActiveMQSession session, ActiveMQDestination destination) + throws JMSException { + super(session, + session.getNextProducerId(), + destination); + } + + /** + * Gets the queue associated with this QueueSender. + * + * @return this sender's queue + * @throws JMSException if the JMS provider fails to get the queue for this + * QueueSender due to some internal error. + */ + + public Queue getQueue() throws JMSException { + return (Queue) super.getDestination(); + } + + /** + * Sends a message to a queue for an unidentified message producer. Uses + * the QueueSender's default delivery mode, priority, and + * time to live. + *

+ *

+ * Typically, a message producer is assigned a queue at creation time; + * however, the JMS API also supports unidentified message producers, which + * require that the queue be supplied every time a message is sent. + * + * @param queue the queue to send this message to + * @param message the message to send + * @throws JMSException if the JMS provider fails to send the message due to some + * internal error. + * @see javax.jms.MessageProducer#getDeliveryMode() + * @see javax.jms.MessageProducer#getTimeToLive() + * @see javax.jms.MessageProducer#getPriority() + */ + + public void send(Queue queue, Message message) throws JMSException { + super.send(queue, message); + } + + /** + * Sends a message to a queue for an unidentified message producer, + * specifying delivery mode, priority and time to live. + *

+ *

+ * Typically, a message producer is assigned a queue at creation time; + * however, the JMS API also supports unidentified message producers, which + * require that the queue be supplied every time a message is sent. + * + * @param queue the queue to send this message to + * @param message the message to send + * @param deliveryMode the delivery mode to use + * @param priority the priority for this message + * @param timeToLive the message's lifetime (in milliseconds) + * @throws JMSException if the JMS provider fails to send the message due to some + * internal error. + */ + + public void send(Queue queue, Message message, int deliveryMode, int priority, long timeToLive) + throws JMSException { + super.send(queue, + message, + deliveryMode, + priority, + timeToLive); + } +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQQueueSession.java b/activemq-core/src/main/java/org/activemq/ActiveMQQueueSession.java new file mode 100755 index 0000000000..51fd6a6b82 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQQueueSession.java @@ -0,0 +1,352 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.io.Serializable; + +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.IllegalStateException; +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.QueueBrowser; +import javax.jms.QueueReceiver; +import javax.jms.QueueSender; +import javax.jms.QueueSession; +import javax.jms.StreamMessage; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicSubscriber; + +/** + * A QueueSession implementation that throws IllegalStateExceptions + * when Topic operations are attempted but which delegates + * to another QueueSession for all other operations. + * + * The ActiveMQSessions implement both Topic and Queue Sessions + * methods but the spec states that Queue session should throw Exceptions + * if topic operations are attempted on it. + * + * @version $Revision: 1.2 $ + */ +public class ActiveMQQueueSession implements QueueSession { + + private final QueueSession next; + + public ActiveMQQueueSession(QueueSession next) { + this.next = next; + } + + /** + * @throws JMSException + */ + public void close() throws JMSException { + next.close(); + } + /** + * @throws JMSException + */ + public void commit() throws JMSException { + next.commit(); + } + /** + * @param queue + * @return + * @throws JMSException + */ + public QueueBrowser createBrowser(Queue queue) throws JMSException { + return next.createBrowser(queue); + } + /** + * @param queue + * @param messageSelector + * @return + * @throws JMSException + */ + public QueueBrowser createBrowser(Queue queue, String messageSelector) + throws JMSException { + return next.createBrowser(queue, messageSelector); + } + /** + * @return + * @throws JMSException + */ + public BytesMessage createBytesMessage() throws JMSException { + return next.createBytesMessage(); + } + /** + * @param destination + * @return + * @throws JMSException + */ + public MessageConsumer createConsumer(Destination destination) + throws JMSException { + if( destination instanceof Topic ) + throw new InvalidDestinationException("Topics are not supported by a QueueSession"); + return next.createConsumer(destination); + } + /** + * @param destination + * @param messageSelector + * @return + * @throws JMSException + */ + public MessageConsumer createConsumer(Destination destination, + String messageSelector) throws JMSException { + if( destination instanceof Topic ) + throw new InvalidDestinationException("Topics are not supported by a QueueSession"); + return next.createConsumer(destination, messageSelector); + } + /** + * @param destination + * @param messageSelector + * @param NoLocal + * @return + * @throws JMSException + */ + public MessageConsumer createConsumer(Destination destination, + String messageSelector, boolean NoLocal) throws JMSException { + if( destination instanceof Topic ) + throw new InvalidDestinationException("Topics are not supported by a QueueSession"); + return next.createConsumer(destination, messageSelector, NoLocal); + } + /** + * @param topic + * @param name + * @return + * @throws JMSException + */ + public TopicSubscriber createDurableSubscriber(Topic topic, String name) + throws JMSException { + throw new IllegalStateException("Operation not supported by a QueueSession"); + } + /** + * @param topic + * @param name + * @param messageSelector + * @param noLocal + * @return + * @throws JMSException + */ + public TopicSubscriber createDurableSubscriber(Topic topic, String name, + String messageSelector, boolean noLocal) throws JMSException { + throw new IllegalStateException("Operation not supported by a QueueSession"); + } + /** + * @return + * @throws JMSException + */ + public MapMessage createMapMessage() throws JMSException { + return next.createMapMessage(); + } + /** + * @return + * @throws JMSException + */ + public Message createMessage() throws JMSException { + return next.createMessage(); + } + /** + * @return + * @throws JMSException + */ + public ObjectMessage createObjectMessage() throws JMSException { + return next.createObjectMessage(); + } + /** + * @param object + * @return + * @throws JMSException + */ + public ObjectMessage createObjectMessage(Serializable object) + throws JMSException { + return next.createObjectMessage(object); + } + /** + * @param destination + * @return + * @throws JMSException + */ + public MessageProducer createProducer(Destination destination) + throws JMSException { + if( destination instanceof Topic ) + throw new InvalidDestinationException("Topics are not supported by a QueueSession"); + return next.createProducer(destination); + } + /** + * @param queueName + * @return + * @throws JMSException + */ + public Queue createQueue(String queueName) throws JMSException { + return next.createQueue(queueName); + } + /** + * @param queue + * @return + * @throws JMSException + */ + public QueueReceiver createReceiver(Queue queue) throws JMSException { + return next.createReceiver(queue); + } + /** + * @param queue + * @param messageSelector + * @return + * @throws JMSException + */ + public QueueReceiver createReceiver(Queue queue, String messageSelector) + throws JMSException { + return next.createReceiver(queue, messageSelector); + } + /** + * @param queue + * @return + * @throws JMSException + */ + public QueueSender createSender(Queue queue) throws JMSException { + return next.createSender(queue); + } + /** + * @return + * @throws JMSException + */ + public StreamMessage createStreamMessage() throws JMSException { + return next.createStreamMessage(); + } + /** + * @return + * @throws JMSException + */ + public TemporaryQueue createTemporaryQueue() throws JMSException { + return next.createTemporaryQueue(); + } + /** + * @return + * @throws JMSException + */ + public TemporaryTopic createTemporaryTopic() throws JMSException { + throw new IllegalStateException("Operation not supported by a QueueSession"); + } + /** + * @return + * @throws JMSException + */ + public TextMessage createTextMessage() throws JMSException { + return next.createTextMessage(); + } + /** + * @param text + * @return + * @throws JMSException + */ + public TextMessage createTextMessage(String text) throws JMSException { + return next.createTextMessage(text); + } + /** + * @param topicName + * @return + * @throws JMSException + */ + public Topic createTopic(String topicName) throws JMSException { + throw new IllegalStateException("Operation not supported by a QueueSession"); + } + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object arg0) { + return next.equals(arg0); + } + /** + * @return + * @throws JMSException + */ + public int getAcknowledgeMode() throws JMSException { + return next.getAcknowledgeMode(); + } + /** + * @return + * @throws JMSException + */ + public MessageListener getMessageListener() throws JMSException { + return next.getMessageListener(); + } + /** + * @return + * @throws JMSException + */ + public boolean getTransacted() throws JMSException { + return next.getTransacted(); + } + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return next.hashCode(); + } + /** + * @throws JMSException + */ + public void recover() throws JMSException { + next.recover(); + } + /** + * @throws JMSException + */ + public void rollback() throws JMSException { + next.rollback(); + } + /** + * + */ + public void run() { + next.run(); + } + /** + * @param listener + * @throws JMSException + */ + public void setMessageListener(MessageListener listener) + throws JMSException { + next.setMessageListener(listener); + } + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + return next.toString(); + } + /** + * @param name + * @throws JMSException + */ + public void unsubscribe(String name) throws JMSException { + throw new IllegalStateException("Operation not supported by a QueueSession"); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQSession.java b/activemq-core/src/main/java/org/activemq/ActiveMQSession.java new file mode 100755 index 0000000000..05e16e980e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQSession.java @@ -0,0 +1,1667 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.IllegalStateException; +import javax.jms.InvalidDestinationException; +import javax.jms.InvalidSelectorException; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.QueueBrowser; +import javax.jms.QueueReceiver; +import javax.jms.QueueSender; +import javax.jms.QueueSession; +import javax.jms.Session; +import javax.jms.StreamMessage; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicPublisher; +import javax.jms.TopicSession; +import javax.jms.TopicSubscriber; +import javax.jms.TransactionRolledBackException; + +import org.activemq.command.ActiveMQBytesMessage; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQMapMessage; +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ActiveMQObjectMessage; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQStreamMessage; +import org.activemq.command.ActiveMQTextMessage; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.Command; +import org.activemq.command.ConsumerId; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageDispatch; +import org.activemq.command.MessageId; +import org.activemq.command.ProducerId; +import org.activemq.command.RedeliveryPolicy; +import org.activemq.command.Response; +import org.activemq.command.SessionId; +import org.activemq.command.SessionInfo; +import org.activemq.command.TransactionId; +import org.activemq.management.JMSSessionStatsImpl; +import org.activemq.management.StatsCapable; +import org.activemq.management.StatsImpl; +import org.activemq.thread.Scheduler; +import org.activemq.transaction.Synchronization; +import org.activemq.util.Callback; +import org.activemq.util.LongSequenceGenerator; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +/** + *

+ * A Session object is a single-threaded context for producing + * and consuming messages. Although it may allocate provider resources outside + * the Java virtual machine (JVM), it is considered a lightweight JMS object. + *

+ * A session serves several purposes: + *

    + *
  • It is a factory for its message producers and consumers. + *
  • It supplies provider-optimized message factories. + *
  • It is a factory for TemporaryTopics and TemporaryQueues. + *
  • It provides a way to create Queue or Topic + * objects for those clients that need to dynamically manipulate + * provider-specific destination names. + *
  • It supports a single series of transactions that combine work spanning + * its producers and consumers into atomic units. + *
  • It defines a serial order for the messages it consumes and the messages + * it produces. + *
  • It retains messages it consumes until they have been acknowledged. + *
  • It serializes execution of message listeners registered with its message + * consumers. + *
  • It is a factory for QueueBrowsers. + *
+ *

+ * A session can create and service multiple message producers and consumers. + *

+ * One typical use is to have a thread block on a synchronous MessageConsumer + * until a message arrives. The thread may then use one or more of the Session'sMessageProducers. + *

+ * If a client desires to have one thread produce messages while others consume + * them, the client should use a separate session for its producing thread. + *

+ * Once a connection has been started, any session with one or more registered + * message listeners is dedicated to the thread of control that delivers + * messages to it. It is erroneous for client code to use this session or any of + * its constituent objects from another thread of control. The only exception to + * this rule is the use of the session or connection close + * method. + *

+ * It should be easy for most clients to partition their work naturally into + * sessions. This model allows clients to start simply and incrementally add + * message processing complexity as their need for concurrency grows. + *

+ * The close method is the only session method that can be called + * while some other session method is being executed in another thread. + *

+ * A session may be specified as transacted. Each transacted session supports a + * single series of transactions. Each transaction groups a set of message sends + * and a set of message receives into an atomic unit of work. In effect, + * transactions organize a session's input message stream and output message + * stream into series of atomic units. When a transaction commits, its atomic + * unit of input is acknowledged and its associated atomic unit of output is + * sent. If a transaction rollback is done, the transaction's sent messages are + * destroyed and the session's input is automatically recovered. + *

+ * The content of a transaction's input and output units is simply those + * messages that have been produced and consumed within the session's current + * transaction. + *

+ * A transaction is completed using either its session's commit + * method or its session's rollback method. The completion of a + * session's current transaction automatically begins the next. The result is + * that a transacted session always has a current transaction within which its + * work is done. + *

+ * The Java Transaction Service (JTS) or some other transaction monitor may be + * used to combine a session's transaction with transactions on other resources + * (databases, other JMS sessions, etc.). Since Java distributed transactions + * are controlled via the Java Transaction API (JTA), use of the session's + * commit and rollback methods in this context is + * prohibited. + *

+ * The JMS API does not require support for JTA; however, it does define how a + * provider supplies this support. + *

+ * Although it is also possible for a JMS client to handle distributed + * transactions directly, it is unlikely that many JMS clients will do this. + * Support for JTA in the JMS API is targeted at systems vendors who will be + * integrating the JMS API into their application server products. + * + * @version $Revision: 1.34 $ + * @see javax.jms.Session + * @see javax.jms.QueueSession + * @see javax.jms.TopicSession + * @see javax.jms.XASession + */ +public class ActiveMQSession implements Session, QueueSession, TopicSession, StatsCapable, ActiveMQDispatcher { + + public static interface DeliveryListener { + public void beforeDelivery(ActiveMQSession session, Message msg); + public void afterDelivery(ActiveMQSession session, Message msg); + } + + private static final Log log = LogFactory.getLog(ActiveMQSession.class); + + protected int acknowledgementMode; + + private MessageListener messageListener; + private JMSSessionStatsImpl stats; + private TransactionContext transactionContext; + private DeliveryListener deliveryListener; + + protected final ActiveMQConnection connection; + protected final SessionInfo info; + protected final LongSequenceGenerator consumerIdGenerator = new LongSequenceGenerator(); + protected final LongSequenceGenerator producerIdGenerator = new LongSequenceGenerator(); + protected final LongSequenceGenerator deliveryIdGenerator = new LongSequenceGenerator(); + protected final ActiveMQSessionExecutor executor = new ActiveMQSessionExecutor(this); + protected final AtomicBoolean started = new AtomicBoolean(false); + + protected final CopyOnWriteArrayList consumers = new CopyOnWriteArrayList(); + protected final CopyOnWriteArrayList producers = new CopyOnWriteArrayList(); + + protected boolean closed; + protected boolean asyncDispatch; + + /** + * Construct the Session + * + * @param connection + * @param acknowledgeMode + * n.b if transacted - the acknowledgeMode == + * Session.SESSION_TRANSACTED + * @throws JMSException + * on internal error + */ + protected ActiveMQSession(ActiveMQConnection connection, SessionId sessionId, int acknowledgeMode, boolean asyncDispatch) + throws JMSException { + + this.connection = connection; + this.acknowledgementMode = acknowledgeMode; + this.asyncDispatch=asyncDispatch; + + this.info = new SessionInfo(connection.getConnectionInfo(), sessionId.getSessionId()); + setTransactionContext(new TransactionContext(connection)); + connection.addSession(this); + stats = new JMSSessionStatsImpl(producers, consumers); + this.connection.asyncSendPacket(info); + + if( connection.isStarted() ) + start(); + + } + + /** + * Sets the transaction context of the session. + * + * @param transactionContext - + * provides the means to control a JMS transaction. + */ + public void setTransactionContext(TransactionContext transactionContext) { + this.transactionContext = transactionContext; + } + + /** + * Returns the transaction context of the session. + * + * @return transactionContext - session's transaction context. + */ + public TransactionContext getTransactionContext() { + return transactionContext; + } + + /* + * (non-Javadoc) + * + * @see org.activemq.management.StatsCapable#getStats() + */ + public StatsImpl getStats() { + return stats; + } + + /** + * Returns the session's statistics. + * + * @return stats - session's statistics. + */ + public JMSSessionStatsImpl getSessionStats() { + return stats; + } + + /** + * Creates a BytesMessage object. A BytesMessage + * object is used to send a message containing a stream of uninterpreted + * bytes. + * + * @return the an ActiveMQBytesMessage + * @throws JMSException + * if the JMS provider fails to create this message due to some + * internal error. + */ + public BytesMessage createBytesMessage() throws JMSException { + checkClosed(); + ActiveMQBytesMessage message = new ActiveMQBytesMessage(); + message.setConnection(connection); + return message; + } + + /** + * Creates a MapMessage object. A MapMessage + * object is used to send a self-defining set of name-value pairs, where + * names are String objects and values are primitive values + * in the Java programming language. + * + * @return an ActiveMQMapMessage + * @throws JMSException + * if the JMS provider fails to create this message due to some + * internal error. + */ + public MapMessage createMapMessage() throws JMSException { + checkClosed(); + ActiveMQMapMessage message = new ActiveMQMapMessage(); + message.setConnection(connection); + return message; + } + + /** + * Creates a Message object. The Message + * interface is the root interface of all JMS messages. A Message + * object holds all the standard message header information. It can be sent + * when a message containing only header information is sufficient. + * + * @return an ActiveMQMessage + * @throws JMSException + * if the JMS provider fails to create this message due to some + * internal error. + */ + public Message createMessage() throws JMSException { + checkClosed(); + ActiveMQMessage message = new ActiveMQMessage(); + message.setConnection(connection); + return message; + } + + /** + * Creates an ObjectMessage object. An ObjectMessage + * object is used to send a message that contains a serializable Java + * object. + * + * @return an ActiveMQObjectMessage + * @throws JMSException + * if the JMS provider fails to create this message due to some + * internal error. + */ + public ObjectMessage createObjectMessage() throws JMSException { + checkClosed(); + ActiveMQObjectMessage message = new ActiveMQObjectMessage(); + message.setConnection(connection); + return message; + } + + /** + * Creates an initialized ObjectMessage object. An ObjectMessage + * object is used to send a message that contains a serializable Java + * object. + * + * @param object + * the object to use to initialize this message + * @return an ActiveMQObjectMessage + * @throws JMSException + * if the JMS provider fails to create this message due to some + * internal error. + */ + public ObjectMessage createObjectMessage(Serializable object) throws JMSException { + checkClosed(); + ActiveMQObjectMessage message = new ActiveMQObjectMessage(); + message.setConnection(connection); + message.setObject(object); + return message; + } + + /** + * Creates a StreamMessage object. A StreamMessage + * object is used to send a self-defining stream of primitive values in the + * Java programming language. + * + * @return an ActiveMQStreamMessage + * @throws JMSException + * if the JMS provider fails to create this message due to some + * internal error. + */ + public StreamMessage createStreamMessage() throws JMSException { + checkClosed(); + ActiveMQStreamMessage message = new ActiveMQStreamMessage(); + message.setConnection(connection); + return message; + } + + /** + * Creates a TextMessage object. A TextMessage + * object is used to send a message containing a String + * object. + * + * @return an ActiveMQTextMessage + * @throws JMSException + * if the JMS provider fails to create this message due to some + * internal error. + */ + public TextMessage createTextMessage() throws JMSException { + checkClosed(); + ActiveMQTextMessage message = new ActiveMQTextMessage(); + message.setConnection(connection); + return message; + } + + /** + * Creates an initialized TextMessage object. A TextMessage + * object is used to send a message containing a String. + * + * @param text + * the string used to initialize this message + * @return an ActiveMQTextMessage + * @throws JMSException + * if the JMS provider fails to create this message due to some + * internal error. + */ + public TextMessage createTextMessage(String text) throws JMSException { + checkClosed(); + ActiveMQTextMessage message = new ActiveMQTextMessage(); + message.setText(text); + message.setConnection(connection); + return message; + } + + /** + * Indicates whether the session is in transacted mode. + * + * @return true if the session is in transacted mode + * @throws JMSException + * if there is some internal error. + */ + public boolean getTransacted() throws JMSException { + checkClosed(); + return ((acknowledgementMode == Session.SESSION_TRANSACTED) || (transactionContext.isInXATransaction())); + } + + /** + * Returns the acknowledgement mode of the session. The acknowledgement mode + * is set at the time that the session is created. If the session is + * transacted, the acknowledgement mode is ignored. + * + * @return If the session is not transacted, returns the current + * acknowledgement mode for the session. If the session is + * transacted, returns SESSION_TRANSACTED. + * @throws JMSException + * @see javax.jms.Connection#createSession(boolean,int) + * @since 1.1 exception JMSException if there is some internal error. + */ + public int getAcknowledgeMode() throws JMSException { + checkClosed(); + return this.acknowledgementMode; + } + + /** + * Commits all messages done in this transaction and releases any locks + * currently held. + * + * @throws JMSException + * if the JMS provider fails to commit the transaction due to + * some internal error. + * @throws TransactionRolledBackException + * if the transaction is rolled back due to some internal error + * during commit. + * @throws javax.jms.IllegalStateException + * if the method is not called by a transacted session. + */ + public void commit() throws JMSException { + checkClosed(); + if (!getTransacted()) { + throw new javax.jms.IllegalStateException("Not a transacted session"); + } + transactionContext.commit(); + } + + /** + * Rolls back any messages done in this transaction and releases any locks + * currently held. + * + * @throws JMSException + * if the JMS provider fails to roll back the transaction due to + * some internal error. + * @throws javax.jms.IllegalStateException + * if the method is not called by a transacted session. + */ + public void rollback() throws JMSException { + checkClosed(); + if (!getTransacted()) { + throw new javax.jms.IllegalStateException("Not a transacted session"); + } + transactionContext.rollback(); + } + + /** + * Closes the session. + *

+ * Since a provider may allocate some resources on behalf of a session + * outside the JVM, clients should close the resources when they are not + * needed. Relying on garbage collection to eventually reclaim these + * resources may not be timely enough. + *

+ * There is no need to close the producers and consumers of a closed + * session. + *

+ * This call will block until a receive call or message + * listener in progress has completed. A blocked message consumer receive + * call returns null when this session is closed. + *

+ * Closing a transacted session must roll back the transaction in progress. + *

+ * This method is the only Session method that can be called + * concurrently. + *

+ * Invoking any other Session method on a closed session must + * throw a JMSException.IllegalStateException. Closing a + * closed session must not throw an exception. + * + * @throws JMSException + * if the JMS provider fails to close the session due to some + * internal error. + */ + public void close() throws JMSException { + if (!closed) { + dispose(); + connection.asyncSendPacket(info.createRemoveCommand()); + } + } + + + public void dispose() throws JMSException { + if (!closed) { + + for (Iterator iter = consumers.iterator(); iter.hasNext();) { + ActiveMQMessageConsumer consumer = (ActiveMQMessageConsumer) iter.next(); + consumer.dispose(); + } + consumers.clear(); + + for (Iterator iter = producers.iterator(); iter.hasNext();) { + ActiveMQMessageProducer producer = (ActiveMQMessageProducer) iter.next(); + producer.dispose(); + } + producers.clear(); + + try { + if (getTransactionContext().isInLocalTransaction()) { + rollback(); + } + } catch (JMSException e) { + } + + + connection.removeSession(this); + this.transactionContext=null; + closed = true; + } + } + + /** + * Check if the session is closed. It is used for ensuring that the session + * is open before performing various operations. + * + * @throws IllegalStateException + * if the Session is closed + */ + protected void checkClosed() throws IllegalStateException { + if (closed) { + throw new IllegalStateException("The Session is closed"); + } + } + + /** + * Stops message delivery in this session, and restarts message delivery + * with the oldest unacknowledged message. + *

+ * All consumers deliver messages in a serial order. Acknowledging a + * received message automatically acknowledges all messages that have been + * delivered to the client. + *

+ * Restarting a session causes it to take the following actions: + *

    + *
  • Stop message delivery + *
  • Mark all messages that might have been delivered but not + * acknowledged as "redelivered" + *
  • Restart the delivery sequence including all unacknowledged messages + * that had been previously delivered. Redelivered messages do not have to + * be delivered in exactly their original delivery order. + *
+ * + * @throws JMSException + * if the JMS provider fails to stop and restart message + * delivery due to some internal error. + * @throws IllegalStateException + * if the method is called by a transacted session. + */ + public void recover() throws JMSException { + + checkClosed(); + if (getTransacted()) { + throw new IllegalStateException("This session is transacted"); + } + + for (Iterator iter = consumers.iterator(); iter.hasNext();) { + ActiveMQMessageConsumer c = (ActiveMQMessageConsumer) iter.next(); + c.rollback(); + } + + } + + /** + * Returns the session's distinguished message listener (optional). + * + * @return the message listener associated with this session + * @throws JMSException + * if the JMS provider fails to get the message listener due to + * an internal error. + * @see javax.jms.Session#setMessageListener(javax.jms.MessageListener) + * @see javax.jms.ServerSessionPool + * @see javax.jms.ServerSession + */ + public MessageListener getMessageListener() throws JMSException { + checkClosed(); + return this.messageListener; + } + + /** + * Sets the session's distinguished message listener (optional). + *

+ * When the distinguished message listener is set, no other form of message + * receipt in the session can be used; however, all forms of sending + * messages are still supported. + *

+ * This is an expert facility not used by regular JMS clients. + * + * @param listener + * the message listener to associate with this session + * @throws JMSException + * if the JMS provider fails to set the message listener due to + * an internal error. + * @see javax.jms.Session#getMessageListener() + * @see javax.jms.ServerSessionPool + * @see javax.jms.ServerSession + */ + public void setMessageListener(MessageListener listener) throws JMSException { + checkClosed(); + this.messageListener = listener; + + if (listener != null) { + executor.setDispatchedBySessionPool(true); + } + } + + /** + * Optional operation, intended to be used only by Application Servers, not + * by ordinary JMS clients. + * + * @see javax.jms.ServerSession + */ + public void run() { + MessageDispatch messageDispatch; + while ((messageDispatch = executor.dequeueNoWait()) != null) { + final MessageDispatch md = messageDispatch; + ActiveMQMessage message = (ActiveMQMessage)md.getMessage(); + if( message.isExpired() ) { + //TODO: Ack it without delivery to client + continue; + } + + if( isClientAcknowledge() ) { + message.setAcknowledgeCallback(new Callback() { + public void execute() throws Throwable { + } + }); + } + + if (deliveryListener != null) { + deliveryListener.beforeDelivery(this, message); + } + + md.setDeliverySequenceId(getNextDeliveryId()); + + try { + messageListener.onMessage(message); + } catch ( Throwable e ) { + // TODO: figure out proper way to handle error. + } + + try { + MessageAck ack = new MessageAck(md,MessageAck.STANDARD_ACK_TYPE,1); + ack.setFirstMessageId(md.getMessage().getMessageId()); + doStartTransaction(); + ack.setTransactionId(getTransactionContext().getTransactionId()); + if( ack.getTransactionId()!=null ) { + getTransactionContext().addSynchronization(new Synchronization(){ + public void afterRollback() throws Throwable { + + md.getMessage().incrementRedeliveryCounter(); + + RedeliveryPolicy redeliveryPolicy = connection.getRedeliveryPolicy(); + int redeliveryCounter = md.getMessage().getRedeliveryCounter(); + if (redeliveryCounter > redeliveryPolicy.getMaximumRedeliveries()) { + + // We need to NACK the messages so that they get sent to the + // DLQ. + + // Acknowledge the last message. + MessageAck ack = new MessageAck(md,MessageAck.POSION_ACK_TYPE,1); + ack.setFirstMessageId(md.getMessage().getMessageId()); + asyncSendPacket(ack); + + } else { + + // Figure out how long we should wait to resend this message. + long redeliveryDelay=0; + for( int i=0; i < redeliveryCounter; i++) { + if (redeliveryDelay == 0) { + redeliveryDelay = redeliveryPolicy.getInitialRedeliveryDelay(); + } else { + if (redeliveryPolicy.isUseExponentialBackOff()) + redeliveryDelay *= redeliveryPolicy.getBackOffMultiplier(); + } + } + + Scheduler.executeAfterDelay(new Runnable() { + public void run() { + ((ActiveMQDispatcher)md.getConsumer()).dispatch(md); + } + }, redeliveryDelay); + + } + } + }); + } + asyncSendPacket(ack); + } catch ( Throwable e ) { + connection.onAsyncException(e); + } + + if (deliveryListener != null) { + deliveryListener.afterDelivery(this, message); + } + } + } + + /** + * Creates a MessageProducer to send messages to the + * specified destination. + *

+ * A client uses a MessageProducer object to send messages to + * a destination. Since Queue and Topic both + * inherit from Destination, they can be used in the + * destination parameter to create a MessageProducer object. + * + * @param destination + * the Destination to send to, or null if this is + * a producer which does not have a specified destination. + * @return the MessageProducer + * @throws JMSException + * if the session fails to create a MessageProducer due to some + * internal error. + * @throws InvalidDestinationException + * if an invalid destination is specified. + * @since 1.1 + */ + public MessageProducer createProducer(Destination destination) throws JMSException { + checkClosed(); + return new ActiveMQMessageProducer(this, getNextProducerId(), ActiveMQMessageTransformation + .transformDestination(destination)); + } + + /** + * Creates a MessageConsumer for the specified destination. + * Since Queue and Topic both inherit from + * Destination, they can be used in the destination + * parameter to create a MessageConsumer. + * + * @param destination + * the Destination to access. + * @return the MessageConsumer + * @throws JMSException + * if the session fails to create a consumer due to some + * internal error. + * @throws InvalidDestinationException + * if an invalid destination is specified. + * @since 1.1 + */ + public MessageConsumer createConsumer(Destination destination) throws JMSException { + checkClosed(); + return createConsumer(destination, null); + } + + /** + * Creates a MessageConsumer for the specified destination, + * using a message selector. Since Queue and Topic + * both inherit from Destination, they can be used in the + * destination parameter to create a MessageConsumer. + *

+ * A client uses a MessageConsumer object to receive messages + * that have been sent to a destination. + * + * @param destination + * the Destination to access + * @param messageSelector + * only messages with properties matching the message selector + * expression are delivered. A value of null or an empty string + * indicates that there is no message selector for the message + * consumer. + * @return the MessageConsumer + * @throws JMSException + * if the session fails to create a MessageConsumer due to some + * internal error. + * @throws InvalidDestinationException + * if an invalid destination is specified. + * @throws InvalidSelectorException + * if the message selector is invalid. + * @since 1.1 + */ + public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException { + checkClosed(); + int prefetch = 0; + + if (destination instanceof Topic) { + prefetch = connection.getPrefetchPolicy().getTopicPrefetch(); + } else { + prefetch = connection.getPrefetchPolicy().getQueuePrefetch(); + } + + return new ActiveMQMessageConsumer(this, getNextConsumerId(), ActiveMQMessageTransformation + .transformDestination(destination), null, messageSelector, prefetch, false, false, asyncDispatch); + } + + /** + * @return + */ + protected ConsumerId getNextConsumerId() { + return new ConsumerId(info.getSessionId(), consumerIdGenerator.getNextSequenceId()); + } + + /** + * @return + */ + protected ProducerId getNextProducerId() { + return new ProducerId(info.getSessionId(), producerIdGenerator.getNextSequenceId()); + } + + /** + * Creates MessageConsumer for the specified destination, + * using a message selector. This method can specify whether messages + * published by its own connection should be delivered to it, if the + * destination is a topic. + *

+ * Since Queue and Topic both inherit from + * Destination, they can be used in the destination + * parameter to create a MessageConsumer. + *

+ * A client uses a MessageConsumer object to receive messages + * that have been published to a destination. + *

+ * In some cases, a connection may both publish and subscribe to a topic. + * The consumer NoLocal attribute allows a consumer to + * inhibit the delivery of messages published by its own connection. The + * default value for this attribute is False. The noLocal + * value must be supported by destinations that are topics. + * + * @param destination + * the Destination to access + * @param messageSelector + * only messages with properties matching the message selector + * expression are delivered. A value of null or an empty string + * indicates that there is no message selector for the message + * consumer. + * @param NoLocal - + * if true, and the destination is a topic, inhibits the delivery + * of messages published by its own connection. The behavior for + * NoLocal is not specified if the destination is + * a queue. + * @return the MessageConsumer + * @throws JMSException + * if the session fails to create a MessageConsumer due to some + * internal error. + * @throws InvalidDestinationException + * if an invalid destination is specified. + * @throws InvalidSelectorException + * if the message selector is invalid. + * @since 1.1 + */ + public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean NoLocal) + throws JMSException { + checkClosed(); + return new ActiveMQMessageConsumer(this, getNextConsumerId(), ActiveMQMessageTransformation + .transformDestination(destination), null, messageSelector, connection.getPrefetchPolicy() + .getTopicPrefetch(), NoLocal, false, asyncDispatch); + } + + /** + * Creates a queue identity given a Queue name. + *

+ * This facility is provided for the rare cases where clients need to + * dynamically manipulate queue identity. It allows the creation of a queue + * identity with a provider-specific name. Clients that depend on this + * ability are not portable. + *

+ * Note that this method is not for creating the physical queue. The + * physical creation of queues is an administrative task and is not to be + * initiated by the JMS API. The one exception is the creation of temporary + * queues, which is accomplished with the createTemporaryQueue + * method. + * + * @param queueName + * the name of this Queue + * @return a Queue with the given name + * @throws JMSException + * if the session fails to create a queue due to some internal + * error. + * @since 1.1 + */ + public Queue createQueue(String queueName) throws JMSException { + checkClosed(); + return new ActiveMQQueue(queueName); + } + + /** + * Creates a topic identity given a Topic name. + *

+ * This facility is provided for the rare cases where clients need to + * dynamically manipulate topic identity. This allows the creation of a + * topic identity with a provider-specific name. Clients that depend on this + * ability are not portable. + *

+ * Note that this method is not for creating the physical topic. The + * physical creation of topics is an administrative task and is not to be + * initiated by the JMS API. The one exception is the creation of temporary + * topics, which is accomplished with the createTemporaryTopic + * method. + * + * @param topicName + * the name of this Topic + * @return a Topic with the given name + * @throws JMSException + * if the session fails to create a topic due to some internal + * error. + * @since 1.1 + */ + public Topic createTopic(String topicName) throws JMSException { + checkClosed(); + return new ActiveMQTopic(topicName); + } + + /** + * Creates a QueueBrowser object to peek at the messages on + * the specified queue. + * + * @param queue + * the queue to access + * @exception InvalidDestinationException + * if an invalid destination is specified + * @since 1.1 + */ + /** + * Creates a durable subscriber to the specified topic. + *

+ * If a client needs to receive all the messages published on a topic, + * including the ones published while the subscriber is inactive, it uses a + * durable TopicSubscriber. The JMS provider retains a + * record of this durable subscription and insures that all messages from + * the topic's publishers are retained until they are acknowledged by this + * durable subscriber or they have expired. + *

+ * Sessions with durable subscribers must always provide the same client + * identifier. In addition, each client must specify a name that uniquely + * identifies (within client identifier) each durable subscription it + * creates. Only one session at a time can have a TopicSubscriber + * for a particular durable subscription. + *

+ * A client can change an existing durable subscription by creating a + * durable TopicSubscriber with the same name and a new topic + * and/or message selector. Changing a durable subscriber is equivalent to + * unsubscribing (deleting) the old one and creating a new one. + *

+ * In some cases, a connection may both publish and subscribe to a topic. + * The subscriber NoLocal attribute allows a subscriber to + * inhibit the delivery of messages published by its own connection. The + * default value for this attribute is false. + * + * @param topic + * the non-temporary Topic to subscribe to + * @param name + * the name used to identify this subscription + * @return the TopicSubscriber + * @throws JMSException + * if the session fails to create a subscriber due to some + * internal error. + * @throws InvalidDestinationException + * if an invalid topic is specified. + * @since 1.1 + */ + public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException { + checkClosed(); + return createDurableSubscriber(topic, name, null, false); + } + + /** + * Creates a durable subscriber to the specified topic, using a message + * selector and specifying whether messages published by its own connection + * should be delivered to it. + *

+ * If a client needs to receive all the messages published on a topic, + * including the ones published while the subscriber is inactive, it uses a + * durable TopicSubscriber. The JMS provider retains a + * record of this durable subscription and insures that all messages from + * the topic's publishers are retained until they are acknowledged by this + * durable subscriber or they have expired. + *

+ * Sessions with durable subscribers must always provide the same client + * identifier. In addition, each client must specify a name which uniquely + * identifies (within client identifier) each durable subscription it + * creates. Only one session at a time can have a TopicSubscriber + * for a particular durable subscription. An inactive durable subscriber is + * one that exists but does not currently have a message consumer associated + * with it. + *

+ * A client can change an existing durable subscription by creating a + * durable TopicSubscriber with the same name and a new topic + * and/or message selector. Changing a durable subscriber is equivalent to + * unsubscribing (deleting) the old one and creating a new one. + * + * @param topic + * the non-temporary Topic to subscribe to + * @param name + * the name used to identify this subscription + * @param messageSelector + * only messages with properties matching the message selector + * expression are delivered. A value of null or an empty string + * indicates that there is no message selector for the message + * consumer. + * @param noLocal + * if set, inhibits the delivery of messages published by its own + * connection + * @return the Queue Browser + * @throws JMSException + * if the session fails to create a subscriber due to some + * internal error. + * @throws InvalidDestinationException + * if an invalid topic is specified. + * @throws InvalidSelectorException + * if the message selector is invalid. + * @since 1.1 + */ + public TopicSubscriber createDurableSubscriber(Topic topic, String name, String messageSelector, boolean noLocal) + throws JMSException { + checkClosed(); + connection.checkClientIDWasManuallySpecified(); + return new ActiveMQTopicSubscriber(this, getNextConsumerId(), ActiveMQMessageTransformation + .transformDestination(topic), name, messageSelector, this.connection.getPrefetchPolicy() + .getDurableTopicPrefetch(), noLocal, false, asyncDispatch); + } + + /** + * Creates a QueueBrowser object to peek at the messages on + * the specified queue. + * + * @param queue + * the queue to access + * @return the Queue Browser + * @throws JMSException + * if the session fails to create a browser due to some internal + * error. + * @throws InvalidDestinationException + * if an invalid destination is specified + * @since 1.1 + */ + public QueueBrowser createBrowser(Queue queue) throws JMSException { + checkClosed(); + return createBrowser(queue, null); + } + + /** + * Creates a QueueBrowser object to peek at the messages on + * the specified queue using a message selector. + * + * @param queue + * the queue to access + * @param messageSelector + * only messages with properties matching the message selector + * expression are delivered. A value of null or an empty string + * indicates that there is no message selector for the message + * consumer. + * @return the Queue Browser + * @throws JMSException + * if the session fails to create a browser due to some internal + * error. + * @throws InvalidDestinationException + * if an invalid destination is specified + * @throws InvalidSelectorException + * if the message selector is invalid. + * @since 1.1 + */ + public QueueBrowser createBrowser(Queue queue, String messageSelector) throws JMSException { + checkClosed(); + return new ActiveMQQueueBrowser(this, getNextConsumerId(), ActiveMQMessageTransformation + .transformDestination(queue), messageSelector, asyncDispatch); + } + + /** + * Creates a TemporaryQueue object. Its lifetime will be that + * of the Connection unless it is deleted earlier. + * + * @return a temporary queue identity + * @throws JMSException + * if the session fails to create a temporary queue due to some + * internal error. + * @since 1.1 + */ + public TemporaryQueue createTemporaryQueue() throws JMSException { + checkClosed(); + return (TemporaryQueue) connection.createTempDestination(false); + } + + /** + * Creates a TemporaryTopic object. Its lifetime will be that + * of the Connection unless it is deleted earlier. + * + * @return a temporary topic identity + * @throws JMSException + * if the session fails to create a temporary topic due to some + * internal error. + * @since 1.1 + */ + public TemporaryTopic createTemporaryTopic() throws JMSException { + checkClosed(); + return (TemporaryTopic)connection.createTempDestination(true); + } + + /** + * Creates a QueueReceiver object to receive messages from + * the specified queue. + * + * @param queue + * the Queue to access + * @return + * @throws JMSException + * if the session fails to create a receiver due to some + * internal error. + * @throws JMSException + * @throws InvalidDestinationException + * if an invalid queue is specified. + */ + public QueueReceiver createReceiver(Queue queue) throws JMSException { + checkClosed(); + return createReceiver(queue, null); + } + + /** + * Creates a QueueReceiver object to receive messages from + * the specified queue using a message selector. + * + * @param queue + * the Queue to access + * @param messageSelector + * only messages with properties matching the message selector + * expression are delivered. A value of null or an empty string + * indicates that there is no message selector for the message + * consumer. + * @return QueueReceiver + * @throws JMSException + * if the session fails to create a receiver due to some + * internal error. + * @throws InvalidDestinationException + * if an invalid queue is specified. + * @throws InvalidSelectorException + * if the message selector is invalid. + */ + public QueueReceiver createReceiver(Queue queue, String messageSelector) throws JMSException { + checkClosed(); + return new ActiveMQQueueReceiver(this, getNextConsumerId(), ActiveMQMessageTransformation + .transformDestination(queue), messageSelector, this.connection.getPrefetchPolicy().getQueuePrefetch(), asyncDispatch); + } + + /** + * Creates a QueueSender object to send messages to the + * specified queue. + * + * @param queue + * the Queue to access, or null if this is an + * unidentified producer + * @return QueueSender + * @throws JMSException + * if the session fails to create a sender due to some internal + * error. + * @throws InvalidDestinationException + * if an invalid queue is specified. + */ + public QueueSender createSender(Queue queue) throws JMSException { + checkClosed(); + return new ActiveMQQueueSender(this, ActiveMQMessageTransformation.transformDestination(queue)); + } + + /** + * Creates a nondurable subscriber to the specified topic.

+ *

+ * A client uses a TopicSubscriber object to receive messages + * that have been published to a topic.

+ *

+ * Regular TopicSubscriber objects are not durable. They + * receive only messages that are published while they are active.

+ *

+ * In some cases, a connection may both publish and subscribe to a topic. + * The subscriber NoLocal attribute allows a subscriber to + * inhibit the delivery of messages published by its own connection. The + * default value for this attribute is false. + * + * @param topic + * the Topic to subscribe to + * @return TopicSubscriber + * @throws JMSException + * if the session fails to create a subscriber due to some + * internal error. + * @throws InvalidDestinationException + * if an invalid topic is specified. + */ + public TopicSubscriber createSubscriber(Topic topic) throws JMSException { + checkClosed(); + return createSubscriber(topic, null, false); + } + + /** + * Creates a nondurable subscriber to the specified topic, using a message + * selector or specifying whether messages published by its own connection + * should be delivered to it.

+ *

+ * A client uses a TopicSubscriber object to receive messages + * that have been published to a topic.

+ *

+ * Regular TopicSubscriber objects are not durable. They + * receive only messages that are published while they are active.

+ *

+ * Messages filtered out by a subscriber's message selector will never be + * delivered to the subscriber. From the subscriber's perspective, they do + * not exist.

+ *

+ * In some cases, a connection may both publish and subscribe to a topic. + * The subscriber NoLocal attribute allows a subscriber to + * inhibit the delivery of messages published by its own connection. The + * default value for this attribute is false. + * + * @param topic + * the Topic to subscribe to + * @param messageSelector + * only messages with properties matching the message selector + * expression are delivered. A value of null or an empty string + * indicates that there is no message selector for the message + * consumer. + * @param noLocal + * if set, inhibits the delivery of messages published by its own + * connection + * @return TopicSubscriber + * @throws JMSException + * if the session fails to create a subscriber due to some + * internal error. + * @throws InvalidDestinationException + * if an invalid topic is specified. + * @throws InvalidSelectorException + * if the message selector is invalid. + */ + public TopicSubscriber createSubscriber(Topic topic, String messageSelector, boolean noLocal) throws JMSException { + checkClosed(); + return new ActiveMQTopicSubscriber(this, getNextConsumerId(), ActiveMQMessageTransformation + .transformDestination(topic), null, messageSelector, this.connection.getPrefetchPolicy() + .getTopicPrefetch(), noLocal, false, asyncDispatch); + } + + /** + * Creates a publisher for the specified topic.

+ *

+ * A client uses a TopicPublisher object to publish messages + * on a topic. Each time a client creates a TopicPublisher on + * a topic, it defines a new sequence of messages that have no ordering + * relationship with the messages it has previously sent. + * + * @param topic + * the Topic to publish to, or null if this is an + * unidentified producer + * @return TopicPublisher + * @throws JMSException + * if the session fails to create a publisher due to some + * internal error. + * @throws InvalidDestinationException + * if an invalid topic is specified. + */ + public TopicPublisher createPublisher(Topic topic) throws JMSException { + checkClosed(); + return new ActiveMQTopicPublisher(this, ActiveMQMessageTransformation.transformDestination(topic)); + } + + /** + * Unsubscribes a durable subscription that has been created by a client. + *

+ * This method deletes the state being maintained on behalf of the + * subscriber by its provider. + *

+ * It is erroneous for a client to delete a durable subscription while there + * is an active MessageConsumer or TopicSubscriber + * for the subscription, or while a consumed message is part of a pending + * transaction or has not been acknowledged in the session. + * + * @param name + * the name used to identify this subscription + * @throws JMSException + * if the session fails to unsubscribe to the durable + * subscription due to some internal error. + * @throws InvalidDestinationException + * if an invalid subscription name is specified. + * @since 1.1 + */ + public void unsubscribe(String name) throws JMSException { + checkClosed(); + connection.unsubscribe(name); + } + + + public void dispatch(MessageDispatch messageDispatch) { + try { + executor.execute(messageDispatch); + } catch (InterruptedException e) { + connection.onAsyncException(e); + } + } + + + + /** + * Acknowledges all consumed messages of the session of this consumed + * message. + *

+ * All consumed JMS messages support the acknowledge method + * for use when a client has specified that its JMS session's consumed + * messages are to be explicitly acknowledged. By invoking acknowledge + * on a consumed message, a client acknowledges all messages consumed by the + * session that the message was delivered to. + *

+ * Calls to acknowledge are ignored for both transacted + * sessions and sessions specified to use implicit acknowledgement modes. + *

+ * A client may individually acknowledge each message as it is consumed, or + * it may choose to acknowledge messages as an application-defined group + * (which is done by calling acknowledge on the last received message of the + * group, thereby acknowledging all messages consumed by the session.) + *

+ * Messages that have been received but not acknowledged may be redelivered. + * + * @param caller - + * the message calling acknowledge on the session + * + * @throws JMSException + * if the JMS provider fails to acknowledge the messages due to + * some internal error. + * @throws javax.jms.IllegalStateException + * if this method is called on a closed session. + * @see javax.jms.Session#CLIENT_ACKNOWLEDGE + */ + public void acknowledge() throws JMSException { + for (Iterator iter = consumers.iterator(); iter.hasNext();) { + ActiveMQMessageConsumer c = (ActiveMQMessageConsumer) iter.next(); + c.acknowledge(); + } + } + + + /** + * Add a message consumer. + * @param id + * + * @param consumer - + * message consumer. + * @throws JMSException + */ + protected void addConsumer(ActiveMQMessageConsumer consumer) throws JMSException { + this.consumers.add(consumer); + if (consumer.isDurableSubscriber()) { + stats.onCreateDurableSubscriber(); + } + this.connection.addDispatcher(consumer.getConsumerId(), this); + } + + /** + * Remove the message consumer. + * + * @param consumer - + * consumer to be removed. + * @throws JMSException + */ + protected void removeConsumer(ActiveMQMessageConsumer consumer) { + this.connection.removeDispatcher(consumer.getConsumerId()); + if (consumer.isDurableSubscriber()) { + stats.onRemoveDurableSubscriber(); + } + this.consumers.remove(consumer); + } + + /** + * Adds a message producer. + * + * @param producer - + * message producer to be added. + * @throws JMSException + */ + protected void addProducer(ActiveMQMessageProducer producer) throws JMSException { + this.producers.add(producer); + } + + /** + * Removes a message producer. + * + * @param producer - + * message producer to be removed. + * @throws JMSException + */ + protected void removeProducer(ActiveMQMessageProducer producer) { + this.producers.remove(producer); + } + + /** + * Start this Session. + * + * @throws JMSException + */ + protected void start() throws JMSException { + started.set(true); + for (Iterator iter = consumers.iterator(); iter.hasNext();) { + ActiveMQMessageConsumer c = (ActiveMQMessageConsumer) iter.next(); + c.start(); + } + executor.start(); + } + + /** + * Stops this session. + * @throws JMSException + */ + protected void stop() throws JMSException { + + for (Iterator iter = consumers.iterator(); iter.hasNext();) { + ActiveMQMessageConsumer c = (ActiveMQMessageConsumer) iter.next(); + c.stop(); + } + + started.set(false); + executor.stop(); + } + + /** + * Returns the session id. + * + * @return sessionId - session id. + */ + protected SessionId getSessionId() { + return info.getSessionId(); + } + + /** + * Sends the message for dispatch by the broker. + * + * @param producer - + * message producer. + * @param destination - + * message destination. + * @param message - + * message to be sent. + * @param deliveryMode - + * JMS messsage delivery mode. + * @param priority - + * message priority. + * @param timeToLive - + * message expiration. + * @param reuseMessageId - + * true if the message id will be reused. + * @throws JMSException + */ + protected void send(ActiveMQMessageProducer producer, ActiveMQDestination destination, Message message, int deliveryMode, + int priority, long timeToLive) throws JMSException { + checkClosed(); + + if( destination.isTemporary() && connection.isDeleted(destination) ) { + throw new JMSException("Cannot publish to a deleted Destination: "+destination); + } + + // tell the Broker we are about to start a new transaction + doStartTransaction(); + TransactionId txid = transactionContext.getTransactionId(); + + message.setJMSDestination(destination); + message.setJMSDeliveryMode(deliveryMode); + long expiration = 0L; + + if (!producer.getDisableMessageTimestamp()) { + long timeStamp = System.currentTimeMillis(); + message.setJMSTimestamp(timeStamp); + if (timeToLive > 0) { + expiration = timeToLive + timeStamp; + } + } + + message.setJMSExpiration(expiration); + message.setJMSPriority(priority); + long sequenceNumber = producer.getMessageSequence(); + + message.setJMSRedelivered(false); + + // transform to our own message format here + ActiveMQMessage msg = ActiveMQMessageTransformation.transformMessage(message, connection); + // Set the message id. + if( msg == message ) { + msg.setMessageId( new MessageId(producer.getProducerInfo().getProducerId(), sequenceNumber) ); + } else { + msg.setMessageId( new MessageId(producer.getProducerInfo().getProducerId(), sequenceNumber) ); + message.setJMSMessageID(msg.getMessageId().toString()); + } + + msg.setTransactionId(txid); + + if ( connection.isCopyMessageOnSend() ){ + msg = (ActiveMQMessage) msg.copy(); + } + msg.onSend(); + msg.setProducerId(msg.getMessageId().getProducerId()); + + if (log.isDebugEnabled()) { + log.debug("Sending message: " + msg); + } + + if(!msg.isPersistent() || connection.isUseAsyncSend() || txid!=null) { + this.connection.asyncSendPacket(msg); + } else { + this.connection.syncSendPacket(msg); + } + + } + + /** + * Send TransactionInfo to indicate transaction has started + * + * @throws JMSException + * if some internal error occurs + */ + protected void doStartTransaction() throws JMSException { + if (getTransacted() && !transactionContext.isInXATransaction()) { + transactionContext.begin(); + } + } + + /** + * Checks whether the session has unconsumed messages. + * + * @return true - if there are unconsumed messages. + */ + public boolean hasUncomsumedMessages() { + return !executor.isEmpty(); + } + + /** + * Checks whether the session uses transactions. + * + * @return true - if the session uses transactions. + */ + public boolean isTransacted() { + return this.acknowledgementMode == Session.SESSION_TRANSACTED; + } + + /** + * Checks whether the session used client acknowledgment. + * + * @return true - if the session uses client acknowledgment. + */ + protected boolean isClientAcknowledge() { + return this.acknowledgementMode == Session.CLIENT_ACKNOWLEDGE; + } + + /** + * Checks whether the session used auto acknowledgment. + * + * @return true - if the session uses client acknowledgment. + */ + public boolean isAutoAcknowledge() { + return acknowledgementMode==Session.AUTO_ACKNOWLEDGE; + } + + /** + * Checks whether the session used dup ok acknowledgment. + * + * @return true - if the session uses client acknowledgment. + */ + public boolean isDupsOkAcknowledge() { + return acknowledgementMode==Session.DUPS_OK_ACKNOWLEDGE; + } + + /** + * Returns the message delivery listener. + * + * @return deliveryListener - message delivery listener. + */ + public DeliveryListener getDeliveryListener() { + return deliveryListener; + } + + /** + * Sets the message delivery listener. + * + * @param deliveryListener - + * message delivery listener. + */ + public void setDeliveryListener(DeliveryListener deliveryListener) { + this.deliveryListener = deliveryListener; + } + + /** + * Returns the SessionInfo bean. + * + * @return info - SessionInfo bean. + * @throws JMSException + */ + protected SessionInfo getSessionInfo() throws JMSException { + SessionInfo info = new SessionInfo(connection.getConnectionInfo(), getSessionId().getSessionId()); + return info; + } + + /** + * Send the asynchronus command. + * + * @param command - + * command to be executed. + * @throws JMSException + */ + public void asyncSendPacket(Command command) throws JMSException { + connection.asyncSendPacket(command); + } + + /** + * Send the synchronus command. + * + * @param command - + * command to be executed. + * @return Response + * @throws JMSException + */ + public Response syncSendPacket(Command command) throws JMSException { + return connection.syncSendPacket(command); + } + + public long getNextDeliveryId() { + return deliveryIdGenerator.getNextSequenceId(); + } + + public void redispatch(MessageDispatchChannel unconsumedMessages) throws JMSException { + + List c = unconsumedMessages.removeAll(); + Collections.reverse(c); + + for (Iterator iter = c.iterator(); iter.hasNext();) { + MessageDispatch md = (MessageDispatch) iter.next(); + executor.executeFirst(md); + } + + } + + public boolean isRunning() { + return started.get(); + } + + public boolean isAsyncDispatch() { + return asyncDispatch; + } + + public void setAsyncDispatch(boolean asyncDispatch) { + this.asyncDispatch = asyncDispatch; + } + + public List getUnconsumedMessages() { + return executor.getUnconsumedMessages(); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQSessionExecutor.java b/activemq-core/src/main/java/org/activemq/ActiveMQSessionExecutor.java new file mode 100755 index 0000000000..657a12fe06 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQSessionExecutor.java @@ -0,0 +1,157 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.util.Iterator; +import java.util.List; + +import javax.jms.JMSException; + +import org.activemq.command.ConsumerId; +import org.activemq.command.MessageDispatch; +import org.activemq.thread.Task; +import org.activemq.thread.TaskRunner; +import org.activemq.util.JMSExceptionSupport; + +/** + * A utility class used by the Session for dispatching messages asynchronously to consumers + * + * @version $Revision$ + * @see javax.jms.Session + */ +public class ActiveMQSessionExecutor implements Task { + + private ActiveMQSession session; + private MessageDispatchChannel messageQueue = new MessageDispatchChannel(); + private boolean dispatchedBySessionPool; + private TaskRunner taskRunner; + + ActiveMQSessionExecutor(ActiveMQSession session) { + this.session = session; + } + + void setDispatchedBySessionPool(boolean value) { + dispatchedBySessionPool = value; + wakeup(); + } + + + void execute(MessageDispatch message) throws InterruptedException { + if (!session.isAsyncDispatch() && !dispatchedBySessionPool){ + dispatch(message); + }else { + messageQueue.enqueue(message); + wakeup(); + } + } + + private void wakeup() { + if( !dispatchedBySessionPool && !messageQueue.isClosed() && messageQueue.isRunning() && !messageQueue.isEmpty() ) { + try { + taskRunner.wakeup(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + void executeFirst(MessageDispatch message) { + messageQueue.enqueueFirst(message); + wakeup(); + } + + boolean hasUncomsumedMessages() { + return !messageQueue.isEmpty(); + } + + /** + * implementation of Runnable + */ + public void run() { + } + + void dispatch(MessageDispatch message){ + for (Iterator i = this.session.consumers.iterator(); i.hasNext();) { + ActiveMQMessageConsumer consumer = (ActiveMQMessageConsumer) i.next(); + ConsumerId consumerId = message.getConsumerId(); + if( consumerId.equals(consumer.getConsumerId()) ) { + consumer.dispatch(message); + } + } + } + + synchronized void start() { + if( !messageQueue.isRunning() ) { + messageQueue.start(); + taskRunner = ActiveMQConnection.SESSION_TASK_RUNNER.createTaskRunner(this); + wakeup(); + } + } + + void stop() throws JMSException { + try { + if( messageQueue.isRunning() ) { + messageQueue.stop(); + taskRunner.shutdown(); + } + } catch (InterruptedException e) { + throw JMSExceptionSupport.create(e); + } + } + + boolean isRunning() { + return messageQueue.isRunning(); + } + + void close() { + messageQueue.close(); + } + + void clear() { + messageQueue.clear(); + } + + MessageDispatch dequeueNoWait() { + return (MessageDispatch) messageQueue.dequeueNoWait(); + } + + protected void clearMessagesInProgress(){ + messageQueue.clear(); + } + + public boolean isEmpty() { + return messageQueue.isEmpty(); + } + + public boolean iterate() { + MessageDispatch message = messageQueue.dequeueNoWait(); + if( message==null ) { + return false; + } else { + dispatch(message); + return true; + } + } + + List getUnconsumedMessages() { + return messageQueue.removeAll(); + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQTopicPublisher.java b/activemq-core/src/main/java/org/activemq/ActiveMQTopicPublisher.java new file mode 100755 index 0000000000..1d25ba7c1b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQTopicPublisher.java @@ -0,0 +1,205 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import org.activemq.command.ActiveMQDestination; + +import javax.jms.Destination; +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageFormatException; +import javax.jms.Session; +import javax.jms.Topic; +import javax.jms.TopicPublisher; +import javax.jms.TopicSession; + +/** + * A client uses a TopicPublisher object to publish messages on + * a topic. A TopicPublisher object is the publish-subscribe + * form of a message producer. + *

+ *

+ * Normally, the Topic is specified when a TopicPublisher + * is created. In this case, an attempt to use the publish + * methods for an unidentified TopicPublisher will throw + * a java.lang.UnsupportedOperationException. + *

+ *

+ * If the TopicPublisher is created with an unidentified + * Topic, an attempt to use the publish methods that + * assume that the Topic has been identified will throw a + * java.lang.UnsupportedOperationException. + *

+ *

+ * During the execution of its publish method, a message must + * not be changed by other threads within the client. If the message is + * modified, the result of the publish is undefined. + *

+ *

+ * After publishing a message, a client may retain and modify it without + * affecting the message that has been published. The same message object may + * be published multiple times. + *

+ *

+ * The following message headers are set as part of publishing a message: + * JMSDestination,JMSDeliveryMode,JMSExpiration, + * JMSPriority,JMSMessageID and JMSTimeStamp. + * When the message is published, the values of these headers are ignored. + * After completion of the publish, the headers hold the values + * specified by the method publishing the message. It is possible for the + * publish method not to set JMSMessageID and + * JMSTimeStamp if the setting of these headers is explicitly + * disabled by the MessageProducer.setDisableMessageID or MessageProducer.setDisableMessageTimestamp + * method. + *

+ *

+ * Creating a MessageProducer provides the same features as + * creating a TopicPublisher. A MessageProducer + * object is recommended when creating new code. The TopicPublisher + * is provided to support existing code. + *

+ *

+ *

+ * Because TopicPublisher inherits from MessageProducer + * , it inherits the send methods that are a part of the + * MessageProducer interface. Using the send + * methods will have the same effect as using the publish + * methods: they are functionally the same. + * + * @see Session#createProducer(Destination) + * @see TopicSession#createPublisher(Topic) + */ + +public class ActiveMQTopicPublisher extends ActiveMQMessageProducer implements + TopicPublisher { + + protected ActiveMQTopicPublisher(ActiveMQSession session, + ActiveMQDestination destination) throws JMSException { + super(session, session.getNextProducerId(), destination); + } + + /** + * Gets the topic associated with this TopicPublisher. + * + * @return this publisher's topic + * @throws JMSException if the JMS provider fails to get the topic for this + * TopicPublisher due to some internal error. + */ + + public Topic getTopic() throws JMSException { + return (Topic) super.getDestination(); + } + + /** + * Publishes a message to the topic. Uses the TopicPublisher's + * default delivery mode, priority, and time to live. + * + * @param message the message to publish + * @throws JMSException if the JMS provider fails to publish the message due to + * some internal error. + * @throws MessageFormatException if an invalid message is specified. + * @throws InvalidDestinationException if a client uses this method with a TopicPublisher + * with an invalid topic. + * @throws java.lang.UnsupportedOperationException + * if a client uses this method with a TopicPublisher + * that did not specify a topic at creation time. + * @see javax.jms.MessageProducer#getDeliveryMode() + * @see javax.jms.MessageProducer#getTimeToLive() + * @see javax.jms.MessageProducer#getPriority() + */ + + public void publish(Message message) throws JMSException { + super.send(message); + } + + /** + * Publishes a message to the topic, specifying delivery mode, priority, + * and time to live. + * + * @param message the message to publish + * @param deliveryMode the delivery mode to use + * @param priority the priority for this message + * @param timeToLive the message's lifetime (in milliseconds) + * @throws JMSException if the JMS provider fails to publish the message due to + * some internal error. + * @throws MessageFormatException if an invalid message is specified. + * @throws InvalidDestinationException if a client uses this method with a TopicPublisher + * with an invalid topic. + * @throws java.lang.UnsupportedOperationException + * if a client uses this method with a TopicPublisher + * that did not specify a topic at creation time. + */ + + public void publish(Message message, int deliveryMode, int priority, + long timeToLive) throws JMSException { + super.send(message, deliveryMode, priority, timeToLive); + } + + /** + * Publishes a message to a topic for an unidentified message producer. + * Uses the TopicPublisher's default delivery mode, + * priority, and time to live. + *

+ *

+ * Typically, a message producer is assigned a topic at creation time; + * however, the JMS API also supports unidentified message producers, which + * require that the topic be supplied every time a message is published. + * + * @param topic the topic to publish this message to + * @param message the message to publish + * @throws JMSException if the JMS provider fails to publish the message due to + * some internal error. + * @throws MessageFormatException if an invalid message is specified. + * @throws InvalidDestinationException if a client uses this method with an invalid topic. + * @see javax.jms.MessageProducer#getDeliveryMode() + * @see javax.jms.MessageProducer#getTimeToLive() + * @see javax.jms.MessageProducer#getPriority() + */ + + public void publish(Topic topic, Message message) throws JMSException { + super.send(topic, message); + } + + /** + * Publishes a message to a topic for an unidentified message producer, + * specifying delivery mode, priority and time to live. + *

+ *

+ * Typically, a message producer is assigned a topic at creation time; + * however, the JMS API also supports unidentified message producers, which + * require that the topic be supplied every time a message is published. + * + * @param topic the topic to publish this message to + * @param message the message to publish + * @param deliveryMode the delivery mode to use + * @param priority the priority for this message + * @param timeToLive the message's lifetime (in milliseconds) + * @throws JMSException if the JMS provider fails to publish the message due to + * some internal error. + * @throws MessageFormatException if an invalid message is specified. + * @throws InvalidDestinationException if a client uses this method with an invalid topic. + */ + + public void publish(Topic topic, Message message, int deliveryMode, + int priority, long timeToLive) throws JMSException { + super.send(topic, message, deliveryMode, priority, timeToLive); + } +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQTopicSession.java b/activemq-core/src/main/java/org/activemq/ActiveMQTopicSession.java new file mode 100755 index 0000000000..99876b4038 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQTopicSession.java @@ -0,0 +1,352 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.io.Serializable; + +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.IllegalStateException; +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.QueueBrowser; +import javax.jms.StreamMessage; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicPublisher; +import javax.jms.TopicSession; +import javax.jms.TopicSubscriber; + +/** + * A TopicSession implementation that throws IllegalStateExceptions + * when Queue operations are attempted but which delegates + * to another TopicSession for all other operations. + * + * The ActiveMQSessions implement both Topic and Queue Sessions + * methods but the spec states that TopicSession should throw Exceptions + * if queue operations are attempted on it. + * + * @version $Revision: 1.2 $ + */ +public class ActiveMQTopicSession implements TopicSession { + + private final TopicSession next; + + public ActiveMQTopicSession(TopicSession next) { + this.next = next; + } + + /** + * @throws JMSException + */ + public void close() throws JMSException { + next.close(); + } + /** + * @throws JMSException + */ + public void commit() throws JMSException { + next.commit(); + } + /** + * @param queue + * @return + * @throws JMSException + */ + public QueueBrowser createBrowser(Queue queue) throws JMSException { + throw new IllegalStateException("Operation not supported by a TopicSession"); + } + /** + * @param queue + * @param messageSelector + * @return + * @throws JMSException + */ + public QueueBrowser createBrowser(Queue queue, String messageSelector) + throws JMSException { + throw new IllegalStateException("Operation not supported by a TopicSession"); + } + /** + * @return + * @throws JMSException + */ + public BytesMessage createBytesMessage() throws JMSException { + return next.createBytesMessage(); + } + /** + * @param destination + * @return + * @throws JMSException + */ + public MessageConsumer createConsumer(Destination destination) + throws JMSException { + if( destination instanceof Queue ) + throw new InvalidDestinationException("Queues are not supported by a TopicSession"); + return next.createConsumer(destination); + } + /** + * @param destination + * @param messageSelector + * @return + * @throws JMSException + */ + public MessageConsumer createConsumer(Destination destination, + String messageSelector) throws JMSException { + if( destination instanceof Queue ) + throw new InvalidDestinationException("Queues are not supported by a TopicSession"); + return next.createConsumer(destination, messageSelector); + } + /** + * @param destination + * @param messageSelector + * @param NoLocal + * @return + * @throws JMSException + */ + public MessageConsumer createConsumer(Destination destination, + String messageSelector, boolean NoLocal) throws JMSException { + if( destination instanceof Queue ) + throw new InvalidDestinationException("Queues are not supported by a TopicSession"); + return next.createConsumer(destination, messageSelector, NoLocal); + } + /** + * @param topic + * @param name + * @return + * @throws JMSException + */ + public TopicSubscriber createDurableSubscriber(Topic topic, String name) + throws JMSException { + return next.createDurableSubscriber(topic, name); + } + /** + * @param topic + * @param name + * @param messageSelector + * @param noLocal + * @return + * @throws JMSException + */ + public TopicSubscriber createDurableSubscriber(Topic topic, String name, + String messageSelector, boolean noLocal) throws JMSException { + return next.createDurableSubscriber(topic, name, messageSelector, + noLocal); + } + /** + * @return + * @throws JMSException + */ + public MapMessage createMapMessage() throws JMSException { + return next.createMapMessage(); + } + /** + * @return + * @throws JMSException + */ + public Message createMessage() throws JMSException { + return next.createMessage(); + } + /** + * @return + * @throws JMSException + */ + public ObjectMessage createObjectMessage() throws JMSException { + return next.createObjectMessage(); + } + /** + * @param object + * @return + * @throws JMSException + */ + public ObjectMessage createObjectMessage(Serializable object) + throws JMSException { + return next.createObjectMessage(object); + } + /** + * @param destination + * @return + * @throws JMSException + */ + public MessageProducer createProducer(Destination destination) + throws JMSException { + if( destination instanceof Queue ) + throw new InvalidDestinationException("Queues are not supported by a TopicSession"); + return next.createProducer(destination); + } + /** + * @param topic + * @return + * @throws JMSException + */ + public TopicPublisher createPublisher(Topic topic) throws JMSException { + return next.createPublisher(topic); + } + /** + * @param queueName + * @return + * @throws JMSException + */ + public Queue createQueue(String queueName) throws JMSException { + throw new IllegalStateException("Operation not supported by a TopicSession"); + } + /** + * @return + * @throws JMSException + */ + public StreamMessage createStreamMessage() throws JMSException { + return next.createStreamMessage(); + } + /** + * @param topic + * @return + * @throws JMSException + */ + public TopicSubscriber createSubscriber(Topic topic) throws JMSException { + return next.createSubscriber(topic); + } + /** + * @param topic + * @param messageSelector + * @param noLocal + * @return + * @throws JMSException + */ + public TopicSubscriber createSubscriber(Topic topic, + String messageSelector, boolean noLocal) throws JMSException { + return next.createSubscriber(topic, messageSelector, noLocal); + } + /** + * @return + * @throws JMSException + */ + public TemporaryQueue createTemporaryQueue() throws JMSException { + throw new IllegalStateException("Operation not supported by a TopicSession"); + } + /** + * @return + * @throws JMSException + */ + public TemporaryTopic createTemporaryTopic() throws JMSException { + return next.createTemporaryTopic(); + } + /** + * @return + * @throws JMSException + */ + public TextMessage createTextMessage() throws JMSException { + return next.createTextMessage(); + } + /** + * @param text + * @return + * @throws JMSException + */ + public TextMessage createTextMessage(String text) throws JMSException { + return next.createTextMessage(text); + } + /** + * @param topicName + * @return + * @throws JMSException + */ + public Topic createTopic(String topicName) throws JMSException { + return next.createTopic(topicName); + } + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object arg0) { + return next.equals(arg0); + } + /** + * @return + * @throws JMSException + */ + public int getAcknowledgeMode() throws JMSException { + return next.getAcknowledgeMode(); + } + /** + * @return + * @throws JMSException + */ + public MessageListener getMessageListener() throws JMSException { + return next.getMessageListener(); + } + /** + * @return + * @throws JMSException + */ + public boolean getTransacted() throws JMSException { + return next.getTransacted(); + } + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return next.hashCode(); + } + /** + * @throws JMSException + */ + public void recover() throws JMSException { + next.recover(); + } + /** + * @throws JMSException + */ + public void rollback() throws JMSException { + next.rollback(); + } + /** + * + */ + public void run() { + next.run(); + } + /** + * @param listener + * @throws JMSException + */ + public void setMessageListener(MessageListener listener) + throws JMSException { + next.setMessageListener(listener); + } + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + return next.toString(); + } + /** + * @param name + * @throws JMSException + */ + public void unsubscribe(String name) throws JMSException { + next.unsubscribe(name); + } +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQTopicSubscriber.java b/activemq-core/src/main/java/org/activemq/ActiveMQTopicSubscriber.java new file mode 100755 index 0000000000..188f12e431 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQTopicSubscriber.java @@ -0,0 +1,149 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConsumerId; + +import javax.jms.JMSException; +import javax.jms.Topic; +import javax.jms.TopicSubscriber; + +/** + * A client uses a TopicSubscriber object to receive messages + * that have been published to a topic. A TopicSubscriber object + * is the publish/subscribe form of a message consumer. A + * MessageConsumer can be created by using + * Session.createConsumer. + *

+ *

+ * A TopicSession allows the creation of multiple + * TopicSubscriber objects per topic. It will deliver each message for + * a topic to each subscriber eligible to receive it. Each copy of the message + * is treated as a completely separate message. Work done on one copy has no + * effect on the others; acknowledging one does not acknowledge the others; one + * message may be delivered immediately, while another waits for its subscriber + * to process messages ahead of it. + *

+ *

+ * Regular TopicSubscriber objects are not durable. They receive + * only messages that are published while they are active. + *

+ *

+ * Messages filtered out by a subscriber's message selector will never be + * delivered to the subscriber. From the subscriber's perspective, they do not + * exist. + *

+ *

+ * In some cases, a connection may both publish and subscribe to a topic. The + * subscriber NoLocal attribute allows a subscriber to inhibit + * the delivery of messages published by its own connection. + *

+ *

+ * If a client needs to receive all the messages published on a topic, + * including the ones published while the subscriber is inactive, it uses a + * durable TopicSubscriber. The JMS provider retains a record + * of this durable subscription and insures that all messages from the topic's + * publishers are retained until they are acknowledged by this durable + * subscriber or they have expired. + *

+ *

+ * Sessions with durable subscribers must always provide the same client + * identifier. In addition, each client must specify a name that uniquely + * identifies (within client identifier) each durable subscription it creates. + * Only one session at a time can have a TopicSubscriber for a + * particular durable subscription. + *

+ *

+ * A client can change an existing durable subscription by creating a durable + * TopicSubscriber with the same name and a new topic and/or + * message selector. Changing a durable subscription is equivalent to + * unsubscribing (deleting) the old one and creating a new one. + *

+ *

+ * The unsubscribe method is used to delete a durable + * subscription. The unsubscribe method can be used at the + * Session or TopicSession level. This method + * deletes the state being maintained on behalf of the subscriber by its + * provider. + *

+ *

+ * Creating a MessageConsumer provides the same features as + * creating a TopicSubscriber. To create a durable subscriber, + * use of Session.CreateDurableSubscriber is recommended. The + * TopicSubscriber is provided to support existing code. + * + * @see javax.jms.Session#createConsumer + * @see javax.jms.Session#createDurableSubscriber + * @see javax.jms.TopicSession + * @see javax.jms.TopicSession#createSubscriber + * @see javax.jms.TopicSubscriber + * @see javax.jms.MessageConsumer + */ + +public class ActiveMQTopicSubscriber extends ActiveMQMessageConsumer implements + TopicSubscriber { + + /** + * @param theSession + * @param consumerId + * @param dest + * @param name + * @param selector + * @param cnum + * @param noLocalValue + * @param browserValue + * @param asyncDispatch + * @throws JMSException + */ + protected ActiveMQTopicSubscriber(ActiveMQSession theSession, + ConsumerId consumerId, ActiveMQDestination dest, String name, String selector, int prefetch, + boolean noLocalValue, boolean browserValue, boolean asyncDispatch) throws JMSException { + super(theSession, consumerId, dest, name, selector, prefetch, noLocalValue, browserValue, asyncDispatch); + } + + /** + * Gets the Topic associated with this subscriber. + * + * @return this subscriber's Topic + * @throws JMSException if the JMS provider fails to get the topic for this topic + * subscriber due to some internal error. + */ + + public Topic getTopic() throws JMSException { + checkClosed(); + return (Topic) super.getDestination(); + } + + /** + * Gets the NoLocal attribute for this subscriber. The + * default value for this attribute is false. + * + * @return true if locally published messages are being inhibited + * @throws JMSException if the JMS provider fails to get the NoLocal + * attribute for this topic subscriber due to some + * internal error. + */ + + public boolean getNoLocal() throws JMSException { + checkClosed(); + return super.isNoLocal(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQXAConnection.java b/activemq-core/src/main/java/org/activemq/ActiveMQXAConnection.java new file mode 100755 index 0000000000..b00d68bdcc --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQXAConnection.java @@ -0,0 +1,82 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.JMSException; +import javax.jms.Session; +import javax.jms.XAConnection; +import javax.jms.XAQueueConnection; +import javax.jms.XAQueueSession; +import javax.jms.XASession; +import javax.jms.XATopicConnection; +import javax.jms.XATopicSession; + +import org.activemq.management.JMSStatsImpl; +import org.activemq.transport.Transport; + +/** + * The XAConnection interface extends the capability of Connection by providing + * an XASession (optional). + *

+ * The XAConnection interface is optional. JMS providers are not required to + * support this interface. This interface is for use by JMS providers to + * support transactional environments. Client programs are strongly encouraged + * to use the transactional support available in their environment, rather + * than use these XA interfaces directly. + * + * @version $Revision: 1.6 $ + * @see javax.jms.Connection + * @see javax.jms.ConnectionFactory + * @see javax.jms.QueueConnection + * @see javax.jms.TopicConnection + * @see javax.jms.TopicConnectionFactory + * @see javax.jms.QueueConnection + * @see javax.jms.QueueConnectionFactory + */ +public class ActiveMQXAConnection extends ActiveMQConnection implements XATopicConnection, XAQueueConnection, XAConnection { + + /** + * @param transport + * @param theUserName + * @param thePassword + * @param factoryStats + * @throws Exception + */ + public ActiveMQXAConnection(Transport transport, String theUserName, String thePassword, JMSStatsImpl factoryStats) throws Exception { + super(transport, theUserName, thePassword, factoryStats); + } + + public XASession createXASession() throws JMSException { + return (XASession) createSession(true, Session.SESSION_TRANSACTED); + } + + public XATopicSession createXATopicSession() throws JMSException { + return (XATopicSession) createSession(true, Session.SESSION_TRANSACTED); + } + + public XAQueueSession createXAQueueSession() throws JMSException { + return (XAQueueSession) createSession(true, Session.SESSION_TRANSACTED); + } + + public Session createSession(boolean transacted, int acknowledgeMode) throws JMSException { + checkClosed(); + ensureConnectionInfoSent(); + return new ActiveMQXASession(this, getNextSessionId(), Session.SESSION_TRANSACTED, asyncDispatch); + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/ActiveMQXASession.java b/activemq-core/src/main/java/org/activemq/ActiveMQXASession.java new file mode 100755 index 0000000000..728e938817 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ActiveMQXASession.java @@ -0,0 +1,116 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.JMSException; +import javax.jms.QueueSession; +import javax.jms.Session; +import javax.jms.TopicSession; +import javax.jms.TransactionInProgressException; +import javax.jms.XAQueueSession; +import javax.jms.XATopicSession; +import javax.transaction.xa.XAResource; + +import org.activemq.command.SessionId; + +/** + * The XASession interface extends the capability of Session by adding access + * to a JMS provider's support for the Java Transaction API (JTA) (optional). + * This support takes the form of a javax.transaction.xa.XAResource object. + * The functionality of this object closely resembles that defined by the + * standard X/Open XA Resource interface. + *

+ * An application server controls the transactional assignment of an XASession + * by obtaining its XAResource. It uses the XAResource to assign the session + * to a transaction, prepare and commit work on the transaction, and so on. + *

+ * An XAResource provides some fairly sophisticated facilities for + * interleaving work on multiple transactions, recovering a list of + * transactions in progress, and so on. A JTA aware JMS provider must fully + * implement this functionality. This could be done by using the services of a + * database that supports XA, or a JMS provider may choose to implement this + * functionality from scratch. + *

+ * A client of the application server is given what it thinks is a regular + * JMS Session. Behind the scenes, the application server controls the + * transaction management of the underlying XASession. + *

+ * The XASession interface is optional. JMS providers are not required to + * support this interface. This interface is for use by JMS providers to + * support transactional environments. Client programs are strongly encouraged + * to use the transactional support available in their environment, rather + * than use these XA interfaces directly. + * + * @version $Revision: 1.5 $ + * @see javax.jms.Session + * @see javax.jms.QueueSession + * @see javax.jms.TopicSession + * @see javax.jms.XASession + */ +public class ActiveMQXASession extends ActiveMQSession implements QueueSession, TopicSession, XAQueueSession, XATopicSession { + + public ActiveMQXASession(ActiveMQXAConnection connection, SessionId sessionId, int theAcknowlegeMode, boolean dispatchAsync) throws JMSException { + super(connection, sessionId, theAcknowlegeMode, dispatchAsync); + } + + public boolean getTransacted() throws JMSException { + return true; + } + + public void rollback() throws JMSException { + throw new TransactionInProgressException("Cannot rollback() inside an XASession"); + } + + public void commit() throws JMSException { + throw new TransactionInProgressException("Cannot commit() inside an XASession"); + } + + public Session getSession() throws JMSException { + return this; + } + + public XAResource getXAResource() { + return getTransactionContext(); + } + + public QueueSession getQueueSession() throws JMSException { + return new ActiveMQQueueSession(this); + } + + public TopicSession getTopicSession() throws JMSException { + return new ActiveMQTopicSession(this); + } + + /** + * This is called before transacted work is done by + * the session. XA Work can only be done when this + * XA resource is associated with an Xid. + * + * @throws JMSException not associated with an Xid + */ + protected void doStartTransaction() throws JMSException { + + if (!getTransactionContext().isInXATransaction()) { + throw new JMSException("Session's XAResource has not been enlisted in a distributed transaction."); + } + + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/AdvisoryConsumer.java b/activemq-core/src/main/java/org/activemq/AdvisoryConsumer.java new file mode 100755 index 0000000000..44472b3aaf --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/AdvisoryConsumer.java @@ -0,0 +1,89 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.JMSException; + +import org.activemq.advisory.AdvisorySupport; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConsumerId; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.DataStructure; +import org.activemq.command.DestinationInfo; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageDispatch; + +public class AdvisoryConsumer implements ActiveMQDispatcher { + + private final ActiveMQConnection connection; + private ConsumerInfo info; + private boolean closed; + int deliveredCounter; + + public AdvisoryConsumer(ActiveMQConnection connection, ConsumerId consumerId) throws JMSException { + this.connection = connection; + info = new ConsumerInfo(consumerId); + info.setDestination(AdvisorySupport.TEMP_DESTINATION_COMPOSITE_ADVISORY_TOPIC); + info.setPrefetchSize(1000); + info.setNoLocal(true); + + this.connection.addDispatcher(info.getConsumerId(), this); + this.connection.syncSendPacket(this.info); + } + + public void dispose() { + if (!closed) { + this.connection.removeDispatcher(info.getConsumerId()); + closed = true; + } + } + + public void dispatch(MessageDispatch md) { + + // Auto ack messages when we reach 75% of the prefetch + deliveredCounter++; + if( deliveredCounter > (0.75 * info.getPrefetchSize()) ) { + try { + MessageAck ack = new MessageAck(md, MessageAck.STANDARD_ACK_TYPE, deliveredCounter); + connection.asyncSendPacket(ack); + deliveredCounter = 0; + } catch (JMSException e) { + connection.onAsyncException(e); + } + } + + DataStructure o = md.getMessage().getDataStructure(); + if( o!=null && o.getClass() == DestinationInfo.class ) { + processDestinationInfo((DestinationInfo) o); + } else { + connection.onAsyncException(new JMSException("Unexpected message was dispatched to the AdvisoryConsumer: "+md)); + } + + } + + private void processDestinationInfo(DestinationInfo dinfo) { + ActiveMQDestination dest = dinfo.getDestination(); + if( dinfo.getOperationType() == DestinationInfo.ADD_OPERATION_TYPE ) { + connection.activeTempDestinations.put(dest,dest); + } else if( dinfo.getOperationType() == DestinationInfo.REMOVE_OPERATION_TYPE ) { + connection.activeTempDestinations.remove(dest); + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/AlreadyClosedException.java b/activemq-core/src/main/java/org/activemq/AlreadyClosedException.java new file mode 100755 index 0000000000..bf2bccec30 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/AlreadyClosedException.java @@ -0,0 +1,40 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.JMSException; + +/** + * An exception which is closed if you try to access a resource which has already + * been closed + * + * @version $Revision: 1.2 $ + */ +public class AlreadyClosedException extends JMSException { + + private static final long serialVersionUID = -3203104889571618702L; + + public AlreadyClosedException() { + super("this connection"); + } + + public AlreadyClosedException(String description) { + super("Cannot use " + description + " as it has already been closed", "AMQ-1001"); + } +} diff --git a/activemq-core/src/main/java/org/activemq/Closeable.java b/activemq-core/src/main/java/org/activemq/Closeable.java new file mode 100755 index 0000000000..85d41c3234 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/Closeable.java @@ -0,0 +1,41 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.JMSException; + +/** + * Provides a uniform interface that can be used to close all the JMS obejcts + * that provide a close() method. Useful for when you want to collect + * a heterogeous set of JMS object in a collection to be closed at a later time. + * + * @version $Revision: 1.2 $ + */ +public interface Closeable { + + /** + * Closes a JMS object. + *

+ * Many JMS objects are closeable such as Connections, Sessions, Consumers and Producers. + * + * @throws JMSException if the JMS provider fails to close the object due to + * some internal error. + */ + public void close() throws JMSException; +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/ConfigurationException.java b/activemq-core/src/main/java/org/activemq/ConfigurationException.java new file mode 100755 index 0000000000..b81a2c9912 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ConfigurationException.java @@ -0,0 +1,34 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.JMSException; + +/** + * An exception thrown when a service is not correctly configured + * + * @version $Revision: 1.2 $ + */ +public class ConfigurationException extends JMSException { + private static final long serialVersionUID = 5639082552451065258L; + + public ConfigurationException(String description) { + super(description, "AMQ-1002"); + } +} diff --git a/activemq-core/src/main/java/org/activemq/ConnectionClosedException.java b/activemq-core/src/main/java/org/activemq/ConnectionClosedException.java new file mode 100755 index 0000000000..6aec83c2c3 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ConnectionClosedException.java @@ -0,0 +1,34 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.IllegalStateException; + +/** + * An exception thrown when attempt is made to use a connection when the connection has been closed. + * + * @version $Revision: 1.2 $ + */ +public class ConnectionClosedException extends IllegalStateException { + private static final long serialVersionUID = -7681404582227153308L; + + public ConnectionClosedException() { + super("The connection is already closed", "AlreadyClosed"); + } +} diff --git a/activemq-core/src/main/java/org/activemq/ConnectionFailedException.java b/activemq-core/src/main/java/org/activemq/ConnectionFailedException.java new file mode 100755 index 0000000000..8542cbb062 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ConnectionFailedException.java @@ -0,0 +1,48 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.io.IOException; + +import javax.jms.JMSException; + +/** + * An exception thrown when the a connection failure is detected (peer might close the connection, or a keep alive + * times out, etc.) + * + * @version $Revision$ + */ +public class ConnectionFailedException extends JMSException { + + private static final long serialVersionUID = 2288453203492073973L; + + public ConnectionFailedException(IOException cause) { + super("The JMS connection has failed: "+extractMessage(cause)); + initCause(cause); + setLinkedException(cause); + } + + static private String extractMessage(IOException cause) { + String m = cause.getMessage(); + if( m==null || m.length()==0 ) + m = cause.toString(); + return m; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/LocalTransactionEventListener.java b/activemq-core/src/main/java/org/activemq/LocalTransactionEventListener.java new file mode 100755 index 0000000000..7550481938 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/LocalTransactionEventListener.java @@ -0,0 +1,31 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + + +/** + * @version $Revision: 1.2 $ + */ +public interface LocalTransactionEventListener { + public void beginEvent(); + + public void commitEvent(); + + public void rollbackEvent(); +} diff --git a/activemq-core/src/main/java/org/activemq/MessageAvailableConsumer.java b/activemq-core/src/main/java/org/activemq/MessageAvailableConsumer.java new file mode 100644 index 0000000000..b6af75f1f1 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/MessageAvailableConsumer.java @@ -0,0 +1,42 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq; + +import javax.jms.MessageConsumer; + +/** + * An extended JMS interface that adds the ability to be notified when a + * message is available for consumption using the receive*() methods + * which is useful in Ajax style subscription models. + * + * @version $Revision: 1.1 $ + */ +public interface MessageAvailableConsumer extends MessageConsumer { + + /** + * Sets the listener used to notify synchronous consumers that there is a message + * available so that the {@link MessageConsumer#receiveNoWait()} can be called. + */ + public void setAvailableListener(MessageAvailableListener availableListener); + + /** + * Gets the listener used to notify synchronous consumers that there is a message + * available so that the {@link MessageConsumer#receiveNoWait()} can be called. + */ + public MessageAvailableListener getAvailableListener(); +} diff --git a/activemq-core/src/main/java/org/activemq/MessageAvailableListener.java b/activemq-core/src/main/java/org/activemq/MessageAvailableListener.java new file mode 100644 index 0000000000..60df47ffaa --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/MessageAvailableListener.java @@ -0,0 +1,36 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq; + +import javax.jms.MessageConsumer; + +/** + * A listener which is notified if a message is available for processing via the + * receive methods. Typically on receiving this notification you can call + * {@link MessageConsumer#receiveNoWait()} to get the new message immediately. + * + * Note that this notification just indicates a message is available for synchronous consumption, + * it does not actually consume the message. + * + * @version $Revision: 1.1 $ + */ +public interface MessageAvailableListener { + + void onMessageAvailable(MessageConsumer consumer); + +} diff --git a/activemq-core/src/main/java/org/activemq/MessageDispatchChannel.java b/activemq-core/src/main/java/org/activemq/MessageDispatchChannel.java new file mode 100755 index 0000000000..51e8d231de --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/MessageDispatchChannel.java @@ -0,0 +1,167 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import javax.jms.JMSException; + +import org.activemq.command.MessageDispatch; + +public class MessageDispatchChannel { + + private final Object mutex = new Object(); + private final LinkedList list; + private boolean closed; + private boolean running; + + public MessageDispatchChannel() { + this.list = new LinkedList(); + } + + public void enqueue(MessageDispatch message) { + synchronized(mutex) { + list.addLast(message); + mutex.notify(); + } + } + + public void enqueueFirst(MessageDispatch message) { + synchronized(mutex) { + list.addFirst(message); + mutex.notify(); + } + } + + public boolean isEmpty() { + synchronized(mutex) { + return list.isEmpty(); + } + } + + /** + * Used to get an enqueued message. + * The amount of time this method blocks is based on the timeout value. + * - if timeout==-1 then it blocks until a message is received. + * - if timeout==0 then it it tries to not block at all, it returns a message if it is available + * - if timeout>0 then it blocks up to timeout amount of time. + * + * Expired messages will consumed by this method. + * + * @throws JMSException + * + * @return null if we timeout or if the consumer is closed. + * @throws InterruptedException + */ + public MessageDispatch dequeue(long timeout) throws InterruptedException { + synchronized (mutex) { + // Wait until the consumer is ready to deliver messages. + while(timeout != 0 && !closed && (list.isEmpty() || !running)) { + if (timeout == -1) { + mutex.wait(); + } else { + mutex.wait(timeout); + break; + } + } + if (closed || !running || list.isEmpty()) { + return null; + } + return (MessageDispatch) list.removeFirst(); + } + } + + public MessageDispatch dequeueNoWait() { + synchronized (mutex) { + if (closed || !running || list.isEmpty()) { + return null; + } + return (MessageDispatch) list.removeFirst(); + } + } + + public MessageDispatch peek() { + synchronized (mutex) { + if (closed || !running || list.isEmpty()) { + return null; + } + return (MessageDispatch) list.getFirst(); + } + } + + public void start() { + synchronized (mutex) { + running = true; + mutex.notifyAll(); + } + } + + public void stop() { + synchronized (mutex) { + running = false; + mutex.notifyAll(); + } + } + + public void close() { + synchronized (mutex) { + if (!closed) { + running = false; + closed = true; + } + mutex.notifyAll(); + } + } + + public void clear() { + synchronized(mutex) { + list.clear(); + } + } + + public boolean isClosed() { + return closed; + } + + public int size() { + synchronized(mutex) { + return list.size(); + } + } + + public Object getMutex() { + return mutex; + } + + public boolean isRunning() { + return running; + } + + public List removeAll() { + synchronized(mutex) { + ArrayList rc = new ArrayList(list); + list.clear(); + return rc; + } + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/NotStartedException.java b/activemq-core/src/main/java/org/activemq/NotStartedException.java new file mode 100755 index 0000000000..754863bb70 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/NotStartedException.java @@ -0,0 +1,36 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.IllegalStateException; + +/** + * An exception thrown when an operation is invoked on a service + * which has not yet been started. + * + * @version $Revision: 1.2 $ + */ +public class NotStartedException extends IllegalStateException { + + private static final long serialVersionUID = -4907909323529887659L; + + public NotStartedException() { + super("IllegalState: This service has not yet been started", "AMQ-1003"); + } +} diff --git a/activemq-core/src/main/java/org/activemq/Service.java b/activemq-core/src/main/java/org/activemq/Service.java new file mode 100644 index 0000000000..84ac007aa2 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/Service.java @@ -0,0 +1,38 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + + +/** + * The core lifecyle interface for ActiveMQ components. + * + * If there was a standard way to do so, it'd be good to register this + * interface with Spring so it treats the start/stop methods as those of + * {@link org.springframework.beans.factory.InitializingBean} + * and {@link org.springframework.beans.factory.DisposableBean} + * + * @version $Revision: 1.1 $ + */ +public interface Service { + + public void start() throws Exception; + + public void stop() throws Exception; + +} diff --git a/activemq-core/src/main/java/org/activemq/StreamConnection.java b/activemq-core/src/main/java/org/activemq/StreamConnection.java new file mode 100644 index 0000000000..114f4ef82c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/StreamConnection.java @@ -0,0 +1,74 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; +import javax.jms.Topic; + +/** + * The StreamConnection interface allows you to send and receive + * data from a Destination in using standard java InputStream and OutputStream + * objects. It's best use case is to send and receive large amounts of data + * that would be to large to hold in a single JMS message. + * + * @version $Revision$ + */ +public interface StreamConnection extends Connection { + + public InputStream createInputStream(Destination dest) throws JMSException; + public InputStream createInputStream(Destination dest, String messageSelector) throws JMSException; + public InputStream createInputStream(Destination dest, String messageSelector, boolean noLocal) throws JMSException; + + public InputStream createDurableInputStream(Topic dest, String name) throws JMSException; + public InputStream createDurableInputStream(Topic dest, String name, String messageSelector) throws JMSException; + public InputStream createDurableInputStream(Topic dest, String name, String messageSelector, boolean noLocal) throws JMSException; + + public OutputStream createOutputStream(Destination dest) throws JMSException; + public OutputStream createOutputStream(Destination dest, Map streamProperties, int deliveryMode, + int priority, long timeToLive) throws JMSException; + + /** + * Unsubscribes a durable subscription that has been created by a client. + *

+ * This method deletes the state being maintained on behalf of the + * subscriber by its provider. + *

+ * It is erroneous for a client to delete a durable subscription while there + * is an active MessageConsumer or TopicSubscriber + * for the subscription, or while a consumed message is part of a pending + * transaction or has not been acknowledged in the session. + * + * @param name + * the name used to identify this subscription + * @throws JMSException + * if the session fails to unsubscribe to the durable + * subscription due to some internal error. + * @throws InvalidDestinationException + * if an invalid subscription name is specified. + * @since 1.1 + */ + public void unsubscribe(String name) throws JMSException; +} diff --git a/activemq-core/src/main/java/org/activemq/ThreadPriorities.java b/activemq-core/src/main/java/org/activemq/ThreadPriorities.java new file mode 100755 index 0000000000..66caf9e07c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/ThreadPriorities.java @@ -0,0 +1,34 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) Simula Labs. +* +* Licensed 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.activemq; + + +/** +* A holder for different thread priorites used in ActiveMQ +* +* @version $Revision: 1.9 $ +*/ + +public interface ThreadPriorities { + public static final int INBOUND_BROKER_CONNECTION = 6; + public static final int OUT_BOUND_BROKER_DISPATCH = 6; + public static final int INBOUND_CLIENT_CONNECTION = 7; + public static final int INBOUND_CLIENT_SESSION = 7; + public static final int BROKER_MANAGEMENT = 9; +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/TransactionContext.java b/activemq-core/src/main/java/org/activemq/TransactionContext.java new file mode 100755 index 0000000000..df9efe7012 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/TransactionContext.java @@ -0,0 +1,605 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; + +import javax.jms.JMSException; +import javax.jms.TransactionInProgressException; +import javax.jms.TransactionRolledBackException; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import org.activemq.command.ConnectionId; +import org.activemq.command.DataArrayResponse; +import org.activemq.command.IntegerResponse; +import org.activemq.command.LocalTransactionId; +import org.activemq.command.TransactionId; +import org.activemq.command.TransactionInfo; +import org.activemq.command.XATransactionId; +import org.activemq.transaction.Synchronization; +import org.activemq.util.JMSExceptionSupport; +import org.activemq.util.LongSequenceGenerator; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * A TransactionContext provides the means to control a JMS transaction. It + * provides a local transaction interface and also an XAResource interface. + * + *

An application server controls the transactional assignment of an + * XASession by obtaining its XAResource. It uses the XAResource to assign the + * session to a transaction, prepare and commit work on the transaction, and so + * on.

An XAResource provides some fairly sophisticated facilities for + * interleaving work on multiple transactions, recovering a list of transactions + * in progress, and so on. A JTA aware JMS provider must fully implement this + * functionality. This could be done by using the services of a database that + * supports XA, or a JMS provider may choose to implement this functionality + * from scratch.

+ * + * @version $Revision: 1.10 $ + * @see javax.jms.Session + * @see javax.jms.QueueSession + * @see javax.jms.TopicSession + * @see javax.jms.XASession + */ +public class TransactionContext implements XAResource { + + static final private Log log = LogFactory.getLog(TransactionContext.class); + + // XATransactionId -> ArrayList of TransactionContext objects + private static final ConcurrentHashMap endedXATransactionContexts = new ConcurrentHashMap(); + + private final ActiveMQConnection connection; + private final LongSequenceGenerator localTransactionIdGenerator; + private final ConnectionId connectionId; + private ArrayList synchornizations; + + // To track XA transactions. + private Xid associatedXid; + private TransactionId transactionId; + private LocalTransactionEventListener localTransactionEventListener; + + + public TransactionContext(ActiveMQConnection connection) { + this.connection = connection; + this.localTransactionIdGenerator = connection.getLocalTransactionIdGenerator(); + this.connectionId = connection.getConnectionInfo().getConnectionId(); + } + + public boolean isInXATransaction() { + return transactionId != null && transactionId.isXATransaction(); + } + + public boolean isInLocalTransaction() { + return transactionId != null && transactionId.isLocalTransaction(); + } + + /** + * @return Returns the localTransactionEventListener. + */ + public LocalTransactionEventListener getLocalTransactionEventListener() { + return localTransactionEventListener; + } + + /** + * Used by the resource adapter to listen to transaction events. + * + * @param localTransactionEventListener + * The localTransactionEventListener to set. + */ + public void setLocalTransactionEventListener(LocalTransactionEventListener localTransactionEventListener) { + this.localTransactionEventListener = localTransactionEventListener; + } + + // /////////////////////////////////////////////////////////// + // + // Methods that work with the Synchronization objects registered with + // the transaction. + // + // /////////////////////////////////////////////////////////// + + public void addSynchronization(Synchronization s) { + if( synchornizations == null ) + synchornizations = new ArrayList(10); + synchornizations.add(s); + } + + private void afterRollback() throws JMSException { + if( synchornizations == null ) + return; + + int size = synchornizations.size(); + try { + for (int i = 0; i < size; i++) { + ((Synchronization) synchornizations.get(i)).afterRollback(); + } + } catch (JMSException e) { + throw e; + } catch (Throwable e) { + throw JMSExceptionSupport.create(e); + } + } + + private void afterCommit() throws JMSException { + if( synchornizations == null ) + return; + + int size = synchornizations.size(); + try { + for (int i = 0; i < size; i++) { + ((Synchronization) synchornizations.get(i)).afterCommit(); + } + } catch (JMSException e) { + throw e; + } catch (Throwable e) { + throw JMSExceptionSupport.create(e); + } + } + + private void beforeEnd() throws JMSException { + if( synchornizations == null ) + return; + + int size = synchornizations.size(); + try { + for (int i = 0; i < size; i++) { + ((Synchronization) synchornizations.get(i)).beforeEnd(); + } + } catch (JMSException e) { + throw e; + } catch (Throwable e) { + throw JMSExceptionSupport.create(e); + } + } + + public TransactionId getTransactionId() { + return transactionId; + } + + // /////////////////////////////////////////////////////////// + // + // Local transaction interface. + // + // /////////////////////////////////////////////////////////// + + /** + * Start a local transaction. + */ + public void begin() throws JMSException { + + if (isInXATransaction()) + throw new TransactionInProgressException( + "Cannot start local transaction. XA transaction is already in progress."); + + if (transactionId==null) { + synchornizations = null; + this.transactionId = new LocalTransactionId(connectionId, localTransactionIdGenerator.getNextSequenceId()); + TransactionInfo info = new TransactionInfo(getConnectionId(), transactionId, TransactionInfo.BEGIN); + this.connection.ensureConnectionInfoSent(); + this.connection.asyncSendPacket(info); + + // Notify the listener that the tx was started. + if (localTransactionEventListener != null) { + localTransactionEventListener.beginEvent(); + } + } + } + + /** + * Rolls back any work done in this transaction and releases any locks + * currently held. + * + * @throws JMSException + * if the JMS provider fails to roll back the transaction due to + * some internal error. + * @throws javax.jms.IllegalStateException + * if the method is not called by a transacted session. + */ + public void rollback() throws JMSException { + if (isInXATransaction()) + throw new TransactionInProgressException("Cannot rollback() if an XA transaction is already in progress "); + + if (transactionId!=null) { + TransactionInfo info = new TransactionInfo(getConnectionId(), transactionId, TransactionInfo.ROLLBACK); + this.transactionId = null; + this.connection.asyncSendPacket(info); + // Notify the listener that the tx was rolled back + if (localTransactionEventListener != null) { + localTransactionEventListener.rollbackEvent(); + } + } + + afterRollback(); + } + + /** + * Commits all work done in this transaction and releases any locks + * currently held. + * + * @throws JMSException + * if the JMS provider fails to commit the transaction due to + * some internal error. + * @throws TransactionRolledBackException + * if the transaction is rolled back due to some internal error + * during commit. + * @throws javax.jms.IllegalStateException + * if the method is not called by a transacted session. + */ + public void commit() throws JMSException { + if (isInXATransaction()) + throw new TransactionInProgressException("Cannot commit() if an XA transaction is already in progress "); + + beforeEnd(); + + // Only send commit if the transaction was started. + if (transactionId!=null) { + TransactionInfo info = new TransactionInfo(getConnectionId(), transactionId, TransactionInfo.COMMIT_ONE_PHASE); + this.transactionId = null; + // Notify the listener that the tx was committed back + this.connection.syncSendPacket(info); + if (localTransactionEventListener != null) { + localTransactionEventListener.commitEvent(); + } + afterCommit(); + } + } + + // /////////////////////////////////////////////////////////// + // + // XAResource Implementation + // + // /////////////////////////////////////////////////////////// + /** + * Associates a transaction with the resource. + */ + public void start(Xid xid, int flags) throws XAException { + + if( log.isDebugEnabled() ) + log.debug("Start: "+xid); + + if (isInLocalTransaction()) + throw new XAException(XAException.XAER_PROTO); + + // Are we already associated? + if (associatedXid != null) { + throw new XAException(XAException.XAER_PROTO); + } + +// if ((flags & TMJOIN) == TMJOIN) { +// // TODO: verify that the server has seen the xid +// } +// if ((flags & TMJOIN) == TMRESUME) { +// // TODO: verify that the xid was suspended. +// } + + // associate + synchornizations = null; + setXid(xid); + } + + /** + * @return + */ + private ConnectionId getConnectionId() { + return connection.getConnectionInfo().getConnectionId(); + } + + public void end(Xid xid, int flags) throws XAException { + + if( log.isDebugEnabled() ) + log.debug("End: "+xid); + + if (isInLocalTransaction()) + throw new XAException(XAException.XAER_PROTO); + + if ((flags & (TMSUSPEND|TMFAIL)) !=0 ) { + // You can only suspend the associated xid. + if (!equals(associatedXid, xid)) { + throw new XAException(XAException.XAER_PROTO); + } + + // TODO: we may want to put the xid in a suspended list. + try { + beforeEnd(); + } catch (JMSException e) { + throw toXAException(e); + } + setXid(null); + } else if ((flags & TMSUCCESS) == TMSUCCESS) { + // set to null if this is the current xid. + // otherwise this could be an asynchronous success call + if (equals(associatedXid, xid)) { + try { + beforeEnd(); + } catch (JMSException e) { + throw toXAException(e); + } + setXid(null); + } + } else { + throw new XAException(XAException.XAER_INVAL); + } + } + + private boolean equals(Xid xid1, Xid xid2) { + if( xid1 == xid2 ) + return true; + if( xid1==null ^ xid2==null ) + return false; + return xid1.getFormatId()==xid2.getFormatId() && + Arrays.equals(xid1.getBranchQualifier(), xid2.getBranchQualifier()) && + Arrays.equals(xid1.getGlobalTransactionId(), xid2.getGlobalTransactionId()); + } + + public int prepare(Xid xid) throws XAException { + if( log.isDebugEnabled() ) + log.debug("Prepare: "+xid); + + // We allow interleaving multiple transactions, so + // we don't limit prepare to the associated xid. + XATransactionId x; + // THIS SHOULD NEVER HAPPEN because end(xid, TMSUCCESS) should have been + // called first + if (xid==null || (equals(associatedXid, xid)) ) { + throw new XAException(XAException.XAER_PROTO); + } else { + // TODO: cache the known xids so we don't keep recreating this one?? + x = new XATransactionId(xid); + } + + try { + TransactionInfo info = new TransactionInfo(getConnectionId(), x, TransactionInfo.PREPARE); + + // Find out if the server wants to commit or rollback. + IntegerResponse response = (IntegerResponse) this.connection.syncSendPacket(info); + return response.getResult(); + + } catch (JMSException e) { + throw toXAException(e); + } + } + + public void rollback(Xid xid) throws XAException { + if( log.isDebugEnabled() ) + log.debug("Rollback: "+xid); + + // We allow interleaving multiple transactions, so + // we don't limit rollback to the associated xid. + XATransactionId x; + if (xid==null) { + throw new XAException(XAException.XAER_PROTO); + } + if (equals(associatedXid, xid)) { + // I think this can happen even without an end(xid) call. Need to + // check spec. + x = (XATransactionId) transactionId; + } else { + x = new XATransactionId(xid); + } + + try { + // Let the server know that the tx is rollback. + TransactionInfo info = new TransactionInfo(getConnectionId(), x, TransactionInfo.ROLLBACK); + this.connection.syncSendPacket(info); + + ArrayList l = (ArrayList) endedXATransactionContexts.remove(x); + if( l!=null && !l.isEmpty()) { + for (Iterator iter = l.iterator(); iter.hasNext();) { + TransactionContext ctx = (TransactionContext) iter.next(); + ctx.afterRollback(); + } + } + + } catch (JMSException e) { + throw toXAException(e); + } + } + + // XAResource interface + public void commit(Xid xid, boolean onePhase) throws XAException { + + if( log.isDebugEnabled() ) + log.debug("Commit: "+xid); + + // We allow interleaving multiple transactions, so + // we don't limit commit to the associated xid. + XATransactionId x; + if (xid==null || (equals(associatedXid, xid)) ) { + // should never happen, end(xid,TMSUCCESS) must have been previously + // called + throw new XAException(XAException.XAER_PROTO); + } else { + x = new XATransactionId(xid); + } + + + try { + + // Notify the server that the tx was committed back + TransactionInfo info = new TransactionInfo(getConnectionId(), x, + onePhase ? TransactionInfo.COMMIT_ONE_PHASE : TransactionInfo.COMMIT_TWO_PHASE); + + this.connection.syncSendPacket(info); + + ArrayList l = (ArrayList) endedXATransactionContexts.remove(x); + if( l!=null && !l.isEmpty()) { + for (Iterator iter = l.iterator(); iter.hasNext();) { + TransactionContext ctx = (TransactionContext) iter.next(); + ctx.afterCommit(); + } + } + + } catch (JMSException e) { + throw toXAException(e); + } + + } + + public void forget(Xid xid) throws XAException { + if( log.isDebugEnabled() ) + log.debug("Forget: "+xid); + + // We allow interleaving multiple transactions, so + // we don't limit forget to the associated xid. + XATransactionId x; + if (xid==null) { + throw new XAException(XAException.XAER_PROTO); + } + if (equals(associatedXid, xid)) { + // TODO determine if this can happen... I think not. + x = (XATransactionId) transactionId; + } else { + x = new XATransactionId(xid); + } + + TransactionInfo info = new TransactionInfo(getConnectionId(), x, TransactionInfo.FORGET); + + try { + // Tell the server to forget the transaction. + this.connection.syncSendPacket(info); + } catch (JMSException e) { + throw toXAException(e); + } + } + + public boolean isSameRM(XAResource xaResource) throws XAException { + if (xaResource == null) { + return false; + } + if (!(xaResource instanceof TransactionContext)) { + return false; + } + TransactionContext xar = (TransactionContext) xaResource; + try { + return getResourceManagerId().equals(xar.getResourceManagerId()); + } catch (Throwable e) { + throw (XAException) new XAException("Could not get resource manager id.").initCause(e); + } + } + + public Xid[] recover(int flag) throws XAException { + if( log.isDebugEnabled() ) + log.debug("Recover: "+flag); + + TransactionInfo info = new TransactionInfo(getConnectionId(), null, TransactionInfo.RECOVER); + try { + DataArrayResponse receipt = (DataArrayResponse) this.connection.syncSendPacket(info); + return (XATransactionId[]) receipt.getData(); + } catch (JMSException e) { + throw toXAException(e); + } + } + + public int getTransactionTimeout() throws XAException { + return 0; + } + + public boolean setTransactionTimeout(int seconds) throws XAException { + return false; + } + + // /////////////////////////////////////////////////////////// + // + // Helper methods. + // + // /////////////////////////////////////////////////////////// + private String getResourceManagerId() throws JMSException { + return this.connection.getResourceManagerId(); + } + + private void setXid(Xid xid) throws XAException { + if (xid != null) { + // associate + associatedXid = xid; + transactionId = new XATransactionId(xid); + + TransactionInfo info = new TransactionInfo(connectionId,transactionId,TransactionInfo.BEGIN); + try { + this.connection.asyncSendPacket(info); + if( log.isDebugEnabled() ) + log.debug("Started XA transaction: "+transactionId); + } catch (JMSException e) { + throw toXAException(e); + } + + } else { + + if( transactionId!=null ) { + TransactionInfo info = new TransactionInfo(connectionId,transactionId,TransactionInfo.END); + try { + this.connection.syncSendPacket(info); + if( log.isDebugEnabled() ) + log.debug("Ended XA transaction: "+transactionId); + } catch (JMSException e) { + throw toXAException(e); + } + + // Add our self to the list of contexts that are interested in + // post commit/rollback events. + ArrayList l = (ArrayList) endedXATransactionContexts.get(transactionId); + if( l==null ) { + l = new ArrayList(3); + endedXATransactionContexts.put(transactionId, l); + } + l.add(this); + } + + // dis-associate + associatedXid = null; + transactionId = null; + } + } + + /** + * Converts a JMSException from the server to an XAException. if the + * JMSException contained a linked XAException that is returned instead. + * + * @param e + * @return + */ + private XAException toXAException(JMSException e) { + if (e.getCause() != null && e.getCause() instanceof XAException) { + XAException original = (XAException) e.getCause(); + XAException xae = new XAException(original.getMessage()); + xae.errorCode = original.errorCode; + xae.initCause(original); + return xae; + } + + XAException xae = new XAException(e.getMessage()); + xae.errorCode = XAException.XAER_RMFAIL; + xae.initCause(e); + return xae; + } + + public ActiveMQConnection getConnection() { + return connection; + } + + public void cleanup() { + associatedXid=null; + transactionId=null; + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/advisory/AdvisoryBroker.java b/activemq-core/src/main/java/org/activemq/advisory/AdvisoryBroker.java new file mode 100755 index 0000000000..e73fae7447 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/advisory/AdvisoryBroker.java @@ -0,0 +1,214 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.advisory; + +import java.util.Iterator; + +import org.activemq.broker.Broker; +import org.activemq.broker.BrokerFilter; +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.Destination; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.Command; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerId; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.DestinationInfo; +import org.activemq.command.MessageId; +import org.activemq.command.ProducerId; +import org.activemq.command.ProducerInfo; +import org.activemq.util.IdGenerator; +import org.activemq.util.LongSequenceGenerator; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * This broker filter handles tracking the state of the broker for purposes of publishing advisory messages + * to advisory consumers. + * + * @version $Revision$ + */ +public class AdvisoryBroker extends BrokerFilter { + + //private static final Log log = LogFactory.getLog(AdvisoryBroker.class); + + protected final ConcurrentHashMap connections = new ConcurrentHashMap(); + protected final ConcurrentHashMap consumers = new ConcurrentHashMap(); + protected final ConcurrentHashMap producers = new ConcurrentHashMap(); + protected final ConcurrentHashMap destinations = new ConcurrentHashMap(); + + static final private IdGenerator idGenerator = new IdGenerator(); + protected final ProducerId advisoryProducerId = new ProducerId(); + final private LongSequenceGenerator messageIdGenerator = new LongSequenceGenerator(); + + public AdvisoryBroker(Broker next) { + super(next); + advisoryProducerId.setConnectionId(idGenerator.generateId()); + } + + public void addConnection(ConnectionContext context, ConnectionInfo info) throws Throwable { + next.addConnection(context, info); + + ActiveMQTopic topic = AdvisorySupport.getConnectionAdvisoryTopic(); + fireAdvisory(context, topic, info); + connections.put(info.getConnectionId(), info); + } + + public void addConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + next.addConsumer(context, info); + + // Don't advise advisory topics. + if( !AdvisorySupport.isAdvisoryTopic(info.getDestination()) ) { + ActiveMQTopic topic = AdvisorySupport.getConsumerAdvisoryTopic(info.getDestination()); + fireAdvisory(context, topic, info); + consumers.put(info.getConsumerId(), info); + } else { + + // We need to replay all the previously collected state objects + // for this newly added consumer. + if( AdvisorySupport.isConnectionAdvisoryTopic(info.getDestination()) ) { + // Replay the connections. + for (Iterator iter = connections.values().iterator(); iter.hasNext();) { + ConnectionInfo value = (ConnectionInfo) iter.next(); + ActiveMQTopic topic = AdvisorySupport.getConnectionAdvisoryTopic(); + fireAdvisory(context, topic, value, info.getConsumerId()); + } + } + + // We need to replay all the previously collected destination objects + // for this newly added consumer. + if( AdvisorySupport.isDestinationAdvisoryTopic(info.getDestination()) ) { + // Replay the destinations. + for (Iterator iter = destinations.values().iterator(); iter.hasNext();) { + DestinationInfo value = (DestinationInfo) iter.next(); + ActiveMQTopic topic = AdvisorySupport.getDestinationAdvisoryTopic(value.getDestination()); + fireAdvisory(context, topic, value, info.getConsumerId()); + } + } + + // Replay the producers. + if( AdvisorySupport.isProducerAdvisoryTopic(info.getDestination()) ) { + for (Iterator iter = producers.values().iterator(); iter.hasNext();) { + ProducerInfo value = (ProducerInfo) iter.next(); + ActiveMQTopic topic = AdvisorySupport.getProducerAdvisoryTopic(value.getDestination()); + fireAdvisory(context, topic, value, info.getConsumerId()); + } + } + + // Replay the consumers. + if( AdvisorySupport.isConsumerAdvisoryTopic(info.getDestination()) ) { + for (Iterator iter = consumers.values().iterator(); iter.hasNext();) { + ConsumerInfo value = (ConsumerInfo) iter.next(); + ActiveMQTopic topic = AdvisorySupport.getConsumerAdvisoryTopic(value.getDestination()); + fireAdvisory(context, topic, value, info.getConsumerId()); + } + } + } + } + + public void addProducer(ConnectionContext context, ProducerInfo info) throws Throwable { + next.addProducer(context, info); + + // Don't advise advisory topics. + if( info.getDestination()!=null && !AdvisorySupport.isAdvisoryTopic(info.getDestination()) ) { + ActiveMQTopic topic = AdvisorySupport.getProducerAdvisoryTopic(info.getDestination()); + fireAdvisory(context, topic, info); + producers.put(info.getProducerId(), info); + } + } + + public Destination addDestination(ConnectionContext context, ActiveMQDestination destination) throws Throwable { + Destination answer = next.addDestination(context, destination); + ActiveMQTopic topic = AdvisorySupport.getDestinationAdvisoryTopic(destination); + DestinationInfo info = new DestinationInfo(context.getConnectionId(), DestinationInfo.ADD_OPERATION_TYPE, destination); + fireAdvisory(context, topic, info); + destinations.put(destination, info); + return answer; + } + + public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Throwable { + next.removeDestination(context, destination, timeout); + ActiveMQTopic topic = AdvisorySupport.getDestinationAdvisoryTopic(destination); + DestinationInfo info = (DestinationInfo) destinations.remove(destination); + if( info !=null ) { + info.setOperationType(DestinationInfo.REMOVE_OPERATION_TYPE); + fireAdvisory(context, topic, info); + } + } + + public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Throwable { + next.removeConnection(context, info, error); + + ActiveMQTopic topic = AdvisorySupport.getConnectionAdvisoryTopic(); + fireAdvisory(context, topic, info.createRemoveCommand()); + connections.remove(info.getConnectionId()); + } + + public void removeConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + next.removeConsumer(context, info); + + // Don't advise advisory topics. + if( !AdvisorySupport.isAdvisoryTopic(info.getDestination()) ) { + ActiveMQTopic topic = AdvisorySupport.getConsumerAdvisoryTopic(info.getDestination()); + fireAdvisory(context, topic, info.createRemoveCommand()); + consumers.remove(info.getConsumerId()); + } + } + + public void removeProducer(ConnectionContext context, ProducerInfo info) throws Throwable { + next.removeProducer(context, info); + + // Don't advise advisory topics. + if( info.getDestination()!=null && !AdvisorySupport.isAdvisoryTopic(info.getDestination()) ) { + ActiveMQTopic topic = AdvisorySupport.getProducerAdvisoryTopic(info.getDestination()); + fireAdvisory(context, topic, info.createRemoveCommand()); + producers.remove(info.getProducerId()); + } + } + + private void fireAdvisory(ConnectionContext context, ActiveMQTopic topic, Command command) throws Throwable { + fireAdvisory(context, topic, command, null); + } + + private void fireAdvisory(ConnectionContext context, ActiveMQTopic topic, Command command, ConsumerId targetConsumerId) throws Throwable { + + ActiveMQMessage advisoryMessage = new ActiveMQMessage(); + advisoryMessage.setDataStructure(command); + advisoryMessage.setPersistent(false); + advisoryMessage.setType(AdvisorySupport.ADIVSORY_MESSAGE_TYPE); + advisoryMessage.setMessageId(new MessageId(advisoryProducerId, messageIdGenerator.getNextSequenceId())); + advisoryMessage.setTargetConsumerId(targetConsumerId); + + advisoryMessage.setDestination(topic); + advisoryMessage.setResponseRequired(false); + advisoryMessage.setProducerId(advisoryProducerId); + + boolean originalFlowControl = context.isProducerFlowControl(); + try { + context.setProducerFlowControl(false); + next.send(context, advisoryMessage); + } finally { + context.setProducerFlowControl(originalFlowControl); + } + + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/advisory/AdvisorySupport.java b/activemq-core/src/main/java/org/activemq/advisory/AdvisorySupport.java new file mode 100755 index 0000000000..b9fb29a10c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/advisory/AdvisorySupport.java @@ -0,0 +1,139 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.advisory; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQTopic; + +public class AdvisorySupport { + + public static final String ADVISORY_TOPIC_PREFIX = "ActiveMQ.Advisory."; + public static final ActiveMQTopic CONNECTION_ADVISORY_TOPIC = new ActiveMQTopic(ADVISORY_TOPIC_PREFIX+"Connection"); + public static final ActiveMQTopic QUEUE_ADVISORY_TOPIC = new ActiveMQTopic(ADVISORY_TOPIC_PREFIX+"Queue"); + public static final ActiveMQTopic TOPIC_ADVISORY_TOPIC = new ActiveMQTopic(ADVISORY_TOPIC_PREFIX+"Topic"); + public static final ActiveMQTopic TEMP_QUEUE_ADVISORY_TOPIC = new ActiveMQTopic(ADVISORY_TOPIC_PREFIX+"TempQueue"); + public static final ActiveMQTopic TEMP_TOPIC_ADVISORY_TOPIC = new ActiveMQTopic(ADVISORY_TOPIC_PREFIX+"TempTopic"); + public static final String PRODUCER_ADVISORY_TOPIC_PREFIX = ADVISORY_TOPIC_PREFIX+"Producer."; + public static final String CONSUMER_ADVISORY_TOPIC_PREFIX = ADVISORY_TOPIC_PREFIX+"Consumer."; + public static final String ADIVSORY_MESSAGE_TYPE = "Advisory"; + public static final ActiveMQTopic TEMP_DESTINATION_COMPOSITE_ADVISORY_TOPIC = new ActiveMQTopic(TEMP_QUEUE_ADVISORY_TOPIC+","+TEMP_TOPIC_ADVISORY_TOPIC); + + public static ActiveMQTopic getConnectionAdvisoryTopic() { + return CONNECTION_ADVISORY_TOPIC; + } + + public static ActiveMQTopic getConsumerAdvisoryTopic(ActiveMQDestination destination) { + String name = CONSUMER_ADVISORY_TOPIC_PREFIX+destination.getQualifiedName(); + return new ActiveMQTopic(name); + } + + public static ActiveMQTopic getProducerAdvisoryTopic(ActiveMQDestination destination) { + String name = PRODUCER_ADVISORY_TOPIC_PREFIX+destination.getQualifiedName(); + return new ActiveMQTopic(name); + } + + public static ActiveMQTopic getDestinationAdvisoryTopic(ActiveMQDestination destination) { + switch( destination.getDestinationType() ) { + case ActiveMQDestination.QUEUE_TYPE: + return QUEUE_ADVISORY_TOPIC; + case ActiveMQDestination.TOPIC_TYPE: + return TOPIC_ADVISORY_TOPIC; + case ActiveMQDestination.TEMP_QUEUE_TYPE: + return TEMP_QUEUE_ADVISORY_TOPIC; + case ActiveMQDestination.TEMP_TOPIC_TYPE: + return TEMP_TOPIC_ADVISORY_TOPIC; + } + throw new RuntimeException("Unknown destination type: "+destination.getDestinationType()); + } + + public static boolean isDestinationAdvisoryTopic(ActiveMQDestination destination) { + if( destination.isComposite() ) { + ActiveMQDestination[] compositeDestinations = destination.getCompositeDestinations(); + for (int i = 0; i < compositeDestinations.length; i++) { + if( isDestinationAdvisoryTopic(compositeDestinations[i]) ) { + return true; + } + } + return false; + } else { + return + destination.equals(TEMP_QUEUE_ADVISORY_TOPIC) + || destination.equals(TEMP_TOPIC_ADVISORY_TOPIC) + || destination.equals(QUEUE_ADVISORY_TOPIC) + || destination.equals(TOPIC_ADVISORY_TOPIC); + } + } + + public static boolean isAdvisoryTopic(ActiveMQDestination destination) { + if( destination.isComposite() ) { + ActiveMQDestination[] compositeDestinations = destination.getCompositeDestinations(); + for (int i = 0; i < compositeDestinations.length; i++) { + if( isAdvisoryTopic(compositeDestinations[i]) ) { + return true; + } + } + return false; + } else { + return destination.isTopic() && destination.getPhysicalName().startsWith(ADVISORY_TOPIC_PREFIX); + } + } + + public static boolean isConnectionAdvisoryTopic(ActiveMQDestination destination) { + if( destination.isComposite() ) { + ActiveMQDestination[] compositeDestinations = destination.getCompositeDestinations(); + for (int i = 0; i < compositeDestinations.length; i++) { + if( isConnectionAdvisoryTopic(compositeDestinations[i]) ) { + return true; + } + } + return false; + } else { + return destination.equals(CONNECTION_ADVISORY_TOPIC); + } + } + + public static boolean isProducerAdvisoryTopic(ActiveMQDestination destination) { + if( destination.isComposite() ) { + ActiveMQDestination[] compositeDestinations = destination.getCompositeDestinations(); + for (int i = 0; i < compositeDestinations.length; i++) { + if( isProducerAdvisoryTopic(compositeDestinations[i]) ) { + return true; + } + } + return false; + } else { + return destination.isTopic() && destination.getPhysicalName().startsWith(PRODUCER_ADVISORY_TOPIC_PREFIX); + } + } + + public static boolean isConsumerAdvisoryTopic(ActiveMQDestination destination) { + if( destination.isComposite() ) { + ActiveMQDestination[] compositeDestinations = destination.getCompositeDestinations(); + for (int i = 0; i < compositeDestinations.length; i++) { + if( isConsumerAdvisoryTopic(compositeDestinations[i]) ) { + return true; + } + } + return false; + } else { + return destination.isTopic() && destination.getPhysicalName().startsWith(CONSUMER_ADVISORY_TOPIC_PREFIX); + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/advisory/package.html b/activemq-core/src/main/java/org/activemq/advisory/package.html new file mode 100755 index 0000000000..10586a4113 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/advisory/package.html @@ -0,0 +1,9 @@ + + + + + +Support for JMS Advisory messages + + + diff --git a/activemq-core/src/main/java/org/activemq/broker/AbstractConnection.java b/activemq-core/src/main/java/org/activemq/broker/AbstractConnection.java new file mode 100755 index 0000000000..deb097dffa --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/AbstractConnection.java @@ -0,0 +1,557 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.activemq.Service; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.BrokerInfo; +import org.activemq.command.Command; +import org.activemq.command.ConnectionId; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerId; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.DataArrayResponse; +import org.activemq.command.DestinationInfo; +import org.activemq.command.ExceptionResponse; +import org.activemq.command.FlushCommand; +import org.activemq.command.KeepAliveInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageDispatch; +import org.activemq.command.ProducerId; +import org.activemq.command.ProducerInfo; +import org.activemq.command.RemoveSubscriptionInfo; +import org.activemq.command.Response; +import org.activemq.command.SessionId; +import org.activemq.command.SessionInfo; +import org.activemq.command.ShutdownInfo; +import org.activemq.command.TransactionId; +import org.activemq.command.TransactionInfo; +import org.activemq.command.WireFormatInfo; +import org.activemq.state.CommandVisitor; +import org.activemq.state.ConsumerState; +import org.activemq.state.ProducerState; +import org.activemq.state.SessionState; +import org.activemq.thread.Task; +import org.activemq.thread.TaskRunner; +import org.activemq.thread.TaskRunnerFactory; +import org.activemq.util.ServiceSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + + +/** + * @version $Revision: 1.26 $ + */ +public abstract class AbstractConnection implements Service, Connection, Task, CommandVisitor { + + private static final Log log = LogFactory.getLog(AbstractConnection.class); + + protected final Broker broker; + + protected final List dispatchQueue = Collections.synchronizedList(new LinkedList()); + protected final TaskRunner taskRunner; + protected final Connector connector; + protected boolean demandForwardingBridge; + + protected final ConcurrentHashMap connectionStates = new ConcurrentHashMap(); + + private WireFormatInfo wireFormatInfo; + protected boolean disposed=false; + + static class ConnectionState extends org.activemq.state.ConnectionState { + private final ConnectionContext context; + + public ConnectionState(ConnectionInfo info, ConnectionContext context) { + super(info); + this.context = context; + } + + public ConnectionContext getContext() { + return context; + } + } + + + /** + * @param connector + * @param transport + * @param broker + * @param taskRunnerFactory - can be null if you want direct dispatch to the transport else commands are sent async. + */ + public AbstractConnection(Connector connector, Broker broker, TaskRunnerFactory taskRunnerFactory) { + + this.connector = connector; + this.broker = broker; + + if( taskRunnerFactory != null ) + taskRunner = taskRunnerFactory.createTaskRunner( this ); + else + taskRunner = null; + } + + public void start() throws Exception { + this.dispatch(connector.getBrokerInfo()); + } + + public void stop() throws Exception { + if( disposed) + return; + + disposed=true; + // + // Remove all logical connection associated with this connection + // from the broker. + ArrayList l = new ArrayList(connectionStates.keySet()); + for (Iterator iter = l.iterator(); iter.hasNext();) { + ConnectionId connectionId = (ConnectionId) iter.next(); + try { + processRemoveConnection(connectionId); + } catch (Throwable ignore) { + } + } + } + + public void serviceTransportException(IOException e) { + if( !disposed ) { + if( log.isDebugEnabled() ) + log.debug("Transport failed: "+e,e); + + log.debug("Transport failed: "+e,e); + ServiceSupport.dispose(this); + } + } + + public void serviceException(Throwable e) { + if( !disposed ) { + if( log.isDebugEnabled() ) + log.debug("Async error occurred: "+e,e); + // TODO: think about how to handle this. Should we send the error down to the client + // so that he can report it to a registered error listener? + // Should we terminate the connection? + } + } + + public Response service(Command command) { + + Response response=null; + boolean responseRequired = command.isResponseRequired(); + short commandId = command.getCommandId(); + try { + response = command.visit(this); + } catch ( Throwable e ) { + if( responseRequired ) { + if( log.isDebugEnabled() ) + log.debug("Sync error occurred: "+e,e); + response = new ExceptionResponse(e); + } else { + serviceException(e); + } + } + if( responseRequired ) { + if( response == null ) { + response = new Response(); + } + response.setCorrelationId(commandId); + } + return response; + + } + + protected ConnectionState lookupConnectionState(ConsumerId id) { + ConnectionState cs = (ConnectionState) connectionStates.get(id.getParentId().getParentId()); + if( cs== null ) + throw new IllegalStateException("Cannot lookup a consumer from a connection that had not been registered: "+id.getParentId().getParentId()); + return cs; + } + protected ConnectionState lookupConnectionState(ProducerId id) { + ConnectionState cs = (ConnectionState) connectionStates.get(id.getParentId().getParentId()); + if( cs== null ) + throw new IllegalStateException("Cannot lookup a producer from a connection that had not been registered: "+id.getParentId().getParentId()); + return cs; + } + protected ConnectionState lookupConnectionState(SessionId id) { + ConnectionState cs = (ConnectionState) connectionStates.get(id.getParentId()); + if( cs== null ) + throw new IllegalStateException("Cannot lookup a session from a connection that had not been registered: "+id.getParentId()); + return cs; + } + protected ConnectionState lookupConnectionState(ConnectionId connectionId) { + ConnectionState cs = (ConnectionState) connectionStates.get(connectionId); + if( cs== null ) + throw new IllegalStateException("Cannot lookup a connection that had not been registered: "+connectionId); + return cs; + } + + public Response processKeepAlive(KeepAliveInfo info) throws Throwable { + return null; + } + + public Response processRemoveSubscription(RemoveSubscriptionInfo info) throws Throwable { + broker.removeSubscription(lookupConnectionState(info.getConnectionId()).getContext(), info); + return null; + } + + public Response processWireFormat(WireFormatInfo info) throws Throwable { + wireFormatInfo = info; + return null; + } + + public Response processShutdown(ShutdownInfo info) throws Throwable { + stop(); + return null; + } + + public Response processFlush(FlushCommand command) throws Throwable { + return null; + } + + public Response processBeginTransaction(TransactionInfo info) throws Throwable { + ConnectionState cs = (ConnectionState) connectionStates.get(info.getConnectionId()); + ConnectionContext context=null; + if( cs!=null ) { + context = cs.getContext(); + } + broker.beginTransaction(context, info.getTransactionId()); + return null; + } + + public Response processEndTransaction(TransactionInfo info) throws Throwable { + // No need to do anything. This packet is just sent by the client + // make sure he is synced with the server as commit command could + // come from a different connection. + return null; + } + + public Response processPrepareTransaction(TransactionInfo info) throws Throwable { + ConnectionState cs = (ConnectionState) connectionStates.get(info.getConnectionId()); + ConnectionContext context=null; + if( cs!=null ) { + context = cs.getContext(); + } + broker.prepareTransaction(context, info.getTransactionId()); + return null; + } + + public Response processCommitTransactionOnePhase(TransactionInfo info) throws Throwable { + ConnectionState cs = (ConnectionState) connectionStates.get(info.getConnectionId()); + ConnectionContext context=null; + if( cs!=null ) { + context = cs.getContext(); + } + broker.commitTransaction(context, info.getTransactionId(), true); + return null; + } + + public Response processCommitTransactionTwoPhase(TransactionInfo info) throws Throwable { + ConnectionState cs = (ConnectionState) connectionStates.get(info.getConnectionId()); + ConnectionContext context=null; + if( cs!=null ) { + context = cs.getContext(); + } + broker.commitTransaction(context, info.getTransactionId(), false); + return null; + } + + public Response processRollbackTransaction(TransactionInfo info) throws Throwable { + ConnectionState cs = (ConnectionState) connectionStates.get(info.getConnectionId()); + ConnectionContext context=null; + if( cs!=null ) { + context = cs.getContext(); + } + broker.rollbackTransaction(context, info.getTransactionId()); + return null; + } + + public Response processForgetTransaction(TransactionInfo info) throws Throwable { + ConnectionState cs = (ConnectionState) connectionStates.get(info.getConnectionId()); + ConnectionContext context=null; + if( cs!=null ) { + context = cs.getContext(); + } + broker.forgetTransaction(context, info.getTransactionId()); + return null; + } + + public Response processRecoverTransactions(TransactionInfo info) throws Throwable { + ConnectionState cs = (ConnectionState) connectionStates.get(info.getConnectionId()); + ConnectionContext context=null; + if( cs!=null ) { + context = cs.getContext(); + } + TransactionId[] preparedTransactions = broker.getPreparedTransactions(context); + return new DataArrayResponse(preparedTransactions); + } + + + public Response processMessage(Message messageSend) throws Throwable { + messageSend.setRecievedByDFBridge(demandForwardingBridge); + broker.send(lookupConnectionState(messageSend.getProducerId()).getContext(), messageSend); + return null; + } + + public Response processMessageAck(MessageAck ack) throws Throwable { + broker.acknowledge(lookupConnectionState(ack.getConsumerId()).getContext(), ack); + return null; + } + + public Response processBrokerInfo(BrokerInfo info) { + demandForwardingBridge = true; + return null; + } + + public Response processAddDestination(DestinationInfo info) throws Throwable { + ConnectionState cs = lookupConnectionState(info.getConnectionId()); + broker.addDestination(cs.getContext(), info.getDestination()); + if( info.getDestination().isTemporary() ) { + cs.addTempDestination(info.getDestination()); + } + return null; + } + + public Response processRemoveDestination(DestinationInfo info) throws Throwable { + ConnectionState cs = lookupConnectionState(info.getConnectionId()); + broker.removeDestination(cs.getContext(), info.getDestination(), info.getTimeout()); + if( info.getDestination().isTemporary() ) { + cs.removeTempDestination(info.getDestination()); + } + return null; + } + + + public Response processAddProducer(ProducerInfo info) throws Throwable { + SessionId sessionId = info.getProducerId().getParentId(); + ConnectionId connectionId = sessionId.getParentId(); + + ConnectionState cs = lookupConnectionState(connectionId); + SessionState ss = cs.getSessionState(sessionId); + if( ss == null ) + throw new IllegalStateException("Cannot add a producer to a session that had not been registered: "+sessionId); + broker.addProducer(cs.getContext(), info); + ss.addProducer(info); + return null; + } + + public Response processRemoveProducer(ProducerId id) throws Throwable { + SessionId sessionId = id.getParentId(); + ConnectionId connectionId = sessionId.getParentId(); + + ConnectionState cs = lookupConnectionState(connectionId); + SessionState ss = cs.getSessionState(sessionId); + if( ss == null ) + throw new IllegalStateException("Cannot remove a producer from a session that had not been registered: "+sessionId); + ProducerState ps = ss.removeProducer(id); + if( ps == null ) + throw new IllegalStateException("Cannot remove a producer that had not been registered: "+id); + + broker.removeProducer(cs.getContext(), ps.getInfo()); + return null; + } + + public Response processAddConsumer(ConsumerInfo info) throws Throwable { + SessionId sessionId = info.getConsumerId().getParentId(); + ConnectionId connectionId = sessionId.getParentId(); + + ConnectionState cs = lookupConnectionState(connectionId); + SessionState ss = cs.getSessionState(sessionId); + if( ss == null ) + throw new IllegalStateException("Cannot add a consumer to a session that had not been registered: "+sessionId); + + broker.addConsumer(cs.getContext(), info); + ss.addConsumer(info); + return null; + } + + public Response processRemoveConsumer(ConsumerId id) throws Throwable { + + SessionId sessionId = id.getParentId(); + ConnectionId connectionId = sessionId.getParentId(); + + ConnectionState cs = lookupConnectionState(connectionId); + SessionState ss = cs.getSessionState(sessionId); + if( ss == null ) + throw new IllegalStateException("Cannot remove a consumer from a session that had not been registered: "+sessionId); + ConsumerState consumerState = ss.removeConsumer(id); + if( consumerState == null ) + throw new IllegalStateException("Cannot remove a consumer that had not been registered: "+id); + + broker.removeConsumer(cs.getContext(), consumerState.getInfo()); + return null; + } + + public Response processAddSession(SessionInfo info) throws Throwable { + ConnectionId connectionId = info.getSessionId().getParentId(); + + ConnectionState cs = lookupConnectionState(connectionId); + broker.addSession(cs.getContext(), info); + cs.addSession(info); + return null; + } + + public Response processRemoveSession(SessionId id) throws Throwable { + + ConnectionId connectionId = id.getParentId(); + + ConnectionState cs = lookupConnectionState(connectionId); + SessionState session = cs.getSessionState(id); + if( session == null ) + throw new IllegalStateException("Cannot remove session that had not been registered: "+id); + + // Cascade the connection stop to the consumers and producers. + for (Iterator iter = session.getConsumerIds().iterator(); iter.hasNext();) { + processRemoveConsumer((ConsumerId) iter.next()); + } + for (Iterator iter = session.getProducerIds().iterator(); iter.hasNext();) { + processRemoveProducer((ProducerId) iter.next()); + } + cs.removeSession(id); + broker.removeSession(cs.getContext(), session.getInfo()); + return null; + } + + public Response processAddConnection(ConnectionInfo info) throws Throwable { + // Setup the context. + ConnectionContext context = new ConnectionContext(); + context.setConnection(this); + context.setBroker(broker); + context.setConnector(connector); + context.setTransactions(new ConcurrentHashMap()); + String clientId = info.getClientId(); + context.setClientId(clientId); + context.setUserName(info.getUserName()); + context.setConnectionId(info.getConnectionId()); + context.setWireFormatInfo(wireFormatInfo); + connectionStates.put(info.getConnectionId(), new ConnectionState(info, context)); + + broker.addConnection(context, info); + return null; + } + + public Response processRemoveConnection(ConnectionId id) throws Throwable { + + ConnectionState cs = lookupConnectionState(id); + + // Cascade the connection stop to the sessions. + for (Iterator iter = cs.getSessionIds().iterator(); iter.hasNext();) { + processRemoveSession((SessionId) iter.next()); + } + + // Cascade the connection stop to temp destinations. + for (Iterator iter = cs.getTempDesinations().iterator(); iter.hasNext();) { + broker.removeDestination(cs.getContext(), (ActiveMQDestination) iter.next(), 0); + iter.remove(); + } + + broker.removeConnection(cs.getContext(), cs.getInfo(), null); + connectionStates.remove(id); + + return null; + } + + + public Connector getConnector() { + return connector; + } + + public void dispatchSync(Command command) { + + if( command.isMessageDispatch() ) { + + MessageDispatch md = (MessageDispatch) command; + Runnable sub = (Runnable) md.getConsumer(); + + try { + dispatch( command ); + } finally { + if( sub != null ) { + sub.run(); + } + } + + } else { + dispatch( command ); + } + } + + public void dispatchAsync(Command message) { + if( taskRunner==null ) { + dispatchSync( message ); + } else { + dispatchQueue.add(message); + try { + taskRunner.wakeup(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + public boolean iterate() { + if( dispatchQueue.isEmpty() ) { + return false; + } else { + Command command = (Command) dispatchQueue.remove(0); + dispatch( command ); + return true; + } + } + + /** + * @return true if the Connection is slow + */ + public boolean isSlow() { + return false; + } + + /** + * @return if after being marked, the Connection is still writing + */ + public boolean isBlocked() { + return false; + } + + + /** + * @return true if the Connection is connected + */ + public boolean isConnected() { + return !disposed; + } + + /** + * @return true if the Connection is active + */ + public boolean isActive() { + return !disposed; + } + + + + abstract protected void dispatch(Command command); + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/Broker.java b/activemq-core/src/main/java/org/activemq/broker/Broker.java new file mode 100755 index 0000000000..12756f151b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/Broker.java @@ -0,0 +1,155 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import org.activemq.Service; +import org.activemq.broker.region.Region; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.BrokerId; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ProducerInfo; +import org.activemq.command.SessionInfo; +import org.activemq.command.TransactionId; + +/** + * The Message Broker which routes messages, + * maintains subscriptions and connections, acknowledges messages and handles + * transactions. + * + * @version $Revision: 1.8 $ + */ +public interface Broker extends Region, Service { + + /** + * Get the id of the broker + * @param context + * @param info + * @param client + */ + public BrokerId getBrokerId(); + + /** + * Get the name of the broker + */ + public String getBrokerName(); + + /** + * A client is establishing a connection with the broker. + * @param context + * @param info + * @param client + */ + public void addConnection(ConnectionContext context, ConnectionInfo info) throws Throwable; + + /** + * A client is disconnecting from the broker. + * @param context the environment the operation is being executed under. + * @param info + * @param client + * @param error null if the client requested the disconnect or the error that caused the client to disconnect. + */ + public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Throwable; + + /** + * Adds a session. + * @param context + * @param info + * @throws Throwable + */ + public void addSession(ConnectionContext context, SessionInfo info) throws Throwable; + + /** + * Removes a session. + * @param context + * @param info + * @throws Throwable + */ + public void removeSession(ConnectionContext context, SessionInfo info) throws Throwable; + + /** + * Adds a producer. + * @param context the enviorment the operation is being executed under. + */ + public void addProducer(ConnectionContext context, ProducerInfo info) throws Throwable; + + /** + * Removes a producer. + * @param context the enviorment the operation is being executed under. + */ + public void removeProducer(ConnectionContext context, ProducerInfo info) throws Throwable; + + /** + * @return all clients added to the Broker. + * @throws Throwable + */ + public Connection[] getClients() throws Throwable; + + /** + * @return all destinations added to the Broker. + * @throws Throwable + */ + public ActiveMQDestination[] getDestinations() throws Throwable; + + /** + * Gets a list of all the prepared xa transactions. + * @param client + */ + public TransactionId[] getPreparedTransactions(ConnectionContext context) throws Throwable; + + /** + * Starts a transaction. + * @param client + * @param xid + */ + public void beginTransaction(ConnectionContext context, TransactionId xid) throws Throwable; + + /** + * Prepares a transaction. Only valid for xa transactions. + * @param client + * @param xid + * @return + */ + public int prepareTransaction(ConnectionContext context, TransactionId xid) throws Throwable; + + /** + * Rollsback a transaction. + * @param client + * @param xid + */ + + public void rollbackTransaction(ConnectionContext context, TransactionId xid) throws Throwable; + + /** + * Commits a transaction. + * @param client + * @param xid + * @param onePhase + */ + public void commitTransaction(ConnectionContext context, TransactionId xid, boolean onePhase) throws Throwable; + + /** + * Forgets a transaction. + * @param client + * @param xid + * @param onePhase + * @throws Throwable + */ + public void forgetTransaction(ConnectionContext context, TransactionId transactionId) throws Throwable; + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/BrokerFactory.java b/activemq-core/src/main/java/org/activemq/broker/BrokerFactory.java new file mode 100755 index 0000000000..e9465ac999 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/BrokerFactory.java @@ -0,0 +1,62 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker; + +import org.activeio.FactoryFinder; +import org.activemq.util.IOExceptionSupport; + +import java.io.IOException; +import java.net.URI; + +/** + * A helper class to create a fully configured broker service using a URI. + * + * @version $Revision$ + */ +public class BrokerFactory { + + static final private FactoryFinder brokerFactoryHandlerFinder = new FactoryFinder("META-INF/services/org/activemq/broker/"); + + public interface BrokerFactoryHandler { + public BrokerService createBroker(URI brokerURI) throws Exception; + } + + public static BrokerFactoryHandler createBrokerFactoryHandler(String type) throws IOException { + try { + return (BrokerFactoryHandler)brokerFactoryHandlerFinder.newInstance(type); + } catch (Throwable e) { + throw IOExceptionSupport.create("Could load "+type+" factory:"+e, e); + } + } + + /** + * Creates a broker from a URI configuration + * @param brokerURI + * @throws Exception + */ + public static BrokerService createBroker(URI brokerURI) throws Exception { + if( brokerURI.getScheme() == null ) + throw new IllegalArgumentException("Invalid broker URI, no scheme specified: "+brokerURI); + + BrokerFactoryHandler handler = createBrokerFactoryHandler(brokerURI.getScheme()); + BrokerService broker = handler.createBroker(brokerURI); + return broker; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/BrokerFilter.java b/activemq-core/src/main/java/org/activemq/broker/BrokerFilter.java new file mode 100755 index 0000000000..5c37d853af --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/BrokerFilter.java @@ -0,0 +1,151 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import org.activemq.broker.region.Destination; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.BrokerId; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.ProducerInfo; +import org.activemq.command.RemoveSubscriptionInfo; +import org.activemq.command.SessionInfo; +import org.activemq.command.TransactionId; + +/** + * Allows you to intercept broker operation so that features such as security can be + * implemented as a pluggable filter. + * + * @version $Revision: 1.10 $ + */ +public class BrokerFilter implements Broker { + + final protected Broker next; + + public BrokerFilter(Broker next) { + this.next=next; + } + + public void acknowledge(ConnectionContext context, MessageAck ack) throws Throwable { + next.acknowledge(context, ack); + } + + public void addConnection(ConnectionContext context, ConnectionInfo info) throws Throwable { + next.addConnection(context, info); + } + + public void addConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + next.addConsumer(context, info); + } + + public void addProducer(ConnectionContext context, ProducerInfo info) throws Throwable { + next.addProducer(context, info); + } + + public void commitTransaction(ConnectionContext context, TransactionId xid, boolean onePhase) throws Throwable { + next.commitTransaction(context, xid, onePhase); + } + + public void removeSubscription(ConnectionContext context, RemoveSubscriptionInfo info) throws Throwable { + next.removeSubscription(context, info); + } + + public TransactionId[] getPreparedTransactions(ConnectionContext context) throws Throwable { + return next.getPreparedTransactions(context); + } + + public int prepareTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + return next.prepareTransaction(context, xid); + } + + public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Throwable { + next.removeConnection(context, info, error); + } + + public void removeConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + next.removeConsumer(context, info); + } + + public void removeProducer(ConnectionContext context, ProducerInfo info) throws Throwable { + next.removeProducer(context, info); + } + + public void rollbackTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + next.rollbackTransaction(context, xid); + } + + public void send(ConnectionContext context, Message messageSend) throws Throwable { + next.send(context, messageSend); + } + + public void beginTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + next.beginTransaction(context, xid); + } + + public void forgetTransaction(ConnectionContext context, TransactionId transactionId) throws Throwable { + next.forgetTransaction(context, transactionId); + } + + public Connection[] getClients() throws Throwable { + return next.getClients(); + } + + public Destination addDestination(ConnectionContext context, ActiveMQDestination destination) throws Throwable { + return next.addDestination(context, destination); + } + + public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Throwable { + next.removeDestination(context, destination, timeout); + } + + public ActiveMQDestination[] getDestinations() throws Throwable { + return next.getDestinations(); + } + + public void start() throws Exception { + next.start(); + } + + public void stop() throws Exception { + next.stop(); + } + + public void addSession(ConnectionContext context, SessionInfo info) throws Throwable { + next.addSession(context, info); + } + + public void removeSession(ConnectionContext context, SessionInfo info) throws Throwable { + next.removeSession(context, info); + } + + public BrokerId getBrokerId() { + return next.getBrokerId(); + } + + public String getBrokerName() { + return next.getBrokerName(); + } + + public void gc() { + next.gc(); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/BrokerRegistry.java b/activemq-core/src/main/java/org/activemq/broker/BrokerRegistry.java new file mode 100755 index 0000000000..8b3ddbb143 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/BrokerRegistry.java @@ -0,0 +1,52 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * + * @version $Revision: 1.3 $ + */ +public class BrokerRegistry { + + static final private BrokerRegistry instance = new BrokerRegistry(); + + public static BrokerRegistry getInstance() { + return instance; + } + + ConcurrentHashMap brokers = new ConcurrentHashMap(); + + private BrokerRegistry() { + } + + public BrokerService lookup(String brokerName) { + return (BrokerService)brokers.get(brokerName); + } + + public void bind(String brokerName, BrokerService broker) { + brokers.put(brokerName, broker); + } + + public void unbind(String brokerName) { + brokers.remove(brokerName); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/BrokerService.java b/activemq-core/src/main/java/org/activemq/broker/BrokerService.java new file mode 100644 index 0000000000..6d4ca72e43 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/BrokerService.java @@ -0,0 +1,909 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.activemq.ActiveMQConnectionMetaData; +import org.activemq.Service; +import org.activemq.advisory.AdvisoryBroker; +import org.activemq.broker.jmx.BrokerView; +import org.activemq.broker.jmx.BrokerViewMBean; +import org.activemq.broker.jmx.ConnectorView; +import org.activemq.broker.jmx.ConnectorViewMBean; +import org.activemq.broker.jmx.ManagedRegionBroker; +import org.activemq.broker.jmx.ManagedTransportConnector; +import org.activemq.broker.jmx.ManagementContext; +import org.activemq.broker.jmx.NetworkConnectorView; +import org.activemq.broker.jmx.NetworkConnectorViewMBean; +import org.activemq.broker.jmx.ProxyConnectorView; +import org.activemq.broker.region.RegionBroker; +import org.activemq.broker.region.policy.PolicyMap; +import org.activemq.memory.UsageManager; +import org.activemq.network.NetworkConnector; +import org.activemq.network.jms.JmsConnector; +import org.activemq.proxy.ProxyConnector; +import org.activemq.store.DefaultPersistenceAdapterFactory; +import org.activemq.store.PersistenceAdapter; +import org.activemq.store.memory.MemoryPersistenceAdapter; +import org.activemq.thread.TaskRunnerFactory; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportServer; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.JMXSupport; +import org.activemq.util.ServiceStopper; +import org.activemq.util.URISupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +/** + * Represents a running broker service which consists of a number of transport + * connectors, network connectors and a bunch of properties which can be used to + * configure the broker as its lazily created. + * + * @version $Revision: 1.1 $ + */ +public class BrokerService implements Service { + public static final String DEFAULT_PORT = "61616"; + + private static final Log log = LogFactory.getLog(BrokerService.class); + + private boolean useJmx = false; + private boolean persistent = true; + private boolean populateJMSXUserID = false; + private boolean useShutdownHook = true; + private boolean useLoggingForShutdownErrors = false; + private String brokerName = "localhost"; + private File dataDirectory; + private Broker broker; + private ManagementContext managementContext; + private ObjectName brokerObjectName; + private TaskRunnerFactory taskRunnerFactory; + private UsageManager memoryManager; + private PersistenceAdapter persistenceAdapter; + private DefaultPersistenceAdapterFactory persistenceFactory; + private List transportConnectors = new CopyOnWriteArrayList(); + private List networkConnectors = new CopyOnWriteArrayList(); + private List proxyConnectors = new CopyOnWriteArrayList(); + private List registeredMBeanNames = new CopyOnWriteArrayList(); + private List jmsConnectors = new CopyOnWriteArrayList(); + private Thread shutdownHook; + private String[] transportConnectorURIs; + private String[] networkConnectorURIs; + private String[] proxyConnectorURIs; + private JmsConnector[] jmsBridgeConnectors; //these are Jms to Jms bridges to other jms messaging systems + private boolean deleteAllMessagesOnStartup; + private URI vmConnectorURI; + private PolicyMap destinationPolicy; + private AtomicBoolean started = new AtomicBoolean(false); + + /** + * Adds a new transport connector for the given bind address + * + * @return the newly created and added transport connector + * @throws Exception + */ + public TransportConnector addConnector(String bindAddress) throws Exception { + return addConnector(new URI(bindAddress)); + } + + /** + * Adds a new transport connector for the given bind address + * + * @return the newly created and added transport connector + * @throws Exception + */ + public TransportConnector addConnector(URI bindAddress) throws Exception { + return addConnector(createTransportConnector(getBroker(), bindAddress)); + } + + /** + * Adds a new transport connector for the given TransportServer transport + * + * @return the newly created and added transport connector + * @throws Exception + */ + public TransportConnector addConnector(TransportServer transport) throws Exception { + return addConnector(new TransportConnector(getBroker(), transport)); + } + + /** + * Adds a new transport connector + * + * @return the transport connector + * @throws Exception + */ + public TransportConnector addConnector(TransportConnector connector) throws Exception { + if (isUseJmx()) { + connector = connector.asManagedConnector(getManagementContext().getMBeanServer(), getBrokerObjectName()); + } + connector.setBroker(getBroker()); + transportConnectors.add(connector); + if (isUseJmx()) { + registerConnectorMBean(connector); + } + return connector; + } + + /** + * Adds a new network connector using the given discovery address + * + * @return the newly created and added network connector + * @throws Exception + */ + public NetworkConnector addNetworkConnector(String discoveryAddress) throws Exception { + return addNetworkConnector(new URI(discoveryAddress)); + } + + /** + * Adds a new proxy connector using the given bind address + * + * @return the newly created and added network connector + * @throws Exception + */ + public ProxyConnector addProxyConnector(String bindAddress) throws Exception { + return addProxyConnector(new URI(bindAddress)); + } + + /** + * Adds a new network connector using the given discovery address + * + * @return the newly created and added network connector + * @throws Exception + */ + public NetworkConnector addNetworkConnector(URI discoveryAddress) throws Exception{ + NetworkConnector connector=new NetworkConnector(); + // add the broker name to the parameters if not set + connector.setUri(discoveryAddress); + return addNetworkConnector(connector); + } + + /** + * Adds a new proxy connector using the given bind address + * + * @return the newly created and added network connector + * @throws Exception + */ + public ProxyConnector addProxyConnector(URI bindAddress) throws Exception{ + ProxyConnector connector=new ProxyConnector(); + connector.setBind(bindAddress); + connector.setRemote(new URI("fanout:multicast://default")); + return addProxyConnector(connector); + } + + /** + * Adds a new network connector to connect this broker to a federated + * network + */ + public NetworkConnector addNetworkConnector(NetworkConnector connector) throws Exception { + URI uri = getVmConnectorURI(); + HashMap map = new HashMap(URISupport.parseParamters(uri)); + map.put("network", "true"); + uri = URISupport.createURIWithQuery(uri, URISupport.createQueryString(map)); + connector.setLocalUri(uri); + connector.setBrokerName(getBrokerName()); + networkConnectors.add(connector); + if (isUseJmx()) { + registerNetworkConnectorMBean(connector); + } + return connector; + } + + public ProxyConnector addProxyConnector(ProxyConnector connector) throws Exception { + URI uri = getVmConnectorURI(); + connector.setLocalUri(uri); + proxyConnectors.add(connector); + if (isUseJmx()) { + registerProxyConnectorMBean(connector); + } + return connector; + } + + public JmsConnector addJmsConnector(JmsConnector connector){ + connector.setBrokerService(this); + jmsConnectors.add(connector); + return connector; + } + + public JmsConnector removeJmsConnector(JmsConnector connector){ + if (jmsConnectors.remove(connector)){ + return connector; + } + return null; + } + + // Service interface + // ------------------------------------------------------------------------- + public void start() throws Exception { + if (! started.compareAndSet(false, true)) { + throw new IllegalStateException("Allready started."); + } + + processHelperProperties(); + + BrokerRegistry.getInstance().bind(getBrokerName(), this); + + addShutdownHook(); + if (deleteAllMessagesOnStartup) { + getPersistenceAdapter().deleteAllMessages(); + } + + if (isUseJmx()) { + getManagementContext().start(); + } + + getBroker().start(); + + for (Iterator iter = getTransportConnectors().iterator(); iter.hasNext();) { + TransportConnector connector = (TransportConnector) iter.next(); + connector.start(); + } + + for (Iterator iter = getNetworkConnectors().iterator(); iter.hasNext();) { + NetworkConnector connector = (NetworkConnector) iter.next(); + connector.start(); + } + + for (Iterator iter = getProxyConnectors().iterator(); iter.hasNext();) { + ProxyConnector connector = (ProxyConnector) iter.next(); + connector.start(); + } + + for (Iterator iter = jmsConnectors.iterator(); iter.hasNext();) { + JmsConnector connector = (JmsConnector) iter.next(); + connector.start(); + } + + log.info("ActiveMQ JMS Message Broker (" + getBrokerName() + ") started"); + } + + public void stop() throws Exception { + if (! started.compareAndSet(true, false)) { + return; + } + log.info("ActiveMQ Message Broker (" + getBrokerName() + ") is shutting down"); + BrokerRegistry.getInstance().unbind(getBrokerName()); + + removeShutdownHook(); + + ServiceStopper stopper = new ServiceStopper(); + + for (Iterator iter = getTransportConnectors().iterator(); iter.hasNext();) { + TransportConnector connector = (TransportConnector) iter.next(); + stopper.stop(connector); + } + + for (Iterator iter = getNetworkConnectors().iterator(); iter.hasNext();) { + NetworkConnector connector = (NetworkConnector) iter.next(); + stopper.stop(connector); + } + + for (Iterator iter = getProxyConnectors().iterator(); iter.hasNext();) { + ProxyConnector connector = (ProxyConnector) iter.next(); + stopper.stop(connector); + } + + for (Iterator iter = jmsConnectors.iterator(); iter.hasNext();) { + JmsConnector connector = (JmsConnector) iter.next(); + stopper.stop(connector); + } + + + stopper.stop(getPersistenceAdapter()); + + if (broker != null) { + stopper.stop(broker); + } + + if (isUseJmx()) { + MBeanServer mbeanServer = getManagementContext().getMBeanServer(); + for (Iterator iter = registeredMBeanNames.iterator(); iter.hasNext();) { + ObjectName name = (ObjectName) iter.next(); + try { + mbeanServer.unregisterMBean(name); + } + catch (Exception e) { + stopper.onException(mbeanServer, e); + } + } + stopper.stop(getManagementContext()); + } + + log.info("ActiveMQ JMS Message Broker (" + getBrokerName() + ") stopped"); + + stopper.throwFirstException(); + } + + // Properties + // ------------------------------------------------------------------------- + public Broker getBroker() throws Exception { + if (broker == null) { + log.info("ActiveMQ " + ActiveMQConnectionMetaData.PROVIDER_VERSION + " JMS Message Broker (" + + getBrokerName() + ") is starting"); + log.info("For help or more information please see: http://www.logicblaze.com"); + broker = createBroker(); + } + return broker; + } + + public String getBrokerName() { + return brokerName; + } + + /** + * Sets the name of this broker; which must be unique in the network + */ + public void setBrokerName(String brokerName) { + this.brokerName = brokerName; + } + + public DefaultPersistenceAdapterFactory getPersistenceFactory() { + if (persistenceFactory == null) { + persistenceFactory = createPersistenceFactory(); + } + return persistenceFactory; + } + + public File getDataDirectory() { + if (dataDirectory == null) { + dataDirectory = new File(new File("activemq-data"), getBrokerName() + .replaceAll("[^a-zA-Z0-9\\.\\_\\-]", "_")); + } + return dataDirectory; + } + + /** + * Sets the directory in which the data files will be stored by default for + * the JDBC and Journal persistence adaptors. + * + * @param dataDirectory + * the directory to store data files + */ + public void setDataDirectory(File dataDirectory) { + this.dataDirectory = dataDirectory; + } + + public void setPersistenceFactory(DefaultPersistenceAdapterFactory persistenceFactory) { + this.persistenceFactory = persistenceFactory; + } + + public boolean isPersistent() { + return persistent; + } + + /** + * Sets whether or not persistence is enabled or disabled. + */ + public void setPersistent(boolean persistent) { + this.persistent = persistent; + } + + public boolean isPopulateJMSXUserID() { + return populateJMSXUserID; + } + + /** + * Sets whether or not the broker should populate the JMSXUserID header. + */ + public void setPopulateJMSXUserID(boolean populateJMSXUserID) { + this.populateJMSXUserID = populateJMSXUserID; + } + + public UsageManager getMemoryManager() { + if (memoryManager == null) { + memoryManager = new UsageManager(); + memoryManager.setLimit(1024 * 1024 * 20); // Default to 20 Meg + // limit + } + return memoryManager; + } + + public void setMemoryManager(UsageManager memoryManager) { + this.memoryManager = memoryManager; + } + + public PersistenceAdapter getPersistenceAdapter() throws IOException { + if (persistenceAdapter == null) { + persistenceAdapter = createPersistenceAdapter(); + } + return persistenceAdapter; + } + + /** + * Sets the persistence adaptor implementation to use for this broker + */ + public void setPersistenceAdapter(PersistenceAdapter persistenceAdapter) { + this.persistenceAdapter = persistenceAdapter; + } + + public TaskRunnerFactory getTaskRunnerFactory() { + if (taskRunnerFactory == null) { + taskRunnerFactory = new TaskRunnerFactory(); + } + return taskRunnerFactory; + } + + public void setTaskRunnerFactory(TaskRunnerFactory taskRunnerFactory) { + this.taskRunnerFactory = taskRunnerFactory; + } + + public boolean isUseJmx() { + return useJmx; + } + + /** + * Sets whether or not the Broker's services should be exposed into JMX or + * not. + */ + public void setUseJmx(boolean useJmx) { + this.useJmx = useJmx; + } + + public ObjectName getBrokerObjectName() throws IOException { + if (brokerObjectName == null) { + brokerObjectName = createBrokerObjectName(); + } + return brokerObjectName; + } + + /** + * Sets the JMX ObjectName for this broker + */ + public void setBrokerObjectName(ObjectName brokerObjectName) { + this.brokerObjectName = brokerObjectName; + } + + public ManagementContext getManagementContext() { + if (managementContext == null) { + managementContext = new ManagementContext(); + } + return managementContext; + } + + public void setManagementContext(ManagementContext managementContext) { + this.managementContext = managementContext; + } + + public String[] getNetworkConnectorURIs() { + return networkConnectorURIs; + } + + public void setNetworkConnectorURIs(String[] networkConnectorURIs) { + this.networkConnectorURIs = networkConnectorURIs; + } + + public String[] getTransportConnectorURIs() { + return transportConnectorURIs; + } + + public void setTransportConnectorURIs(String[] transportConnectorURIs) { + this.transportConnectorURIs = transportConnectorURIs; + } + + /** + * @return Returns the jmsBridgeConnectors. + */ + public JmsConnector[] getJmsBridgeConnectors(){ + return jmsBridgeConnectors; + } + + /** + * @param jmsBridgeConnectors The jmsBridgeConnectors to set. + */ + public void setJmsBridgeConnectors(JmsConnector[] jmsConnectors){ + this.jmsBridgeConnectors=jmsConnectors; + } + + public boolean isUseLoggingForShutdownErrors() { + return useLoggingForShutdownErrors; + } + + /** + * Sets whether or not we should use commons-logging when reporting errors + * when shutting down the broker + */ + public void setUseLoggingForShutdownErrors(boolean useLoggingForShutdownErrors) { + this.useLoggingForShutdownErrors = useLoggingForShutdownErrors; + } + + public boolean isUseShutdownHook() { + return useShutdownHook; + } + + /** + * Sets whether or not we should use a shutdown handler to close down the + * broker cleanly if the JVM is terminated. It is recommended you leave this + * enabled. + */ + public void setUseShutdownHook(boolean useShutdownHook) { + this.useShutdownHook = useShutdownHook; + } + + public List getTransportConnectors() { + return new ArrayList(transportConnectors); + } + + /** + * Sets the transport connectors which this broker will listen on for new + * clients + */ + public void setTransportConnectors(List transportConnectors) throws Exception { + for (Iterator iter = transportConnectors.iterator(); iter.hasNext();) { + TransportConnector connector = (TransportConnector) iter.next(); + connector.setBroker(getBroker()); + connector.setBrokerName(getBrokerName()); + addConnector(connector); + } + } + + public List getNetworkConnectors() { + return new ArrayList(networkConnectors); + } + + public List getProxyConnectors() { + return new ArrayList(proxyConnectors); + } + + /** + * Sets the network connectors which this broker will use to connect to + * other brokers in a federated network + */ + public void setNetworkConnectors(List networkConnectors) throws Exception { + for (Iterator iter = networkConnectors.iterator(); iter.hasNext();) { + NetworkConnector connector = (NetworkConnector) iter.next(); + addNetworkConnector(connector); + } + } + + /** + * Sets the network connectors which this broker will use to connect to + * other brokers in a federated network + */ + public void setProxyConnectors(List proxyConnectors) throws Exception { + for (Iterator iter = proxyConnectors.iterator(); iter.hasNext();) { + ProxyConnector connector = (ProxyConnector) iter.next(); + addProxyConnector(connector); + } + } + + public PolicyMap getDestinationPolicy() { + return destinationPolicy; + } + + /** + * Sets the destination specific policies available either for exact + * destinations or for wildcard areas of destinations. + */ + public void setDestinationPolicy(PolicyMap policyMap) { + this.destinationPolicy = policyMap; + } + + // Implementation methods + // ------------------------------------------------------------------------- + /** + * Handles any lazy-creation helper properties which are added to make + * things easier to configure inside environments such as Spring + * + * @throws Exception + */ + protected void processHelperProperties() throws Exception { + if (transportConnectorURIs != null) { + for (int i = 0; i < transportConnectorURIs.length; i++) { + String uri = transportConnectorURIs[i]; + addConnector(uri); + } + } + if (networkConnectorURIs != null) { + for (int i = 0; i < transportConnectorURIs.length; i++) { + String uri = transportConnectorURIs[i]; + addNetworkConnector(uri); + } + } + if (proxyConnectorURIs != null) { + for (int i = 0; i < proxyConnectorURIs.length; i++) { + String uri = proxyConnectorURIs[i]; + addProxyConnector(uri); + } + } + if (jmsBridgeConnectors != null){ + for (int i = 0; i < jmsBridgeConnectors.length; i++){ + addJmsConnector(jmsBridgeConnectors[i]); + } + } + } + + protected void registerConnectorMBean(TransportConnector connector) throws IOException, URISyntaxException { + MBeanServer mbeanServer = getManagementContext().getMBeanServer(); + ConnectorViewMBean view = new ConnectorView(connector); + Hashtable map = new Hashtable(); + map.put("Type", "Connector"); + map.put("BrokerName", JMXSupport.encodeObjectNamePart(getBrokerName())); + map.put("ConnectorName", JMXSupport.encodeObjectNamePart(connector.getName())); + try { + ObjectName objectName = new ObjectName("org.activemq", map); + mbeanServer.registerMBean(view, objectName); + registeredMBeanNames.add(objectName); + } + catch (Throwable e) { + throw IOExceptionSupport.create("Broker could not be registered in JMX: " + e.getMessage(), e); + } + } + + protected void registerNetworkConnectorMBean(NetworkConnector connector) throws IOException { + MBeanServer mbeanServer = getManagementContext().getMBeanServer(); + NetworkConnectorViewMBean view = new NetworkConnectorView(connector); + Hashtable map = new Hashtable(); + map.put("Type", "NetworkConnector"); + map.put("BrokerName", JMXSupport.encodeObjectNamePart(getBrokerName())); + // map.put("ConnectorName", + // JMXSupport.encodeObjectNamePart(connector.())); + try { + ObjectName objectName = new ObjectName("org.activemq", map); + mbeanServer.registerMBean(view, objectName); + registeredMBeanNames.add(objectName); + } + catch (Throwable e) { + throw IOExceptionSupport.create("Broker could not be registered in JMX: " + e.getMessage(), e); + } + } + + protected void registerProxyConnectorMBean(ProxyConnector connector) throws IOException { + MBeanServer mbeanServer = getManagementContext().getMBeanServer(); + ProxyConnectorView view = new ProxyConnectorView(connector); + Hashtable map = new Hashtable(); + map.put("Type", "ProxyConnector"); + map.put("BrokerName", JMXSupport.encodeObjectNamePart(getBrokerName())); + // map.put("ConnectorName", + // JMXSupport.encodeObjectNamePart(connector.())); + try { + ObjectName objectName = new ObjectName("org.activemq", map); + mbeanServer.registerMBean(view, objectName); + registeredMBeanNames.add(objectName); + } + catch (Throwable e) { + throw IOExceptionSupport.create("Broker could not be registered in JMX: " + e.getMessage(), e); + } + } + + /** + * Factory method to create a new broker + * + * @throws Exception + * + * @throws + * @throws + */ + protected Broker createBroker() throws Exception { + Broker regionBroker = createRegionBroker(); + Broker broker = addInterceptors(regionBroker); + + // Add a filter that will stop access to the broker once stopped + broker = new MutableBrokerFilter(broker) { + public void stop() throws Exception { + super.stop(); + setNext(new ErrorBroker("Broker has been stopped.") { + // Just ignore additional stop actions. + public void stop() throws Exception { + } + }); + } + }; + + if (isUseJmx()) { + ManagedRegionBroker managedBroker = (ManagedRegionBroker) regionBroker; + BrokerViewMBean view = new BrokerView(broker, managedBroker.getDestinationStatistics(), getMemoryManager()); + MBeanServer mbeanServer = getManagementContext().getMBeanServer(); + ObjectName objectName = getBrokerObjectName(); + mbeanServer.registerMBean(view, objectName); + registeredMBeanNames.add(objectName); + } + + return broker; + + } + + /** + * Factory method to create the core region broker onto which interceptors + * are added + * + * @throws Exception + */ + protected Broker createRegionBroker() throws Exception { + // we must start the persistence adaptor before we can create the region + // broker + getPersistenceAdapter().start(); + RegionBroker regionBroker = null; + if (isUseJmx()) { + MBeanServer mbeanServer = getManagementContext().getMBeanServer(); + regionBroker = new ManagedRegionBroker(mbeanServer, getBrokerObjectName(), + getTaskRunnerFactory(), getMemoryManager(), getPersistenceAdapter(), getDestinationPolicy()); + } + else { + regionBroker = new RegionBroker(getTaskRunnerFactory(), getMemoryManager(), getPersistenceAdapter(), + getDestinationPolicy()); + } + regionBroker.setBrokerName(getBrokerName()); + return regionBroker; + } + + /** + * Strategy method to add interceptors to the broker + * + * @throws IOException + */ + protected Broker addInterceptors(Broker broker) throws IOException { + broker = new TransactionBroker(broker, getPersistenceAdapter().createTransactionStore()); + broker = new AdvisoryBroker(broker); + broker = new CompositeDestinationBroker(broker); + if (isPopulateJMSXUserID()) { + broker = new UserIDBroker(broker); + } + return broker; + } + + protected PersistenceAdapter createPersistenceAdapter() throws IOException { + if (isPersistent()) { + return getPersistenceFactory().createPersistenceAdapter(); + } + else { + return new MemoryPersistenceAdapter(); + } + } + + protected DefaultPersistenceAdapterFactory createPersistenceFactory() { + DefaultPersistenceAdapterFactory factory = new DefaultPersistenceAdapterFactory(); + factory.setMemManager(getMemoryManager()); + factory.setDataDirectory(getDataDirectory()); + factory.setTaskRunnerFactory(getTaskRunnerFactory()); + return factory; + } + + protected ObjectName createBrokerObjectName() throws IOException { + try { + Hashtable map = new Hashtable(); + map.put("Type", "Broker"); + map.put("BrokerName", JMXSupport.encodeObjectNamePart(getBrokerName())); + return new ObjectName("org.activemq", map); + } + catch (Throwable e) { + throw IOExceptionSupport.create("Invalid JMX broker name: " + brokerName, e); + } + } + + protected TransportConnector createTransportConnector(Broker broker, URI brokerURI) throws Exception { + + TransportServer transport = TransportFactory.bind(getBrokerName(),brokerURI); + if (isUseJmx()) { + MBeanServer mbeanServer = getManagementContext().getMBeanServer(); + return new ManagedTransportConnector(mbeanServer, getBrokerObjectName(), broker, transport); + } + else { + return new TransportConnector(broker, transport); + } + } + + /** + * Extracts the port from the options + */ + protected Object getPort(Map options) { + Object port = options.get("port"); + if (port == null) { + port = DEFAULT_PORT; + log.warn("No port specified so defaulting to: " + port); + } + return port; + } + + protected void addShutdownHook() { + if (useShutdownHook) { + shutdownHook = new Thread("ActiveMQ ShutdownHook") { + public void run() { + containerShutdown(); + } + }; + Runtime.getRuntime().addShutdownHook(shutdownHook); + } + } + + protected void removeShutdownHook() { + if (shutdownHook != null) { + try { + Runtime.getRuntime().removeShutdownHook(shutdownHook); + } + catch (Exception e) { + log.debug("Caught exception, must be shutting down: " + e); + } + } + } + + /** + * Causes a clean shutdown of the container when the VM is being shut down + */ + protected void containerShutdown() { + try { + stop(); + } + catch (IOException e) { + Throwable linkedException = e.getCause(); + if (linkedException != null) { + logError("Failed to shut down: " + e + ". Reason: " + linkedException, linkedException); + } + else { + logError("Failed to shut down: " + e, e); + } + if (!useLoggingForShutdownErrors) { + e.printStackTrace(System.err); + } + } + catch (Exception e) { + logError("Failed to shut down: " + e, e); + } + } + + protected void logError(String message, Throwable e) { + if (useLoggingForShutdownErrors) { + log.error("Failed to shut down: " + e); + } + else { + System.err.println("Failed to shut down: " + e); + } + } + + public boolean isDeleteAllMessagesOnStartup() { + return deleteAllMessagesOnStartup; + } + + /** + * Sets whether or not all messages are deleted on startup - mostly only + * useful for testing. + */ + public void setDeleteAllMessagesOnStartup(boolean deletePersistentMessagesOnStartup) { + this.deleteAllMessagesOnStartup = deletePersistentMessagesOnStartup; + } + + public URI getVmConnectorURI() { + if (vmConnectorURI == null) { + try { + vmConnectorURI = new URI("vm://" + getBrokerName()); + } + catch (URISyntaxException e) { + } + } + return vmConnectorURI; + } + + public void setVmConnectorURI(URI vmConnectorURI) { + this.vmConnectorURI = vmConnectorURI; + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/CompositeDestinationBroker.java b/activemq-core/src/main/java/org/activemq/broker/CompositeDestinationBroker.java new file mode 100755 index 0000000000..54842b5170 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/CompositeDestinationBroker.java @@ -0,0 +1,103 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.Message; +import org.activemq.command.ProducerInfo; + +/** + * This broker filter handles composite destinations. + * + * If a broker operation is invoked using a composite destination, this filter + * repeats the operation using each destination of the composite. + * + * HRC: I think this filter is dangerous to use to with the consumer operations. Multiple + * Subscription objects will be associated with a single JMS consumer each having a + * different idea of what the current pre-fetch dispatch size is. + * + * If this is used, then the client has to expect many more messages to be dispatched + * than the pre-fetch setting allows. + * + * @version $Revision: 1.8 $ + */ +public class CompositeDestinationBroker extends BrokerFilter { + + public CompositeDestinationBroker(Broker next) { + super(next); + } + + /** + * A producer may register to send to multiple destinations via a composite destination. + */ + public void addProducer(ConnectionContext context, ProducerInfo info) throws Throwable { + // The destination may be null. + ActiveMQDestination destination = info.getDestination(); + if( destination!=null && destination.isComposite() ) { + ActiveMQDestination[] destinations = destination.getCompositeDestinations(); + for (int i = 0; i < destinations.length; i++) { + ProducerInfo copy = info.copy(); + copy.setDestination(destinations[i]); + next.addProducer(context, copy); + } + } else { + next.addProducer(context, info); + } + } + + /** + * A producer may de-register from sending to multiple destinations via a composite destination. + */ + public void removeProducer(ConnectionContext context, ProducerInfo info) throws Throwable { + // The destination may be null. + ActiveMQDestination destination = info.getDestination(); + if( destination!=null && destination.isComposite() ) { + ActiveMQDestination[] destinations = destination.getCompositeDestinations(); + for (int i = 0; i < destinations.length; i++) { + ProducerInfo copy = info.copy(); + copy.setDestination(destinations[i]); + next.removeProducer(context, copy); + } + } else { + next.removeProducer(context, info); + } + } + + /** + * + */ + public void send(ConnectionContext context, Message message) throws Throwable { + ActiveMQDestination destination = message.getDestination(); + if( destination.isComposite() ) { + ActiveMQDestination[] destinations = destination.getCompositeDestinations(); + for (int i = 0; i < destinations.length; i++) { + if( i!=0 ) { + message = message.copy(); + } + message.setOriginalDestination(destination); + message.setDestination(destinations[i]); + message.evictMarshlledForm(); + next.send(context, message); + } + } else { + next.send(context, message); + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/Connection.java b/activemq-core/src/main/java/org/activemq/broker/Connection.java new file mode 100755 index 0000000000..c8a7329681 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/Connection.java @@ -0,0 +1,85 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import org.activemq.Service; +import org.activemq.command.Command; +import org.activemq.command.Response; + +/** + * + * @version $Revision: 1.5 $ + */ +public interface Connection extends Service { + + /** + * @return the connector that created this connection. + */ + public Connector getConnector(); + + /** + * Sends a message to the client. + * + * @param message the message to send to the client. + */ + public void dispatchSync(Command message); + + /** + * Sends a message to the client. + * + * @param command + */ + public void dispatchAsync(Command command); + + + /** + * Services a client command and submits it to the broker. + * @param command + */ + public Response service(Command command); + + /** + * Handles an unexpected error associated with a connection. + * + * @param error + */ + public void serviceException(Throwable error); + + /** + * @return true if the Connection is slow + */ + public boolean isSlow(); + + /** + * @return if after being marked, the Connection is still writing + */ + public boolean isBlocked(); + + + /** + * @return true if the Connection is connected + */ + public boolean isConnected(); + + /** + * @return true if the Connection is active + */ + public boolean isActive(); + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/ConnectionContext.java b/activemq-core/src/main/java/org/activemq/broker/ConnectionContext.java new file mode 100755 index 0000000000..9f4d8c0f82 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/ConnectionContext.java @@ -0,0 +1,200 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +import org.activemq.command.ConnectionId; +import org.activemq.command.WireFormatInfo; +import org.activemq.filter.MessageEvaluationContext; +import org.activemq.security.SecurityContext; +import org.activemq.transaction.Transaction; + +/** + * Used to hold context information needed to process requests sent to a broker. + * + * @version $Revision: 1.5 $ + */ +public class ConnectionContext { + + private Connection connection; + private Connector connector; + private Broker broker; + private boolean inRecoveryMode; + private Transaction transaction; + private ConcurrentHashMap transactions; + private SecurityContext securityContext; + private ConnectionId connectionId; + private String clientId; + private String userName; + private boolean haAware; + private WireFormatInfo wireFormatInfo; + private Object longTermStoreContext; + private boolean producerFlowControl=true; + + private final MessageEvaluationContext messageEvaluationContext = new MessageEvaluationContext(); + + public SecurityContext getSecurityContext() { + return securityContext; + } + + public void setSecurityContext(SecurityContext subject) { + this.securityContext = subject; + } + + /** + * @return the broker being used. + */ + public Broker getBroker() { + return broker; + } + + /** + * @param broker being used + */ + public void setBroker(Broker broker) { + this.broker = broker; + } + + /** + * @return the connection being used + */ + public Connection getConnection() { + return connection; + } + + /** + * @param connection being used + */ + public void setConnection(Connection connection) { + this.connection = connection; + } + + /** + * @return the transaction being used. + */ + public Transaction getTransaction() { + return transaction; + } + + /** + * @param transaction being used. + */ + public void setTransaction(Transaction transaction) { + this.transaction = transaction; + } + + /** + * @return the connector being used. + */ + public Connector getConnector() { + return connector; + } + + /** + * @param connector being used. + */ + public void setConnector(Connector connector) { + this.connector = connector; + } + + /** + * @return + */ + public boolean isInRecoveryMode() { + return inRecoveryMode; + } + + public void setInRecoveryMode(boolean inRecoveryMode) { + this.inRecoveryMode = inRecoveryMode; + } + + public ConcurrentHashMap getTransactions() { + return transactions; + } + + public void setTransactions(ConcurrentHashMap transactions) { + this.transactions = transactions; + } + + public boolean isInTransaction() { + return transaction!=null; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public boolean isHaAware() { + return haAware; + } + + public void setHaAware(boolean haAware) { + this.haAware = haAware; + } + + public WireFormatInfo getWireFormatInfo() { + return wireFormatInfo; + } + + public void setWireFormatInfo(WireFormatInfo wireFormatInfo) { + this.wireFormatInfo = wireFormatInfo; + } + + public ConnectionId getConnectionId() { + return connectionId; + } + + public void setConnectionId(ConnectionId connectionId) { + this.connectionId = connectionId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public MessageEvaluationContext getMessageEvaluationContext() { + return messageEvaluationContext; + } + + public Object getLongTermStoreContext() { + return longTermStoreContext; + } + + public void setLongTermStoreContext(Object longTermStoreContext) { + this.longTermStoreContext = longTermStoreContext; + } + + public boolean isProducerFlowControl() { + return producerFlowControl; + } + + public void setProducerFlowControl(boolean disableProducerFlowControl) { + this.producerFlowControl = disableProducerFlowControl; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/Connector.java b/activemq-core/src/main/java/org/activemq/broker/Connector.java new file mode 100755 index 0000000000..f582f91d65 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/Connector.java @@ -0,0 +1,37 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import org.activemq.Service; +import org.activemq.command.BrokerInfo; + +/** + * A connector creates and manages client connections that talk to the Broker. + * + * @version $Revision: 1.3 $ + */ +public interface Connector extends Service { + + /** + * + * @return + */ + public BrokerInfo getBrokerInfo(); + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/DefaultBrokerFactory.java b/activemq-core/src/main/java/org/activemq/broker/DefaultBrokerFactory.java new file mode 100644 index 0000000000..520e89622c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/DefaultBrokerFactory.java @@ -0,0 +1,65 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker; + +import org.activemq.broker.BrokerFactory.BrokerFactoryHandler; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.URISupport; +import org.activemq.util.URISupport.CompositeData; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +/** + * Simple BrokerFactorySPI which using the brokerURI to extract the configuration + * parameters for the broker service. This directly configures the pojo model + * so there is no dependency on spring for configuration. + * + * @version $Revision$ + */ +public class DefaultBrokerFactory implements BrokerFactoryHandler { + + public BrokerService createBroker(URI brokerURI) throws Exception { + + CompositeData compositeData = URISupport.parseComposite(brokerURI); + Map params = new HashMap(compositeData.getParameters()); + + BrokerService brokerService = new BrokerService(); + IntrospectionSupport.setProperties(brokerService, params); + if( compositeData.getPath()!=null ) + brokerService.setBrokerName(compositeData.getPath()); + + URI[] components = compositeData.getComponents(); + for (int i = 0; i < components.length; i++) { + if( "network".equals(components[i].getScheme()) ) { + brokerService.addNetworkConnector(components[i].getSchemeSpecificPart()); + } else if( "proxy".equals(components[i].getScheme()) ) { + brokerService.addProxyConnector(components[i].getSchemeSpecificPart()); + } else { + brokerService.addConnector(components[i]); + } + } + + // TODO we want folks to be able to add other connectors and start the broker afterwards + //brokerService.start(); + return brokerService; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/ErrorBroker.java b/activemq-core/src/main/java/org/activemq/broker/ErrorBroker.java new file mode 100644 index 0000000000..f2b8873b8b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/ErrorBroker.java @@ -0,0 +1,149 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import org.activemq.broker.region.Destination; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.BrokerId; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.ProducerInfo; +import org.activemq.command.RemoveSubscriptionInfo; +import org.activemq.command.SessionInfo; +import org.activemq.command.TransactionId; + +/** + * Implementation of the broker where all it's methods throw an IllegalStateException. + * + * @version $Revision$ + */ +public class ErrorBroker implements Broker { + + private final String message; + + public ErrorBroker(String message) { + this.message=message; + } + + public BrokerId getBrokerId() { + throw new IllegalStateException(this.message); + } + + public String getBrokerName() { + throw new IllegalStateException(this.message); + } + + public void addConnection(ConnectionContext context, ConnectionInfo info) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void addSession(ConnectionContext context, SessionInfo info) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void removeSession(ConnectionContext context, SessionInfo info) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void addProducer(ConnectionContext context, ProducerInfo info) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void removeProducer(ConnectionContext context, ProducerInfo info) throws Throwable { + throw new IllegalStateException(this.message); + } + + public Connection[] getClients() throws Throwable { + throw new IllegalStateException(this.message); + } + + public ActiveMQDestination[] getDestinations() throws Throwable { + throw new IllegalStateException(this.message); + } + + public TransactionId[] getPreparedTransactions(ConnectionContext context) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void beginTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + throw new IllegalStateException(this.message); + } + + public int prepareTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void rollbackTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void commitTransaction(ConnectionContext context, TransactionId xid, boolean onePhase) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void forgetTransaction(ConnectionContext context, TransactionId transactionId) throws Throwable { + throw new IllegalStateException(this.message); + } + + public Destination addDestination(ConnectionContext context, ActiveMQDestination destination) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void addConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void removeConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void removeSubscription(ConnectionContext context, RemoveSubscriptionInfo info) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void send(ConnectionContext context, Message message) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void acknowledge(ConnectionContext context, MessageAck ack) throws Throwable { + throw new IllegalStateException(this.message); + } + + public void gc() { + throw new IllegalStateException(this.message); + } + + public void start() throws Exception { + throw new IllegalStateException(this.message); + } + + public void stop() throws Exception { + throw new IllegalStateException(this.message); + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/Main.java b/activemq-core/src/main/java/org/activemq/broker/Main.java new file mode 100755 index 0000000000..79d6efe88a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/Main.java @@ -0,0 +1,329 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * Main class that can bootstrap a ActiveMQ Broker. Handles command line + * argument parsing to set up the broker classpath and System properties. + * + * @version $Revision$ + */ +public class Main { + + private static final String BROKER_FACTORY_CLASS = "org.activemq.broker.BrokerFactory"; + private static File activeMQHome; + private final ArrayList extensions = new ArrayList(); + private URI uri; + private ClassLoader classLoader; + + public static void main(String[] args) throws Throwable { + Main main = new Main(); + + for (int i = 0; i < args.length; i++) { + if (args[i].startsWith("-D")) { + String key = args[i].substring(2); + String value = ""; + int pos = key.indexOf("="); + if (pos >= 0) { + value = key.substring(pos + 1); + key = key.substring(0, pos); + } + System.setProperty(key, value); + } else if (args[i].equals("--extdir")) { + if( !canUseExtdir() ) { + System.out.println("Extension directory feature not available due to the system classpath being able to load: "+BROKER_FACTORY_CLASS); + printUsage(); + return; + } + i++; + if (i >= args.length) { + System.out.println("Extension directory not specified."); + printUsage(); + return; + } + + File directory = new File(args[i]); + if (!directory.isDirectory()) { + System.out.println("Extension directory specified is not valid directory: " + directory); + printUsage(); + return; + } + main.addExtensionDirectory(directory); + + } else if (args[i].equals("--version")) { + System.out.println(); + System.out.println("ActiveMQ " + main.getVersion()); + System.out.println("For help or more information please see: http://www.logicblaze.com"); + System.out.println(); + return; + } else if (args[i].equals("-h") || args[i].equals("--help") || args[i].equals("-?")) { + printUsage(); + return; + } else { + if (main.getUri() != null) { + System.out.println("Multiple configuration uris cannot be specified."); + printUsage(); + return; + } + try { + main.setUri(new URI(args[i])); + } catch (URISyntaxException e) { + System.out.println("Invalid broker configuration URI: " + args[i] + ", reason: " + e.getMessage()); + printUsage(); + return; + } + } + } + + // Add the default directories. + if( canUseExtdir() ) { + main.addExtensionDirectory(new File(main.getActiveMQHome(), "conf")); + main.addExtensionDirectory(new File(main.getActiveMQHome(), "lib")); + main.addExtensionDirectory(new File(new File(main.getActiveMQHome(), "lib"), "optional")); + } + + + if (main.getUri() == null) { + main.setUri(getDefaultUri()); + } + main.run(); + } + + + public static URI getDefaultUri() { + try { + return new URI("xbean:activemq.xml"); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + /** + * The extension directory feature will not work if the broker factory is already in the classpath + * since we have to load him from a child ClassLoader we build for it to work correctly. + * + * @return + */ + public static boolean canUseExtdir() { + try { + Main.class.getClassLoader().loadClass(BROKER_FACTORY_CLASS); + return false; + } catch (ClassNotFoundException e) { + return true; + } + } + + private static void printUsage() { + System.out.println(); + System.out.println("Usage: Main [options] uri"); + System.out.println(); + System.out.println("Options:"); + if( canUseExtdir() ) { + System.out.println(" --extdir dir Add the jar files in the directory to the classpath."); + } + System.out.println(" -Dname=value Define a system property"); + System.out.println(" --version Display version information"); + System.out.println(" -h,-?,--help Display help information"); + System.out.println(); + System.out.println("URI:"); + System.out.println(); + System.out.println(" XBean based broker configuration:"); + System.out.println(" "); + System.out.println(); + System.out.println(" Example: Main xbean:file:activemq.xml"); + System.out.println(" Loads the xbean configuration file from the current working directory"); + System.out.println(" Example: Main xbean:activemq.xml"); + System.out.println(" Loads the xbean configuration file from the classpath"); + System.out.println(); + System.out.println(" Spring based broker configuration:"); + System.out.println(" "); + System.out.println(" Example: Main spring:file:activemq.xml"); + System.out.println(" Loads the spring configuration file from the current working directory"); + System.out.println(" Example: Main spring:activemq.xml"); + System.out.println(" Loads the spring configuration file from the classpath"); + System.out.println(); + System.out.println(" URI Parameter based broker configuration:"); + System.out.println(" Example: Main broker:(tcp://localhost:61616, tcp://localhost:5000)?useJmx=true"); + System.out.println(" Configures the broker with 2 transport connectors and jmx enabled"); + System.out + .println(" Example: Main broker:(tcp://localhost:61616, network:tcp://localhost:5000)?persistent=false"); + System.out + .println(" Configures the broker with 1 transport connector, and 1 network connector and persistence disabled"); + System.out.println(); + } + + public String getVersion() throws Throwable { + ClassLoader cl = getClassLoader(); + // Use reflection to get teh version + Object broker; + try { + Class activeMQConnectionMetaData = cl.loadClass("org.activemq.ActiveMQConnectionMetaData"); + Field field = activeMQConnectionMetaData.getField("PROVIDER_VERSION"); + return (String)field.get(null); + } catch (Throwable e) { + throw e; + } + } + + /** + * @throws Throwable + * + */ + public void run() throws Throwable { + + System.out.println("Loading Message Broker from: " + uri); + System.out.println("ACTIVEMQ_HOME: "+getActiveMQHome()); + + ClassLoader cl = getClassLoader(); + + // Use reflection to start the broker up. + Object broker; + try { + + Class brokerFactory = cl.loadClass(BROKER_FACTORY_CLASS); + Method createBroker = brokerFactory.getMethod("createBroker", new Class[] { URI.class }); + broker = createBroker.invoke(null, new Object[] { uri }); + + Method start = broker.getClass().getMethod("start", new Class[]{}); + start.invoke(broker, new Object[]{}); + + } catch (InvocationTargetException e) { + throw e.getCause(); + } catch (Throwable e) { + throw e; + } + + final boolean[] shutdown = new boolean[]{false}; + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + synchronized(shutdown) { + shutdown[0]=true; + shutdown.notify(); + } + } + }); + synchronized(shutdown) { + while( !shutdown[0] ) { + shutdown.wait(); + } + } + + // Use reflection to stop the broker + try { + Method stop = broker.getClass().getMethod("stop", new Class[] {}); + stop.invoke(broker, new Object[] {}); + } catch (InvocationTargetException e) { + throw e.getCause(); + } catch (Throwable e) { + throw e; + } + } + + + /** + * @return + * @throws MalformedURLException + */ + public ClassLoader getClassLoader() throws MalformedURLException { + if( classLoader==null ) { + + // + // Setup the ClassLoader + // + classLoader = Main.class.getClassLoader(); + if (!extensions.isEmpty()) { + + ArrayList urls = new ArrayList(); + for (Iterator iter = extensions.iterator(); iter.hasNext();) { + File dir = (File) iter.next(); + urls.add(dir.toURL()); + File[] files = dir.listFiles(); + if( files!=null ) { + for (int j = 0; j < files.length; j++) { + if( files[j].getName().endsWith(".zip") || files[j].getName().endsWith(".jar") ) { + urls.add(files[j].toURL()); + } + } + } + } + + URL u[] = new URL[urls.size()]; + urls.toArray(u); + classLoader = new URLClassLoader(u, classLoader); + } + + Thread.currentThread().setContextClassLoader(classLoader); + } + return classLoader; + } + + public URI getUri() { + return uri; + } + + public void setUri(URI config) { + this.uri = config; + } + + public void addExtensionDirectory(File directory) { + extensions.add(directory); + } + + public File getActiveMQHome() { + if( activeMQHome==null ) { + if( System.getProperty("activemq.home") != null ) { + activeMQHome = new File(System.getProperty("activemq.home")); + } + if( activeMQHome==null ){ + // guess from the location of the jar + URL url = Main.class.getClassLoader().getResource("org/activemq/broker/Main.class"); + if (url != null) { + try { + JarURLConnection jarConnection = (JarURLConnection) url.openConnection(); + url = jarConnection.getJarFileURL(); + URI baseURI = new URI(url.toString()).resolve(".."); + activeMQHome = new File(baseURI).getCanonicalFile(); + } catch (Exception ignored) { + } + } + } + if(activeMQHome==null){ + activeMQHome = new File("."); + } + } + return activeMQHome; + } + + public static void setActiveMQHome(File activeMQHome) { + Main.activeMQHome = activeMQHome; + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/MutableBrokerFilter.java b/activemq-core/src/main/java/org/activemq/broker/MutableBrokerFilter.java new file mode 100644 index 0000000000..4437944e8a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/MutableBrokerFilter.java @@ -0,0 +1,165 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import org.activemq.broker.region.Destination; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.BrokerId; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.ProducerInfo; +import org.activemq.command.RemoveSubscriptionInfo; +import org.activemq.command.SessionInfo; +import org.activemq.command.TransactionId; + +/** + * Like a BrokerFilter but it allows you to switch the getNext().broker. This has more + * overhead than a BrokerFilter since access to the getNext().broker has to synchronized + * since it is mutable + * + * @version $Revision: 1.10 $ + */ +public class MutableBrokerFilter implements Broker { + + private Broker next; + private final Object mutext = new Object(); + + public MutableBrokerFilter(Broker next) { + this.next = next; + } + + public Broker getNext() { + synchronized(mutext) { + return next; + } + } + + public void setNext(Broker next) { + synchronized(mutext) { + this.next=next; + } + } + + public void acknowledge(ConnectionContext context, MessageAck ack) throws Throwable { + getNext().acknowledge(context, ack); + } + + public void addConnection(ConnectionContext context, ConnectionInfo info) throws Throwable { + getNext().addConnection(context, info); + } + + public void addConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + getNext().addConsumer(context, info); + } + + public void addProducer(ConnectionContext context, ProducerInfo info) throws Throwable { + getNext().addProducer(context, info); + } + + public void commitTransaction(ConnectionContext context, TransactionId xid, boolean onePhase) throws Throwable { + getNext().commitTransaction(context, xid, onePhase); + } + + public void removeSubscription(ConnectionContext context, RemoveSubscriptionInfo info) throws Throwable { + getNext().removeSubscription(context, info); + } + + public TransactionId[] getPreparedTransactions(ConnectionContext context) throws Throwable { + return getNext().getPreparedTransactions(context); + } + + public int prepareTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + return getNext().prepareTransaction(context, xid); + } + + public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Throwable { + getNext().removeConnection(context, info, error); + } + + public void removeConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + getNext().removeConsumer(context, info); + } + + public void removeProducer(ConnectionContext context, ProducerInfo info) throws Throwable { + getNext().removeProducer(context, info); + } + + public void rollbackTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + getNext().rollbackTransaction(context, xid); + } + + public void send(ConnectionContext context, Message messageSend) throws Throwable { + getNext().send(context, messageSend); + } + + public void beginTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + getNext().beginTransaction(context, xid); + } + + public void forgetTransaction(ConnectionContext context, TransactionId transactionId) throws Throwable { + getNext().forgetTransaction(context, transactionId); + } + + public Connection[] getClients() throws Throwable { + return getNext().getClients(); + } + + public Destination addDestination(ConnectionContext context, ActiveMQDestination destination) throws Throwable { + return getNext().addDestination(context, destination); + } + + public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Throwable { + getNext().removeDestination(context, destination, timeout); + } + + public ActiveMQDestination[] getDestinations() throws Throwable { + return getNext().getDestinations(); + } + + public void start() throws Exception { + getNext().start(); + } + + public void stop() throws Exception { + getNext().stop(); + } + + public void addSession(ConnectionContext context, SessionInfo info) throws Throwable { + getNext().addSession(context, info); + } + + public void removeSession(ConnectionContext context, SessionInfo info) throws Throwable { + getNext().removeSession(context, info); + } + + public BrokerId getBrokerId() { + return getNext().getBrokerId(); + } + + public String getBrokerName() { + return getNext().getBrokerName(); + } + + public void gc() { + getNext().gc(); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/TransactionBroker.java b/activemq-core/src/main/java/org/activemq/broker/TransactionBroker.java new file mode 100755 index 0000000000..6f9e016347 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/TransactionBroker.java @@ -0,0 +1,239 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +import org.activemq.command.ConnectionInfo; +import org.activemq.command.LocalTransactionId; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.TransactionId; +import org.activemq.command.XATransactionId; +import org.activemq.store.TransactionRecoveryListener; +import org.activemq.store.TransactionStore; +import org.activemq.transaction.LocalTransaction; +import org.activemq.transaction.Transaction; +import org.activemq.transaction.XATransaction; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.WrappedException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.jms.JMSException; +import javax.transaction.xa.XAException; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; + +/** + * This broker filter handles the transaction related operations in the Broker interface. + * + * @version $Revision: 1.10 $ + */ +public class TransactionBroker extends BrokerFilter { + + private static final Log log = LogFactory.getLog(TransactionBroker.class); + + // The prepared XA transactions. + private TransactionStore transactionStore; + private ConcurrentHashMap xaTransactions = new ConcurrentHashMap(); + + public TransactionBroker(Broker next, TransactionStore transactionStore) { + super(next); + this.transactionStore=transactionStore; + } + + ////////////////////////////////////////////////////////////////////////////// + // + // Life cycle Methods + // + ////////////////////////////////////////////////////////////////////////////// + + /** + * Recovers any prepared transactions. + */ + public void start() throws Exception { + next.start(); + transactionStore.start(); + try { + final ConnectionContext context = new ConnectionContext(); + context.setBroker(this); + context.setInRecoveryMode(true); + context.setTransactions(new ConcurrentHashMap()); + context.setProducerFlowControl(false); + + transactionStore.recover(new TransactionRecoveryListener() { + public void recover(XATransactionId xid, Message[] addedMessages, MessageAck[] aks) { + try { + beginTransaction(context, xid); + for (int i = 0; i < addedMessages.length; i++) { + send(context, addedMessages[i]); + } + for (int i = 0; i < aks.length; i++) { + acknowledge(context, aks[i]); + } + prepareTransaction(context, xid); + } catch (Throwable e) { + throw new WrappedException(e); + } + } + }); + } catch (WrappedException e) { + Throwable cause = e.getCause(); + throw IOExceptionSupport.create("Recovery Failed: "+cause.getMessage(), cause); + } + } + + public void stop() throws Exception { + transactionStore.stop(); + next.stop(); + } + + + ////////////////////////////////////////////////////////////////////////////// + // + // BrokerFilter overrides + // + ////////////////////////////////////////////////////////////////////////////// + public TransactionId[] getPreparedTransactions(ConnectionContext context) throws Throwable { + ArrayList txs = new ArrayList(); + for (Iterator iter = xaTransactions.values().iterator(); iter.hasNext();) { + Transaction tx = (Transaction) iter.next(); + if( tx.isPrepared() ) + txs.add(tx.getTransactionId()); + } + XATransactionId rc[] = new XATransactionId[txs.size()]; + txs.toArray(rc); + return rc; + } + + public void beginTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + + // the transaction may have already been started. + if( xid.isXATransaction() ) { + Transaction transaction = (Transaction)xaTransactions.get(xid); + if( transaction != null ) + return; + transaction = new XATransaction(transactionStore, (XATransactionId)xid, this); + xaTransactions.put(xid, transaction); + } else { + Map transactionMap = context.getTransactions(); + Transaction transaction = (Transaction)transactionMap.get(xid); + if( transaction != null ) + throw new JMSException("Transaction '"+xid+"' has already been started."); + + transaction = new LocalTransaction(transactionStore, (LocalTransactionId)xid, context); + transactionMap.put(xid, transaction); + } + } + + public int prepareTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + Transaction transaction = getTransaction(context, xid, false); + return transaction.prepare(); + } + + public void commitTransaction(ConnectionContext context, TransactionId xid, boolean onePhase) throws Throwable { + Transaction transaction = getTransaction(context, xid, true); + transaction.commit(onePhase); + } + + public void rollbackTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + Transaction transaction = getTransaction(context, xid, true); + transaction.rollback(); + } + + public void forgetTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + Transaction transaction = getTransaction(context, xid, true); + transaction.rollback(); + } + + public void acknowledge(ConnectionContext context, MessageAck ack) throws Throwable { + // This method may be invoked recursively. + // Track original tx so that it can be restored. + Transaction originalTx = context.getTransaction(); + Transaction transaction=null; + if( ack.isInTransaction() ) { + transaction = getTransaction(context, ack.getTransactionId(), false); + } + context.setTransaction(transaction); + try { + next.acknowledge(context, ack); + } finally { + context.setTransaction(originalTx); + } + } + + public void send(ConnectionContext context, Message message) throws Throwable { + // This method may be invoked recursively. + // Track original tx so that it can be restored. + Transaction originalTx = context.getTransaction(); + Transaction transaction=null; + if( message.getTransactionId()!=null ) { + transaction = getTransaction(context, message.getTransactionId(), false); + } + context.setTransaction(transaction); + try { + next.send(context, message); + } finally { + context.setTransaction(originalTx); + } + } + + public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Throwable { + for (Iterator iter = context.getTransactions().values().iterator(); iter.hasNext();) { + try { + Transaction transaction = (Transaction) iter.next(); + transaction.rollback(); + } + catch (Exception e) { + log.warn("ERROR Rolling back disconnected client's transactions: ", e); + } + iter.remove(); + } + next.removeConnection(context, info, error); + } + + ////////////////////////////////////////////////////////////////////////////// + // + // Implementation help methods. + // + ////////////////////////////////////////////////////////////////////////////// + public Transaction getTransaction(ConnectionContext context, TransactionId xid, boolean mightBePrepared) throws JMSException, XAException { + Map transactionMap = xid.isXATransaction() ? xaTransactions : context.getTransactions(); + Transaction transaction = (Transaction)transactionMap.get(xid); + + if( transaction != null ) + return transaction; + + if( xid.isXATransaction() ) { + XAException e = new XAException("Transaction '" + xid + "' has not been started."); + e.errorCode = XAException.XAER_NOTA; + throw e; + } else { + throw new JMSException("Transaction '" + xid + "' has not been started."); + } + } + + public void removeTransaction(XATransactionId xid) { + xaTransactions.remove(xid); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/TransportConnection.java b/activemq-core/src/main/java/org/activemq/broker/TransportConnection.java new file mode 100755 index 0000000000..44f417d495 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/TransportConnection.java @@ -0,0 +1,214 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import java.io.IOException; + +import org.activemq.command.Command; +import org.activemq.command.CommandTypes; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageDispatch; +import org.activemq.command.Response; +import org.activemq.thread.TaskRunnerFactory; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportListener; + +/** + * + * @version $Revision: 1.8 $ + */ +public class TransportConnection extends AbstractConnection { + + private final Transport transport; + private boolean slow; + private boolean markedCandidate; + private boolean blockedCandidate; + private boolean blocked; + private boolean connected; + private boolean active; + private long timeStamp=0; + + /** + * @param connector + * @param transport + * @param broker + * @param taskRunnerFactory - can be null if you want direct dispatch to the transport else commands are sent async. + */ + public TransportConnection(TransportConnector connector, final Transport transport, Broker broker, TaskRunnerFactory taskRunnerFactory) { + super(connector, broker, taskRunnerFactory); + connector.setBrokerName(broker.getBrokerName()); + this.transport = transport; + this.transport.setTransportListener(new TransportListener() { + public void onCommand(Command command) { + Response response = service(command); + if( response!=null ) { + dispatch(response); + } + } + public void onException(IOException exception) { + serviceTransportException(exception); + } + }); + connected =true; + } + + public void start() throws Exception { + transport.start(); + active = true; + super.start(); + } + + public void stop() throws Exception { + transport.stop(); + active = false; + super.stop(); + } + + /** + * @return Returns the blockedCandidate. + */ + public boolean isBlockedCandidate(){ + return blockedCandidate; + } + /** + * @param blockedCandidate + * The blockedCandidate to set. + */ + public void setBlockedCandidate(boolean blockedCandidate){ + this.blockedCandidate=blockedCandidate; + } + /** + * @return Returns the markedCandidate. + */ + public boolean isMarkedCandidate(){ + return markedCandidate; + } + /** + * @param markedCandidate + * The markedCandidate to set. + */ + public void setMarkedCandidate(boolean markedCandidate){ + this.markedCandidate=markedCandidate; + if(!markedCandidate){ + timeStamp=0; + blockedCandidate=false; + } + } + /** + * @param slow + * The slow to set. + */ + public void setSlow(boolean slow){ + this.slow=slow; + } + /** + * @return true if the Connection is slow + */ + public boolean isSlow(){ + return slow; + } + /** + * @return true if the Connection is potentially blocked + */ + public boolean isMarkedBlockedCandidate(){ + return markedCandidate; + } + + /** + * Mark the Connection, so we can deem if it's collectable on the next sweep + */ + public void doMark(){ + if(timeStamp==0){ + timeStamp=System.currentTimeMillis(); + } + } + /** + * @return if after being marked, the Connection is still writing + */ + public boolean isBlocked(){ + return blocked; + } + /** + * @return true if the Connection is connected + */ + public boolean isConnected(){ + return connected; + } + /** + * @param blocked + * The blocked to set. + */ + public void setBlocked(boolean blocked){ + this.blocked=blocked; + } + /** + * @param connected + * The connected to set. + */ + public void setConnected(boolean connected){ + this.connected=connected; + } + /** + * @return true if the Connection is active + */ + public boolean isActive(){ + return active; + } + /** + * @param active + * The active to set. + */ + public void setActive(boolean active){ + this.active=active; + } + + + + protected void dispatch(Command command){ + if(isValidForNetwork(command)){ + try{ + setMarkedCandidate(true); + transport.oneway(command); + }catch(IOException e){ + serviceException(e); + }finally{ + setMarkedCandidate(false); + } + } + } + + protected boolean isValidForNetwork(Command command){ + boolean result=true; + if(demandForwardingBridge&&command.isMessageDispatch()){ + MessageDispatch md=(MessageDispatch) command; + Message message=md.getMessage(); + if(message.isAdvisory()&&message.getDataStructure()!=null + &&message.getDataStructure().getDataStructureType()==CommandTypes.CONSUMER_INFO){ + ConsumerInfo info=(ConsumerInfo) message.getDataStructure(); + if(info.isNetworkSubscription()){ + // don't want to forward these + result=false; + } + } + } + return result; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/TransportConnector.java b/activemq-core/src/main/java/org/activemq/broker/TransportConnector.java new file mode 100755 index 0000000000..a37d71d52a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/TransportConnector.java @@ -0,0 +1,273 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Iterator; + +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.activemq.broker.jmx.ManagedTransportConnector; +import org.activemq.command.BrokerInfo; +import org.activemq.command.ConnectionInfo; +import org.activemq.thread.TaskRunnerFactory; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportAcceptListener; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportServer; +import org.activemq.transport.discovery.DiscoveryAgent; +import org.activemq.transport.discovery.DiscoveryAgentFactory; +import org.activemq.util.ServiceStopper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + +/** + * @org.xbean.XBean + * + * @version $Revision: 1.6 $ + */ +public class TransportConnector implements Connector { + + private static final Log log = LogFactory.getLog(TransportConnector.class); + + private Broker broker; + private BrokerFilter brokerFilter; + private TransportServer server; + private URI uri; + private BrokerInfo brokerInfo = new BrokerInfo(); + private TaskRunnerFactory taskRunnerFactory = null; + protected CopyOnWriteArrayList connections = new CopyOnWriteArrayList(); + protected TransportStatusDetector statusDector; + private DiscoveryAgent discoveryAgent; + private URI discoveryUri; + + private URI connectUri; + + /** + * @return Returns the connections. + */ + public CopyOnWriteArrayList getConnections(){ + return connections; + } + + public TransportConnector() { + this.statusDector = new TransportStatusDetector(this); + } + + public TransportConnector(Broker broker, TransportServer server) { + this(); + setBroker(broker); + setServer(server); + } + + /** + * Factory method to create a JMX managed version of this transport connector + */ + public ManagedTransportConnector asManagedConnector(MBeanServer mbeanServer, ObjectName connectorName) throws IOException, URISyntaxException { + return new ManagedTransportConnector(mbeanServer,connectorName, getBroker(), getServer()); + } + + public BrokerInfo getBrokerInfo() { + return brokerInfo; + } + + public void setBrokerInfo(BrokerInfo brokerInfo) { + this.brokerInfo = brokerInfo; + } + + public TransportServer getServer() throws IOException, URISyntaxException { + if (server == null) { + setServer(createTransportServer()); + } + return server; + } + + public String getName() throws IOException, URISyntaxException { + return getServer().getConnectURI().toString(); + } + + public Broker getBroker() { + return broker; + } + + public void setBroker(Broker broker) { + this.broker = broker; + brokerInfo.setBrokerId(broker.getBrokerId()); + } + + public void setBrokerName(String brokerName) { + brokerInfo.setBrokerName(brokerName); + } + + public void setServer(TransportServer server) { + this.server = server; + this.server.setAcceptListener(new TransportAcceptListener() { + public void onAccept(Transport transport) { + try { + Connection connection = createConnection(transport); + connection.start(); + } + catch (Exception e) { + onAcceptError(e); + } + } + + public void onAcceptError(Exception error) { + log.error("Could not accept connection: " + error, error); + } + }); + this.server.setBrokerInfo(brokerInfo); + } + + public URI getUri() { + return uri; + } + + /** + * Sets the server transport URI to use if there is not a + * {@link TransportServer} configured via the + * {@link #setServer(TransportServer)} method. This value is used to lazy + * create a {@link TransportServer} instance + * + * @param uri + */ + public void setUri(URI uri) { + this.uri = uri; + } + + public TaskRunnerFactory getTaskRunnerFactory() { + return taskRunnerFactory; + } + + public void setTaskRunnerFactory(TaskRunnerFactory taskRunnerFactory) { + this.taskRunnerFactory = taskRunnerFactory; + } + + public void start() throws Exception { + getServer().start(); + log.info("Accepting connection on: "+getServer().getConnectURI()); + + DiscoveryAgent da = getDiscoveryAgent(); + if( da!=null ) { + da.registerService(getConnectUri().toString()); + da.start(); + } + + this.statusDector.start(); + } + + public void stop() throws Exception { + ServiceStopper ss = new ServiceStopper(); + if( discoveryAgent!=null ) { + ss.stop(discoveryAgent); + } + if (server != null) { + ss.stop(server); + } + this.statusDector.stop(); + for (Iterator iter = connections.iterator(); iter.hasNext();) { + ConnectionContext context = (ConnectionContext) iter.next(); + ss.stop(context.getConnection()); + } + ss.throwFirstException(); + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected Connection createConnection(Transport transport) throws IOException { + return new TransportConnection(this, transport, getBrokerFilter(), taskRunnerFactory); + } + + protected BrokerFilter getBrokerFilter() { + if (brokerFilter == null) { + if (broker == null) { + throw new IllegalArgumentException("You must specify the broker property. Maybe this connector should be added to a broker?"); + } + this.brokerFilter = new BrokerFilter(broker) { + public void addConnection(ConnectionContext context, ConnectionInfo info) throws Throwable { + connections.add(context); + super.addConnection(context, info); + } + + public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Throwable { + connections.remove(context); + super.removeConnection(context, info, error); + } + }; + + } + return brokerFilter; + } + + protected TransportServer createTransportServer() throws IOException, URISyntaxException { + if (uri == null) { + throw new IllegalArgumentException("You must specify either a server or uri property"); + } + if (broker == null) { + throw new IllegalArgumentException("You must specify the broker property. Maybe this connector should be added to a broker?"); + } + return TransportFactory.bind(broker.getBrokerId().getBrokerId(),uri); + } + + public DiscoveryAgent getDiscoveryAgent() throws IOException { + if( discoveryAgent==null ) { + discoveryAgent = createDiscoveryAgent(); + } + return discoveryAgent; + } + + protected DiscoveryAgent createDiscoveryAgent() throws IOException { + if( discoveryUri!=null ) { + return DiscoveryAgentFactory.createDiscoveryAgent(discoveryUri); + } + return null; + } + + public void setDiscoveryAgent(DiscoveryAgent discoveryAgent) { + this.discoveryAgent = discoveryAgent; + } + + public URI getDiscoveryUri() { + return discoveryUri; + } + + public void setDiscoveryUri(URI discoveryUri) { + this.discoveryUri = discoveryUri; + } + + public URI getConnectUri() throws IOException, URISyntaxException { + if( connectUri==null ) { + if( getServer().getConnectURI()==null ) { + throw new IllegalStateException("The transportConnector has not been started."); + } + connectUri = getServer().getConnectURI(); + } + return connectUri; + } + + public void setConnectUri(URI transportUri) { + this.connectUri = transportUri; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/TransportStatusDetector.java b/activemq-core/src/main/java/org/activemq/broker/TransportStatusDetector.java new file mode 100755 index 0000000000..30a298423f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/TransportStatusDetector.java @@ -0,0 +1,118 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs + * + * Licensed 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.activemq.broker; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArraySet; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +import org.activemq.Service; +import org.activemq.ThreadPriorities; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Iterator; +import java.util.Set; +/** + * Used to provide information on the status of the Connection + * + * @version $Revision: 1.5 $ + */ +public class TransportStatusDetector implements Service,Runnable{ + private static final Log log=LogFactory.getLog(TransportStatusDetector.class); + private TransportConnector connector; + private Set collectionCandidates=new CopyOnWriteArraySet(); + private AtomicBoolean started=new AtomicBoolean(false); + private Thread runner; + private int sweepInterval=5000; + + TransportStatusDetector(TransportConnector connector){ + this.connector=connector; + } + /** + * @return Returns the sweepInterval. + */ + public int getSweepInterval(){ + return sweepInterval; + } + + /** + * The sweepInterval to set. + * @param sweepInterval + * + */ + public void setSweepInterval(int sweepInterval){ + this.sweepInterval=sweepInterval; + } + + protected void doCollection(){ + for(Iterator i=collectionCandidates.iterator();i.hasNext();){ + TransportConnection tc=(TransportConnection) i.next(); + if(tc.isMarkedCandidate()){ + if(tc.isBlockedCandidate()){ + collectionCandidates.remove(tc); + doCollection(tc); + }else{ + tc.doMark(); + } + }else{ + collectionCandidates.remove(tc); + } + } + } + protected void doSweep(){ + for(Iterator i=connector.getConnections().iterator();i.hasNext();){ + ConnectionContext cc=(ConnectionContext) i.next(); + Connection connection=cc.getConnection(); + if(connection instanceof TransportConnection){ + TransportConnection tc=(TransportConnection) connection; + if(tc.isMarkedCandidate()){ + tc.doMark(); + collectionCandidates.add(tc); + } + } + } + } + protected void doCollection(TransportConnection tc){ + log.warn("found a blocked client - stopping: "+tc); + try{ + tc.stop(); + }catch(Exception e){ + log.error("Error stopping "+tc,e); + } + } + public void run(){ + while(started.get()){ + try{ + doCollection(); + doSweep(); + Thread.sleep(sweepInterval); + }catch(Throwable e){ + log.error("failed to complete a sweep for blocked clients",e); + } + } + } + public void start() throws Exception{ + if(started.compareAndSet(false,true)){ + runner=new Thread(this,"Transport Status Dector "+connector); + runner.setDaemon(true); + runner.setPriority(ThreadPriorities.BROKER_MANAGEMENT); + runner.start(); + } + } + public void stop() throws Exception{ + started.set(false); + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/UserIDBroker.java b/activemq-core/src/main/java/org/activemq/broker/UserIDBroker.java new file mode 100644 index 0000000000..2c89e62c18 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/UserIDBroker.java @@ -0,0 +1,42 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import org.activemq.command.Message; + +/** + * This broker filter will append the producer's user ID into the JMSXUserID header + * to allow folks to know reliably who the user was who produced a message. + * Note that you cannot trust the client, especially if working over the internet + * as they can spoof headers to be anything they like. + * + * @version $Revision: 1.8 $ + */ +public class UserIDBroker extends BrokerFilter { + + public UserIDBroker(Broker next) { + super(next); + } + + public void send(ConnectionContext context, Message messageSend) throws Throwable { + String userID = context.getUserName(); + messageSend.setUserID(userID); + super.send(context, messageSend); + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/BrokerView.java b/activemq-core/src/main/java/org/activemq/broker/jmx/BrokerView.java new file mode 100755 index 0000000000..f435f3e613 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/BrokerView.java @@ -0,0 +1,87 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.broker.Broker; +import org.activemq.broker.region.DestinationStatistics; +import org.activemq.memory.UsageManager; + +public class BrokerView implements BrokerViewMBean { + + private final Broker broker; + private final DestinationStatistics destinationStatistics; + private final UsageManager usageManager; + + public BrokerView(Broker broker, DestinationStatistics destinationStatistics, UsageManager usageManager) { + this.broker = broker; + this.destinationStatistics = destinationStatistics; + this.usageManager = usageManager; + } + + public String getBrokerId() { + return broker.getBrokerId().toString(); + } + + public void gc() { + broker.gc(); + } + + public void start() throws Exception { + broker.start(); + } + + public void stop() throws Exception { + broker.stop(); + } + + public long getTotalEnqueueCount() { + return destinationStatistics.getEnqueues().getCount(); + } + public long getTotalDequeueCount() { + return destinationStatistics.getDequeues().getCount(); + } + public long getTotalConsumerCount() { + return destinationStatistics.getConsumers().getCount(); + } + public long getTotalMessages() { + return destinationStatistics.getMessages().getCount(); + } + public long getTotalMessagesCached() { + return destinationStatistics.getMessagesCached().getCount(); + } + + public int getMemoryPercentageUsed() { + return usageManager.getPercentUsage(); + } + public long getMemoryLimit() { + return usageManager.getLimit(); + } + public void setMemoryLimit(long limit) { + usageManager.setLimit(limit); + } + + public void resetStatistics() { + destinationStatistics.reset(); + } + + public void terminateJVM(int exitCode) { + System.exit(exitCode); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/BrokerViewMBean.java b/activemq-core/src/main/java/org/activemq/broker/jmx/BrokerViewMBean.java new file mode 100755 index 0000000000..3491fc5b99 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/BrokerViewMBean.java @@ -0,0 +1,42 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.Service; + +public interface BrokerViewMBean extends Service { + + public abstract String getBrokerId(); + + public abstract void gc(); + public void resetStatistics(); + + public long getTotalEnqueueCount(); + public long getTotalDequeueCount(); + public long getTotalConsumerCount(); + public long getTotalMessages(); + public long getTotalMessagesCached(); + + public int getMemoryPercentageUsed(); + public long getMemoryLimit(); + public void setMemoryLimit(long limit); + + public void terminateJVM(int exitCode); + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/ConnectionView.java b/activemq-core/src/main/java/org/activemq/broker/jmx/ConnectionView.java new file mode 100755 index 0000000000..0958bd9bec --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/ConnectionView.java @@ -0,0 +1,69 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.broker.Connection; + +public class ConnectionView implements ConnectionViewMBean { + + private final Connection connection; + + public ConnectionView(Connection connection) { + this.connection = connection; + } + + public void start() throws Exception { + connection.start(); + } + + public void stop() throws Exception { + connection.stop(); + } + + /** + * @return true if the Connection is slow + */ + public boolean isSlow() { + return connection.isSlow(); + } + + /** + * @return if after being marked, the Connection is still writing + */ + public boolean isBlocked() { + return connection.isBlocked(); + } + + + /** + * @return true if the Connection is connected + */ + public boolean isConnected() { + return connection.isConnected(); + } + + /** + * @return true if the Connection is active + */ + public boolean isActive() { + return connection.isActive(); + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/ConnectionViewMBean.java b/activemq-core/src/main/java/org/activemq/broker/jmx/ConnectionViewMBean.java new file mode 100755 index 0000000000..12d3043e70 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/ConnectionViewMBean.java @@ -0,0 +1,45 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.Service; + +public interface ConnectionViewMBean extends Service { + /** + * @return true if the Connection is slow + */ + public boolean isSlow(); + + /** + * @return if after being marked, the Connection is still writing + */ + public boolean isBlocked(); + + + /** + * @return true if the Connection is connected + */ + public boolean isConnected(); + + /** + * @return true if the Connection is active + */ + public boolean isActive(); + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/ConnectorView.java b/activemq-core/src/main/java/org/activemq/broker/jmx/ConnectorView.java new file mode 100755 index 0000000000..87851d0364 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/ConnectorView.java @@ -0,0 +1,44 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.broker.Connector; +import org.activemq.command.BrokerInfo; + +public class ConnectorView implements ConnectorViewMBean { + + private final Connector connector; + + public ConnectorView(Connector connector) { + this.connector = connector; + } + + public void start() throws Exception { + connector.start(); + } + + public void stop() throws Exception { + connector.stop(); + } + + public BrokerInfo getBrokerInfo() { + return connector.getBrokerInfo(); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/ConnectorViewMBean.java b/activemq-core/src/main/java/org/activemq/broker/jmx/ConnectorViewMBean.java new file mode 100755 index 0000000000..1b59d4674d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/ConnectorViewMBean.java @@ -0,0 +1,28 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.Service; +import org.activemq.command.BrokerInfo; + +public interface ConnectorViewMBean extends Service { + + public BrokerInfo getBrokerInfo(); + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/DestinationView.java b/activemq-core/src/main/java/org/activemq/broker/jmx/DestinationView.java new file mode 100755 index 0000000000..b1c799403a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/DestinationView.java @@ -0,0 +1,58 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.broker.region.Destination; + +public class DestinationView implements DestinationViewMBean { + + private final Destination destination; + + public DestinationView(Destination destination) { + this.destination = destination; + } + + public void gc() { + destination.gc(); + } + public void resetStatistics() { + destination.getDestinationStatistics().reset(); + } + + public long getEnqueueCount() { + return destination.getDestinationStatistics().getEnqueues().getCount(); + + } + public long getDequeueCount() { + return destination.getDestinationStatistics().getDequeues().getCount(); + } + + public long getConsumerCount() { + return destination.getDestinationStatistics().getConsumers().getCount(); + } + + public long getMessages() { + return destination.getDestinationStatistics().getMessages().getCount(); + } + + public long getMessagesCached() { + return destination.getDestinationStatistics().getMessagesCached().getCount(); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/DestinationViewMBean.java b/activemq-core/src/main/java/org/activemq/broker/jmx/DestinationViewMBean.java new file mode 100755 index 0000000000..ef788bdb1e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/DestinationViewMBean.java @@ -0,0 +1,33 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + + +public interface DestinationViewMBean { + + public void gc(); + public void resetStatistics(); + + public long getEnqueueCount(); + public long getDequeueCount(); + public long getConsumerCount(); + public long getMessages(); + public long getMessagesCached(); + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedQueueRegion.java b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedQueueRegion.java new file mode 100755 index 0000000000..59b9485e91 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedQueueRegion.java @@ -0,0 +1,50 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.Destination; +import org.activemq.broker.region.DestinationStatistics; +import org.activemq.broker.region.QueueRegion; +import org.activemq.broker.region.policy.PolicyMap; +import org.activemq.command.ActiveMQDestination; +import org.activemq.memory.UsageManager; +import org.activemq.store.PersistenceAdapter; +import org.activemq.thread.TaskRunnerFactory; + +public class ManagedQueueRegion extends QueueRegion { + + private final ManagedRegionBroker regionBroker; + + public ManagedQueueRegion(ManagedRegionBroker broker, DestinationStatistics destinationStatistics, UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory, PersistenceAdapter persistenceAdapter, PolicyMap policyMap) { + super(destinationStatistics, memoryManager, taskRunnerFactory, persistenceAdapter, policyMap); + regionBroker = broker; + } + + protected Destination createDestination(ActiveMQDestination destination) throws Throwable { + Destination rc = super.createDestination(destination); + regionBroker.register(destination, rc); + return rc; + } + + public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Throwable { + super.removeDestination(context, destination, timeout); + regionBroker.unregister(destination); + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedRegionBroker.java b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedRegionBroker.java new file mode 100755 index 0000000000..c60a776c1d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedRegionBroker.java @@ -0,0 +1,86 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.broker.region.Destination; +import org.activemq.broker.region.Region; +import org.activemq.broker.region.RegionBroker; +import org.activemq.broker.region.policy.PolicyMap; +import org.activemq.command.ActiveMQDestination; +import org.activemq.memory.UsageManager; +import org.activemq.store.PersistenceAdapter; +import org.activemq.thread.TaskRunnerFactory; +import org.activemq.util.JMXSupport; + +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import java.io.IOException; +import java.util.Hashtable; + +public class ManagedRegionBroker extends RegionBroker { + + private final MBeanServer mbeanServer; + private final ObjectName brokerObjectName; + + public ManagedRegionBroker(MBeanServer mbeanServer, ObjectName brokerObjectName, TaskRunnerFactory taskRunnerFactory, UsageManager memoryManager, PersistenceAdapter adapter, PolicyMap policyMap) throws IOException { + super(taskRunnerFactory, memoryManager, adapter, policyMap); + this.mbeanServer = mbeanServer; + this.brokerObjectName = brokerObjectName; + } + + protected Region createQueueRegion(UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory, PersistenceAdapter adapter, PolicyMap policyMap) { + return new ManagedQueueRegion(this, destinationStatistics, memoryManager, taskRunnerFactory, adapter, policyMap); + } + + protected Region createTempQueueRegion(UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory) { + return new ManagedTempQueueRegion(this, destinationStatistics, memoryManager, taskRunnerFactory); + } + + protected Region createTempTopicRegion(UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory) { + return new ManagedTempTopicRegion(this, destinationStatistics, memoryManager, taskRunnerFactory); + } + + protected Region createTopicRegion(UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory, PersistenceAdapter adapter, PolicyMap policyMap) { + return new ManagedTopicRegion(this, destinationStatistics, memoryManager, taskRunnerFactory, adapter, policyMap); + } + + public void register(ActiveMQDestination destName, Destination destination) throws Throwable { + + // Build the object name for the destination + Hashtable map = new Hashtable(brokerObjectName.getKeyPropertyList()); + map.put("Type",destName.getDestinationTypeAsString()); + map.put("Destination", JMXSupport.encodeObjectNamePart(destName.getPhysicalName())); + ObjectName destObjectName= new ObjectName(brokerObjectName.getDomain(), map); + + DestinationViewMBean view = new DestinationView(destination); + + mbeanServer.registerMBean(view, destObjectName); + } + + public void unregister(ActiveMQDestination destName) throws Throwable { + // Build the object name for the destination + Hashtable map = new Hashtable(brokerObjectName.getKeyPropertyList()); + map.put("Type",destName.getDestinationTypeAsString()); + map.put("Destination", JMXSupport.encodeObjectNamePart(destName.getPhysicalName())); + ObjectName destObjectName= new ObjectName(brokerObjectName.getDomain(), map); + + mbeanServer.unregisterMBean(destObjectName); + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTempQueueRegion.java b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTempQueueRegion.java new file mode 100755 index 0000000000..e30b955cf3 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTempQueueRegion.java @@ -0,0 +1,50 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.Destination; +import org.activemq.broker.region.DestinationStatistics; +import org.activemq.broker.region.TempQueueRegion; +import org.activemq.command.ActiveMQDestination; +import org.activemq.memory.UsageManager; +import org.activemq.thread.TaskRunnerFactory; + +public class ManagedTempQueueRegion extends TempQueueRegion { + + private final ManagedRegionBroker regionBroker; + + + public ManagedTempQueueRegion(ManagedRegionBroker regionBroker, DestinationStatistics destinationStatistics, UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory) { + super(destinationStatistics, memoryManager, taskRunnerFactory); + this.regionBroker = regionBroker; + } + + protected Destination createDestination(ActiveMQDestination destination) throws Throwable { + Destination rc = super.createDestination(destination); + regionBroker.register(destination, rc); + return rc; + } + + public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Throwable { + super.removeDestination(context, destination, timeout); + regionBroker.unregister(destination); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTempTopicRegion.java b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTempTopicRegion.java new file mode 100755 index 0000000000..f7413198e4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTempTopicRegion.java @@ -0,0 +1,49 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.Destination; +import org.activemq.broker.region.DestinationStatistics; +import org.activemq.broker.region.TempTopicRegion; +import org.activemq.command.ActiveMQDestination; +import org.activemq.memory.UsageManager; +import org.activemq.thread.TaskRunnerFactory; + +public class ManagedTempTopicRegion extends TempTopicRegion { + + private final ManagedRegionBroker regionBroker; + + public ManagedTempTopicRegion(ManagedRegionBroker regionBroker, DestinationStatistics destinationStatistics, UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory) { + super(destinationStatistics, memoryManager, taskRunnerFactory); + this.regionBroker = regionBroker; + } + + protected Destination createDestination(ActiveMQDestination destination) throws Throwable { + Destination rc = super.createDestination(destination); + regionBroker.register(destination, rc); + return rc; + } + + public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Throwable { + super.removeDestination(context, destination, timeout); + regionBroker.unregister(destination); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTopicRegion.java b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTopicRegion.java new file mode 100755 index 0000000000..e793bb31c2 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTopicRegion.java @@ -0,0 +1,51 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.Destination; +import org.activemq.broker.region.DestinationStatistics; +import org.activemq.broker.region.TopicRegion; +import org.activemq.broker.region.policy.PolicyMap; +import org.activemq.command.ActiveMQDestination; +import org.activemq.memory.UsageManager; +import org.activemq.store.PersistenceAdapter; +import org.activemq.thread.TaskRunnerFactory; + +public class ManagedTopicRegion extends TopicRegion { + + private final ManagedRegionBroker regionBroker; + + public ManagedTopicRegion(ManagedRegionBroker broker, DestinationStatistics destinationStatistics, UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory, PersistenceAdapter persistenceAdapter, PolicyMap policyMap) { + super(destinationStatistics, memoryManager, taskRunnerFactory, persistenceAdapter, policyMap); + regionBroker = broker; + } + + protected Destination createDestination(ActiveMQDestination destination) throws Throwable { + Destination rc = super.createDestination(destination); + regionBroker.register(destination, rc); + return rc; + } + + public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Throwable { + super.removeDestination(context, destination, timeout); + regionBroker.unregister(destination); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTransportConnection.java b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTransportConnection.java new file mode 100644 index 0000000000..259a40a4f1 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTransportConnection.java @@ -0,0 +1,126 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.broker.Broker; +import org.activemq.broker.TransportConnection; +import org.activemq.broker.TransportConnector; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.Response; +import org.activemq.thread.TaskRunnerFactory; +import org.activemq.transport.Transport; +import org.activemq.util.IOExceptionSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import java.io.IOException; +import java.util.Hashtable; + +/** + * A managed transport connection + * + * @version $Revision: 1.1 $ + */ +public class ManagedTransportConnection extends TransportConnection { + private static final Log log = LogFactory.getLog(ManagedTransportConnection.class); + + private final MBeanServer server; + private final ObjectName connectorName; + private ConnectionViewMBean mbean; + private ObjectName name; + private String connectionId; + + public ManagedTransportConnection(TransportConnector connector, Transport transport, Broker broker, TaskRunnerFactory factory, MBeanServer server, + ObjectName connectorName, String connectionId) throws IOException { + super(connector, transport, broker, factory); + this.server = server; + this.connectorName = connectorName; + this.mbean = new ConnectionView(this); + setConnectionId(connectionId); + } + + public void stop() throws Exception { + unregisterMBean(); + super.stop(); + } + + public String getConnectionId() { + return connectionId; + } + + /** + * Sets the connection ID of this connection. On startup this connection ID + * is set to an incrementing counter; once the client registers it is set to + * the clientID of the JMS client. + */ + public void setConnectionId(String connectionId) throws IOException { + this.connectionId = connectionId; + unregisterMBean(); + name = createObjectName(); + registerMBean(); + } + + public Response processAddConnection(ConnectionInfo info) throws Throwable { + Response answer = super.processAddConnection(info); + String clientId = info.getClientId(); + if (clientId != null) { + // lets update the MBean name + setConnectionId(clientId); + } + return answer; + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected void registerMBean() throws IOException { + try { + server.registerMBean(mbean, name); + } + catch (Throwable e) { + throw IOExceptionSupport.create(e); + } + + } + + protected void unregisterMBean() { + if (name != null) { + try { + server.unregisterMBean(name); + } + catch (Throwable e) { + log.warn("Failed to unregister mbean: " + name); + } + } + } + + protected ObjectName createObjectName() throws IOException { + // Build the object name for the destination + Hashtable map = new Hashtable(connectorName.getKeyPropertyList()); + map.put("Type", "Connection"); + map.put("Connection", connectionId); + try { + return new ObjectName(connectorName.getDomain(), map); + } + catch (Throwable e) { + throw IOExceptionSupport.create(e); + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTransportConnector.java b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTransportConnector.java new file mode 100755 index 0000000000..9127e9e7be --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagedTransportConnector.java @@ -0,0 +1,65 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.broker.Broker; +import org.activemq.broker.Connection; +import org.activemq.broker.TransportConnector; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportServer; + +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import java.io.IOException; +import java.net.URISyntaxException; + +/** + * A managed transport connector which can create multiple managed connections + * as clients connect. + * + * @version $Revision: 1.1 $ + */ +public class ManagedTransportConnector extends TransportConnector { + + private final MBeanServer mbeanServer; + private final ObjectName connectorName; + long nextConnectionId = 1; + + public ManagedTransportConnector(MBeanServer mbeanServer, ObjectName connectorName, Broker next, TransportServer server) { + super(next, server); + this.mbeanServer = mbeanServer; + this.connectorName = connectorName; + } + + public ManagedTransportConnector asManagedConnector(MBeanServer mbeanServer, ObjectName connectorName) throws IOException, URISyntaxException { + return this; + } + + protected Connection createConnection(Transport transport) throws IOException { + + final String connectionId; + synchronized (this) { + connectionId = "" + (nextConnectionId++); + } + + return new ManagedTransportConnection(this, transport, getBrokerFilter(), getTaskRunnerFactory(), mbeanServer, connectorName, connectionId); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/ManagementContext.java b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagementContext.java new file mode 100644 index 0000000000..bb4abd4f3d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/ManagementContext.java @@ -0,0 +1,342 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.rmi.registry.LocateRegistry; +import java.util.List; + +import javax.management.Attribute; +import javax.management.JMException; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +import org.activemq.Service; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A Flow provides different dispatch policies within the NMR + * + * @org.xbean.XBean + * + * @version $Revision$ + */ +public class ManagementContext implements Service { + /** + * Default activemq domain + */ + public static final String DEFAULT_DOMAIN = "org.activemq"; + + private final static Log log = LogFactory.getLog(ManagementContext.class); + + private MBeanServer beanServer; + private String jmxDomainName = DEFAULT_DOMAIN; + private boolean useMBeanServer = true; + private boolean createMBeanServer = true; + private boolean locallyCreateMBeanServer = false; + + private boolean createConnector = true; + private int connectorPort = 1099; + private String connectorPath = "/jmxconnector"; + + private JMXConnectorServer connectorServer; + + private ObjectName namingServiceObjectName; + + public ManagementContext() { + this(null); + } + + public ManagementContext(MBeanServer server) { + this.beanServer = server; + } + + public void start() throws IOException { + // lets force the MBeanServer to be created if needed + getMBeanServer(); + if( connectorServer!=null ) { + try { + getMBeanServer().invoke(namingServiceObjectName, "start", null, null); + } catch (Throwable ignore) { + } + try { + connectorServer.start(); + log.info("JMX consoles can connect to " + connectorServer.getAddress()); + } catch (IOException e) { + log.warn("Failed to start jmx connector: "+e.getMessage()); + } + } + } + + public void stop() throws IOException { + + if( connectorServer!=null ) { + try { + connectorServer.stop(); + } catch (IOException e) { + log.warn("Failed to stop jmx connector: "+e.getMessage()); + } + connectorServer=null; + try { + getMBeanServer().invoke(namingServiceObjectName, "stop", null, null); + } catch (Throwable ignore) { + } + } + + if (locallyCreateMBeanServer && beanServer != null) { + // check to see if the factory knows about this server + List list = MBeanServerFactory.findMBeanServer(null); + if (list != null && !list.isEmpty() && list.contains(beanServer)) { + MBeanServerFactory.releaseMBeanServer(beanServer); + } + } + } + + /** + * @return Returns the jmxDomainName. + */ + public String getJmxDomainName() { + return jmxDomainName; + } + + /** + * @param jmxDomainName + * The jmxDomainName to set. + */ + public void setJmxDomainName(String jmxDomainName) { + this.jmxDomainName = jmxDomainName; + } + + /** + * Get the MBeanServer + * + * @return the MBeanServer + */ + public MBeanServer getMBeanServer() { + if (this.beanServer == null) { + this.beanServer = findMBeanServer(); + } + return beanServer; + } + + /** + * @return Returns the useMBeanServer. + */ + public boolean isUseMBeanServer() { + return useMBeanServer; + } + + /** + * @param useMBeanServer + * The useMBeanServer to set. + */ + public void setUseMBeanServer(boolean useMBeanServer) { + this.useMBeanServer = useMBeanServer; + } + + /** + * @return Returns the createMBeanServer flag. + */ + public boolean isCreateMBeanServer() { + return createMBeanServer; + } + + /** + * @param enableJMX + * Set createMBeanServer. + */ + public void setCreateMBeanServer(boolean enableJMX) { + this.createMBeanServer = enableJMX; + } + + /** + * Formulate and return the MBean ObjectName of a custom control MBean + * + * @param type + * @param name + * @return the JMX ObjectName of the MBean, or null if + * customName is invalid. + */ + public ObjectName createCustomComponentMBeanName(String type, String name) { + ObjectName result = null; + String tmp = jmxDomainName + ":" + "type=" + sanitizeString(type) + ",name=" + sanitizeString(name); + try { + result = new ObjectName(tmp); + } + catch (MalformedObjectNameException e) { + log.error("Couldn't create ObjectName from: " + type + " , " + name); + } + return result; + } + + /** + * The ':' and '/' characters are reserved in ObjectNames + * + * @param in + * @return sanitized String + */ + private static String sanitizeString(String in) { + String result = null; + if (in != null) { + result = in.replace(':', '_'); + result = result.replace('/', '_'); + result = result.replace('\\', '_'); + } + return result; + } + + /** + * Retrive an System ObjectName + * + * @param domainName + * @param containerName + * @param theClass + * @return the ObjectName + * @throws MalformedObjectNameException + */ + public static ObjectName getSystemObjectName(String domainName, String containerName, Class theClass) + throws MalformedObjectNameException, NullPointerException { + String tmp = domainName + ":" + "type=" + theClass.getName() + ",name=" + + getRelativeName(containerName, theClass); + return new ObjectName(tmp); + } + + private static String getRelativeName(String containerName, Class theClass) { + String name = theClass.getName(); + int index = name.lastIndexOf("."); + if (index >= 0 && (index + 1) < name.length()) { + name = name.substring(index + 1); + } + return containerName + "." + name; + } + + /** + * Unregister an MBean + * + * @param name + * @throws JMException + */ + public void unregisterMBean(ObjectName name) throws JMException { + if (beanServer != null && beanServer.isRegistered(name)) { + beanServer.unregisterMBean(name); + } + } + + protected synchronized MBeanServer findMBeanServer() { + MBeanServer result = null; + // create the mbean server + try { + if (useMBeanServer) { + // lets piggy back on another MBeanServer - + // we could be in an appserver! + List list = MBeanServerFactory.findMBeanServer(null); + if (list != null && list.size() > 0) { + result = (MBeanServer) list.get(0); + } + } + + if (result == null && createMBeanServer) { + result = createMBeanServer(); + } + } + catch (NoClassDefFoundError e) { + log.error("Couldnot load MBeanServer", e); + } + catch (Throwable e) { + // probably don't have access to system properties + log.error("Failed to initialize MBeanServer", e); + } + return result; + } + + /** + * @return + * @throws NullPointerException + * @throws MalformedObjectNameException + * @throws IOException + */ + protected MBeanServer createMBeanServer() throws MalformedObjectNameException, IOException { + MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer(jmxDomainName); + locallyCreateMBeanServer = true; + + if( createConnector ) { + createConnector(mbeanServer); + } + + + return mbeanServer; + } + + /** + * @param mbeanServer + * @throws MalformedObjectNameException + * @throws MalformedURLException + * @throws IOException + */ + private void createConnector(MBeanServer mbeanServer) throws MalformedObjectNameException, MalformedURLException, IOException { + + // Create the NamingService, needed by JSR 160 + try { + LocateRegistry.createRegistry(connectorPort); + + namingServiceObjectName = ObjectName.getInstance("naming:type=rmiregistry"); + mbeanServer.createMBean("mx4j.tools.naming.NamingService", namingServiceObjectName, null); + // set the naming port + Attribute attr = new Attribute("Port", new Integer(connectorPort)); + mbeanServer.setAttribute(namingServiceObjectName, attr); + } catch (Throwable e) { + log.debug("Failed to create local registry", e); + } + + // Create the JMXConnectorServer + String serviceURL = "service:jmx:rmi:///jndi/rmi://localhost:" + connectorPort + connectorPath; + JMXServiceURL url = new JMXServiceURL(serviceURL); + connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer); + + //log.info("JMX consoles can connect to serviceURL: " + serviceURL); + } + + public String getConnectorPath() { + return connectorPath; + } + public void setConnectorPath(String connectorPath) { + this.connectorPath = connectorPath; + } + + public int getConnectorPort() { + return connectorPort; + } + public void setConnectorPort(int connectorPort) { + this.connectorPort = connectorPort; + } + + public boolean isCreateConnector() { + return createConnector; + } + public void setCreateConnector(boolean createConnector) { + this.createConnector = createConnector; + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/NetworkConnectorView.java b/activemq-core/src/main/java/org/activemq/broker/jmx/NetworkConnectorView.java new file mode 100644 index 0000000000..8c06f51848 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/NetworkConnectorView.java @@ -0,0 +1,38 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.network.NetworkConnector; + +public class NetworkConnectorView implements NetworkConnectorViewMBean { + + private final NetworkConnector connector; + + public NetworkConnectorView(NetworkConnector connector) { + this.connector = connector; + } + + public void start() throws Exception { + connector.start(); + } + + public void stop() throws Exception { + connector.stop(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/NetworkConnectorViewMBean.java b/activemq-core/src/main/java/org/activemq/broker/jmx/NetworkConnectorViewMBean.java new file mode 100644 index 0000000000..2193d1cf64 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/NetworkConnectorViewMBean.java @@ -0,0 +1,25 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.Service; + +public interface NetworkConnectorViewMBean extends Service { + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/ProxyConnectorView.java b/activemq-core/src/main/java/org/activemq/broker/jmx/ProxyConnectorView.java new file mode 100644 index 0000000000..608609541a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/ProxyConnectorView.java @@ -0,0 +1,38 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.proxy.ProxyConnector; + +public class ProxyConnectorView implements ProxyConnectorViewMBean { + + private final ProxyConnector connector; + + public ProxyConnectorView(ProxyConnector connector) { + this.connector = connector; + } + + public void start() throws Exception { + connector.start(); + } + + public void stop() throws Exception { + connector.stop(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/ProxyConnectorViewMBean.java b/activemq-core/src/main/java/org/activemq/broker/jmx/ProxyConnectorViewMBean.java new file mode 100644 index 0000000000..075abd5a11 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/ProxyConnectorViewMBean.java @@ -0,0 +1,25 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.jmx; + +import org.activemq.Service; + +public interface ProxyConnectorViewMBean extends Service { + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/broker/jmx/package.html b/activemq-core/src/main/java/org/activemq/broker/jmx/package.html new file mode 100755 index 0000000000..19e6512024 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/jmx/package.html @@ -0,0 +1,9 @@ + + + + + +JMX MBeans for the broker and its core connectors. + + + diff --git a/activemq-core/src/main/java/org/activemq/broker/package.html b/activemq-core/src/main/java/org/activemq/broker/package.html new file mode 100755 index 0000000000..8cb5d8dbb9 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/package.html @@ -0,0 +1,9 @@ + + + + + +The core classes for the ActiveMQ Message Broker and its connectors. + + + diff --git a/activemq-core/src/main/java/org/activemq/broker/region/AbstractRegion.java b/activemq-core/src/main/java/org/activemq/broker/region/AbstractRegion.java new file mode 100755 index 0000000000..736db855bb --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/AbstractRegion.java @@ -0,0 +1,222 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import java.util.Iterator; +import java.util.Set; + +import javax.jms.JMSException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.RemoveSubscriptionInfo; +import org.activemq.filter.DestinationMap; +import org.activemq.memory.UsageManager; +import org.activemq.store.PersistenceAdapter; +import org.activemq.thread.TaskRunnerFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * + * @version $Revision: 1.14 $ + */ +abstract public class AbstractRegion implements Region { + + protected final ConcurrentHashMap destinations = new ConcurrentHashMap(); + protected final DestinationMap destinationMap = new DestinationMap(); + protected final ConcurrentHashMap subscriptions = new ConcurrentHashMap(); + protected final UsageManager memoryManager; + protected final PersistenceAdapter persistenceAdapter; + protected final DestinationStatistics destinationStatistics; + protected boolean autoCreateDestinations=true; + protected final TaskRunnerFactory taskRunnerFactory; + protected final Object destinationsMutex = new Object(); + + public AbstractRegion(DestinationStatistics destinationStatistics, UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory, PersistenceAdapter persistenceAdapter) { + this.destinationStatistics = destinationStatistics; + this.memoryManager = memoryManager; + this.taskRunnerFactory = taskRunnerFactory; + this.persistenceAdapter = persistenceAdapter; + } + + public Destination addDestination(ConnectionContext context, ActiveMQDestination destination) throws Throwable { + Destination dest = createDestination(destination); + dest.start(); + synchronized(destinationsMutex){ + destinations.put(destination,dest); + destinationMap.put(destination,dest); + } + + // Add all consumers that are interested in the destination. + for (Iterator iter = subscriptions.values().iterator(); iter.hasNext();) { + Subscription sub = (Subscription) iter.next(); + if( sub.matches(destination) ) { + dest.addSubscription(context, sub); + } + } + return dest; + } + + public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) + throws Throwable { + + // The destination cannot be removed if there are any active subscriptions + for (Iterator iter = subscriptions.values().iterator(); iter.hasNext();) { + Subscription sub = (Subscription) iter.next(); + if( sub.matches(destination) ) { + throw new JMSException("Destination still has an active subscription: "+ destination); + } + } + + synchronized(destinationsMutex){ + Destination dest=(Destination) destinations.remove(destination); + if(dest==null) + throw new IllegalArgumentException("The destination does not exist: "+destination); + + destinationMap.removeAll(destination); + dest.dispose(context); + dest.stop(); + } + } + + public void addConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + + Subscription sub = createSubscription(context, info); + + // We may need to add some destinations that are in persistent store but not active + // in the broker. + // + // TODO: think about this a little more. This is good cause destinations are not loaded into + // memory until a client needs to use the queue, but a management agent viewing the + // broker will not see a destination that exists in persistent store. We may want to + // eagerly load all destinations into the broker but have an inactive state for the + // destination which has reduced memory usage. + // + if( persistenceAdapter!=null ) { + Set inactiveDests = getInactiveDestinations(); + for (Iterator iter = inactiveDests.iterator(); iter.hasNext();) { + ActiveMQDestination dest = (ActiveMQDestination) iter.next(); + if( sub.matches(dest) ) { + context.getBroker().addDestination(context, dest); + } + } + } + + subscriptions.put(info.getConsumerId(), sub); + + // Add the subscription to all the matching queues. + for (Iterator iter = destinationMap.get(info.getDestination()).iterator(); iter.hasNext();) { + Destination dest = (Destination) iter.next(); + dest.addSubscription(context, sub); + } + + if( info.isBrowser() ) { + ((QueueBrowserSubscription)sub).browseDone(); + } + + } + + /** + * @return + */ + protected Set getInactiveDestinations() { + Set inactiveDests = persistenceAdapter.getDestinations(); + inactiveDests.removeAll( destinations.keySet() ); + return inactiveDests; + } + + public void removeConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + + Subscription sub = (Subscription) subscriptions.remove(info.getConsumerId()); + if( sub==null ) + throw new IllegalArgumentException("The subscription does not exist: "+info.getConsumerId()); + + // remove the subscription from all the matching queues. + for (Iterator iter = destinationMap.get(info.getDestination()).iterator(); iter.hasNext();) { + Destination dest = (Destination) iter.next(); + dest.removeSubscription(context, sub); + } + } + + public void removeSubscription(ConnectionContext context, RemoveSubscriptionInfo info) throws Throwable { + throw new JMSException("Invalid operation."); + } + + public void send(ConnectionContext context, Message messageSend) + throws Throwable { + Destination dest = lookup(context, messageSend.getDestination()); + dest.send(context, messageSend); + } + + public void acknowledge(ConnectionContext context, MessageAck ack) throws Throwable { + + Subscription sub = (Subscription) subscriptions.get(ack.getConsumerId()); + if( sub==null ) + throw new IllegalArgumentException("The subscription does not exist: "+ack.getConsumerId()); + sub.acknowledge(context, ack); + + } + + protected Destination lookup(ConnectionContext context, ActiveMQDestination destination) throws Throwable { + synchronized(destinationsMutex){ + Destination dest=(Destination) destinations.get(destination); + if(dest==null){ + if(autoCreateDestinations){ + // Try to auto create the destination... re-invoke broker from the + // top so that the proper security checks are performed. + context.getBroker().addDestination(context,destination); + // We should now have the dest created. + dest=(Destination) destinations.get(destination); + } + if(dest==null){ + throw new JMSException("The destination "+destination+" does not exist."); + } + } + return dest; + } + } + + public void gc() { + for (Iterator iter = subscriptions.values().iterator(); iter.hasNext();) { + Subscription sub = (Subscription) iter.next(); + sub.gc(); + } + for (Iterator iter = destinations.values() .iterator(); iter.hasNext();) { + Destination dest = (Destination) iter.next(); + dest.gc(); + } + } + + protected abstract Subscription createSubscription(ConnectionContext context, ConsumerInfo info) throws Throwable; + abstract protected Destination createDestination(ActiveMQDestination destination) throws Throwable; + + public boolean isAutoCreateDestinations() { + return autoCreateDestinations; + } + + public void setAutoCreateDestinations(boolean autoCreateDestinations) { + this.autoCreateDestinations = autoCreateDestinations; + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/AbstractSubscription.java b/activemq-core/src/main/java/org/activemq/broker/region/AbstractSubscription.java new file mode 100755 index 0000000000..30b1b6f7a3 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/AbstractSubscription.java @@ -0,0 +1,112 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import javax.jms.InvalidSelectorException; +import javax.jms.JMSException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConsumerId; +import org.activemq.command.ConsumerInfo; +import org.activemq.filter.BooleanExpression; +import org.activemq.filter.DestinationFilter; +import org.activemq.filter.LogicExpression; +import org.activemq.filter.MessageEvaluationContext; +import org.activemq.filter.NoLocalExpression; +import org.activemq.selector.SelectorParser; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + +abstract public class AbstractSubscription implements Subscription { + + protected final Log log; + + protected ConnectionContext context; + protected ConsumerInfo info; + final protected DestinationFilter destinationFilter; + final protected BooleanExpression selector; + + final protected CopyOnWriteArrayList destinations = new CopyOnWriteArrayList(); + + public AbstractSubscription(ConnectionContext context, ConsumerInfo info) throws InvalidSelectorException { + this.context = context; + this.info = info; + this.destinationFilter = DestinationFilter.parseFilter(info.getDestination()); + this.selector = parseSelector(info); + this.log = LogFactory.getLog(getClass().getName()+"."+info.getConsumerId()); + } + + static private BooleanExpression parseSelector(ConsumerInfo info) throws InvalidSelectorException { + BooleanExpression rc=null; + if( info.getSelector() !=null ) { + rc = new SelectorParser().parse(info.getSelector()); + } + if( info.isNoLocal() ) { + if( rc == null ) { + rc = new NoLocalExpression(info.getConsumerId().getConnectionId()); + } else { + rc = LogicExpression.createAND(new NoLocalExpression(info.getConsumerId().getConnectionId()), rc); + } + } + if( info.getAdditionalPredicate() != null ) { + if( rc == null ) { + rc = info.getAdditionalPredicate(); + } else { + rc = LogicExpression.createAND(info.getAdditionalPredicate(), rc); + } + } + return rc; + } + + public boolean matches(MessageReference node, MessageEvaluationContext context) { + ConsumerId targetConsumerId = node.getTargetConsumerId(); + if ( targetConsumerId!=null) { + if( !targetConsumerId.equals(info.getConsumerId()) ) + return false; + } + try { + return selector == null || selector.matches(context); + } catch (JMSException e) { + log.info("Selector failed to evaluate: " + e.getMessage(), e); + return false; + } + } + + public boolean matches(ActiveMQDestination destination) { + return destinationFilter.matches(destination); + } + + public void add(ConnectionContext context, Destination destination) throws Throwable { + destinations.add(destination); + } + + public void remove(ConnectionContext context, Destination destination) throws Throwable { + destinations.remove(destination); + } + + public ConsumerInfo getConsumerInfo() { + return info; + } + + public void gc() { + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/Destination.java b/activemq-core/src/main/java/org/activemq/broker/region/Destination.java new file mode 100755 index 0000000000..9ea837103b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/Destination.java @@ -0,0 +1,55 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import java.io.IOException; + +import org.activemq.Service; +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageId; +import org.activemq.memory.UsageManager; +import org.activemq.store.MessageStore; + +/** + * + * @version $Revision: 1.12 $ + */ +public interface Destination extends Service { + + void addSubscription(ConnectionContext context, Subscription sub) throws Throwable; + void removeSubscription(ConnectionContext context, Subscription sub) throws Throwable; + + void send(ConnectionContext context, Message messageSend) throws Throwable; + boolean lock(MessageReference node, Subscription subscription); + void acknowledge(ConnectionContext context, Subscription sub, final MessageAck ack, final MessageReference node) throws IOException; + + void gc(); + Message loadMessage(MessageId messageId) throws IOException; + + ActiveMQDestination getActiveMQDestination(); + UsageManager getUsageManager(); + + void dispose(ConnectionContext context) throws IOException; + + DestinationStatistics getDestinationStatistics(); + MessageStore getMessageStore(); +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/broker/region/DestinationStatistics.java b/activemq-core/src/main/java/org/activemq/broker/region/DestinationStatistics.java new file mode 100755 index 0000000000..e956f41114 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/DestinationStatistics.java @@ -0,0 +1,94 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; +import org.activemq.management.CountStatisticImpl; +import org.activemq.management.PollCountStatisticImpl; +import org.activemq.management.StatsImpl; + +/** + * The J2EE Statistics for the a Destination. + * + * @version $Revision$ + */ +public class DestinationStatistics extends StatsImpl { + + protected CountStatisticImpl enqueues; + protected CountStatisticImpl dequeues; + protected CountStatisticImpl consumers; + protected CountStatisticImpl messages; + protected PollCountStatisticImpl messagesCached; + + public DestinationStatistics() { + + enqueues = new CountStatisticImpl("enqueues", "The number of messages that have been sent to the destination"); + dequeues = new CountStatisticImpl("dequeues", "The number of messages that have been dispatched from the destination"); + consumers = new CountStatisticImpl("consumers", "The number of consumers that that are subscribing to messages from the destination"); + messages = new CountStatisticImpl("messages", "The number of messages that that are being held by the destination"); + messagesCached = new PollCountStatisticImpl("messagesCached", "The number of messages that are held in the destination's memory cache"); + + addStatistic("enqueues", enqueues); + addStatistic("dequeues", dequeues); + addStatistic("consumers", consumers); + addStatistic("messages", messages); + addStatistic("messagesCached", messagesCached); + } + + public CountStatisticImpl getEnqueues() { + return enqueues; + } + public CountStatisticImpl getDequeues() { + return dequeues; + } + public CountStatisticImpl getConsumers() { + return consumers; + } + public PollCountStatisticImpl getMessagesCached() { + return messagesCached; + } + public CountStatisticImpl getMessages() { + return messages; + } + + public void reset() { + super.reset(); + enqueues.reset(); + dequeues.reset(); + } + + public void setParent(DestinationStatistics parent) { + if( parent!=null ) { + enqueues.setParent(parent.enqueues); + dequeues.setParent(parent.dequeues); + consumers.setParent(parent.consumers); + messagesCached.setParent(parent.messagesCached); + messages.setParent(parent.messages); + } else { + enqueues.setParent(null); + dequeues.setParent(null); + consumers.setParent(null); + messagesCached.setParent(null); + messages.setParent(null); + } + } + + public void setMessagesCached(PollCountStatisticImpl messagesCached) { + this.messagesCached = messagesCached; + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/broker/region/DurableTopicSubscription.java b/activemq-core/src/main/java/org/activemq/broker/region/DurableTopicSubscription.java new file mode 100755 index 0000000000..369fb14f89 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/DurableTopicSubscription.java @@ -0,0 +1,167 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import java.io.IOException; +import java.util.Iterator; + +import javax.jms.InvalidSelectorException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageDispatch; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +public class DurableTopicSubscription extends PrefetchSubscription { + + final protected String clientId; + final protected String subscriptionName; + final ConcurrentHashMap redeliveredMessages = new ConcurrentHashMap(); + + boolean active=true; + boolean recovered=true; + + public DurableTopicSubscription(ConnectionContext context, ConsumerInfo info) throws InvalidSelectorException { + super(context, info); + this.clientId = context.getClientId(); + this.subscriptionName = info.getSubcriptionName(); + } + + synchronized public boolean isActive() { + return active; + } + synchronized public boolean isRecovered() { + return recovered; + } + + protected boolean isFull() { + return !active || super.isFull(); + } + + synchronized public void gc() { + if( !active && recovered ) { + recovered = false; + + for (Iterator iter = dispatched.iterator(); iter.hasNext();) { + MessageReference node = (MessageReference) iter.next(); + // node.decrementTargetCount(); + iter.remove(); + } + + for (Iterator iter = matched.iterator(); iter.hasNext();) { + MessageReference node = (MessageReference) iter.next(); + // node.decrementTargetCount(); + iter.remove(); + } + + delivered=0; + } + } + + synchronized public void deactivate() { + active=false; + for (Iterator iter = dispatched.iterator(); iter.hasNext();) { + + MessageReference node = (MessageReference) iter.next(); + Integer count = (Integer) redeliveredMessages.get(node.getMessageId()); + if( count !=null ) { + redeliveredMessages.put(node.getMessageId(), new Integer(count.intValue()+1)); + } else { + redeliveredMessages.put(node.getMessageId(), new Integer(1)); + } + + // Undo the dispatch. + matched.addFirst(node); + iter.remove(); + } + delivered=0; + } + + synchronized public void activate(ConnectionContext context, ConsumerInfo info) throws Throwable { + if( !active ) { + this.active = true; + this.context = context; + this.info = info; + if( !recovered ) { + recovered=true; + for (Iterator iter = destinations.iterator(); iter.hasNext();) { + Topic topic = (Topic) iter.next(); + topic.recover(this, false); + } + } else { + if( !isFull() ) { + dispatchMatched(); + } + } + } + } + + protected MessageDispatch createMessageDispatch(MessageReference node, Message message) { + MessageDispatch md = super.createMessageDispatch(node, message); + Integer count = (Integer) redeliveredMessages.get(node.getMessageId()); + if( count !=null ) { + md.setRedeliveryCounter(count.intValue()); + } + return md; + } + + synchronized public void add(MessageReference node) throws Throwable { + assert recovered; + node = new IndirectMessageReference(node.getRegionDestination(), (Message) node); + super.add(node); + node.decrementReferenceCount(); + } + + protected boolean canDispatch(MessageReference node) { + return active; + } + + public synchronized void acknowledge(ConnectionContext context, MessageAck ack) throws Throwable { + assert recovered; + super.acknowledge(context, ack); + } + + protected void acknowledge(ConnectionContext context, MessageAck ack, MessageReference node) throws IOException { + node.getRegionDestination().acknowledge(context, this, ack, node); + redeliveredMessages.remove(node.getMessageId()); + ((IndirectMessageReference)node).drop(); + } + + public String getSubscriptionName() { + return subscriptionName; + } + + public String toString() { + return + "DurableTopicSubscription:" + + " consumer="+info.getConsumerId()+ + ", destinations="+destinations.size()+ + ", dispatched="+dispatched.size()+ + ", delivered="+this.delivered+ + ", matched="+this.matched.size(); + } + + public String getClientId() { + return clientId; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/IndirectMessageReference.java b/activemq-core/src/main/java/org/activemq/broker/region/IndirectMessageReference.java new file mode 100644 index 0000000000..5a34a16270 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/IndirectMessageReference.java @@ -0,0 +1,207 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import java.io.IOException; + +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ConsumerId; +import org.activemq.command.Message; +import org.activemq.command.MessageId; + +/** + * Keeps track of a message that is flowing through the Broker. This + * object may hold a hard reference to the message or only hold the + * id of the message if the message has been persisted on in a MessageStore. + * + * @version $Revision: 1.15 $ + */ +public class IndirectMessageReference implements MessageReference { + + public static final ActiveMQMessage END_OF_BROWSE_MARKER_MESSAGE = new ActiveMQMessage(); + public static final IndirectMessageReference END_OF_BROWSE_MARKER = new IndirectMessageReference(END_OF_BROWSE_MARKER_MESSAGE); + + /** The destination that is managing the message */ + private final Destination regionDestination; + /** The id of the message is always valid */ + private final MessageId messageId; + /** Is the message persistent? */ + private final boolean persistent; + private final String groupID; + private final int groupSequence; + private final ConsumerId targetConsumerId; + + /** The number of times the message has been delivered.*/ + private short redeliveryCounter = 0; + /** The subscription that has locked the message */ + private Subscription lockOwner; + /** Has the message been dropped? */ + private boolean dropped; + /** Has the message been acked? */ + private boolean acked; + /** Direct reference to the message */ + private Message message; + /** The number of times the message has requested being hardened */ + private int referenceCount; + + /** + * Only used by the END_OF_BROWSE_MARKER singleton + */ + private IndirectMessageReference(ActiveMQMessage message) { + this.regionDestination=null; + this.message = message; + this.messageId=null; + this.persistent=false; + this.groupID = null; + this.groupSequence = 0; + this.targetConsumerId=null; + } + + public IndirectMessageReference(Destination destination, Message message) { + this.regionDestination=destination; + this.message = message; + this.messageId=message.getMessageId(); + this.persistent=message.isPersistent() && destination.getMessageStore()!=null; + this.groupID = message.getGroupID(); + this.groupSequence = message.getGroupSequence(); + this.targetConsumerId=message.getTargetConsumerId(); + + this.referenceCount=1; + message.incrementReferenceCount(); + } + + synchronized public Message getMessageHardRef() { + return message; + } + + synchronized public int getReferenceCount() { + return referenceCount; + } + + synchronized public int incrementReferenceCount() { + int rc = ++referenceCount; + if( persistent && rc==1 ) { + assert message == null; + try { + message = regionDestination.loadMessage(messageId); + if( message == null ) { + dropped = true; + } else { + message.incrementReferenceCount(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return rc; + } + + synchronized public int decrementReferenceCount() { + int rc = --referenceCount; + if( persistent && rc == 0 && message!=null) { + message.decrementReferenceCount(); + message=null; + } + return rc; + } + + + synchronized public Message getMessage() { + return message; + } + + public String toString() { + return "Message "+messageId+" dropped="+dropped+" locked="+(lockOwner!=null); + } + + synchronized public void incrementRedeliveryCounter() { + this.redeliveryCounter++; + } + + synchronized public boolean isDropped() { + return dropped; + } + + synchronized public void drop() { + dropped=true; + if( !persistent && message!=null ) { + message.decrementReferenceCount(); + message=null; + } + } + + public boolean lock(Subscription subscription) { + if( !regionDestination.lock(this, subscription) ) + return false; + synchronized (this) { + if( lockOwner!=null && lockOwner!=subscription ) + return false; + lockOwner = subscription; + return true; + } + } + + synchronized public void unlock() { + lockOwner = null; + } + + synchronized public Subscription getLockOwner() { + return lockOwner; + } + + synchronized public int getRedeliveryCounter() { + return redeliveryCounter; + } + + public MessageId getMessageId() { + return messageId; + } + + public Destination getRegionDestination() { + return regionDestination; + } + + public boolean isPersistent() { + return persistent; + } + + synchronized public boolean isLocked() { + return lockOwner!=null; + } + + synchronized public boolean isAcked() { + return acked; + } + + synchronized public void setAcked(boolean b) { + acked=b; + } + + public String getGroupID() { + return groupID; + } + + public int getGroupSequence() { + return groupSequence; + } + + public ConsumerId getTargetConsumerId() { + return targetConsumerId; + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/MessageReference.java b/activemq-core/src/main/java/org/activemq/broker/region/MessageReference.java new file mode 100755 index 0000000000..1474b6cb6e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/MessageReference.java @@ -0,0 +1,52 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import java.io.IOException; + +import org.activemq.command.ConsumerId; +import org.activemq.command.Message; +import org.activemq.command.MessageId; + +/** + * Keeps track of a message that is flowing through the Broker. This + * object may hold a hard reference to the message or only hold the + * id of the message if the message has been persisted on in a MessageStore. + * + * @version $Revision: 1.15 $ + */ +public interface MessageReference { + + public MessageId getMessageId(); + public Message getMessageHardRef(); + public Message getMessage() throws IOException; + public boolean isPersistent(); + + public Destination getRegionDestination(); + + public int getRedeliveryCounter(); + public void incrementRedeliveryCounter(); + + public int getReferenceCount(); + + public int incrementReferenceCount(); + public int decrementReferenceCount(); + public ConsumerId getTargetConsumerId(); + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/PrefetchSubscription.java b/activemq-core/src/main/java/org/activemq/broker/region/PrefetchSubscription.java new file mode 100755 index 0000000000..865dcc937f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/PrefetchSubscription.java @@ -0,0 +1,334 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedList; + +import javax.jms.InvalidSelectorException; +import javax.jms.JMSException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageDispatch; +import org.activemq.command.MessageId; +import org.activemq.transaction.Synchronization; + +/** + * A subscription that honors the pre-fetch option of the ConsumerInfo. + * + * @version $Revision: 1.15 $ + */ +abstract public class PrefetchSubscription extends AbstractSubscription { + + final protected LinkedList matched = new LinkedList(); + final protected LinkedList dispatched = new LinkedList(); + + final protected ActiveMQDestination dlqDestination = new ActiveMQQueue("ActiveMQ.DLQ"); + protected int delivered=0; + + int preLoadLimit=1024*100; + int preLoadSize=0; + boolean dispatching=false; + + public PrefetchSubscription(ConnectionContext context, ConsumerInfo info) throws InvalidSelectorException { + super(context, info); + } + + synchronized public void add(MessageReference node) throws Throwable { + if( !isFull() ) { + dispatch(node); + } else { + matched.addLast(node); + } + } + + synchronized public void acknowledge(final ConnectionContext context, final MessageAck ack) throws Throwable { + + // Handle the standard acknowledgment case. + boolean wasFull = isFull(); + if( ack.isStandardAck() ) { + + // Acknowledge all dispatched messages up till the message id of the acknowledgment. + int index=0; + boolean inAckRange=false; + for (Iterator iter = dispatched.iterator(); iter.hasNext();) { + final MessageReference node = (MessageReference)iter.next(); + MessageId messageId = node.getMessageId(); + + if( ack.getFirstMessageId()==null || ack.getFirstMessageId().equals(messageId)) { + inAckRange = true; + } + + if( inAckRange ) { + + // Don't remove the nodes until we are committed. + if ( !context.isInTransaction() ) { + iter.remove(); + } else { + // setup a Synchronization to remove nodes from the dispatched list. + context.getTransaction().addSynchronization(new Synchronization(){ + public void afterCommit() throws Throwable { + synchronized(PrefetchSubscription.this) { + + // Now that we are committed, we can remove the nodes. + boolean inAckRange=false; + int index=0; + for (Iterator iter = dispatched.iterator(); iter.hasNext();) { + final MessageReference node = (MessageReference)iter.next(); + MessageId messageId = node.getMessageId(); + if( ack.getFirstMessageId()==null || ack.getFirstMessageId().equals(messageId)) { + inAckRange = true; + } + if( inAckRange ) { + index++; + iter.remove(); + if( ack.getLastMessageId().equals(messageId)) { + delivered = Math.max(0, delivered - (index+1)); + return; + } + } + } + + } + } + }); + } + + index++; + acknowledge(context, ack, node); + if( ack.getLastMessageId().equals(messageId)) { + if ( context.isInTransaction() ) + delivered = Math.max(delivered,index+1); + else + delivered = Math.max(0, delivered - (index+1)); + + if( wasFull && !isFull() ) { + dispatchMatched(); + } + return; + } else { +// System.out.println("no match: "+ack.getLastMessageId()+","+messageId); + } + } + + } + log.info("Could not correlate acknowledgment with dispatched message: "+ack); + + } else if( ack.isDeliveredAck() ) { + + // Message was delivered but not acknowledged: update pre-fetch counters. + // Acknowledge all dispatched messages up till the message id of the acknowledgment. + int index=0; + for (Iterator iter = dispatched.iterator(); iter.hasNext();index++) { + final MessageReference node = (MessageReference)iter.next(); + if( ack.getLastMessageId().equals(node.getMessageId()) ) { + delivered = Math.max(delivered,index+1); + if( wasFull && !isFull() ) { + dispatchMatched(); + } + return; + } + } + throw new JMSException("Could not correlate acknowledgment with dispatched message: "+ack); + + } else if( ack.isPoisonAck() ) { + + // Handle the poison ACK case: we need to send the message to a DLQ + if( ack.isInTransaction() ) + throw new JMSException("Poison ack cannot be transacted: "+ack); + + // Acknowledge all dispatched messages up till the message id of the acknowledgment. + int index=0; + boolean inAckRange=false; + for (Iterator iter = dispatched.iterator(); iter.hasNext();) { + final MessageReference node = (MessageReference)iter.next(); + MessageId messageId = node.getMessageId(); + + if( ack.getFirstMessageId()==null || ack.getFirstMessageId().equals(messageId)) { + inAckRange = true; + } + + if( inAckRange ) { + + // Send the message to the DLQ + node.incrementReferenceCount(); + try { + Message message = node.getMessage(); + if( message !=null ) { + + if( message.getOriginalDestination()!=null ) + message.setOriginalDestination(message.getDestination()); + message.setDestination(dlqDestination); + + if( message.getOriginalTransactionId()!=null ) + message.setOriginalTransactionId(message.getTransactionId()); + message.setTransactionId(null); + message.evictMarshlledForm(); + + boolean originalFlowControl = context.isProducerFlowControl(); + try { + context.setProducerFlowControl(false); + context.getBroker().send(context, message); + } finally { + context.setProducerFlowControl(originalFlowControl); + } + + } + } finally { + node.decrementReferenceCount(); + } + + iter.remove(); + index++; + acknowledge(context, ack, node); + if( ack.getLastMessageId().equals(messageId)) { + + delivered = Math.max(0, delivered - (index+1)); + + if( wasFull && !isFull() ) { + dispatchMatched(); + } + return; + } + } + } + throw new JMSException("Could not correlate acknowledgment with dispatched message: "+ack); + } + + throw new JMSException("Invalid acknowledgment: "+ack); + } + + protected boolean isFull() { + return dispatched.size()-delivered >= info.getPrefetchSize() || preLoadSize > preLoadLimit; + } + + protected void dispatchMatched() throws IOException { + if(!dispatching) { + dispatching = true; + try { + for (Iterator iter = matched.iterator(); iter.hasNext() && !isFull();) { + MessageReference node = (MessageReference) iter.next(); + iter.remove(); + dispatch(node); + } + } finally { + dispatching=false; + } + } + } + + private void dispatch(final MessageReference node) throws IOException { + + node.incrementReferenceCount(); + + final Message message = node.getMessage(); + if( message == null ) { + return; + } + incrementPreloadSize(node.getMessage().getSize()); + + // Make sure we can dispatch a message. + if( canDispatch(node) ) { + + MessageDispatch md = createMessageDispatch(node, message); + dispatched.addLast(node); + + if( info.isDispatchAsync() ) { + md.setConsumer(new Runnable(){ + public void run() { + onDispatch(node, message); + } + }); + context.getConnection().dispatchAsync(md); + } else { + context.getConnection().dispatchSync(md); + onDispatch(node, message); + } + } + + } + + synchronized private void onDispatch(final MessageReference node, final Message message) { + + boolean wasFull = isFull(); + decrementPreloadSize(message.getSize()); + node.decrementReferenceCount(); + + if( node.getRegionDestination() !=null ) { + node.getRegionDestination().getDestinationStatistics().getDequeues().increment(); + + if( wasFull && !isFull() ) { + try { + dispatchMatched(); + } catch (IOException e) { + context.getConnection().serviceException(e); + } + } + } + + } + + private int incrementPreloadSize(int size) { + preLoadSize += size; + return preLoadSize; + } + + private int decrementPreloadSize(int size) { + preLoadSize -= size; + return preLoadSize; + } + + + /** + * @param node + * @param message TODO + * @return + */ + protected MessageDispatch createMessageDispatch(MessageReference node, Message message) { + MessageDispatch md = new MessageDispatch(); + md.setConsumerId( info.getConsumerId() ); + md.setDestination( node.getRegionDestination().getActiveMQDestination() ); + md.setMessage(message); + md.setRedeliveryCounter( node.getRedeliveryCounter() ); + return md; + } + + /** + * Use when a matched message is about to be dispatched to the client. + * + * @param node + * @return false if the message should not be dispatched to the client (another sub may have already dispatched it for example). + */ + abstract protected boolean canDispatch(MessageReference node); + + /** + * Used during acknowledgment to remove the message. + * @throws IOException + */ + protected void acknowledge(ConnectionContext context, final MessageAck ack, final MessageReference node) throws IOException { + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/Queue.java b/activemq-core/src/main/java/org/activemq/broker/region/Queue.java new file mode 100755 index 0000000000..ca678583e8 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/Queue.java @@ -0,0 +1,382 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.region; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.policy.DispatchPolicy; +import org.activemq.broker.region.policy.RoundRobinDispatchPolicy; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConsumerId; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageId; +import org.activemq.filter.MessageEvaluationContext; +import org.activemq.memory.UsageManager; +import org.activemq.store.MessageRecoveryListener; +import org.activemq.store.MessageStore; +import org.activemq.thread.TaskRunnerFactory; +import org.activemq.thread.Valve; +import org.activemq.transaction.Synchronization; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + +/** + * The Queue is a List of MessageEntry objects that are dispatched to matching + * subscriptions. + * + * @version $Revision: 1.28 $ + */ +public class Queue implements Destination { + + private final Log log; + + protected final ActiveMQDestination destination; + protected final CopyOnWriteArrayList consumers = new CopyOnWriteArrayList(); + protected final LinkedList messages = new LinkedList(); + protected final Valve dispatchValve = new Valve(true); + protected final UsageManager usageManager; + protected final DestinationStatistics destinationStatistics = new DestinationStatistics(); + + private Subscription exclusiveOwner; + private final ConcurrentHashMap messageGroupOwners = new ConcurrentHashMap(); + + protected long garbageSize = 0; + protected long garbageSizeBeforeCollection = 1000; + private DispatchPolicy dispatchPolicy = new RoundRobinDispatchPolicy(); + protected final MessageStore store; + protected int highestSubscriptionPriority; + + public Queue(ActiveMQDestination destination, final UsageManager memoryManager, MessageStore store, + DestinationStatistics parentStats, TaskRunnerFactory taskFactory) throws Throwable { + this.destination = destination; + this.usageManager = memoryManager; + this.store = store; + + destinationStatistics.setParent(parentStats); + this.log = LogFactory.getLog(getClass().getName() + "." + destination.getPhysicalName()); + + if (store != null) { + // Restore the persistent messages. + store.recover(new MessageRecoveryListener() { + public void recoverMessage(Message message) { + message.setRegionDestination(Queue.this); + MessageReference reference = createMessageReference(message); + messages.add(reference); + reference.decrementReferenceCount(); + destinationStatistics.getMessages().increment(); + } + + public void recoverMessageReference(String messageReference) throws Throwable { + throw new RuntimeException("Should not be called."); + } + }); + } + } + + public synchronized boolean lock(MessageReference node, Subscription sub) { + if (exclusiveOwner == sub) + return true; + if (exclusiveOwner != null) + return false; + if (sub.getConsumerInfo().getPriority() != highestSubscriptionPriority) + return false; + if (sub.getConsumerInfo().isExclusive()) { + exclusiveOwner = sub; + } + return true; + } + + public void addSubscription(ConnectionContext context, Subscription sub) throws Throwable { + sub.add(context, this); + destinationStatistics.getConsumers().increment(); + + // synchronize with dispatch method so that no new messages are sent + // while + // setting up a subscription. avoid out of order messages, duplicates + // etc. + dispatchValve.turnOff(); + + MessageEvaluationContext msgContext = context.getMessageEvaluationContext(); + try { + consumers.add(sub); + + highestSubscriptionPriority = calcHighestSubscriptionPriority(); + msgContext.setDestination(destination); + + synchronized (messages) { + // Add all the matching messages in the queue to the + // subscription. + for (Iterator iter = messages.iterator(); iter.hasNext();) { + + IndirectMessageReference node = (IndirectMessageReference) iter.next(); + if (node.isDropped() ) { + continue; + } + + try { + msgContext.setMessageReference(node); + if (sub.matches(node, msgContext)) { + sub.add(node); + } + } + catch (IOException e) { + log.warn("Could not load message: " + e, e); + } + } + } + + } + finally { + msgContext.clear(); + dispatchValve.turnOn(); + } + } + + public void removeSubscription(ConnectionContext context, Subscription sub) throws Throwable { + + destinationStatistics.getConsumers().decrement(); + + // synchronize with dispatch method so that no new messages are sent + // while + // removing up a subscription. + dispatchValve.turnOff(); + try { + + consumers.remove(sub); + sub.remove(context, this); + + highestSubscriptionPriority = calcHighestSubscriptionPriority(); + + boolean wasExclusiveOwner = false; + if (exclusiveOwner == sub) { + exclusiveOwner = null; + wasExclusiveOwner = true; + } + + HashSet ownedGroups = new HashSet(); + ConsumerId consumerId = sub.getConsumerInfo().getConsumerId(); + for (Iterator iter = messageGroupOwners.keySet().iterator(); iter.hasNext();) { + String group = (String) iter.next(); + ConsumerId owner = (ConsumerId) messageGroupOwners.get(group); + if (owner.equals(consumerId)) { + ownedGroups.add(group); + iter.remove(); + } + } + + synchronized (messages) { + if (!sub.getConsumerInfo().isBrowser()) { + MessageEvaluationContext msgContext = context.getMessageEvaluationContext(); + try { + msgContext.setDestination(destination); + + for (Iterator iter = messages.iterator(); iter.hasNext();) { + IndirectMessageReference node = (IndirectMessageReference) iter.next(); + if (node.isDropped() ) { + continue; + } + + String groupID = node.getGroupID(); + + // Re-deliver all messages that the sub locked + if (node.getLockOwner() == sub || wasExclusiveOwner || (groupID != null && ownedGroups.contains(groupID))) { + node.incrementRedeliveryCounter(); + node.unlock(); + msgContext.setMessageReference(node); + dispatchPolicy.dispatch(context, node, msgContext, consumers); + } + } + } finally { + msgContext.clear(); + } + } + } + + } + finally { + dispatchValve.turnOn(); + } + + } + + public void send(final ConnectionContext context, final Message message) throws Throwable { + + if( context.isProducerFlowControl() ) + usageManager.waitForSpace(); + + message.setRegionDestination(this); + + if (store != null && message.isPersistent()) + store.addMessage(context, message); + + final MessageReference node = createMessageReference(message); + try { + + if (context.isInTransaction()) { + context.getTransaction().addSynchronization(new Synchronization() { + public void afterCommit() throws Throwable { + dispatch(context, node, message); + } + }); + } + else { + dispatch(context, node, message); + } + } finally { + node.decrementReferenceCount(); + } + } + + public void dispose(ConnectionContext context) throws IOException { + if (store != null) { + store.removeAllMessages(context); + } + destinationStatistics.setParent(null); + } + + public void dropEvent() { + // TODO: need to also decrement when messages expire. + destinationStatistics.getMessages().decrement(); + synchronized (messages) { + garbageSize++; + if (garbageSize > garbageSizeBeforeCollection) { + gc(); + } + } + } + + public void gc() { + synchronized (messages) { + for (Iterator iter = messages.iterator(); iter.hasNext();) { + // Remove dropped messages from the queue. + IndirectMessageReference node = (IndirectMessageReference) iter.next(); + if (node.isDropped()) { + garbageSize--; + iter.remove(); + continue; + } + } + } + } + + public void acknowledge(ConnectionContext context, Subscription sub, final MessageAck ack, final MessageReference node) throws IOException { + if (store != null && node.isPersistent()) { + store.removeMessage(context, ack); + } + } + + public Message loadMessage(MessageId messageId) throws IOException { + Message msg = store.getMessage(messageId); + if( msg!=null ) { + msg.setRegionDestination(this); + } + return msg; + } + + public String toString() { + return "Queue: destination=" + destination.getPhysicalName() + ", subscriptions=" + consumers.size() + ", memory=" + usageManager.getPercentUsage() + + "%, size=" + messages.size() + ", in flight groups=" + messageGroupOwners.size(); + } + + public void start() throws Exception { + } + + public void stop() throws Exception { + } + + // Properties + // ------------------------------------------------------------------------- + public ActiveMQDestination getActiveMQDestination() { + return destination; + } + + public UsageManager getUsageManager() { + return usageManager; + } + + public DestinationStatistics getDestinationStatistics() { + return destinationStatistics; + } + + public ConcurrentHashMap getMessageGroupOwners() { + return messageGroupOwners; + } + + public DispatchPolicy getDispatchPolicy() { + return dispatchPolicy; + } + + public void setDispatchPolicy(DispatchPolicy dispatchPolicy) { + this.dispatchPolicy = dispatchPolicy; + } + + // Implementation methods + // ------------------------------------------------------------------------- + private MessageReference createMessageReference(Message message) { + return new IndirectMessageReference(this, message); + } + + private void dispatch(ConnectionContext context, MessageReference node, Message message) throws Throwable { + dispatchValve.increment(); + MessageEvaluationContext msgContext = context.getMessageEvaluationContext(); + try { + destinationStatistics.getEnqueues().increment(); + destinationStatistics.getMessages().increment(); + synchronized (messages) { + messages.add(node); + } + + if (consumers.isEmpty()) + return; + + msgContext.setDestination(destination); + msgContext.setMessageReference(node); + + dispatchPolicy.dispatch(context, node, msgContext, consumers); + } + finally { + msgContext.clear(); + dispatchValve.decrement(); + } + } + + private int calcHighestSubscriptionPriority() { + int rc = Integer.MIN_VALUE; + for (Iterator iter = consumers.iterator(); iter.hasNext();) { + Subscription sub = (Subscription) iter.next(); + if (sub.getConsumerInfo().getPriority() > rc) { + rc = sub.getConsumerInfo().getPriority(); + } + } + return rc; + } + + public MessageStore getMessageStore() { + return store; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/QueueBrowserSubscription.java b/activemq-core/src/main/java/org/activemq/broker/region/QueueBrowserSubscription.java new file mode 100755 index 0000000000..004f78bb94 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/QueueBrowserSubscription.java @@ -0,0 +1,70 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import javax.jms.InvalidSelectorException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageDispatch; +import org.activemq.filter.MessageEvaluationContext; + +public class QueueBrowserSubscription extends PrefetchSubscription { + + boolean browseDone; + + public QueueBrowserSubscription(ConnectionContext context, ConsumerInfo info) throws InvalidSelectorException { + super(context, info); + } + + protected boolean canDispatch(MessageReference node) { + return !((IndirectMessageReference)node).isAcked(); + } + + public String toString() { + return + "QueueBrowserSubscription:" + + " consumer="+info.getConsumerId()+ + ", destinations="+destinations.size()+ + ", dispatched="+dispatched.size()+ + ", delivered="+this.delivered+ + ", matched="+this.matched.size(); + } + + public void browseDone() throws Throwable { + browseDone = true; + add(IndirectMessageReference.END_OF_BROWSE_MARKER); + } + + protected MessageDispatch createMessageDispatch(MessageReference node, Message message) { + if( node == IndirectMessageReference.END_OF_BROWSE_MARKER ) { + MessageDispatch md = new MessageDispatch(); + md.setMessage(null); + md.setConsumerId( info.getConsumerId() ); + md.setDestination( null ); + return md; + } else { + return super.createMessageDispatch(node, message); + } + } + public boolean matches(MessageReference node, MessageEvaluationContext context) { + return !browseDone && super.matches(node, context); + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/QueueRegion.java b/activemq-core/src/main/java/org/activemq/broker/region/QueueRegion.java new file mode 100755 index 0000000000..9b3524f644 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/QueueRegion.java @@ -0,0 +1,92 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.region; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.policy.PolicyEntry; +import org.activemq.broker.region.policy.PolicyMap; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ConsumerInfo; +import org.activemq.memory.UsageManager; +import org.activemq.store.MessageStore; +import org.activemq.store.PersistenceAdapter; +import org.activemq.thread.TaskRunnerFactory; + +import javax.jms.InvalidSelectorException; + +import java.util.Iterator; +import java.util.Set; + +/** + * + * @version $Revision: 1.9 $ + */ +public class QueueRegion extends AbstractRegion { + + private final PolicyMap policyMap; + + public QueueRegion(DestinationStatistics destinationStatistics, UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory, + PersistenceAdapter persistenceAdapter, PolicyMap policyMap) { + super(destinationStatistics, memoryManager, taskRunnerFactory, persistenceAdapter); + this.policyMap = policyMap; + } + + public String toString() { + return "QueueRegion: destinations=" + destinations.size() + ", subscriptions=" + subscriptions.size() + ", memory=" + memoryManager.getPercentUsage() + + "%"; + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected Destination createDestination(ActiveMQDestination destination) throws Throwable { + MessageStore store = persistenceAdapter.createQueueMessageStore((ActiveMQQueue) destination); + Queue queue = new Queue(destination, memoryManager, store, destinationStatistics, taskRunnerFactory); + configureQueue(queue, destination); + return queue; + } + + protected void configureQueue(Queue queue, ActiveMQDestination destination) { + if (policyMap != null) { + PolicyEntry entry = policyMap.getEntryFor(destination); + if (entry != null) { + entry.configure(queue); + } + } + } + + protected Subscription createSubscription(ConnectionContext context, ConsumerInfo info) throws InvalidSelectorException { + if (info.isBrowser()) { + return new QueueBrowserSubscription(context, info); + } + else { + return new QueueSubscription(context, info); + } + } + + protected Set getInactiveDestinations() { + Set inactiveDestinations = super.getInactiveDestinations(); + for (Iterator iter = inactiveDestinations.iterator(); iter.hasNext();) { + ActiveMQDestination dest = (ActiveMQDestination) iter.next(); + if (!dest.isQueue()) + iter.remove(); + } + return inactiveDestinations; + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/QueueSubscription.java b/activemq-core/src/main/java/org/activemq/broker/region/QueueSubscription.java new file mode 100755 index 0000000000..34d75d1bf4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/QueueSubscription.java @@ -0,0 +1,134 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import java.io.IOException; + +import javax.jms.InvalidSelectorException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ConsumerId; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.MessageAck; +import org.activemq.transaction.Synchronization; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +public class QueueSubscription extends PrefetchSubscription { + + public QueueSubscription(ConnectionContext context, ConsumerInfo info) throws InvalidSelectorException { + super(context, info); + } + + public void add(MessageReference node) throws Throwable { + super.add(node); + } + /** + * In the queue case, mark the node as dropped and then a gc cycle will remove it from + * the queue. + * @throws IOException + */ + protected void acknowledge(ConnectionContext context, final MessageAck ack, final MessageReference n) throws IOException { + + final IndirectMessageReference node = (IndirectMessageReference) n; + + final Queue queue = (Queue)node.getRegionDestination(); + queue.acknowledge(context, this, ack, node); + + if( !ack.isInTransaction() ) { + node.drop(); + queue.dropEvent(); + } else { + node.setAcked(true); + context.getTransaction().addSynchronization(new Synchronization(){ + public void afterCommit() throws Throwable { + node.drop(); + queue.dropEvent(); + } + public void afterRollback() throws Throwable { + node.setAcked(false); + } + }); + } + } + + protected boolean canDispatch(MessageReference n) { + IndirectMessageReference node = (IndirectMessageReference) n; + if( node.isAcked() ) + return false; + + // Keep message groups together. + String groupId = node.getGroupID(); + int sequence = node.getGroupSequence(); + if( groupId!=null ) { + + ConcurrentHashMap messageGroupOwners = ((Queue)node.getRegionDestination()).getMessageGroupOwners(); + + // If we can own the first, then no-one else should own the rest. + if( sequence==1 ) { + if( node.lock(this) ) { + messageGroupOwners.put(groupId, info.getConsumerId()); + return true; + } else { + return false; + } + } + + // Make sure that the previous owner is still valid, we may + // need to become the new owner. + ConsumerId groupOwner; + synchronized(node) { + groupOwner = (ConsumerId) messageGroupOwners.get(groupId); + if( groupOwner==null ) { + if( node.lock(this) ) { + messageGroupOwners.put(groupId, info.getConsumerId()); + return true; + } else { + return false; + } + } + } + + if( groupOwner.equals(info.getConsumerId()) ) { + // A group sequence < 1 is an end of group signal. + if ( sequence < 1 ) { + messageGroupOwners.remove(groupId); + } + return true; + } + + return false; + + } else { + return node.lock(this); + } + + } + + public String toString() { + return + "QueueSubscription:" + + " consumer="+info.getConsumerId()+ + ", destinations="+destinations.size()+ + ", dispatched="+dispatched.size()+ + ", delivered="+this.delivered+ + ", matched="+this.matched.size(); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/Region.java b/activemq-core/src/main/java/org/activemq/broker/region/Region.java new file mode 100755 index 0000000000..dd1ab09d3d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/Region.java @@ -0,0 +1,93 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.RemoveSubscriptionInfo; + +/** + * A Region is used to implement the different QOS options available to + * a broker. A Broker is composed of multiple mesasge processing Regions that + * provide different QOS options. + * + * @version $Revision$ + */ +public interface Region { + + /** + * Used to create a destination. Usually, this method is invoked as a side-effect of sending + * a message to a destiantion that does not exist yet. + * + * @param context + * @param destination the destination to create. + * @return TODO + */ + public Destination addDestination(ConnectionContext context, ActiveMQDestination destination) throws Throwable; + + /** + * Used to destory a destination. + * This shoud try to quiesce use of the destination up to the timeout alotted time before removing the destination. + * This will remove all persistent messages associated with the destination. + * + * @param context the enviorment the operation is being executed under. + * @param destination what is being removed from the broker. + * @param timeout the max amount of time to wait for the destination to quiesce + */ + public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Throwable; + + /** + * Adds a consumer. + * @param context the enviorment the operation is being executed under. + */ + public void addConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable; + + /** + * Removes a consumer. + * @param context the enviorment the operation is being executed under. + */ + public void removeConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable; + + /** + * Deletes a durable subscription. + * @param context the enviorment the operation is being executed under. + * @param info TODO + */ + public void removeSubscription(ConnectionContext context, RemoveSubscriptionInfo info) throws Throwable; + + /** + * Send a message to the broker to using the specified destination. The destination specified + * in the message does not need to match the destination the message is sent to. This is + * handy in case the message is being sent to a dead letter destination. + * @param context the enviorment the operation is being executed under. + */ + public void send(ConnectionContext context, Message message) throws Throwable; + + /** + * Used to acknowledge the receipt of a message by a client. + * @param context the enviorment the operation is being executed under. + */ + public void acknowledge(ConnectionContext context, MessageAck ack) throws Throwable; + + public void gc(); + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/RegionBroker.java b/activemq-core/src/main/java/org/activemq/broker/region/RegionBroker.java new file mode 100755 index 0000000000..ec959812a2 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/RegionBroker.java @@ -0,0 +1,351 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArraySet; + +import org.activemq.broker.Broker; +import org.activemq.broker.Connection; +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.policy.PolicyMap; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.BrokerId; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.ProducerInfo; +import org.activemq.command.RemoveSubscriptionInfo; +import org.activemq.command.SessionInfo; +import org.activemq.command.TransactionId; +import org.activemq.memory.UsageManager; +import org.activemq.store.PersistenceAdapter; +import org.activemq.store.memory.MemoryPersistenceAdapter; +import org.activemq.thread.TaskRunnerFactory; +import org.activemq.util.IdGenerator; +import org.activemq.util.LongSequenceGenerator; + +import javax.jms.JMSException; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * Routes Broker operations to the correct messaging regions for processing. + * + * @version $Revision$ + */ +public class RegionBroker implements Broker { + + private static final IdGenerator brokerIdGenerator = new IdGenerator(); + + private final Region queueRegion; + private final Region topicRegion; + private final Region tempQueueRegion; + private final Region tempTopicRegion; + + protected final DestinationStatistics destinationStatistics = new DestinationStatistics(); + + private final CopyOnWriteArrayList connections = new CopyOnWriteArrayList(); + private final CopyOnWriteArraySet destinations = new CopyOnWriteArraySet(); + + private final LongSequenceGenerator sequenceGenerator = new LongSequenceGenerator(); + private BrokerId brokerId; + private String brokerName; + + public RegionBroker(TaskRunnerFactory taskRunnerFactory, UsageManager memoryManager, PersistenceAdapter adapter) throws IOException { + this(taskRunnerFactory, memoryManager, createDefaultPersistenceAdapter(memoryManager), null); + } + + public RegionBroker(TaskRunnerFactory taskRunnerFactory, UsageManager memoryManager, PersistenceAdapter adapter, PolicyMap policyMap) throws IOException { + this.sequenceGenerator.setLastSequenceId( adapter.getLastMessageBrokerSequenceId() ); + + queueRegion = createQueueRegion(memoryManager, taskRunnerFactory, adapter, policyMap); + topicRegion = createTopicRegion(memoryManager, taskRunnerFactory, adapter, policyMap); + + tempQueueRegion = createTempQueueRegion(memoryManager, taskRunnerFactory); + tempTopicRegion = createTempTopicRegion(memoryManager, taskRunnerFactory); + } + + protected Region createTempTopicRegion(UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory) { + return new TempTopicRegion(destinationStatistics, memoryManager, taskRunnerFactory); + } + + protected Region createTempQueueRegion(UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory) { + return new TempQueueRegion(destinationStatistics, memoryManager, taskRunnerFactory); + } + + protected Region createTopicRegion(UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory, PersistenceAdapter adapter, PolicyMap policyMap) { + return new TopicRegion(destinationStatistics, memoryManager, taskRunnerFactory, adapter, policyMap); + } + + protected Region createQueueRegion(UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory, PersistenceAdapter adapter, PolicyMap policyMap) { + return new QueueRegion(destinationStatistics, memoryManager, taskRunnerFactory, adapter, policyMap); + } + + private static PersistenceAdapter createDefaultPersistenceAdapter(UsageManager memoryManager) throws IOException { + return new MemoryPersistenceAdapter(); + } + + + public void start() throws Exception { + } + + public void stop() throws Exception { + } + + public void addConnection(ConnectionContext context, ConnectionInfo info) throws Throwable { + connections.add(context.getConnection()); + } + + public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Throwable { + connections.remove(context.getConnection()); + } + + public Connection[] getClients() throws Throwable { + ArrayList l = new ArrayList(connections); + Connection rc[] = new Connection[l.size()]; + l.toArray(rc); + return rc; + } + + public Destination addDestination(ConnectionContext context, ActiveMQDestination destination) throws Throwable { + if( destinations.contains(destination) ) + throw new JMSException("Destination already exists: "+destination); + + Destination answer = null; + switch(destination.getDestinationType()) { + case ActiveMQDestination.QUEUE_TYPE: + answer = queueRegion.addDestination(context, destination); + break; + case ActiveMQDestination.TOPIC_TYPE: + answer = topicRegion.addDestination(context, destination); + break; + case ActiveMQDestination.TEMP_QUEUE_TYPE: + answer = tempQueueRegion.addDestination(context, destination); + break; + case ActiveMQDestination.TEMP_TOPIC_TYPE: + answer = tempTopicRegion.addDestination(context, destination); + break; + default: + throwUnknownDestinationType(destination); + } + + destinations.add(destination); + return answer; + } + + public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Throwable { + if( !destinations.contains(destination) ) + throw new JMSException("Destination does not exist: "+destination); + + switch(destination.getDestinationType()) { + case ActiveMQDestination.QUEUE_TYPE: + queueRegion.removeDestination(context, destination, timeout); + break; + case ActiveMQDestination.TOPIC_TYPE: + topicRegion.removeDestination(context, destination, timeout); + break; + case ActiveMQDestination.TEMP_QUEUE_TYPE: + tempQueueRegion.removeDestination(context, destination, timeout); + break; + case ActiveMQDestination.TEMP_TOPIC_TYPE: + tempTopicRegion.removeDestination(context, destination, timeout); + break; + default: + throwUnknownDestinationType(destination); + } + + destinations.remove(destination); + } + + public ActiveMQDestination[] getDestinations() throws Throwable { + ArrayList l = new ArrayList(destinations); + ActiveMQDestination rc[] = new ActiveMQDestination[l.size()]; + l.toArray(rc); + return rc; + } + + + public void addSession(ConnectionContext context, SessionInfo info) throws Throwable { + } + + public void removeSession(ConnectionContext context, SessionInfo info) throws Throwable { + } + + public void addProducer(ConnectionContext context, ProducerInfo info) throws Throwable { + } + + public void removeProducer(ConnectionContext context, ProducerInfo info) throws Throwable { + } + + public void addConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + ActiveMQDestination destination = info.getDestination(); + switch(destination.getDestinationType()) { + case ActiveMQDestination.QUEUE_TYPE: + queueRegion.addConsumer(context, info); + break; + case ActiveMQDestination.TOPIC_TYPE: + topicRegion.addConsumer(context, info); + break; + case ActiveMQDestination.TEMP_QUEUE_TYPE: + tempQueueRegion.addConsumer(context, info); + break; + case ActiveMQDestination.TEMP_TOPIC_TYPE: + tempTopicRegion.addConsumer(context, info); + break; + default: + throwUnknownDestinationType(destination); + } + } + + public void removeConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + ActiveMQDestination destination = info.getDestination(); + switch(destination.getDestinationType()) { + case ActiveMQDestination.QUEUE_TYPE: + queueRegion.removeConsumer(context, info); + break; + case ActiveMQDestination.TOPIC_TYPE: + topicRegion.removeConsumer(context, info); + break; + case ActiveMQDestination.TEMP_QUEUE_TYPE: + tempQueueRegion.removeConsumer(context, info); + break; + case ActiveMQDestination.TEMP_TOPIC_TYPE: + tempTopicRegion.removeConsumer(context, info); + break; + default: + throwUnknownDestinationType(destination); + } + } + + public void removeSubscription(ConnectionContext context, RemoveSubscriptionInfo info) throws Throwable { + topicRegion.removeSubscription(context, info); + } + + public void send(ConnectionContext context, Message message) throws Throwable { + + message.getMessageId().setBrokerSequenceId(sequenceGenerator.getNextSequenceId()); + ActiveMQDestination destination = message.getDestination(); + switch(destination.getDestinationType()) { + case ActiveMQDestination.QUEUE_TYPE: + queueRegion.send(context, message); + break; + case ActiveMQDestination.TOPIC_TYPE: + topicRegion.send(context, message); + break; + case ActiveMQDestination.TEMP_QUEUE_TYPE: + tempQueueRegion.send(context, message); + break; + case ActiveMQDestination.TEMP_TOPIC_TYPE: + tempTopicRegion.send(context, message); + break; + default: + throwUnknownDestinationType(destination); + } + } + + public void acknowledge(ConnectionContext context, MessageAck ack) throws Throwable { + ActiveMQDestination destination = ack.getDestination(); + switch(destination.getDestinationType()) { + case ActiveMQDestination.QUEUE_TYPE: + queueRegion.acknowledge(context, ack); + break; + case ActiveMQDestination.TOPIC_TYPE: + topicRegion.acknowledge(context, ack); + break; + case ActiveMQDestination.TEMP_QUEUE_TYPE: + tempQueueRegion.acknowledge(context, ack); + break; + case ActiveMQDestination.TEMP_TOPIC_TYPE: + tempTopicRegion.acknowledge(context, ack); + break; + default: + throwUnknownDestinationType(destination); + } + } + + public TransactionId[] getPreparedTransactions(ConnectionContext context) throws Throwable { + throw new IllegalAccessException("Transaction operation not implemented by this broker."); + } + + public void beginTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + throw new IllegalAccessException("Transaction operation not implemented by this broker."); + } + + public int prepareTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + throw new IllegalAccessException("Transaction operation not implemented by this broker."); + } + + public void rollbackTransaction(ConnectionContext context, TransactionId xid) throws Throwable { + throw new IllegalAccessException("Transaction operation not implemented by this broker."); + } + + public void commitTransaction(ConnectionContext context, TransactionId xid, boolean onePhase) throws Throwable { + throw new IllegalAccessException("Transaction operation not implemented by this broker."); + } + + public void forgetTransaction(ConnectionContext context, TransactionId transactionId) throws Throwable { + throw new IllegalAccessException("Transaction operation not implemented by this broker."); + } + + + public void gc() { + queueRegion.gc(); + topicRegion.gc(); + } + + public BrokerId getBrokerId() { + if( brokerId==null ) { + // TODO: this should persist the broker id so that subsequent startup + // uses the same broker id. + brokerId=new BrokerId(brokerIdGenerator.generateId()); + } + return brokerId; + } + + public void setBrokerId(BrokerId brokerId) { + this.brokerId = brokerId; + } + + public String getBrokerName() { + if( brokerName==null ) { + try { + brokerName = java.net.InetAddress.getLocalHost().getHostName().toLowerCase(); + } catch (Exception e) { + brokerName="localhost"; + } + } + return brokerName; + } + + public void setBrokerName(String brokerName) { + this.brokerName = brokerName; + } + + public DestinationStatistics getDestinationStatistics() { + return destinationStatistics; + } + + protected void throwUnknownDestinationType(ActiveMQDestination destination) throws JMSException { + throw new JMSException("Unknown destination type: " + destination.getDestinationType()); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/Subscription.java b/activemq-core/src/main/java/org/activemq/broker/region/Subscription.java new file mode 100755 index 0000000000..a4d0071b7b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/Subscription.java @@ -0,0 +1,92 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import java.io.IOException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.MessageAck; +import org.activemq.filter.MessageEvaluationContext; + +/** + * @version $Revision: 1.5 $ + */ +public interface Subscription { + + /** + * Used to add messages that match the subscription. + * @param node + * @throws InterruptedException + * @throws IOException + */ + void add(MessageReference node) throws Throwable; + + /** + * Used when client acknowledge receipt of dispatched message. + * @param node + * @throws IOException + * @throws Throwable + */ + void acknowledge(ConnectionContext context, final MessageAck ack) throws Throwable; + + /** + * Is the subscription interested in the message? + * @param node + * @param context + * @return + */ + boolean matches(MessageReference node, MessageEvaluationContext context); + + /** + * Is the subscription interested in messages in the destination? + * @param context + * @return + */ + boolean matches(ActiveMQDestination destination); + + /** + * The subscription will be receiving messages from the destination. + * @param context + * @param destination + * @throws Throwable + */ + void add(ConnectionContext context, Destination destination) throws Throwable; + + /** + * The subscription will be no longer be receiving messages from the destination. + * @param context + * @param destination + */ + void remove(ConnectionContext context, Destination destination) throws Throwable; + + /** + * The ConsumerInfo object that created the subscription. + * @param destination + */ + ConsumerInfo getConsumerInfo(); + + /** + * The subscription should release as may references as it can to help the garbage collector + * reclaim memory. + */ + void gc(); + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/broker/region/TempQueueRegion.java b/activemq-core/src/main/java/org/activemq/broker/region/TempQueueRegion.java new file mode 100755 index 0000000000..2cb7e1f78e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/TempQueueRegion.java @@ -0,0 +1,70 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import javax.jms.InvalidSelectorException; +import javax.jms.JMSException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQTempDestination; +import org.activemq.command.ConsumerInfo; +import org.activemq.memory.UsageManager; +import org.activemq.thread.TaskRunnerFactory; + +/** + * + * @version $Revision: 1.7 $ + */ +public class TempQueueRegion extends AbstractRegion { + + public TempQueueRegion(DestinationStatistics destinationStatistics, UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory) { + super(destinationStatistics, memoryManager, taskRunnerFactory, null); + setAutoCreateDestinations(false); + } + + protected Destination createDestination(ActiveMQDestination destination) throws Throwable { + final ActiveMQTempDestination tempDest = (ActiveMQTempDestination) destination; + return new Queue(destination, memoryManager, null, destinationStatistics, taskRunnerFactory) { + + public void addSubscription(ConnectionContext context,Subscription sub) throws Throwable { + // Only consumers on the same connection can consume from + // the temporary destination + if( !tempDest.getConnectionId().equals( sub.getConsumerInfo().getConsumerId().getConnectionId() ) ) { + throw new JMSException("Cannot subscribe to remote temporary destination: "+tempDest); + } + super.addSubscription(context, sub); + }; + + }; + } + + protected Subscription createSubscription(ConnectionContext context, ConsumerInfo info) throws InvalidSelectorException { + if( info.isBrowser() ) { + return new QueueBrowserSubscription(context, info); + } else { + return new QueueSubscription(context, info); + } + } + + public String toString() { + return "TempQueueRegion: destinations="+destinations.size()+", subscriptions="+subscriptions.size()+", memory="+memoryManager.getPercentUsage()+"%"; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/TempTopicRegion.java b/activemq-core/src/main/java/org/activemq/broker/region/TempTopicRegion.java new file mode 100755 index 0000000000..01151537af --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/TempTopicRegion.java @@ -0,0 +1,70 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import javax.jms.JMSException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQTempDestination; +import org.activemq.command.ConsumerInfo; +import org.activemq.memory.UsageManager; +import org.activemq.thread.TaskRunnerFactory; + +/** + * + * @version $Revision: 1.7 $ + */ +public class TempTopicRegion extends AbstractRegion { + + public TempTopicRegion(DestinationStatistics destinationStatistics, UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory) { + super(destinationStatistics, memoryManager, taskRunnerFactory, null); + setAutoCreateDestinations(false); + } + + protected Destination createDestination(ActiveMQDestination destination) throws Throwable { + final ActiveMQTempDestination tempDest = (ActiveMQTempDestination) destination; + return new Topic(destination, null, memoryManager, destinationStatistics, taskRunnerFactory) { + + public void addSubscription(ConnectionContext context,Subscription sub) throws Throwable { + // Only consumers on the same connection can consume from + // the temporary destination + if( !tempDest.getConnectionId().equals( sub.getConsumerInfo().getConsumerId().getConnectionId() ) ) { + throw new JMSException("Cannot subscribe to remote temporary destination: "+tempDest); + } + super.addSubscription(context, sub); + }; + + }; + } + + protected Subscription createSubscription(ConnectionContext context, ConsumerInfo info) throws JMSException { + if( info.isDurable() ) { + throw new JMSException("A durable subscription cannot be created for a temporary topic."); + } else { + return new TopicSubscription(context, info, this.memoryManager); + } + } + + public String toString() { + return "TempTopicRegion: destinations="+destinations.size()+", subscriptions="+subscriptions.size()+", memory="+memoryManager.getPercentUsage()+"%"; + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/Topic.java b/activemq-core/src/main/java/org/activemq/broker/region/Topic.java new file mode 100755 index 0000000000..1bcf0d1b33 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/Topic.java @@ -0,0 +1,295 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.region; + +import java.io.IOException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.policy.DispatchPolicy; +import org.activemq.broker.region.policy.LastImageSubscriptionRecoveryPolicy; +import org.activemq.broker.region.policy.SimpleDispatchPolicy; +import org.activemq.broker.region.policy.SubscriptionRecoveryPolicy; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageId; +import org.activemq.command.SubscriptionInfo; +import org.activemq.filter.MessageEvaluationContext; +import org.activemq.memory.UsageManager; +import org.activemq.store.MessageRecoveryListener; +import org.activemq.store.MessageStore; +import org.activemq.store.TopicMessageStore; +import org.activemq.thread.TaskRunnerFactory; +import org.activemq.thread.Valve; +import org.activemq.transaction.Synchronization; +import org.activemq.util.SubscriptionKey; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + +/** + * The Topic is a destination that sends a copy of a message to every active + * Subscription registered. + * + * @version $Revision: 1.21 $ + */ +public class Topic implements Destination { + + protected final ActiveMQDestination destination; + protected final CopyOnWriteArrayList consumers = new CopyOnWriteArrayList(); + protected final Valve dispatchValve = new Valve(true); + protected final TopicMessageStore store; + protected final UsageManager usageManager; + protected final DestinationStatistics destinationStatistics = new DestinationStatistics(); + + private DispatchPolicy dispatchPolicy = new SimpleDispatchPolicy(); + private SubscriptionRecoveryPolicy subscriptionRecoveryPolicy = new LastImageSubscriptionRecoveryPolicy(); + + public Topic(ActiveMQDestination destination, TopicMessageStore store, UsageManager memoryManager, + DestinationStatistics parentStats, TaskRunnerFactory taskFactory) { + + this.destination = destination; + this.store = store; + this.usageManager = memoryManager; + + // TODO: switch back when cache is working again. + // this.cache = cache; + // destinationStatistics.setMessagesCached(cache.getMessagesCached()); + // CacheEvictionUsageListener listener = new + // CacheEvictionUsageListener(memoryManager, 90, 50, taskFactory); + // listener.add(cache); + // this.memoryManager.addUsageListener(listener); + + this.destinationStatistics.setParent(parentStats); + } + + public boolean lock(MessageReference node, Subscription sub) { + return true; + } + + public void addSubscription(ConnectionContext context, final Subscription sub) throws Throwable { + destinationStatistics.getConsumers().increment(); + sub.add(context, this); + if (sub.getConsumerInfo().isDurable()) { + recover((DurableTopicSubscription) sub, true); + } + else { + if (sub.getConsumerInfo().isRetroactive()) { + subscriptionRecoveryPolicy.recover(context, sub); + } + consumers.add(sub); + } + } + + public void recover(final DurableTopicSubscription sub, boolean initialActivation) throws Throwable { + + // synchronize with dispatch method so that no new messages are sent + // while + // we are recovering a subscription to avoid out of order messages. + dispatchValve.turnOff(); + try { + + if (initialActivation) + consumers.add(sub); + + if (store != null) { + String clientId = sub.getClientId(); + String subscriptionName = sub.getSubscriptionName(); + String selector = sub.getConsumerInfo().getSelector(); + SubscriptionInfo info = store.lookupSubscription(clientId, subscriptionName); + if (info != null) { + // Check to see if selector changed. + String s1 = info.getSelector(); + if (s1 == null ^ selector == null || (s1 != null && !s1.equals(selector))) { + // Need to delete the subscription + store.deleteSubscription(clientId, subscriptionName); + info = null; + } + } + // Do we need to crate the subscription? + if (info == null) { + store.addSubsciption(clientId, subscriptionName, selector, sub.getConsumerInfo().isRetroactive()); + } + + if (sub.isRecovered()) { + final MessageEvaluationContext msgContext = new MessageEvaluationContext(); + msgContext.setDestination(destination); + store.recoverSubscription(clientId, subscriptionName, new MessageRecoveryListener() { + public void recoverMessage(Message message) throws Throwable { + message.setRegionDestination(Topic.this); + try { + msgContext.setMessageReference(message); + if (sub.matches(message, msgContext)) { + sub.add(message); + } + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + catch (IOException e) { + // TODO: Need to handle this better. + e.printStackTrace(); + } + } + + public void recoverMessageReference(String messageReference) throws Throwable { + throw new RuntimeException("Should not be called."); + } + }); + } + } + + } + finally { + dispatchValve.turnOn(); + } + } + + public void removeSubscription(ConnectionContext context, Subscription sub) throws Throwable { + destinationStatistics.getConsumers().decrement(); + consumers.remove(sub); + sub.remove(context, this); + } + + public void send(final ConnectionContext context, final Message message) throws Throwable { + + if( context.isProducerFlowControl() ) + usageManager.waitForSpace(); + + message.setRegionDestination(this); + + if (store != null && message.isPersistent()) + store.addMessage(context, message); + + message.incrementReferenceCount(); + try { + + if (context.isInTransaction()) { + context.getTransaction().addSynchronization(new Synchronization() { + public void afterCommit() throws Throwable { + dispatch(context, message); + } + }); + + } + else { + dispatch(context, message); + } + + } finally { + message.decrementReferenceCount(); + } + + } + + public void deleteSubscription(ConnectionContext context, SubscriptionKey key) throws IOException { + if (store != null) { + store.deleteSubscription(key.clientId, key.subscriptionName); + } + } + + public String toString() { + return "Topic: destination=" + destination.getPhysicalName() + ", subscriptions=" + consumers.size(); + } + + public void acknowledge(ConnectionContext context, Subscription sub, final MessageAck ack, final MessageReference node) throws IOException { + if (store != null && node.isPersistent()) { + DurableTopicSubscription dsub = (DurableTopicSubscription) sub; + store.acknowledge(context, dsub.getClientId(), dsub.getSubscriptionName(), ack.getLastMessageId()); + } + } + + public void dispose(ConnectionContext context) throws IOException { + if (store != null) { + store.removeAllMessages(context); + } + destinationStatistics.setParent(null); + } + + public void gc() { + } + + public Message loadMessage(MessageId messageId) throws IOException { + return store.getMessage(messageId); + } + + public void start() throws Exception { + this.subscriptionRecoveryPolicy.start(); + } + + public void stop() throws Exception { + this.subscriptionRecoveryPolicy.stop(); + } + + // Properties + // ------------------------------------------------------------------------- + + public UsageManager getUsageManager() { + return usageManager; + } + + public DestinationStatistics getDestinationStatistics() { + return destinationStatistics; + } + + public ActiveMQDestination getActiveMQDestination() { + return destination; + } + + public DispatchPolicy getDispatchPolicy() { + return dispatchPolicy; + } + + public void setDispatchPolicy(DispatchPolicy dispatchPolicy) { + this.dispatchPolicy = dispatchPolicy; + } + + public SubscriptionRecoveryPolicy getSubscriptionRecoveryPolicy() { + return subscriptionRecoveryPolicy; + } + + public void setSubscriptionRecoveryPolicy(SubscriptionRecoveryPolicy subscriptionRecoveryPolicy) { + this.subscriptionRecoveryPolicy = subscriptionRecoveryPolicy; + } + + protected void dispatch(ConnectionContext context, Message message) throws Throwable { + destinationStatistics.getEnqueues().increment(); + dispatchValve.increment(); + MessageEvaluationContext msgContext = context.getMessageEvaluationContext(); + try { + + subscriptionRecoveryPolicy.add(context, message); + if (consumers.isEmpty()) + return; + + msgContext.setDestination(destination); + msgContext.setMessageReference(message); + + dispatchPolicy.dispatch(context, message, msgContext, consumers); + } + finally { + msgContext.clear(); + dispatchValve.decrement(); + } + } + + public MessageStore getMessageStore() { + return store; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/TopicRegion.java b/activemq-core/src/main/java/org/activemq/broker/region/TopicRegion.java new file mode 100755 index 0000000000..b98c7aa288 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/TopicRegion.java @@ -0,0 +1,191 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.region; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.policy.PolicyEntry; +import org.activemq.broker.region.policy.PolicyMap; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.RemoveSubscriptionInfo; +import org.activemq.memory.UsageManager; +import org.activemq.store.PersistenceAdapter; +import org.activemq.store.TopicMessageStore; +import org.activemq.thread.TaskRunnerFactory; +import org.activemq.util.SubscriptionKey; + +import javax.jms.InvalidDestinationException; +import javax.jms.JMSException; + +import java.util.Iterator; +import java.util.Set; + +/** + * + * @version $Revision: 1.12 $ + */ +public class TopicRegion extends AbstractRegion { + + protected final ConcurrentHashMap durableSubscriptions = new ConcurrentHashMap(); + private final PolicyMap policyMap; + + public TopicRegion(DestinationStatistics destinationStatistics, UsageManager memoryManager, TaskRunnerFactory taskRunnerFactory, + PersistenceAdapter persistenceAdapter, PolicyMap policyMap) { + super(destinationStatistics, memoryManager, taskRunnerFactory, persistenceAdapter); + this.policyMap = policyMap; + } + + public void addConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + if (info.isDurable()) { + SubscriptionKey key = new SubscriptionKey(context.getClientId(), info.getSubcriptionName()); + DurableTopicSubscription sub = (DurableTopicSubscription) durableSubscriptions.get(key); + if (sub != null) { + + if (sub.isActive()) { + throw new JMSException("Durable consumer is in use"); + } + + // Has the selector changed?? + if (hasDurableSubChanged(info, sub.getConsumerInfo())) { + + // Remove the consumer first then add it. + durableSubscriptions.remove(key); + for (Iterator iter = destinations.values().iterator(); iter.hasNext();) { + Topic topic = (Topic) iter.next(); + topic.deleteSubscription(context, key); + } + super.removeConsumer(context, sub.getConsumerInfo()); + + super.addConsumer(context, info); + + } + else { + // Change the consumer id key of the durable sub. + subscriptions.remove(sub.getConsumerInfo().getConsumerId()); + subscriptions.put(info.getConsumerId(), sub); + sub.activate(context, info); + } + } + else { + super.addConsumer(context, info); + } + } + else { + super.addConsumer(context, info); + } + } + + public void removeConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + if (info.isDurable()) { + + SubscriptionKey key = new SubscriptionKey(context.getClientId(), info.getSubcriptionName()); + DurableTopicSubscription sub = (DurableTopicSubscription) durableSubscriptions.get(key); + if (sub != null) { + sub.deactivate(); + } + + } + else { + super.removeConsumer(context, info); + } + } + + public void removeSubscription(ConnectionContext context, RemoveSubscriptionInfo info) throws Throwable { + SubscriptionKey key = new SubscriptionKey(info.getClientId(), info.getSubcriptionName()); + DurableTopicSubscription sub = (DurableTopicSubscription) durableSubscriptions.get(key); + if (sub == null) { + throw new InvalidDestinationException("No durable subscription exists for: " + info.getSubcriptionName()); + } + if (sub.isActive()) { + throw new JMSException("Durable consumer is in use"); + } + + durableSubscriptions.remove(key); + for (Iterator iter = destinations.values().iterator(); iter.hasNext();) { + Topic topic = (Topic) iter.next(); + topic.deleteSubscription(context, key); + } + super.removeConsumer(context, sub.getConsumerInfo()); + } + + public String toString() { + return "TopicRegion: destinations=" + destinations.size() + ", subscriptions=" + subscriptions.size() + ", memory=" + memoryManager.getPercentUsage() + + "%"; + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected Destination createDestination(ActiveMQDestination destination) throws Throwable { + TopicMessageStore store = persistenceAdapter.createTopicMessageStore((ActiveMQTopic) destination); + Topic topic = new Topic(destination, store, memoryManager, destinationStatistics, taskRunnerFactory); + configureTopic(topic, destination); + return topic; + } + + protected void configureTopic(Topic topic, ActiveMQDestination destination) { + if (policyMap != null) { + PolicyEntry entry = policyMap.getEntryFor(destination); + if (entry != null) { + entry.configure(topic); + } + } + } + + protected Subscription createSubscription(ConnectionContext context, ConsumerInfo info) throws JMSException { + if (info.isDurable()) { + SubscriptionKey key = new SubscriptionKey(context.getClientId(), info.getSubcriptionName()); + DurableTopicSubscription sub = (DurableTopicSubscription) durableSubscriptions.get(key); + if (sub == null) { + sub = new DurableTopicSubscription(context, info); + durableSubscriptions.put(key, sub); + } + else { + throw new JMSException("That durable subscription is already active."); + } + return sub; + } + else { + return new TopicSubscription(context, info, memoryManager); + } + } + + /** + */ + private boolean hasDurableSubChanged(ConsumerInfo info1, ConsumerInfo info2) { + if (info1.getSelector() != null ^ info2.getSelector() != null) + return true; + if (info1.getSelector() != null && !info1.getSelector().equals(info2.getSelector())) + return true; + return !info1.getDestination().equals(info2.getDestination()); + } + + protected Set getInactiveDestinations() { + Set inactiveDestinations = super.getInactiveDestinations(); + for (Iterator iter = inactiveDestinations.iterator(); iter.hasNext();) { + ActiveMQDestination dest = (ActiveMQDestination) iter.next(); + if (!dest.isTopic()) + iter.remove(); + } + return inactiveDestinations; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/TopicSubscription.java b/activemq-core/src/main/java/org/activemq/broker/region/TopicSubscription.java new file mode 100755 index 0000000000..25360b4166 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/TopicSubscription.java @@ -0,0 +1,143 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region; + +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedList; + +import javax.jms.InvalidSelectorException; +import javax.jms.JMSException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageDispatch; +import org.activemq.memory.UsageManager; +import org.activemq.transaction.Synchronization; + +public class TopicSubscription extends AbstractSubscription { + + final protected LinkedList matched = new LinkedList(); + final protected ActiveMQDestination dlqDestination = new ActiveMQQueue("ActiveMQ.DLQ"); + final protected UsageManager usageManager; + protected int dispatched=0; + protected int delivered=0; + + public TopicSubscription(ConnectionContext context, ConsumerInfo info, UsageManager usageManager) throws InvalidSelectorException { + super(context, info); + this.usageManager=usageManager; + } + + public void add(MessageReference node) throws InterruptedException, IOException { + node.incrementReferenceCount(); + if( !isFull() ) { + dispatch(node); + } else { + matched.addLast(node); + } + } + + public void acknowledge(final ConnectionContext context, final MessageAck ack) throws Throwable { + + // Handle the standard acknowledgment case. + boolean wasFull = isFull(); + if( ack.isStandardAck() || ack.isPoisonAck() ) { + if ( context.isInTransaction() ) { + delivered += ack.getMessageCount(); + context.getTransaction().addSynchronization(new Synchronization(){ + public void afterCommit() throws Throwable { + synchronized(TopicSubscription.this) { + dispatched -= ack.getMessageCount(); + delivered = Math.max(0, delivered - ack.getMessageCount()); + } + } + }); + } else { + dispatched -= ack.getMessageCount(); + delivered = Math.max(0, delivered - ack.getMessageCount()); + } + + if( wasFull && !isFull() ) { + dispatchMatched(); + } + return; + + } else if( ack.isDeliveredAck() ) { + // Message was delivered but not acknowledged: update pre-fetch counters. + delivered += ack.getMessageCount(); + if( wasFull && !isFull() ) { + dispatchMatched(); + } + return; + } + + throw new JMSException("Invalid acknowledgment: "+ack); + } + + private boolean isFull() { + return dispatched-delivered >= info.getPrefetchSize(); + } + + private void dispatchMatched() throws IOException { + for (Iterator iter = matched.iterator(); iter.hasNext() && !isFull();) { + MessageReference message = (MessageReference) iter.next(); + iter.remove(); + dispatch(message); + } + } + + private void dispatch(final MessageReference node) throws IOException { + + Message message = (Message) node; + + // Make sure we can dispatch a message. + MessageDispatch md = new MessageDispatch(); + md.setMessage(message); + md.setConsumerId( info.getConsumerId() ); + md.setDestination( node.getRegionDestination().getActiveMQDestination() ); + + dispatched++; + if( info.isDispatchAsync() ) { + md.setConsumer(new Runnable(){ + public void run() { + node.decrementReferenceCount(); + } + }); + context.getConnection().dispatchAsync(md); + } else { + context.getConnection().dispatchSync(md); + node.decrementReferenceCount(); + } + } + + public String toString() { + return + "TopicSubscription:" + + " consumer="+info.getConsumerId()+ + ", destinations="+destinations.size()+ + ", dispatched="+dispatched+ + ", delivered="+this.delivered+ + ", matched="+this.matched.size(); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/package.html b/activemq-core/src/main/java/org/activemq/broker/region/package.html new file mode 100755 index 0000000000..b5596fcfc6 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/package.html @@ -0,0 +1,9 @@ + + + + + + Region abstraction and implementations in the Broker. + + + diff --git a/activemq-core/src/main/java/org/activemq/broker/region/policy/DispatchPolicy.java b/activemq-core/src/main/java/org/activemq/broker/region/policy/DispatchPolicy.java new file mode 100755 index 0000000000..6f027dd05d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/policy/DispatchPolicy.java @@ -0,0 +1,49 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region.policy; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.MessageReference; +import org.activemq.filter.MessageEvaluationContext; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + +/** + * Abstraction to allow different dispatching policies to be plugged + * into the region implementations. This is used by a queue to deliver + * messages to the matching subscriptions. + * + * @version $Revision$ + */ +public interface DispatchPolicy { + + /** + * Decides how to dispatch a selected message to a collection of consumers. A safe + * approach is to dispatch to every subscription that matches. Queue Subscriptions that + * have not exceeded their pre-fetch limit will attempt to lock the message before + * dispatching to the client. First subscription to lock the message wins. + * + * Order of dispatching to the subscriptions matters since a subscription with a + * large pre-fetch may take all the messages if he is always dispatched to first. + * Once a message has been locked, it does not need to be dispatched to any + * further subscriptions. + */ + void dispatch(ConnectionContext newParam, MessageReference node, MessageEvaluationContext msgContext, CopyOnWriteArrayList consumers) throws Throwable; + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/policy/FixedSizedSubscriptionRecoveryPolicy.java b/activemq-core/src/main/java/org/activemq/broker/region/policy/FixedSizedSubscriptionRecoveryPolicy.java new file mode 100644 index 0000000000..205ab7dfad --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/policy/FixedSizedSubscriptionRecoveryPolicy.java @@ -0,0 +1,119 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.region.policy; + +import java.util.Iterator; +import java.util.List; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.MessageReference; +import org.activemq.broker.region.Subscription; +import org.activemq.filter.MessageEvaluationContext; +import org.activemq.memory.list.DestinationBasedMessageList; +import org.activemq.memory.list.MessageList; +import org.activemq.memory.list.SimpleMessageList; + +/** + * This implementation of {@link SubscriptionRecoveryPolicy} will keep a fixed + * amount of memory available in RAM for message history which is evicted in + * time order. + * + * @org.xbean.XBean + * + * @version $Revision$ + */ +public class FixedSizedSubscriptionRecoveryPolicy implements SubscriptionRecoveryPolicy { + + private MessageList buffer; + private int maximumSize = 100 * 64 * 1024; + private boolean useSharedBuffer = true; + + public void add(ConnectionContext context, MessageReference message) throws Throwable { + buffer.add(message); + } + + public void recover(ConnectionContext context, Subscription sub) throws Throwable { + // Re-dispatch the messages from the buffer. + List copy = buffer.getMessages(sub); + if( !copy.isEmpty() ) { + MessageEvaluationContext msgContext = context.getMessageEvaluationContext(); + try { + for (Iterator iter = copy.iterator(); iter.hasNext();) { + MessageReference node = (MessageReference) iter.next(); + msgContext.setDestination(node.getRegionDestination().getActiveMQDestination()); + msgContext.setMessageReference(node); + if (sub.matches(node, msgContext) ) { + sub.add(node); + } + } + } finally { + msgContext.clear(); + } + } + } + + public void start() throws Exception { + buffer = createMessageList(); + } + + public void stop() throws Exception { + buffer.clear(); + } + + // Properties + // ------------------------------------------------------------------------- + public MessageList getBuffer() { + return buffer; + } + + public void setBuffer(MessageList buffer) { + this.buffer = buffer; + } + + public int getMaximumSize() { + return maximumSize; + } + + /** + * Sets the maximum amount of RAM in bytes that this buffer can hold in RAM + */ + public void setMaximumSize(int maximumSize) { + this.maximumSize = maximumSize; + } + + public boolean isUseSharedBuffer() { + return useSharedBuffer; + } + + public void setUseSharedBuffer(boolean useSharedBuffer) { + this.useSharedBuffer = useSharedBuffer; + } + + // Implementation methods + + // ------------------------------------------------------------------------- + protected MessageList createMessageList() { + if (useSharedBuffer) { + return new SimpleMessageList(maximumSize); + } + else { + return new DestinationBasedMessageList(maximumSize); + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/policy/LastImageSubscriptionRecoveryPolicy.java b/activemq-core/src/main/java/org/activemq/broker/region/policy/LastImageSubscriptionRecoveryPolicy.java new file mode 100644 index 0000000000..d70754ec0b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/policy/LastImageSubscriptionRecoveryPolicy.java @@ -0,0 +1,65 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region.policy; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.MessageReference; +import org.activemq.broker.region.Subscription; +import org.activemq.filter.MessageEvaluationContext; + +/** + * This implementation of {@link SubscriptionRecoveryPolicy} will only keep + * the last message. + * + * @org.xbean.XBean + * + * @version $Revision$ + */ +public class LastImageSubscriptionRecoveryPolicy implements SubscriptionRecoveryPolicy { + + volatile private MessageReference lastImage; + + public void add(ConnectionContext context, MessageReference node) throws Throwable { + lastImage = node; + } + + public void recover(ConnectionContext context, Subscription sub) throws Throwable { + // Re-dispatch the last message seen. + MessageReference node = lastImage; + if( node != null ){ + MessageEvaluationContext msgContext = context.getMessageEvaluationContext(); + try { + msgContext.setDestination(node.getRegionDestination().getActiveMQDestination()); + msgContext.setMessageReference(node); + if (sub.matches(node, msgContext)) { + sub.add(node); + } + } finally { + msgContext.clear(); + } + } + } + + public void start() throws Exception { + } + + public void stop() throws Exception { + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/policy/NoSubscriptionRecoveryPolicy.java b/activemq-core/src/main/java/org/activemq/broker/region/policy/NoSubscriptionRecoveryPolicy.java new file mode 100644 index 0000000000..3c8201dd99 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/policy/NoSubscriptionRecoveryPolicy.java @@ -0,0 +1,46 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region.policy; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.MessageReference; +import org.activemq.broker.region.Subscription; + +/** + * This is the default Topic recovery policy which does not recover any messages. + * + * @org.xbean.XBean + * + * @version $Revision$ + */ +public class NoSubscriptionRecoveryPolicy implements SubscriptionRecoveryPolicy { + + public void add(ConnectionContext context, MessageReference node) throws Throwable { + } + + public void recover(ConnectionContext context, Subscription sub) throws Throwable { + } + + public void start() throws Exception { + } + + public void stop() throws Exception { + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/policy/PolicyEntry.java b/activemq-core/src/main/java/org/activemq/broker/region/policy/PolicyEntry.java new file mode 100644 index 0000000000..4fabfb5fe3 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/policy/PolicyEntry.java @@ -0,0 +1,80 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.region.policy; + +import org.activemq.broker.region.Queue; +import org.activemq.broker.region.Topic; +import org.activemq.command.RedeliveryPolicy; +import org.activemq.filter.DestinationMapEntry; + +/** + * Represents an entry in a {@link PolicyMap} for assigning policies to a + * specific destination or a hierarchial wildcard area of destinations. + * + * @org.xbean.XBean + * + * @version $Revision: 1.1 $ + */ +public class PolicyEntry extends DestinationMapEntry { + + private DispatchPolicy dispatchPolicy; + private SubscriptionRecoveryPolicy subscriptionRecoveryPolicy; + private RedeliveryPolicy redeliveryPolicy; + + public void configure(Queue queue) { + if (dispatchPolicy != null) { + queue.setDispatchPolicy(dispatchPolicy); + } + } + + public void configure(Topic topic) { + if (dispatchPolicy != null) { + topic.setDispatchPolicy(dispatchPolicy); + } + if (subscriptionRecoveryPolicy != null) { + topic.setSubscriptionRecoveryPolicy(subscriptionRecoveryPolicy); + } + } + + // Properties + // ------------------------------------------------------------------------- + public DispatchPolicy getDispatchPolicy() { + return dispatchPolicy; + } + + public void setDispatchPolicy(DispatchPolicy policy) { + this.dispatchPolicy = policy; + } + + public RedeliveryPolicy getRedeliveryPolicy() { + return redeliveryPolicy; + } + + public void setRedeliveryPolicy(RedeliveryPolicy redeliveryPolicy) { + this.redeliveryPolicy = redeliveryPolicy; + } + + public SubscriptionRecoveryPolicy getSubscriptionRecoveryPolicy() { + return subscriptionRecoveryPolicy; + } + + public void setSubscriptionRecoveryPolicy(SubscriptionRecoveryPolicy subscriptionRecoveryPolicy) { + this.subscriptionRecoveryPolicy = subscriptionRecoveryPolicy; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/policy/PolicyMap.java b/activemq-core/src/main/java/org/activemq/broker/region/policy/PolicyMap.java new file mode 100644 index 0000000000..5921ebd3bf --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/policy/PolicyMap.java @@ -0,0 +1,66 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.region.policy; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.filter.DestinationMap; + +import java.util.List; + +/** + * Represents a destination based configuration of policies so that individual + * destinations or wildcard hierarchies of destinations can be configured using + * different policies. + * + * @org.xbean.XBean + * + * @version $Revision: 1.1 $ + */ +public class PolicyMap extends DestinationMap { + + private PolicyEntry defaultEntry; + + public PolicyEntry getEntryFor(ActiveMQDestination destination) { + PolicyEntry answer = (PolicyEntry) chooseValue(destination); + if (answer == null) { + answer = getDefaultEntry(); + } + return answer; + } + + /** + * Sets the individual entries on the policy map + * + * @org.xbean.ElementType class="org.activemq.broker.region.policy.PolicyEntry" + */ + public void setPolicyEntries(List entries) { + super.setEntries(entries); + } + + public PolicyEntry getDefaultEntry() { + return defaultEntry; + } + + public void setDefaultEntry(PolicyEntry defaultEntry) { + this.defaultEntry = defaultEntry; + } + + protected Class getEntryClass() { + return PolicyEntry.class; + } +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/policy/RoundRobinDispatchPolicy.java b/activemq-core/src/main/java/org/activemq/broker/region/policy/RoundRobinDispatchPolicy.java new file mode 100755 index 0000000000..c11731efe8 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/policy/RoundRobinDispatchPolicy.java @@ -0,0 +1,68 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region.policy; + +import java.util.Iterator; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.MessageReference; +import org.activemq.broker.region.Subscription; +import org.activemq.filter.MessageEvaluationContext; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + +/** + * Simple dispatch policy that sends a message to every subscription that + * matches the message. + * + * @org.xbean.XBean + * + * @version $Revision$ + */ +public class RoundRobinDispatchPolicy implements DispatchPolicy { + + private final Object mutex = new Object(); + + public void dispatch(ConnectionContext newParam, MessageReference node, MessageEvaluationContext msgContext, CopyOnWriteArrayList consumers) throws Throwable { + + // Big synch here so that only 1 message gets dispatched at a time. Ensures + // Everyone sees the same order and that the consumer list is not used while + // it's being rotated. + synchronized(mutex) { + + for (Iterator iter = consumers.iterator(); iter.hasNext();) { + Subscription sub = (Subscription) iter.next(); + + // Only dispatch to interested subscriptions + if (!sub.matches(node, msgContext)) + continue; + + sub.add(node); + } + + // Rotate the consumer list. + try { + consumers.add(consumers.remove(0)); + } catch (Throwable bestEffort) { + } + } + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/policy/SimpleDispatchPolicy.java b/activemq-core/src/main/java/org/activemq/broker/region/policy/SimpleDispatchPolicy.java new file mode 100755 index 0000000000..75cb3db3f6 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/policy/SimpleDispatchPolicy.java @@ -0,0 +1,56 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region.policy; + +import java.util.Iterator; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.MessageReference; +import org.activemq.broker.region.Subscription; +import org.activemq.filter.MessageEvaluationContext; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + +/** + * Simple dispatch policy that sends a message to every subscription that + * matches the message. + * + * @org.xbean.XBean + * + * @version $Revision$ + */ +public class SimpleDispatchPolicy implements DispatchPolicy { + + public void dispatch(ConnectionContext newParam, MessageReference node, MessageEvaluationContext msgContext, CopyOnWriteArrayList consumers) throws Throwable { + + for (Iterator iter = consumers.iterator(); iter.hasNext();) { + Subscription sub = (Subscription) iter.next(); + + // Don't deliver to browsers + if( sub.getConsumerInfo().isBrowser() ) + continue; + // Only dispatch to interested subscriptions + if (!sub.matches(node, msgContext)) + continue; + + sub.add(node); + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/policy/StrictOrderDispatchPolicy.java b/activemq-core/src/main/java/org/activemq/broker/region/policy/StrictOrderDispatchPolicy.java new file mode 100755 index 0000000000..8673b3401b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/policy/StrictOrderDispatchPolicy.java @@ -0,0 +1,59 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region.policy; + +import java.util.Iterator; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.MessageReference; +import org.activemq.broker.region.Subscription; +import org.activemq.filter.MessageEvaluationContext; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + +/** + * Dispatch policy that causes every subscription to see messages in the same order. + * + * @org.xbean.XBean + * + * @version $Revision$ + */ +public class StrictOrderDispatchPolicy implements DispatchPolicy { + int i=0; + private final Object mutex = new Object(); + + public void dispatch(ConnectionContext newParam, MessageReference node, MessageEvaluationContext msgContext, CopyOnWriteArrayList consumers) throws Throwable { + + // Big synch here so that only 1 message gets dispatched at a time. Ensures + // Everyone sees the same order. + synchronized(mutex) { + i++; + for (Iterator iter = consumers.iterator(); iter.hasNext();) { + Subscription sub = (Subscription) iter.next(); + + // Only dispatch to interested subscriptions + if (!sub.matches(node, msgContext)) + continue; + + sub.add(node); + } + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/policy/SubscriptionRecoveryPolicy.java b/activemq-core/src/main/java/org/activemq/broker/region/policy/SubscriptionRecoveryPolicy.java new file mode 100644 index 0000000000..77554f131a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/policy/SubscriptionRecoveryPolicy.java @@ -0,0 +1,53 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.region.policy; + +import org.activemq.Service; +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.MessageReference; +import org.activemq.broker.region.Subscription; + +/** + * Abstraction to allow different recovery policies to be plugged + * into the region implementations. This is used by a topic to retroactively recover + * messages that the subscription missed. + * + * @version $Revision$ + */ +public interface SubscriptionRecoveryPolicy extends Service { + + /** + * A message was sent to the destination. + * + * @param context + * @param node + * @throws Throwable + */ + void add(ConnectionContext context, MessageReference message) throws Throwable; + + /** + * Let a subscription recover message held by the policy. + * + * @param context + * @param node + * @throws Throwable + */ + void recover(ConnectionContext context, Subscription sub) throws Throwable; + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/policy/TimedSubscriptionRecoveryPolicy.java b/activemq-core/src/main/java/org/activemq/broker/region/policy/TimedSubscriptionRecoveryPolicy.java new file mode 100644 index 0000000000..7e64d11da1 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/policy/TimedSubscriptionRecoveryPolicy.java @@ -0,0 +1,126 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker.region.policy; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.MessageReference; +import org.activemq.broker.region.Subscription; +import org.activemq.filter.MessageEvaluationContext; +import org.activemq.thread.Scheduler; + +/** + * This implementation of {@link SubscriptionRecoveryPolicy} will keep a timed + * buffer of messages around in memory and use that to recover new + * subscriptions. + * + * @org.xbean.XBean + * + * @version $Revision$ + */ +public class TimedSubscriptionRecoveryPolicy implements SubscriptionRecoveryPolicy { + + private static final int GC_INTERVAL = 1000; + + // TODO: need to get a better synchronized linked list that has little + // contention between enqueuing and dequeuing + private final List buffer = Collections.synchronizedList(new LinkedList()); + private volatile long lastGCRun = System.currentTimeMillis(); + + private long recoverDuration = 60 * 1000; // Buffer for 1 min. + + static class TimestampWrapper { + public MessageReference message; + public long timestamp; + + public TimestampWrapper(MessageReference message, long timestamp) { + this.message = message; + this.timestamp = timestamp; + } + } + + private final Runnable gcTask = new Runnable() { + public void run() { + gc(); + } + }; + + public void add(ConnectionContext context, MessageReference message) throws Throwable { + buffer.add(new TimestampWrapper(message, lastGCRun)); + } + + public void recover(ConnectionContext context, Subscription sub) throws Throwable { + + // Re-dispatch the messages from the buffer. + ArrayList copy = new ArrayList(buffer); + + if (!copy.isEmpty()) { + MessageEvaluationContext msgContext = context.getMessageEvaluationContext(); + try { + for (Iterator iter = copy.iterator(); iter.hasNext();) { + TimestampWrapper timestampWrapper = (TimestampWrapper) iter.next(); + MessageReference message = timestampWrapper.message; + msgContext.setDestination(message.getRegionDestination().getActiveMQDestination()); + msgContext.setMessageReference(message); + if (sub.matches(message, msgContext)) { + sub.add(timestampWrapper.message); + } + } + }finally { + msgContext.clear(); + } + } + } + + public void start() throws Exception { + Scheduler.executePeriodically(gcTask, GC_INTERVAL); + } + + public void stop() throws Exception { + Scheduler.cancel(gcTask); + } + + public void gc() { + lastGCRun = System.currentTimeMillis(); + while (buffer.size() > 0) { + TimestampWrapper timestampWrapper = (TimestampWrapper) buffer.get(0); + if( lastGCRun > timestampWrapper.timestamp+recoverDuration ) { + // GC it. + buffer.remove(0); + } + else { + break; + } + } + } + + public long getRecoverDuration() { + return recoverDuration; + } + + public void setRecoverDuration(long recoverDuration) { + this.recoverDuration = recoverDuration; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/broker/region/policy/package.html b/activemq-core/src/main/java/org/activemq/broker/region/policy/package.html new file mode 100755 index 0000000000..0f6ad485d4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/broker/region/policy/package.html @@ -0,0 +1,9 @@ + + + + + +The policies which can be associated with a particular destination or wildcard. + + + diff --git a/activemq-core/src/main/java/org/activemq/command/ActiveMQBytesMessage.java b/activemq-core/src/main/java/org/activemq/command/ActiveMQBytesMessage.java new file mode 100755 index 0000000000..d7d4e14f0d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ActiveMQBytesMessage.java @@ -0,0 +1,749 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +import javax.jms.BytesMessage; +import javax.jms.JMSException; +import javax.jms.MessageEOFException; +import javax.jms.MessageFormatException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; + +import org.activeio.ByteArrayInputStream; +import org.activeio.ByteArrayOutputStream; +import org.activeio.ByteSequence; +import org.activeio.PacketData; +import org.activeio.packet.ByteArrayPacket; +import org.activemq.ActiveMQConnection; +import org.activemq.util.JMSExceptionSupport; + +/** + * A BytesMessage object is used to send a message containing a stream of uninterpreted bytes. It inherits + * from the Message interface and adds a bytes message body. The receiver of the message supplies the + * interpretation of the bytes.

The BytesMessage methods are based largely on those found in + * java.io.DataInputStream and java.io.DataOutputStream.

This message type is for client + * encoding of existing message formats. If possible, one of the other self-defining message types should be used + * instead.

Although the JMS API allows the use of message properties with byte messages, they are typically not + * used, since the inclusion of properties may affect the format.

The primitive types can be written explicitly + * using methods for each type. They may also be written generically as objects. For instance, a call to + * BytesMessage.writeInt(6) is equivalent to BytesMessage.writeObject(new Integer(6)). Both + * forms are provided, because the explicit form is convenient for static programming, and the object form is needed + * when types are not known at compile time.

When the message is first created, and when clearBody is + * called, the body of the message is in write-only mode. After the first call to reset has been made, the + * message body is in read-only mode. After a message has been sent, the client that sent it can retain and modify it + * without affecting the message that has been sent. The same message object can be sent multiple times. When a message + * has been received, the provider has called reset so that the message body is in read-only mode for the + * client.

If clearBody is called on a message in read-only mode, the message body is cleared and the + * message is in write-only mode.

If a client attempts to read a message in write-only mode, a + * MessageNotReadableException is thrown.

If a client attempts to write a message in read-only mode, a + * MessageNotWriteableException is thrown. + * + * @openwire:marshaller + * @see javax.jms.Session#createBytesMessage() + * @see javax.jms.MapMessage + * @see javax.jms.Message + * @see javax.jms.ObjectMessage + * @see javax.jms.StreamMessage + * @see javax.jms.TextMessage + */ +public class ActiveMQBytesMessage extends ActiveMQMessage implements BytesMessage { + + public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_BYTES_MESSAGE; + + transient protected DataOutputStream dataOut; + transient protected ByteArrayOutputStream bytesOut; + transient protected DataInputStream dataIn; + transient protected int length; + + public Message copy() { + ActiveMQBytesMessage copy = new ActiveMQBytesMessage(); + copy(copy); + return copy; + } + + private void copy(ActiveMQBytesMessage copy) { + storeContent(); + super.copy(copy); + copy.dataOut = null; + copy.bytesOut = null; + copy.dataIn = null; + } + + public void onSend() { + super.onSend(); + storeContent(); + } + + private void storeContent() { + try { + if (dataOut != null) { + dataOut.close(); + ByteSequence bs = bytesOut.toByteSequence(); + if( compressed ) { + // Prefix the real length + ByteArrayPacket packet = new ByteArrayPacket(bs); + PacketData.writeIntBig(packet, length); + } + setContent(bs); + bytesOut = null; + dataOut = null; + } + } catch (IOException ioe) { + throw new RuntimeException(ioe.getMessage(), ioe); //TODO verify RuntimeException + } + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * Clears out the message body. Clearing a message's body does not clear its header values or property entries.

+ * If this message body was read-only, calling this method leaves the message body in the same state as an empty + * body in a newly created message. + * + * @throws JMSException if the JMS provider fails to clear the message body due to some internal error. + */ + public void clearBody() throws JMSException { + super.clearBody(); + this.dataOut = null; + this.dataIn = null; + this.bytesOut = null; + } + + /** + * Gets the number of bytes of the message body when the message is in read-only mode. The value returned can be + * used to allocate a byte array. The value returned is the entire length of the message body, regardless of where + * the pointer for reading the message is currently located. + * + * @return number of bytes in the message + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageNotReadableException if the message is in write-only mode. + * @since 1.1 + */ + + public long getBodyLength() throws JMSException { + initializeReading(); + return length; + } + + /** + * Reads a boolean from the bytes message stream. + * + * @return the boolean value read + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageEOFException if unexpected end of bytes stream has been reached. + * @throws MessageNotReadableException if the message is in write-only mode. + */ + public boolean readBoolean() throws JMSException { + initializeReading(); + try { + return this.dataIn.readBoolean(); + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a signed 8-bit value from the bytes message stream. + * + * @return the next byte from the bytes message stream as a signed 8-bit byte + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageEOFException if unexpected end of bytes stream has been reached. + * @throws MessageNotReadableException if the message is in write-only mode. + */ + public byte readByte() throws JMSException { + initializeReading(); + try { + return this.dataIn.readByte(); + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads an unsigned 8-bit number from the bytes message stream. + * + * @return the next byte from the bytes message stream, interpreted as an unsigned 8-bit number + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageEOFException if unexpected end of bytes stream has been reached. + * @throws MessageNotReadableException if the message is in write-only mode. + */ + public int readUnsignedByte() throws JMSException { + initializeReading(); + try { + return this.dataIn.readUnsignedByte(); + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a signed 16-bit number from the bytes message stream. + * + * @return the next two bytes from the bytes message stream, interpreted as a signed 16-bit number + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageEOFException if unexpected end of bytes stream has been reached. + * @throws MessageNotReadableException if the message is in write-only mode. + */ + public short readShort() throws JMSException { + initializeReading(); + try { + return this.dataIn.readShort(); + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads an unsigned 16-bit number from the bytes message stream. + * + * @return the next two bytes from the bytes message stream, interpreted as an unsigned 16-bit integer + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageEOFException if unexpected end of bytes stream has been reached. + * @throws MessageNotReadableException if the message is in write-only mode. + */ + public int readUnsignedShort() throws JMSException { + initializeReading(); + try { + return this.dataIn.readUnsignedShort(); + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a Unicode character value from the bytes message stream. + * + * @return the next two bytes from the bytes message stream as a Unicode character + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageEOFException if unexpected end of bytes stream has been reached. + * @throws MessageNotReadableException if the message is in write-only mode. + */ + public char readChar() throws JMSException { + initializeReading(); + try { + return this.dataIn.readChar(); + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a signed 32-bit integer from the bytes message stream. + * + * @return the next four bytes from the bytes message stream, interpreted as an int + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageEOFException if unexpected end of bytes stream has been reached. + * @throws MessageNotReadableException if the message is in write-only mode. + */ + public int readInt() throws JMSException { + initializeReading(); + try { + return this.dataIn.readInt(); + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a signed 64-bit integer from the bytes message stream. + * + * @return the next eight bytes from the bytes message stream, interpreted as a long + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageEOFException if unexpected end of bytes stream has been reached. + * @throws MessageNotReadableException if the message is in write-only mode. + */ + public long readLong() throws JMSException { + initializeReading(); + try { + return this.dataIn.readLong(); + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a float from the bytes message stream. + * + * @return the next four bytes from the bytes message stream, interpreted as a float + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageEOFException if unexpected end of bytes stream has been reached. + * @throws MessageNotReadableException if the message is in write-only mode. + */ + public float readFloat() throws JMSException { + initializeReading(); + try { + return this.dataIn.readFloat(); + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a double from the bytes message stream. + * + * @return the next eight bytes from the bytes message stream, interpreted as a double + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageEOFException if unexpected end of bytes stream has been reached. + * @throws MessageNotReadableException if the message is in write-only mode. + */ + public double readDouble() throws JMSException { + initializeReading(); + try { + return this.dataIn.readDouble(); + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a string that has been encoded using a modified UTF-8 format from the bytes message stream.

For more + * information on the UTF-8 format, see "File System Safe UCS Transformation Format (FSS_UTF)", X/Open Preliminary + * Specification, X/Open Company Ltd., Document Number: P316. This information also appears in ISO/IEC 10646, Annex + * P. + * + * @return a Unicode string from the bytes message stream + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageEOFException if unexpected end of bytes stream has been reached. + * @throws MessageNotReadableException if the message is in write-only mode. + */ + public String readUTF() throws JMSException { + initializeReading(); + try { + return this.dataIn.readUTF(); + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a byte array from the bytes message stream.

If the length of array value is less than the + * number of bytes remaining to be read from the stream, the array should be filled. A subsequent call reads the + * next increment, and so on.

If the number of bytes remaining in the stream is less than the length of array + * value, the bytes should be read into the array. The return value of the total number of bytes read + * will be less than the length of the array, indicating that there are no more bytes left to be read from the + * stream. The next read of the stream returns -1. + * + * @param value the buffer into which the data is read + * @return the total number of bytes read into the buffer, or -1 if there is no more data because the end of the + * stream has been reached + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageNotReadableException if the message is in write-only mode. + */ + public int readBytes(byte[] value) throws JMSException { + return readBytes(value, value.length); + } + + /** + * Reads a portion of the bytes message stream.

If the length of array value is less than the + * number of bytes remaining to be read from the stream, the array should be filled. A subsequent call reads the + * next increment, and so on.

If the number of bytes remaining in the stream is less than the length of array + * value, the bytes should be read into the array. The return value of the total number of bytes read + * will be less than the length of the array, indicating that there are no more bytes left to be read from the + * stream. The next read of the stream returns -1. + *

+ * If length is negative, or length is greater than the length of the array + * value, then an IndexOutOfBoundsException is thrown. No bytes will be read from the + * stream for this exception case. + * + * @param value the buffer into which the data is read + * @param length the number of bytes to read; must be less than or equal to value.length + * @return the total number of bytes read into the buffer, or -1 if there is no more data because the end of the + * stream has been reached + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageNotReadableException if the message is in write-only mode. + */ + public int readBytes(byte[] value, int length) throws JMSException { + initializeReading(); + try { + int n = 0; + while (n < length) { + int count = this.dataIn.read(value, n, length - n); + if (count < 0) { + break; + } + n += count; + } + if (n == 0 && length > 0) { + n = -1; + } + return n; + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Writes a boolean to the bytes message stream as a 1-byte value. The value true is + * written as the value (byte)1; the value false is written as the value + * (byte)0. + * + * @param value the boolean value to be written + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void writeBoolean(boolean value) throws JMSException { + initializeWriting(); + try { + this.dataOut.writeBoolean(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a byte to the bytes message stream as a 1-byte value. + * + * @param value the byte value to be written + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void writeByte(byte value) throws JMSException { + initializeWriting(); + try { + this.dataOut.writeByte(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a short to the bytes message stream as two bytes, high byte first. + * + * @param value the short to be written + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void writeShort(short value) throws JMSException { + initializeWriting(); + try { + this.dataOut.writeShort(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a char to the bytes message stream as a 2-byte value, high byte first. + * + * @param value the char value to be written + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void writeChar(char value) throws JMSException { + initializeWriting(); + try { + this.dataOut.writeChar(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes an int to the bytes message stream as four bytes, high byte first. + * + * @param value the int to be written + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void writeInt(int value) throws JMSException { + initializeWriting(); + try { + this.dataOut.writeInt(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a long to the bytes message stream as eight bytes, high byte first. + * + * @param value the long to be written + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void writeLong(long value) throws JMSException { + initializeWriting(); + try { + this.dataOut.writeLong(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Converts the float argument to an int using the floatToIntBits method in + * class Float, and then writes that int value to the bytes message stream as a 4-byte + * quantity, high byte first. + * + * @param value the float value to be written + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void writeFloat(float value) throws JMSException { + initializeWriting(); + try { + this.dataOut.writeFloat(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Converts the double argument to a long using the doubleToLongBits method + * in class Double, and then writes that long value to the bytes message stream as an + * 8-byte quantity, high byte first. + * + * @param value the double value to be written + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void writeDouble(double value) throws JMSException { + initializeWriting(); + try { + this.dataOut.writeDouble(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a string to the bytes message stream using UTF-8 encoding in a machine-independent manner.

For more + * information on the UTF-8 format, see "File System Safe UCS Transformation Format (FSS_UTF)", X/Open Preliminary + * Specification, X/Open Company Ltd., Document Number: P316. This information also appears in ISO/IEC 10646, Annex + * P. + * + * @param value the String value to be written + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void writeUTF(String value) throws JMSException { + initializeWriting(); + try { + this.dataOut.writeUTF(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a byte array to the bytes message stream. + * + * @param value the byte array to be written + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void writeBytes(byte[] value) throws JMSException { + initializeWriting(); + try { + this.dataOut.write(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a portion of a byte array to the bytes message stream. + * + * @param value the byte array value to be written + * @param offset the initial offset within the byte array + * @param length the number of bytes to use + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void writeBytes(byte[] value, int offset, int length) throws JMSException { + initializeWriting(); + try { + this.dataOut.write(value, offset, length); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes an object to the bytes message stream.

This method works only for the objectified primitive object + * types (Integer,Double, Long  ...), String objects, and + * byte arrays. + * + * @param value the object in the Java programming language ("Java object") to be written; it must not be null + * @throws JMSException if the JMS provider fails to write the message due to some internal + * error. + * @throws MessageFormatException if the object is of an invalid type. + * @throws MessageNotWriteableException if the message is in read-only mode. + * @throws java.lang.NullPointerException if the parameter value is null. + */ + public void writeObject(Object value) throws JMSException { + if (value == null) { + throw new NullPointerException(); + } + initializeWriting(); + if (value instanceof Boolean) { + writeBoolean(((Boolean) value).booleanValue()); + } else if (value instanceof Character) { + writeChar(((Character) value).charValue()); + } else if (value instanceof Byte) { + writeByte(((Byte) value).byteValue()); + } else if (value instanceof Short) { + writeShort(((Short) value).shortValue()); + } else if (value instanceof Integer) { + writeInt(((Integer) value).intValue()); + } else if (value instanceof Long) { + writeLong(((Long) value).longValue()); + } else if (value instanceof Float) { + writeFloat(((Float) value).floatValue()); + } else if (value instanceof Double) { + writeDouble(((Double) value).doubleValue()); + } else if (value instanceof String) { + writeUTF(value.toString()); + } else if (value instanceof byte[]) { + writeBytes((byte[]) value); + } else { + throw new MessageFormatException("Cannot write non-primitive type:" + value.getClass()); + } + } + + /** + * Puts the message body in read-only mode and repositions the stream of bytes to the beginning. + * + * @throws JMSException if an internal error occurs + */ + public void reset() throws JMSException { + storeContent(); + this.bytesOut = null; + this.dataIn = null; + this.dataOut = null; + setReadOnlyBody(true); + } + + private void initializeWriting() throws JMSException { + checkReadOnlyBody(); + if (this.dataOut == null) { + this.bytesOut = new ByteArrayOutputStream(); + OutputStream os = bytesOut; + ActiveMQConnection connection = getConnection(); + if( connection!=null && connection.isUseCompression() ) { + // keep track of the real length of the content if + // we are compressed. + try { + os.write(new byte[4]); + } catch (IOException e) { + throw JMSExceptionSupport.create(e); + } + length=0; + compressed = true; + Deflater deflater = new Deflater(Deflater.BEST_SPEED); + os = new FilterOutputStream(new DeflaterOutputStream(os,deflater)) { + public void write(byte[] arg0) throws IOException { + length+=arg0.length; + out.write(arg0); + } + public void write(byte[] arg0, int arg1, int arg2) throws IOException { + length+=arg2; + out.write(arg0, arg1, arg2); + } + public void write(int arg0) throws IOException { + length++; + out.write(arg0); + } + }; + } + this.dataOut = new DataOutputStream(os); + } + } + + protected void checkWriteOnlyBody() throws MessageNotReadableException { + if (!readOnlyBody) { + throw new MessageNotReadableException("Message body is write-only"); + } + } + + private void initializeReading() throws JMSException { + checkWriteOnlyBody(); + if (dataIn == null ) { + ByteSequence data = getContent(); + if( data==null ) + data = new ByteSequence(new byte[]{}, 0, 0); + InputStream is = new ByteArrayInputStream(data); + if( isCompressed() ) { + // keep track of the real length of the content if + // we are compressed. + try { + DataInputStream dis = new DataInputStream(is); + length = dis.readInt(); + dis.close(); + } catch (IOException e) { + throw JMSExceptionSupport.create(e); + } + is = new InflaterInputStream(is); + } else { + length = data.getLength(); + } + dataIn = new DataInputStream(is); + } + } + + public void setObjectProperty(String name, Object value) throws JMSException { + initializeWriting(); + super.setObjectProperty(name, value); + } + + public String toString() { + return super.toString() + " ActiveMQBytesMessage{ " + + "bytesOut = " + bytesOut + + ", dataOut = " + dataOut + + ", dataIn = " + dataIn + + " }"; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/ActiveMQDestination.java b/activemq-core/src/main/java/org/activemq/command/ActiveMQDestination.java new file mode 100755 index 0000000000..b3d047f585 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ActiveMQDestination.java @@ -0,0 +1,301 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.Topic; + +import org.activemq.util.URISupport; + +/** + * @openwire:marshaller + * @version $Revision: 1.10 $ + */ +abstract public class ActiveMQDestination implements DataStructure, Destination, Externalizable { + + private static final long serialVersionUID = -3885260014960795889L; + + public static final String PATH_SEPERATOR = "."; + public static final String COMPOSITE_SEPERATOR = ","; + + public static final byte QUEUE_TYPE = 0x01; + public static final byte TOPIC_TYPE = 0x02; + public static final byte TEMP_MASK = 0x04; + public static final byte TEMP_TOPIC_TYPE = TOPIC_TYPE | TEMP_MASK; + public static final byte TEMP_QUEUE_TYPE = QUEUE_TYPE | TEMP_MASK; + + public static final String QUEUE_QUALIFIED_PREFIX = "queue://"; + public static final String TOPIC_QUALIFIED_PREFIX = "topic://"; + public static final String TEMP_QUEUE_QUALIFED_PREFIX= "temp-queue://"; + public static final String TEMP_TOPIC_QUALIFED_PREFIX = "temp-topic://"; + + protected String physicalName; + + transient protected ActiveMQDestination[] compositeDestinations; + transient protected String[] destinationPaths; + transient protected boolean isPattern; + transient protected int hashValue; + protected Map options; + + public ActiveMQDestination() { + } + + protected ActiveMQDestination(String name) { + setPhysicalName(name); + } + + public ActiveMQDestination(ActiveMQDestination composites[]) { + setCompositeDestinations(composites); + } + + public boolean isComposite() { + return compositeDestinations!=null; + } + public ActiveMQDestination[] getCompositeDestinations() { + return compositeDestinations; + } + + public void setCompositeDestinations(ActiveMQDestination[] destinations) { + this.compositeDestinations=destinations; + this.destinationPaths=null; + this.hashValue=0; + this.isPattern=false; + + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < destinations.length; i++) { + if( i!=0 ) + sb.append(COMPOSITE_SEPERATOR); + if( getDestinationType()==destinations[i].getDestinationType()) { + sb.append(destinations[i].getQualifiedName()); + } else { + sb.append(destinations[i].getQualifiedName()); + } + } + physicalName = sb.toString(); + } + + public String getQualifiedName() { + if( isComposite() ) + return physicalName; + return getQualifiedPrefix()+physicalName; + } + + abstract protected String getQualifiedPrefix(); + + /** + * @openwire:property version=1 + */ + public String getPhysicalName() { + return physicalName; + } + + public void setPhysicalName(String physicalName) { + + // Strip off any options + int p = physicalName.indexOf("?"); + if( p >= 0 ) { + String optstring = physicalName.substring(p+1); + physicalName = physicalName.substring(0, p); + try { + options = URISupport.parseQuery(optstring); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Invalid destination name: "+physicalName+", it's options are not encoded properly: "+e); + } + } + + this.physicalName = physicalName; + this.destinationPaths=null; + this.hashValue=0; + this.isPattern = false; + + // Check to see if it is a composite. + ArrayList l = new ArrayList(); + StringTokenizer iter = new StringTokenizer(physicalName, COMPOSITE_SEPERATOR); + while (iter.hasMoreTokens()) { + String name = iter.nextToken().trim(); + if( name.length() == 0 ) + continue; + l.add(name); + } + + if( l.size()>1 ) { + compositeDestinations = new ActiveMQDestination[l.size()]; + int counter=0; + for (Iterator iterator = l.iterator(); iterator.hasNext();) { + compositeDestinations[counter++] =createDestination((String) iterator.next()); + } + } else { + compositeDestinations=null; + // If this is a pattern destination. + if( !isTemporary() && ( + physicalName.indexOf("*")>=0 || + physicalName.indexOf("<")>=0 ) ) { + isPattern = true; + } + } + } + + public ActiveMQDestination createDestination(String name) { + return createDestination(name, getDestinationType()); + } + + static public ActiveMQDestination createDestination(String name, byte defaultType) { + + if( name.startsWith(QUEUE_QUALIFIED_PREFIX) ) { + return new ActiveMQQueue(name.substring(QUEUE_QUALIFIED_PREFIX.length())); + } else if( name.startsWith(TOPIC_QUALIFIED_PREFIX) ) { + return new ActiveMQTopic(name.substring(TOPIC_QUALIFIED_PREFIX.length())); + } else if( name.startsWith(TEMP_QUEUE_QUALIFED_PREFIX) ) { + return new ActiveMQTempQueue(name.substring(TEMP_QUEUE_QUALIFED_PREFIX.length())); + } else if( name.startsWith(TEMP_TOPIC_QUALIFED_PREFIX) ) { + return new ActiveMQTempTopic(name.substring(TEMP_TOPIC_QUALIFED_PREFIX.length())); + } + + switch(defaultType) { + case QUEUE_TYPE: + return new ActiveMQQueue(name); + case TOPIC_TYPE: + return new ActiveMQTopic(name); + case TEMP_QUEUE_TYPE: + return new ActiveMQTempQueue(name); + case TEMP_TOPIC_TYPE: + return new ActiveMQTempTopic(name); + default: + throw new IllegalArgumentException("Invalid default destination type: "+defaultType); + } + } + + public String[] getDestinationPaths() { + + if( destinationPaths!=null ) + return destinationPaths; + + ArrayList l = new ArrayList(); + StringTokenizer iter = new StringTokenizer(physicalName, PATH_SEPERATOR); + while (iter.hasMoreTokens()) { + String name = iter.nextToken().trim(); + if( name.length() == 0 ) + continue; + l.add(name); + } + + destinationPaths = new String[l.size()]; + l.toArray(destinationPaths); + return destinationPaths; + } + + abstract public byte getDestinationType(); + + public boolean isQueue() { + return false; + } + + public boolean isTopic() { + return false; + } + + public boolean isTemporary() { + return false; + } + + public boolean equals(Object o) { + if( this == o ) + return true; + if( o==null || getClass()!=o.getClass() ) + return false; + + ActiveMQDestination d = (ActiveMQDestination) o; + return physicalName.equals(d.physicalName); + } + + public int hashCode() { + if( hashValue==0 ) { + hashValue = physicalName.hashCode(); + } + return hashValue; + } + + public static ActiveMQDestination transform(Destination dest) throws JMSException { + if( dest == null ) + return null; + if( dest instanceof ActiveMQDestination ) + return (ActiveMQDestination) dest; + if( dest instanceof TemporaryQueue ) + return new ActiveMQTempQueue(((TemporaryQueue)dest).getQueueName()); + if( dest instanceof TemporaryTopic ) + return new ActiveMQTempTopic(((TemporaryTopic)dest).getTopicName()); + if( dest instanceof Queue ) + return new ActiveMQQueue(((Queue)dest).getQueueName()); + if( dest instanceof Topic ) + return new ActiveMQTopic(((Topic)dest).getTopicName()); + throw new JMSException("Could not transform the destination into a ActiveMQ destination: "+dest); + } + + public String toString() { + return getQualifiedName(); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeUTF(this.getPhysicalName()); + out.writeObject(options); + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + this.setPhysicalName(in.readUTF()); + this.options = (Map) in.readObject(); + } + + public String getDestinationTypeAsString() { + switch(getDestinationType()) { + case QUEUE_TYPE: + return "Queue"; + case TOPIC_TYPE: + return "Topic"; + case TEMP_QUEUE_TYPE: + return "TempQueue"; + case TEMP_TOPIC_TYPE: + return "TempTopic"; + default: + throw new IllegalArgumentException("Invalid destination type: "+getDestinationType()); + } + } + + public Map getOptions() { + return options; + } + + public boolean isMarshallAware() { + return false; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/ActiveMQMapMessage.java b/activemq-core/src/main/java/org/activemq/command/ActiveMQMapMessage.java new file mode 100755 index 0000000000..d5db2dbbb9 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ActiveMQMapMessage.java @@ -0,0 +1,666 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.MessageFormatException; +import javax.jms.MessageNotWriteableException; + +import org.activeio.ByteArrayInputStream; +import org.activeio.ByteArrayOutputStream; +import org.activeio.ByteSequence; +import org.activeio.command.WireFormat; +import org.activemq.ActiveMQConnection; +import org.activemq.util.JMSExceptionSupport; +import org.activemq.util.MarshallingSupport; + +/** + * A MapMessage object is used to send a set of name-value pairs. The names are String + * objects, and the values are primitive data types in the Java programming language. The names must have a value that + * is not null, and not an empty string. The entries can be accessed sequentially or randomly by name. The order of the + * entries is undefined. MapMessage inherits from the Message interface and adds a message + * body that contains a Map.

The primitive types can be read or written explicitly using methods for each type. They + * may also be read or written generically as objects. For instance, a call to MapMessage.setInt("foo", 6) + * is equivalent to MapMessage.setObject("foo", new Integer(6)). Both forms are provided, because the + * explicit form is convenient for static programming, and the object form is needed when types are not known at compile + * time.

When a client receives a MapMessage, it is in read-only mode. If a client attempts to write to + * the message at this point, a MessageNotWriteableException is thrown. If clearBody is + * called, the message can now be both read from and written to.

MapMessage objects support the + * following conversion table. The marked cases must be supported. The unmarked cases must throw a + * JMSException. The String -to-primitive conversions may throw a runtime exception if the + * primitive's valueOf() method does not accept it as a valid String representation of the + * primitive.

A value written as the row type can be read as the column type. + *

+ *

| | boolean byte short char int long float double String byte[] |----------------------------------------------------------------------
+ * |boolean | X X |byte | X X X X X |short | X X X X |char | X X |int | X X X |long | X X |float | X X X |double | X X
+ * |String | X X X X X X X X |byte[] | X |----------------------------------------------------------------------
+ * 

+ *

+ *

+ *

Attempting to read a null value as a primitive type must be treated as calling the primitive's corresponding + * valueOf(String) conversion method with a null value. Since char does not support a + * String conversion, attempting to read a null value as a char must throw a + * NullPointerException. + * + * @openwire:marshaller + * @see javax.jms.Session#createMapMessage() + * @see javax.jms.BytesMessage + * @see javax.jms.Message + * @see javax.jms.ObjectMessage + * @see javax.jms.StreamMessage + * @see javax.jms.TextMessage + */ +public class ActiveMQMapMessage extends ActiveMQMessage implements MapMessage { + + public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_MAP_MESSAGE; + + transient protected HashMap map = new HashMap(); + + public Message copy() { + ActiveMQMapMessage copy = new ActiveMQMapMessage(); + copy(copy); + return copy; + } + + private void copy(ActiveMQMapMessage copy) { + storeContent(); + super.copy(copy); + } + + // We only need to marshal the content if we are hitting the wire. + public void beforeMarshall(WireFormat wireFormat) throws IOException { + super.beforeMarshall(wireFormat); + storeContent(); + } + + private void storeContent() { + try { + if( getContent()==null && !map.isEmpty()) { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + OutputStream os = bytesOut; + ActiveMQConnection connection = getConnection(); + if( connection!= null && connection.isUseCompression() ) { + compressed = true; + os = new DeflaterOutputStream(os); + } + DataOutputStream dataOut = new DataOutputStream(os); + MarshallingSupport.marshalPrimitiveMap(map, dataOut); + dataOut.close(); + setContent(bytesOut.toByteSequence()); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Builds the message body from data + * @throws JMSException + * + * @throws IOException + */ + private void loadContent() throws JMSException { + try { + if( getContent()!=null && map.isEmpty() ) { + ByteSequence content = getContent(); + InputStream is = new ByteArrayInputStream(content); + if( isCompressed() ) { + is = new InflaterInputStream(is); + } + DataInputStream dataIn = new DataInputStream(is); + map = MarshallingSupport.unmarshalPrimitiveMap(dataIn); + dataIn.close(); + } + } catch (IOException e) { + throw JMSExceptionSupport.create(e); + } + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * Clears out the message body. Clearing a message's body does not clear its header values or property entries.

+ * If this message body was read-only, calling this method leaves the message body in the same state as an empty + * body in a newly created message. + */ + public void clearBody() throws JMSException { + super.clearBody(); + map.clear(); + } + + /** + * Returns the boolean value with the specified name. + * + * @param name the name of the boolean + * @return the boolean value with the specified name + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + public boolean getBoolean(String name) throws JMSException { + initializeReading(); + Object value = map.get(name); + if (value == null) { + return false; + } + if (value instanceof Boolean) { + return ((Boolean) value).booleanValue(); + } + if (value instanceof String) { + return Boolean.valueOf(value.toString()).booleanValue(); + } else { + throw new MessageFormatException(" cannot read a boolean from " + value.getClass().getName()); + } + } + + /** + * Returns the byte value with the specified name. + * + * @param name the name of the byte + * @return the byte value with the specified name + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + public byte getByte(String name) throws JMSException { + initializeReading(); + Object value = map.get(name); + if (value == null) { + return 0; + } + if (value instanceof Byte) { + return ((Byte) value).byteValue(); + } + if (value instanceof String) { + return Byte.valueOf(value.toString()).byteValue(); + } else { + throw new MessageFormatException(" cannot read a byte from " + value.getClass().getName()); + } + } + + /** + * Returns the short value with the specified name. + * + * @param name the name of the short + * @return the short value with the specified name + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + public short getShort(String name) throws JMSException { + initializeReading(); + Object value = map.get(name); + if (value == null) { + return 0; + } + if (value instanceof Short) { + return ((Short) value).shortValue(); + } + if (value instanceof Byte) { + return ((Byte) value).shortValue(); + } + if (value instanceof String) { + return Short.valueOf(value.toString()).shortValue(); + } else { + throw new MessageFormatException(" cannot read a short from " + value.getClass().getName()); + } + } + + /** + * Returns the Unicode character value with the specified name. + * + * @param name the name of the Unicode character + * @return the Unicode character value with the specified name + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + public char getChar(String name) throws JMSException { + initializeReading(); + Object value = map.get(name); + if (value == null) { + throw new NullPointerException(); + } + if (value instanceof Character) { + return ((Character) value).charValue(); + } else { + throw new MessageFormatException(" cannot read a short from " + value.getClass().getName()); + } + } + + /** + * Returns the int value with the specified name. + * + * @param name the name of the int + * @return the int value with the specified name + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + public int getInt(String name) throws JMSException { + initializeReading(); + Object value = map.get(name); + if (value == null) { + return 0; + } + if (value instanceof Integer) { + return ((Integer) value).intValue(); + } + if (value instanceof Short) { + return ((Short) value).intValue(); + } + if (value instanceof Byte) { + return ((Byte) value).intValue(); + } + if (value instanceof String) { + return Integer.valueOf(value.toString()).intValue(); + } else { + throw new MessageFormatException(" cannot read an int from " + value.getClass().getName()); + } + } + + /** + * Returns the long value with the specified name. + * + * @param name the name of the long + * @return the long value with the specified name + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + public long getLong(String name) throws JMSException { + initializeReading(); + Object value = map.get(name); + if (value == null) { + return 0; + } + if (value instanceof Long) { + return ((Long) value).longValue(); + } + if (value instanceof Integer) { + return ((Integer) value).longValue(); + } + if (value instanceof Short) { + return ((Short) value).longValue(); + } + if (value instanceof Byte) { + return ((Byte) value).longValue(); + } + if (value instanceof String) { + return Long.valueOf(value.toString()).longValue(); + } else { + throw new MessageFormatException(" cannot read a long from " + value.getClass().getName()); + } + } + + /** + * Returns the float value with the specified name. + * + * @param name the name of the float + * @return the float value with the specified name + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + public float getFloat(String name) throws JMSException { + initializeReading(); + Object value = map.get(name); + if (value == null) { + return 0; + } + if (value instanceof Float) { + return ((Float) value).floatValue(); + } + if (value instanceof String) { + return Float.valueOf(value.toString()).floatValue(); + } else { + throw new MessageFormatException(" cannot read a float from " + value.getClass().getName()); + } + } + + /** + * Returns the double value with the specified name. + * + * @param name the name of the double + * @return the double value with the specified name + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + public double getDouble(String name) throws JMSException { + initializeReading(); + Object value = map.get(name); + if (value == null) { + return 0; + } + if (value instanceof Double) { + return ((Double) value).doubleValue(); + } + if (value instanceof Float) { + return ((Float) value).floatValue(); + } + if (value instanceof String) { + return Float.valueOf(value.toString()).floatValue(); + } else { + throw new MessageFormatException(" cannot read a double from " + value.getClass().getName()); + } + } + + /** + * Returns the String value with the specified name. + * + * @param name the name of the String + * @return the String value with the specified name; if there is no item by this name, a null value is + * returned + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + public String getString(String name) throws JMSException { + initializeReading(); + Object value = map.get(name); + if (value == null) { + return null; + } + if (value instanceof byte[]) { + throw new MessageFormatException("Use getBytes to read a byte array"); + } else { + return value.toString(); + } + } + + /** + * Returns the byte array value with the specified name. + * + * @param name the name of the byte array + * @return a copy of the byte array value with the specified name; if there is no item by this name, a null value is + * returned. + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + public byte[] getBytes(String name) throws JMSException { + initializeReading(); + Object value = map.get(name); + if ( value instanceof byte[] ) { + return (byte[]) value; + } else { + throw new MessageFormatException(" cannot read a byte[] from " + value.getClass().getName()); + } + } + + /** + * Returns the value of the object with the specified name.

This method can be used to return, in objectified + * format, an object in the Java programming language ("Java object") that had been stored in the Map with the + * equivalent setObject method call, or its equivalent primitive set type method. + *

Note that byte values are returned as byte[], not Byte[]. + * + * @param name the name of the Java object + * @return a copy of the Java object value with the specified name, in objectified format (for example, if the + * object was set as an int, an Integer is returned); if there is no item by this + * name, a null value is returned + * @throws JMSException if the JMS provider fails to read the message due to some internal error. + */ + public Object getObject(String name) throws JMSException { + initializeReading(); + return map.get(name); + } + + /** + * Returns an Enumeration of all the names in the MapMessage object. + * + * @return an enumeration of all the names in this MapMessage + * @throws JMSException + */ + public Enumeration getMapNames() throws JMSException { + initializeReading(); + return Collections.enumeration(map.keySet()); + } + + protected void put(String name, Object value) throws JMSException { + if (name == null) { + throw new IllegalArgumentException("The name of the property cannot be null."); + } + if (name.length() == 0) { + throw new IllegalArgumentException("The name of the property cannot be an emprty string."); + } + map.put(name, value); + } + + /** + * Sets a boolean value with the specified name into the Map. + * + * @param name the name of the boolean + * @param value the boolean value to set in the Map + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is an empty string. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void setBoolean(String name, boolean value) throws JMSException { + initializeWriting(); + put(name, (value) ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Sets a byte value with the specified name into the Map. + * + * @param name the name of the byte + * @param value the byte value to set in the Map + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is an empty string. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void setByte(String name, byte value) throws JMSException { + initializeWriting(); + put(name, new Byte(value)); + } + + /** + * Sets a short value with the specified name into the Map. + * + * @param name the name of the short + * @param value the short value to set in the Map + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is an empty string. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void setShort(String name, short value) throws JMSException { + initializeWriting(); + put(name, new Short(value)); + } + + /** + * Sets a Unicode character value with the specified name into the Map. + * + * @param name the name of the Unicode character + * @param value the Unicode character value to set in the Map + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is an empty string. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void setChar(String name, char value) throws JMSException { + initializeWriting(); + put(name, new Character(value)); + } + + /** + * Sets an int value with the specified name into the Map. + * + * @param name the name of the int + * @param value the int value to set in the Map + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is an empty string. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void setInt(String name, int value) throws JMSException { + initializeWriting(); + put(name, new Integer(value)); + } + + /** + * Sets a long value with the specified name into the Map. + * + * @param name the name of the long + * @param value the long value to set in the Map + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is an empty string. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void setLong(String name, long value) throws JMSException { + initializeWriting(); + put(name, new Long(value)); + } + + /** + * Sets a float value with the specified name into the Map. + * + * @param name the name of the float + * @param value the float value to set in the Map + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is an empty string. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void setFloat(String name, float value) throws JMSException { + initializeWriting(); + put(name, new Float(value)); + } + + /** + * Sets a double value with the specified name into the Map. + * + * @param name the name of the double + * @param value the double value to set in the Map + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is an empty string. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void setDouble(String name, double value) throws JMSException { + initializeWriting(); + put(name, new Double(value)); + } + + /** + * Sets a String value with the specified name into the Map. + * + * @param name the name of the String + * @param value the String value to set in the Map + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is an empty string. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void setString(String name, String value) throws JMSException { + initializeWriting(); + put(name, value); + } + + /** + * Sets a byte array value with the specified name into the Map. + * + * @param name the name of the byte array + * @param value the byte array value to set in the Map; the array is copied so that the value for name + * will not be altered by future modifications + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws NullPointerException if the name is null, or if the name is an empty string. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void setBytes(String name, byte[] value) throws JMSException { + initializeWriting(); + if (value != null) { + put(name, value); + } else { + map.remove(name); + } + } + + /** + * Sets a portion of the byte array value with the specified name into the Map. + * + * @param name the name of the byte array + * @param value the byte array value to set in the Map + * @param offset the initial offset within the byte array + * @param length the number of bytes to use + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is an empty string. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void setBytes(String name, byte[] value, int offset, int length) throws JMSException { + initializeWriting(); + byte[] data = new byte[length]; + System.arraycopy(value, offset, data, 0, length); + put(name, data); + } + + /** + * Sets an object value with the specified name into the Map.

This method works only for the objectified + * primitive object types (Integer,Double, Long  ...), + * String objects, and byte arrays. + * + * @param name the name of the Java object + * @param value the Java object value to set in the Map + * @throws JMSException if the JMS provider fails to write the message due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is an empty string. + * @throws MessageFormatException if the object is invalid. + * @throws MessageNotWriteableException if the message is in read-only mode. + */ + public void setObject(String name, Object value) throws JMSException { + initializeWriting(); + if (value != null) { + if (value instanceof Number || value instanceof String || value instanceof Boolean || + value instanceof Byte + || value instanceof Character || value instanceof byte[]) { + put(name, value); + } else { + throw new MessageFormatException(value.getClass() + " is not a primitive type"); + } + } else { + put(name, null); + } + } + + /** + * Indicates whether an item exists in this MapMessage object. + * + * @param name the name of the item to test + * @return true if the item exists + * @throws JMSException if the JMS provider fails to determine if the item exists due to some internal error. + */ + public boolean itemExists(String name) throws JMSException { + initializeReading(); + return map.containsKey(name); + } + + private void initializeReading() throws JMSException { + loadContent(); + } + + private void initializeWriting() throws MessageNotWriteableException { + checkReadOnlyBody(); + setContent(null); + } + + public String toString() { + return super.toString() + " ActiveMQMapMessage{ " + + "theTable = " + map + + " }"; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/ActiveMQMessage.java b/activemq-core/src/main/java/org/activemq/command/ActiveMQMessage.java new file mode 100755 index 0000000000..2082fc9e89 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ActiveMQMessage.java @@ -0,0 +1,506 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Vector; + +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageFormatException; +import javax.jms.MessageNotWriteableException; + +import org.activemq.filter.PropertyExpression; +import org.activemq.state.CommandVisitor; +import org.activemq.util.Callback; +import org.activemq.util.JMSExceptionSupport; +import org.activemq.util.TypeConversionSupport; + +/** + * @version $Revision$ + * @openwire:marshaller + */ +public class ActiveMQMessage extends Message implements javax.jms.Message { + + public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_MESSAGE; + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + transient protected Callback acknowledgeCallback; + + public Message copy() { + ActiveMQMessage copy = new ActiveMQMessage(); + copy(copy); + return copy; + } + + + protected void copy(ActiveMQMessage copy) { + super.copy(copy); + copy.acknowledgeCallback = acknowledgeCallback; + } + + public int hashCode() { + return this.getMessageId().hashCode(); + } + + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || o.getClass() != getClass()) + return false; + + ActiveMQMessage msg = (ActiveMQMessage) o; + MessageId oMsg = msg.getMessageId(); + MessageId thisMsg = this.getMessageId(); + return thisMsg != null && oMsg != null && oMsg.equals(thisMsg); + } + + public void acknowledge() throws JMSException { + if (acknowledgeCallback != null) { + try { + acknowledgeCallback.execute(); + } catch (JMSException e) { + throw e; + } catch (Throwable e) { + throw JMSExceptionSupport.create(e); + } + } + } + + public void clearBody() throws JMSException { + setContent(null); + readOnlyBody = false; + } + + public String getJMSMessageID() { + MessageId messageId = this.getMessageId(); + if (messageId == null) { + return null; + } + return messageId.toString(); + } + + /** + * Seems to be invalid because the parameter doesn't initialize MessageId instance variables ProducerId and + * ProducerSequenceId + * + * @param value + * @throws JMSException + */ + public void setJMSMessageID(String value) throws JMSException { + if( value !=null ) { + try { + MessageId id = new MessageId(value); + this.setMessageId(id); + } catch (Throwable e) { + throw JMSExceptionSupport.create("Invalid message id '" + value + "', reason: " + e.getMessage(), e); + } + } else { + this.setMessageId(null); + } + } + + /** + * This will create an object of MessageId. For it to be valid, the instance variable ProducerId and + * producerSequenceId must be initialized. + * + * @param producerId + * @param producerSequenceId + * @throws JMSException + */ + public void setJMSMessageID(ProducerId producerId, long producerSequenceId) throws JMSException { + MessageId id = null; + try { + id = new MessageId(producerId, producerSequenceId); + this.setMessageId(id); + } catch (Throwable e) { + throw JMSExceptionSupport.create("Invalid message id '" + id + "', reason: " + e.getMessage(), e); + } + } + + public long getJMSTimestamp() { + return this.getTimestamp(); + } + + public void setJMSTimestamp(long timestamp) { + this.setTimestamp(timestamp); + } + + public String getJMSCorrelationID() { + return this.getCorrelationId(); + } + + public void setJMSCorrelationID(String correlationId) { + this.setCorrelationId(correlationId); + } + + public byte[] getJMSCorrelationIDAsBytes() throws JMSException { + return encodeString(this.getCorrelationId()); + } + + public void setJMSCorrelationIDAsBytes(byte[] correlationId) throws JMSException { + this.setCorrelationId(decodeString(correlationId)); + } + + static protected String decodeString(byte[] data) throws JMSException { + try { + if (data == null) { + return null; + } + return new String(data, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new JMSException("Invalid UTF-8 encoding: " + e.getMessage()); + } + } + + static protected byte[] encodeString(String data) throws JMSException { + try { + if (data == null) { + return null; + } + return data.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new JMSException("Invalid UTF-8 encoding: " + e.getMessage()); + } + } + + public Destination getJMSReplyTo() { + return this.getReplyTo(); + } + + public void setJMSReplyTo(Destination destination) throws JMSException { + this.setReplyTo(ActiveMQDestination.transform(destination)); + } + + public Destination getJMSDestination() { + return this.getDestination(); + } + + public void setJMSDestination(Destination destination) throws JMSException { + this.setDestination(ActiveMQDestination.transform(destination)); + } + + public int getJMSDeliveryMode() { + return this.isPersistent() ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT; + } + + public void setJMSDeliveryMode(int mode) { + this.setPersistent(mode==DeliveryMode.PERSISTENT); + } + + public boolean getJMSRedelivered() { + return this.isRedelivered(); + } + + public void setJMSRedelivered(boolean redelivered) { + this.setRedelivered(redelivered); + } + + public String getJMSType() { + return this.getType(); + } + + public void setJMSType(String type) { + this.setType(type); + } + + public long getJMSExpiration() { + return this.getExpiration(); + } + + public void setJMSExpiration(long expiration) { + this.setExpiration(expiration); + } + + public int getJMSPriority() { + return this.getPriority(); + } + + public void setJMSPriority(int priority) { + this.setPriority((byte) priority); + } + + public void clearProperties() { + super.clearProperties(); + readOnlyProperties = false; + } + + public boolean propertyExists(String name) throws JMSException { + try { + return this.getProperties().containsKey(name); + } catch (IOException e) { + throw JMSExceptionSupport.create(e); + } + } + + public Enumeration getPropertyNames() throws JMSException { + try { + return new Vector(this.getProperties().keySet()).elements(); + } catch (IOException e) { + throw JMSExceptionSupport.create(e); + } + } + + interface PropertySetter { + public void set(Message message, Object value) throws MessageFormatException; + } + + static final private HashMap JMS_PROPERTY_SETERS = new HashMap(); + static { + JMS_PROPERTY_SETERS.put("JMSXDeliveryCount", new PropertySetter() { + public void set(Message message, Object value) throws MessageFormatException { + Integer rc = (Integer)TypeConversionSupport.convert(value, Integer.class); + if( rc == null ) { + throw new MessageFormatException("Property JMSXDeliveryCount cannot be set from a "+value.getClass().getName()+"."); + } + message.setRedeliveryCounter(rc.intValue()-1); + } + }); + JMS_PROPERTY_SETERS.put("JMSXGroupID", new PropertySetter() { + public void set(Message message, Object value) throws MessageFormatException { + String rc = (String)TypeConversionSupport.convert(value, String.class); + if( rc == null ) { + throw new MessageFormatException("Property JMSXGroupID cannot be set from a "+value.getClass().getName()+"."); + } + message.setGroupID(rc); + } + }); + JMS_PROPERTY_SETERS.put("JMSXGroupSeq", new PropertySetter() { + public void set(Message message, Object value) throws MessageFormatException { + Integer rc = (Integer)TypeConversionSupport.convert(value, Integer.class); + if( rc == null ) { + throw new MessageFormatException("Property JMSXGroupSeq cannot be set from a "+value.getClass().getName()+"."); + } + message.setGroupSequence(rc.intValue()); + } + }); + } + + public void setObjectProperty(String name, Object value) throws JMSException { + checkReadOnlyProperties(); + if (name == null || name.equals("")) { + throw new IllegalArgumentException("Property name cannot be empty or null"); + } + + checkValidObject(value); + PropertySetter setter = (PropertySetter) JMS_PROPERTY_SETERS.get(name); + + if( setter != null ) { + setter.set(this,value); + } else { + try { + this.setProperty(name, value); + } catch (IOException e) { + throw JMSExceptionSupport.create(e); + } + } + } + + private void checkValidObject(Object value) throws MessageFormatException { + if(!(value instanceof Boolean || value instanceof Byte || value instanceof Short || value instanceof Integer || + value instanceof Long || value instanceof Float || value instanceof Double || value instanceof String || + value == null)) { + throw new MessageFormatException("Only objectified primitive objects and String types are allowed"); + } + } + + public Object getObjectProperty(String name) throws JMSException { + if (name == null) { + throw new NullPointerException("Property name cannot be null"); + } + + // PropertyExpression handles converting message headers to properties. + PropertyExpression expression = new PropertyExpression(name); + return expression.evaluate(this); + } + + public boolean getBooleanProperty(String name) throws JMSException { + Object value = getObjectProperty(name); + if( value == null ) + return false; + Boolean rc = (Boolean)TypeConversionSupport.convert(value, Boolean.class); + if( rc == null ) { + throw new MessageFormatException("Property "+name+" was a "+value.getClass().getName()+" and cannot be read as a boolean"); + } + return rc.booleanValue(); + } + + public byte getByteProperty(String name) throws JMSException { + Object value = getObjectProperty(name); + if( value == null ) + throw new NumberFormatException("property "+name+" was null"); + Byte rc = (Byte)TypeConversionSupport.convert(value, Byte.class); + if( rc == null ) { + throw new MessageFormatException("Property "+name+" was a "+value.getClass().getName()+" and cannot be read as a byte"); + } + return rc.byteValue(); + } + + public short getShortProperty(String name) throws JMSException { + Object value = getObjectProperty(name); + if( value == null ) + throw new NumberFormatException("property "+name+" was null"); + Short rc = (Short)TypeConversionSupport.convert(value, Short.class); + if( rc == null ) { + throw new MessageFormatException("Property "+name+" was a "+value.getClass().getName()+" and cannot be read as a short"); + } + return rc.shortValue(); + } + + public int getIntProperty(String name) throws JMSException { + Object value = getObjectProperty(name); + if( value == null ) + throw new NumberFormatException("property "+name+" was null"); + Integer rc = (Integer)TypeConversionSupport.convert(value, Integer.class); + if( rc == null ) { + throw new MessageFormatException("Property "+name+" was a "+value.getClass().getName()+" and cannot be read as an integer"); + } + return rc.intValue(); + } + + public long getLongProperty(String name) throws JMSException { + Object value = getObjectProperty(name); + if( value == null ) + throw new NumberFormatException("property "+name+" was null"); + Long rc = (Long)TypeConversionSupport.convert(value, Long.class); + if( rc == null ) { + throw new MessageFormatException("Property "+name+" was a "+value.getClass().getName()+" and cannot be read as a long"); + } + return rc.longValue(); + } + + public float getFloatProperty(String name) throws JMSException { + Object value = getObjectProperty(name); + if( value == null ) + throw new NullPointerException("property "+name+" was null"); + Float rc = (Float)TypeConversionSupport.convert(value, Float.class); + if( rc == null ) { + throw new MessageFormatException("Property "+name+" was a "+value.getClass().getName()+" and cannot be read as a float"); + } + return rc.floatValue(); + } + + public double getDoubleProperty(String name) throws JMSException { + Object value = getObjectProperty(name); + if( value == null ) + throw new NullPointerException("property "+name+" was null"); + Double rc = (Double)TypeConversionSupport.convert(value, Double.class); + if( rc == null ) { + throw new MessageFormatException("Property "+name+" was a "+value.getClass().getName()+" and cannot be read as a double"); + } + return rc.doubleValue(); + } + + public String getStringProperty(String name) throws JMSException { + Object value = getObjectProperty(name); + if( value == null ) { + if (name.equals("JMSXUserID")) { + value = getUserID(); + } + } + if( value == null ) + return null; + String rc = (String)TypeConversionSupport.convert(value, String.class); + if( rc == null ) { + throw new MessageFormatException("Property "+name+" was a "+value.getClass().getName()+" and cannot be read as a String"); + } + return rc; + } + + public void setBooleanProperty(String name, boolean value) throws JMSException { + setObjectProperty(name, value ? Boolean.TRUE : Boolean.FALSE); + } + + public void setByteProperty(String name, byte value) throws JMSException { + setObjectProperty(name, new Byte(value)); + } + + public void setShortProperty(String name, short value) throws JMSException { + setObjectProperty(name, new Short(value)); + } + + public void setIntProperty(String name, int value) throws JMSException { + setObjectProperty(name, new Integer(value)); + } + + public void setLongProperty(String name, long value) throws JMSException { + setObjectProperty(name, new Long(value)); + } + + public void setFloatProperty(String name, float value) throws JMSException { + setObjectProperty(name, new Float(value)); + } + + public void setDoubleProperty(String name, double value) throws JMSException { + setObjectProperty(name, new Double(value)); + } + + public void setStringProperty(String name, String value) throws JMSException { + setObjectProperty(name, value); + } + + + private void checkReadOnlyProperties() throws MessageNotWriteableException { + if (readOnlyProperties) { + throw new MessageNotWriteableException("Message properties are read-only"); + } + } + + protected void checkReadOnlyBody() throws MessageNotWriteableException { + if (readOnlyBody) { + throw new MessageNotWriteableException("Message body is read-only"); + } + } + + public boolean isExpired() { + long expireTime = this.getExpiration(); + if (expireTime > 0 && System.currentTimeMillis() > expireTime) { + return true; + } + return false; + } + + public Callback getAcknowledgeCallback() { + return acknowledgeCallback; + } + + public void setAcknowledgeCallback(Callback acknowledgeCallback) { + this.acknowledgeCallback = acknowledgeCallback; + } + + /** + * Send operation event listener. Used to get the message ready to be sent. + */ + public void onSend() { + setReadOnlyBody(true); + setReadOnlyProperties(true); + } + + + public Response visit(CommandVisitor visitor) throws Throwable { + return visitor.processMessage( this ); + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/ActiveMQObjectMessage.java b/activemq-core/src/main/java/org/activemq/command/ActiveMQObjectMessage.java new file mode 100755 index 0000000000..9cfe186df6 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ActiveMQObjectMessage.java @@ -0,0 +1,220 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + + + + +import org.activeio.ByteArrayInputStream; +import org.activeio.ByteArrayOutputStream; +import org.activeio.ByteSequence; +import org.activemq.ActiveMQConnection; +import org.activemq.util.ClassLoading; +import org.activemq.util.JMSExceptionSupport; + +import javax.jms.JMSException; +import javax.jms.ObjectMessage; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.io.ObjectStreamClass; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; +import java.lang.reflect.Proxy; + +/** + * An ObjectMessage object is used to send a message that contains a serializable object in the Java + * programming language ("Java object"). It inherits from the Message interface and adds a body containing + * a single reference to an object. Only Serializable Java objects can be used. + *

+ *

If a collection of Java objects must be sent, one of the Collection classes provided since JDK 1.2 + * can be used. + *

+ *

When a client receives an ObjectMessage, it is in read-only mode. If a client attempts to write to + * the message at this point, a MessageNotWriteableException is thrown. If clearBody is + * called, the message can now be both read from and written to. + * + * @openwire:marshaller + * @see javax.jms.Session#createObjectMessage() + * @see javax.jms.Session#createObjectMessage(Serializable) + * @see javax.jms.BytesMessage + * @see javax.jms.MapMessage + * @see javax.jms.Message + * @see javax.jms.StreamMessage + * @see javax.jms.TextMessage + */ +public class ActiveMQObjectMessage extends ActiveMQMessage implements ObjectMessage { + static final private ClassLoader ACTIVEMQ_CLASSLOADER = ActiveMQObjectMessage.class.getClassLoader(); //TODO verify classloader + public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_OBJECT_MESSAGE; + + protected transient Serializable object; + + public Message copy() { + ActiveMQObjectMessage copy = new ActiveMQObjectMessage(); + copy(copy); + return copy; + } + + private void copy(ActiveMQObjectMessage copy) { + storeContent(); + super.copy(copy); + copy.object=null; + } + + public void storeContent() { + ByteSequence bodyAsBytes = getContent(); + if (bodyAsBytes == null && object != null) { + try { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + OutputStream os = bytesOut; + ActiveMQConnection connection = getConnection(); + if (connection!=null && connection.isUseCompression()) { + compressed = true; + os = new DeflaterOutputStream(os); + } + DataOutputStream dataOut = new DataOutputStream(os); + ObjectOutputStream objOut = new ObjectOutputStream(dataOut); + objOut.writeObject(object); + objOut.close(); + setContent(bytesOut.toByteSequence()); + } catch (IOException ioe) { + throw new RuntimeException(ioe.getMessage(), ioe); + } + } + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * Clears out the message body. Clearing a message's body does not clear its header values or property entries. + *

+ *

If this message body was read-only, calling this method leaves the message body in the same state as an empty + * body in a newly created message. + * + * @throws JMSException if the JMS provider fails to clear the message body due to some internal error. + */ + + public void clearBody() throws JMSException { + super.clearBody(); + this.object = null; + } + + /** + * Sets the serializable object containing this message's data. It is important to note that an + * ObjectMessage contains a snapshot of the object at the time setObject() is called; + * subsequent modifications of the object will have no effect on the ObjectMessage body. + * + * @param newObject the message's data + * @throws JMSException if the JMS provider fails to set the object due to some internal error. + * @throws javax.jms.MessageFormatException + * if object serialization fails. + * @throws javax.jms.MessageNotWriteableException + * if the message is in read-only mode. + */ + + public void setObject(Serializable newObject) throws JMSException { + checkReadOnlyBody(); + this.object = newObject; + setContent(null); + ActiveMQConnection connection = getConnection(); + if( connection==null || !connection.isObjectMessageSerializationDefered() ) { + storeContent(); + } + } + + + /** + * Gets the serializable object containing this message's data. The default value is null. + * + * @return the serializable object containing this message's data + * @throws JMSException + */ + public Serializable getObject() throws JMSException { + if (object == null && getContent()!=null ) { + try { + ByteSequence content = getContent(); + InputStream is = new ByteArrayInputStream(content); + if( isCompressed() ) { + is = new InflaterInputStream(is); + } + DataInputStream dataIn = new DataInputStream(is); + ObjectInputStreamExt objIn = new ObjectInputStreamExt(dataIn); + try { + object = (Serializable) objIn.readObject(); + } catch (ClassNotFoundException ce) { + throw new IOException(ce.getMessage()); + } + dataIn.close(); + } catch (IOException e) { + throw JMSExceptionSupport.create("Failed to build body from bytes. Reason: " + e, e); + } + } + return this.object; + } + + public String toString() { + try { + getObject(); + } catch (JMSException e) { + } + return super.toString(); + } + + static public class ObjectInputStreamExt extends ObjectInputStream { + + public ObjectInputStreamExt(InputStream in) throws IOException { + super(in); + } + + protected Class resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + return load(classDesc.getName(), cl); + } + + protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Class[] cinterfaces = new Class[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) + cinterfaces[i] = load(interfaces[i], cl); + + try { + return Proxy.getProxyClass(cinterfaces[0].getClassLoader(), cinterfaces); + } catch (IllegalArgumentException e) { + throw new ClassNotFoundException(null, e); + } + } + + private Class load(String className, ClassLoader cl) throws ClassNotFoundException { + try { + return ClassLoading.loadClass(className, cl); + } catch ( ClassNotFoundException e ) { + return ClassLoading.loadClass(className, ACTIVEMQ_CLASSLOADER); + } + } + + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/ActiveMQQueue.java b/activemq-core/src/main/java/org/activemq/command/ActiveMQQueue.java new file mode 100755 index 0000000000..097ae2ee37 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ActiveMQQueue.java @@ -0,0 +1,60 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import javax.jms.JMSException; +import javax.jms.Queue; + +/** + * @openwire:marshaller + * @version $Revision: 1.5 $ + */ +public class ActiveMQQueue extends ActiveMQDestination implements Queue { + + private static final long serialVersionUID = -3885260014960795889L; + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.ACTIVEMQ_QUEUE; + + public ActiveMQQueue() { + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public ActiveMQQueue(String name) { + super(name); + } + + public boolean isQueue() { + return true; + } + + public String getQueueName() throws JMSException { + return getPhysicalName(); + } + + public byte getDestinationType() { + return QUEUE_TYPE; + } + + protected String getQualifiedPrefix() { + return QUEUE_QUALIFIED_PREFIX; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/ActiveMQStreamMessage.java b/activemq-core/src/main/java/org/activemq/command/ActiveMQStreamMessage.java new file mode 100755 index 0000000000..b2e6ecfca2 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ActiveMQStreamMessage.java @@ -0,0 +1,1236 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import javax.jms.JMSException; +import javax.jms.MessageEOFException; +import javax.jms.MessageFormatException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; +import javax.jms.StreamMessage; + +import org.activeio.ByteArrayInputStream; +import org.activeio.ByteArrayOutputStream; +import org.activeio.ByteSequence; +import org.activemq.ActiveMQConnection; +import org.activemq.util.JMSExceptionSupport; + +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +/** + * A StreamMessage object is used to send a stream of primitive + * types in the Java programming language. It is filled and read sequentially. + * It inherits from the Message interface and adds a stream + * message body. Its methods are based largely on those found in java.io.DataInputStream + * and java.io.DataOutputStream.

+ *

+ * The primitive types can be read or written explicitly using methods for each + * type. They may also be read or written generically as objects. For instance, + * a call to StreamMessage.writeInt(6) is equivalent to StreamMessage.writeObject(new + * Integer(6)). Both forms are provided, because the explicit form is + * convenient for static programming, and the object form is needed when types + * are not known at compile time.

+ *

+ * When the message is first created, and when clearBody is + * called, the body of the message is in write-only mode. After the first call + * to reset has been made, the message body is in read-only mode. + * After a message has been sent, the client that sent it can retain and modify + * it without affecting the message that has been sent. The same message object + * can be sent multiple times. When a message has been received, the provider + * has called reset so that the message body is in read-only mode + * for the client.

+ *

+ * If clearBody is called on a message in read-only mode, the + * message body is cleared and the message body is in write-only mode.

+ *

+ * If a client attempts to read a message in write-only mode, a MessageNotReadableException + * is thrown.

+ *

+ * If a client attempts to write a message in read-only mode, a MessageNotWriteableException + * is thrown.

+ *

+ * StreamMessage objects support the following conversion table. + * The marked cases must be supported. The unmarked cases must throw a JMSException. + * The String-to-primitive conversions may throw a runtime + * exception if the primitive's valueOf() method does not accept + * it as a valid String representation of the primitive.

+ *

+ * A value written as the row type can be read as the column type.

+ * + *

 | | boolean byte short char int long float double String byte[]
+ * |----------------------------------------------------------------------
+ * |boolean | X X |byte | X X X X X |short | X X X X |char | X X |int | X X X
+ * |long | X X |float | X X X |double | X X |String | X X X X X X X X |byte[] |
+ * X |----------------------------------------------------------------------
+ * 
+ * 
+ * + *

+ *

+ * Attempting to read a null value as a primitive type must be treated as + * calling the primitive's corresponding valueOf(String) + * conversion method with a null value. Since char does not + * support a String conversion, attempting to read a null value + * as a char must throw a NullPointerException. + * + * @openwire:marshaller + * @see javax.jms.Session#createStreamMessage() + * @see javax.jms.BytesMessage + * @see javax.jms.MapMessage + * @see javax.jms.Message + * @see javax.jms.ObjectMessage + * @see javax.jms.TextMessage + */ +public class ActiveMQStreamMessage extends ActiveMQMessage implements StreamMessage { + + public static final byte DATA_STRUCTURE_TYPE = CommandTypes.ACTIVEMQ_STREAM_MESSAGE; + + /** + * message property types + */ + private final static byte BYTES = 3; + private final static byte STRING = 4; + private final static byte BOOLEAN = 5; + private final static byte CHAR = 6; + private final static byte BYTE = 7; + private final static byte SHORT = 8; + private final static byte INT = 9; + private final static byte LONG = 10; + private final static byte FLOAT = 11; + private final static byte DOUBLE = 12; + private final static byte NULL = 13; + + transient protected DataOutputStream dataOut; + transient protected ByteArrayOutputStream bytesOut; + transient protected DataInputStream dataIn; + transient protected int remainingBytes = -1; + + public Message copy() { + ActiveMQStreamMessage copy = new ActiveMQStreamMessage(); + copy(copy); + return copy; + } + + private void copy(ActiveMQStreamMessage copy) { + storeContent(); + super.copy(copy); + copy.dataOut = null; + copy.bytesOut = null; + copy.dataIn = null; + } + + public void onSend() { + super.onSend(); + storeContent(); + } + + private void storeContent() { + if (dataOut != null) { + try { + dataOut.close(); + setContent(bytesOut.toByteSequence()); + bytesOut = null; + dataOut = null; + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * Clears out the message body. Clearing a message's body does not clear its + * header values or property entries.

+ *

+ * If this message body was read-only, calling this method leaves the + * message body in the same state as an empty body in a newly created + * message. + * + * @throws JMSException + * if the JMS provider fails to clear the message body due to + * some internal error. + */ + + public void clearBody() throws JMSException { + super.clearBody(); + this.dataOut = null; + this.dataIn = null; + this.bytesOut = null; + this.remainingBytes=-1; + } + + /** + * Reads a boolean from the stream message. + * + * @return the boolean value read + * @throws JMSException + * if the JMS provider fails to read the message due to some + * internal error. + * @throws MessageEOFException + * if unexpected end of message stream has been reached. + * @throws MessageFormatException + * if this type conversion is invalid. + * @throws MessageNotReadableException + * if the message is in write-only mode. + */ + + public boolean readBoolean() throws JMSException { + initializeReading(); + try { + + this.dataIn.mark(10); + int type = this.dataIn.read(); + if (type == -1) { + throw new MessageEOFException("reached end of data"); + } + if (type == BOOLEAN) { + return this.dataIn.readBoolean(); + } + if (type == STRING) { + return Boolean.valueOf(this.dataIn.readUTF()).booleanValue(); + } + if (type == NULL) { + this.dataIn.reset(); + throw new NullPointerException("Cannot convert NULL value to boolean."); + } else { + this.dataIn.reset(); + throw new MessageFormatException(" not a boolean type"); + } + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a byte value from the stream message. + * + * @return the next byte from the stream message as a 8-bit + * byte + * @throws JMSException + * if the JMS provider fails to read the message due to some + * internal error. + * @throws MessageEOFException + * if unexpected end of message stream has been reached. + * @throws MessageFormatException + * if this type conversion is invalid. + * @throws MessageNotReadableException + * if the message is in write-only mode. + */ + + public byte readByte() throws JMSException { + initializeReading(); + try { + + this.dataIn.mark(10); + int type = this.dataIn.read(); + if (type == -1) { + throw new MessageEOFException("reached end of data"); + } + if (type == BYTE) { + return this.dataIn.readByte(); + } + if (type == STRING) { + return Byte.valueOf(this.dataIn.readUTF()).byteValue(); + } + if (type == NULL) { + this.dataIn.reset(); + throw new NullPointerException("Cannot convert NULL value to byte."); + } else { + this.dataIn.reset(); + throw new MessageFormatException(" not a byte type"); + } + } catch (NumberFormatException mfe) { + try { + this.dataIn.reset(); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + throw mfe; + + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a 16-bit integer from the stream message. + * + * @return a 16-bit integer from the stream message + * @throws JMSException + * if the JMS provider fails to read the message due to some + * internal error. + * @throws MessageEOFException + * if unexpected end of message stream has been reached. + * @throws MessageFormatException + * if this type conversion is invalid. + * @throws MessageNotReadableException + * if the message is in write-only mode. + */ + + public short readShort() throws JMSException { + initializeReading(); + try { + + this.dataIn.mark(17); + int type = this.dataIn.read(); + if (type == -1) { + throw new MessageEOFException("reached end of data"); + } + if (type == SHORT) { + return this.dataIn.readShort(); + } + if (type == BYTE) { + return this.dataIn.readByte(); + } + if (type == STRING) { + return Short.valueOf(this.dataIn.readUTF()).shortValue(); + } + if (type == NULL) { + this.dataIn.reset(); + throw new NullPointerException("Cannot convert NULL value to short."); + } else { + this.dataIn.reset(); + throw new MessageFormatException(" not a short type"); + } + } catch (NumberFormatException mfe) { + try { + this.dataIn.reset(); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + throw mfe; + + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + + } + + /** + * Reads a Unicode character value from the stream message. + * + * @return a Unicode character from the stream message + * @throws JMSException + * if the JMS provider fails to read the message due to some + * internal error. + * @throws MessageEOFException + * if unexpected end of message stream has been reached. + * @throws MessageFormatException + * if this type conversion is invalid + * @throws MessageNotReadableException + * if the message is in write-only mode. + */ + + public char readChar() throws JMSException { + initializeReading(); + try { + + this.dataIn.mark(17); + int type = this.dataIn.read(); + if (type == -1) { + throw new MessageEOFException("reached end of data"); + } + if (type == CHAR) { + return this.dataIn.readChar(); + } + if (type == NULL) { + this.dataIn.reset(); + throw new NullPointerException("Cannot convert NULL value to char."); + } else { + this.dataIn.reset(); + throw new MessageFormatException(" not a char type"); + } + } catch (NumberFormatException mfe) { + try { + this.dataIn.reset(); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + throw mfe; + + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a 32-bit integer from the stream message. + * + * @return a 32-bit integer value from the stream message, interpreted as an + * int + * @throws JMSException + * if the JMS provider fails to read the message due to some + * internal error. + * @throws MessageEOFException + * if unexpected end of message stream has been reached. + * @throws MessageFormatException + * if this type conversion is invalid. + * @throws MessageNotReadableException + * if the message is in write-only mode. + */ + + public int readInt() throws JMSException { + initializeReading(); + try { + + this.dataIn.mark(33); + int type = this.dataIn.read(); + if (type == -1) { + throw new MessageEOFException("reached end of data"); + } + if (type == INT) { + return this.dataIn.readInt(); + } + if (type == SHORT) { + return this.dataIn.readShort(); + } + if (type == BYTE) { + return this.dataIn.readByte(); + } + if (type == STRING) { + return Integer.valueOf(this.dataIn.readUTF()).intValue(); + } + if (type == NULL) { + this.dataIn.reset(); + throw new NullPointerException("Cannot convert NULL value to int."); + } else { + this.dataIn.reset(); + throw new MessageFormatException(" not an int type"); + } + } catch (NumberFormatException mfe) { + try { + this.dataIn.reset(); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + throw mfe; + + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a 64-bit integer from the stream message. + * + * @return a 64-bit integer value from the stream message, interpreted as a + * long + * @throws JMSException + * if the JMS provider fails to read the message due to some + * internal error. + * @throws MessageEOFException + * if unexpected end of message stream has been reached. + * @throws MessageFormatException + * if this type conversion is invalid. + * @throws MessageNotReadableException + * if the message is in write-only mode. + */ + + public long readLong() throws JMSException { + initializeReading(); + try { + + this.dataIn.mark(65); + int type = this.dataIn.read(); + if (type == -1) { + throw new MessageEOFException("reached end of data"); + } + if (type == LONG) { + return this.dataIn.readLong(); + } + if (type == INT) { + return this.dataIn.readInt(); + } + if (type == SHORT) { + return this.dataIn.readShort(); + } + if (type == BYTE) { + return this.dataIn.readByte(); + } + if (type == STRING) { + return Long.valueOf(this.dataIn.readUTF()).longValue(); + } + if (type == NULL) { + this.dataIn.reset(); + throw new NullPointerException("Cannot convert NULL value to long."); + } else { + this.dataIn.reset(); + throw new MessageFormatException(" not a long type"); + } + } catch (NumberFormatException mfe) { + try { + this.dataIn.reset(); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + throw mfe; + + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a float from the stream message. + * + * @return a float value from the stream message + * @throws JMSException + * if the JMS provider fails to read the message due to some + * internal error. + * @throws MessageEOFException + * if unexpected end of message stream has been reached. + * @throws MessageFormatException + * if this type conversion is invalid. + * @throws MessageNotReadableException + * if the message is in write-only mode. + */ + + public float readFloat() throws JMSException { + initializeReading(); + try { + this.dataIn.mark(33); + int type = this.dataIn.read(); + if (type == -1) { + throw new MessageEOFException("reached end of data"); + } + if (type == FLOAT) { + return this.dataIn.readFloat(); + } + if (type == STRING) { + return Float.valueOf(this.dataIn.readUTF()).floatValue(); + } + if (type == NULL) { + this.dataIn.reset(); + throw new NullPointerException("Cannot convert NULL value to float."); + } else { + this.dataIn.reset(); + throw new MessageFormatException(" not a float type"); + } + } catch (NumberFormatException mfe) { + try { + this.dataIn.reset(); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + throw mfe; + + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a double from the stream message. + * + * @return a double value from the stream message + * @throws JMSException + * if the JMS provider fails to read the message due to some + * internal error. + * @throws MessageEOFException + * if unexpected end of message stream has been reached. + * @throws MessageFormatException + * if this type conversion is invalid. + * @throws MessageNotReadableException + * if the message is in write-only mode. + */ + + public double readDouble() throws JMSException { + initializeReading(); + try { + + this.dataIn.mark(65); + int type = this.dataIn.read(); + if (type == -1) { + throw new MessageEOFException("reached end of data"); + } + if (type == DOUBLE) { + return this.dataIn.readDouble(); + } + if (type == FLOAT) { + return this.dataIn.readFloat(); + } + if (type == STRING) { + return Double.valueOf(this.dataIn.readUTF()).doubleValue(); + } + if (type == NULL) { + this.dataIn.reset(); + throw new NullPointerException("Cannot convert NULL value to double."); + } else { + this.dataIn.reset(); + throw new MessageFormatException(" not a double type"); + } + } catch (NumberFormatException mfe) { + try { + this.dataIn.reset(); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + throw mfe; + + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a String from the stream message. + * + * @return a Unicode string from the stream message + * @throws JMSException + * if the JMS provider fails to read the message due to some + * internal error. + * @throws MessageEOFException + * if unexpected end of message stream has been reached. + * @throws MessageFormatException + * if this type conversion is invalid. + * @throws MessageNotReadableException + * if the message is in write-only mode. + */ + + public String readString() throws JMSException { + initializeReading(); + try { + + this.dataIn.mark(65); + int type = this.dataIn.read(); + if (type == -1) { + throw new MessageEOFException("reached end of data"); + } + if (type == NULL) { + return null; + } + if (type == STRING) { + return this.dataIn.readUTF(); + } + if (type == LONG) { + return new Long(this.dataIn.readLong()).toString(); + } + if (type == INT) { + return new Integer(this.dataIn.readInt()).toString(); + } + if (type == SHORT) { + return new Short(this.dataIn.readShort()).toString(); + } + if (type == BYTE) { + return new Byte(this.dataIn.readByte()).toString(); + } + if (type == FLOAT) { + return new Float(this.dataIn.readFloat()).toString(); + } + if (type == DOUBLE) { + return new Double(this.dataIn.readDouble()).toString(); + } + if (type == BOOLEAN) { + return (this.dataIn.readBoolean() ? Boolean.TRUE : Boolean.FALSE).toString(); + } + if (type == CHAR) { + return new Character(this.dataIn.readChar()).toString(); + } else { + this.dataIn.reset(); + throw new MessageFormatException(" not a String type"); + } + } catch (NumberFormatException mfe) { + try { + this.dataIn.reset(); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + throw mfe; + + } catch (EOFException e) { + throw JMSExceptionSupport.createMessageEOFException(e); + } catch (IOException e) { + throw JMSExceptionSupport.createMessageFormatException(e); + } + } + + /** + * Reads a byte array field from the stream message into the specified + * byte[] object (the read buffer).

+ *

+ * To read the field value, readBytes should be successively + * called until it returns a value less than the length of the read buffer. + * The value of the bytes in the buffer following the last byte read is + * undefined.

+ *

+ * If readBytes returns a value equal to the length of the + * buffer, a subsequent readBytes call must be made. If there + * are no more bytes to be read, this call returns -1.

+ *

+ * If the byte array field value is null, readBytes returns + * -1.

+ *

+ * If the byte array field value is empty, readBytes returns + * 0.

+ *

+ * Once the first readBytes call on a byte[] + * field value has been made, the full value of the field must be read + * before it is valid to read the next field. An attempt to read the next + * field before that has been done will throw a MessageFormatException. + *

+ *

+ * To read the byte field value into a new byte[] object, use + * the readObject method. + * + * @param value + * the buffer into which the data is read + * @return the total number of bytes read into the buffer, or -1 if there is + * no more data because the end of the byte field has been reached + * @throws JMSException + * if the JMS provider fails to read the message due to some + * internal error. + * @throws MessageEOFException + * if unexpected end of message stream has been reached. + * @throws MessageFormatException + * if this type conversion is invalid. + * @throws MessageNotReadableException + * if the message is in write-only mode. + * @see #readObject() + */ + + public int readBytes(byte[] value) throws JMSException { + + initializeReading(); + try { + if (value == null) { + throw new NullPointerException(); + } + + if( remainingBytes == -1 ) { + this.dataIn.mark(value.length + 1); + int type = this.dataIn.read(); + if (type == -1) { + throw new MessageEOFException("reached end of data"); + } + if (type != BYTES) { + throw new MessageFormatException("Not a byte array"); + } + remainingBytes = this.dataIn.readInt(); + } else if ( remainingBytes == 0 ) { + remainingBytes = -1; + return -1; + } + + if (value.length <= remainingBytes) { + // small buffer + remainingBytes -= value.length; + this.dataIn.readFully(value); + return value.length; + } else { + // big buffer + int rc = this.dataIn.read(value, 0, remainingBytes); + remainingBytes=0; + return rc; + } + + } catch (EOFException e) { + JMSException jmsEx = new MessageEOFException(e.getMessage()); + jmsEx.setLinkedException(e); + throw jmsEx; + } catch (IOException e) { + JMSException jmsEx = new MessageFormatException(e.getMessage()); + jmsEx.setLinkedException(e); + throw jmsEx; + } + } + + /** + * Reads an object from the stream message.

+ *

+ * This method can be used to return, in objectified format, an object in + * the Java programming language ("Java object") that has been written to + * the stream with the equivalent writeObject method call, or + * its equivalent primitive writetype method.

+ *

+ * Note that byte values are returned as byte[], not Byte[]. + *

+ *

+ * An attempt to call readObject to read a byte field value + * into a new byte[] object before the full value of the byte + * field has been read will throw a MessageFormatException. + * + * @return a Java object from the stream message, in objectified format (for + * example, if the object was written as an int, an + * Integer is returned) + * @throws JMSException + * if the JMS provider fails to read the message due to some + * internal error. + * @throws MessageEOFException + * if unexpected end of message stream has been reached. + * @throws MessageFormatException + * if this type conversion is invalid. + * @throws MessageNotReadableException + * if the message is in write-only mode. + * @see #readBytes(byte[] value) + */ + + public Object readObject() throws JMSException { + initializeReading(); + try { + this.dataIn.mark(65); + int type = this.dataIn.read(); + if (type == -1) { + throw new MessageEOFException("reached end of data"); + } + if (type == NULL) { + return null; + } + if (type == STRING) { + return this.dataIn.readUTF(); + } + if (type == LONG) { + return new Long(this.dataIn.readLong()); + } + if (type == INT) { + return new Integer(this.dataIn.readInt()); + } + if (type == SHORT) { + return new Short(this.dataIn.readShort()); + } + if (type == BYTE) { + return new Byte(this.dataIn.readByte()); + } + if (type == FLOAT) { + return new Float(this.dataIn.readFloat()); + } + if (type == DOUBLE) { + return new Double(this.dataIn.readDouble()); + } + if (type == BOOLEAN) { + return this.dataIn.readBoolean() ? Boolean.TRUE : Boolean.FALSE; + } + if (type == CHAR) { + return new Character(this.dataIn.readChar()); + } + if (type == BYTES) { + int len = this.dataIn.readInt(); + byte[] value = new byte[len]; + this.dataIn.readFully(value); + return value; + } else { + this.dataIn.reset(); + throw new MessageFormatException("unknown type"); + } + } catch (NumberFormatException mfe) { + try { + this.dataIn.reset(); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + throw mfe; + + } catch (EOFException e) { + JMSException jmsEx = new MessageEOFException(e.getMessage()); + jmsEx.setLinkedException(e); + throw jmsEx; + } catch (IOException e) { + JMSException jmsEx = new MessageFormatException(e.getMessage()); + jmsEx.setLinkedException(e); + throw jmsEx; + } + } + + /** + * Writes a boolean to the stream message. The value + * true is written as the value (byte)1; the + * value false is written as the value (byte)0. + * + * @param value + * the boolean value to be written + * @throws JMSException + * if the JMS provider fails to write the message due to some + * internal error. + * @throws MessageNotWriteableException + * if the message is in read-only mode. + */ + + public void writeBoolean(boolean value) throws JMSException { + initializeWriting(); + try { + this.dataOut.write(BOOLEAN); + this.dataOut.writeBoolean(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a byte to the stream message. + * + * @param value + * the byte value to be written + * @throws JMSException + * if the JMS provider fails to write the message due to some + * internal error. + * @throws MessageNotWriteableException + * if the message is in read-only mode. + */ + + public void writeByte(byte value) throws JMSException { + initializeWriting(); + try { + this.dataOut.write(BYTE); + this.dataOut.writeByte(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a short to the stream message. + * + * @param value + * the short value to be written + * @throws JMSException + * if the JMS provider fails to write the message due to some + * internal error. + * @throws MessageNotWriteableException + * if the message is in read-only mode. + */ + + public void writeShort(short value) throws JMSException { + initializeWriting(); + try { + this.dataOut.write(SHORT); + this.dataOut.writeShort(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a char to the stream message. + * + * @param value + * the char value to be written + * @throws JMSException + * if the JMS provider fails to write the message due to some + * internal error. + * @throws MessageNotWriteableException + * if the message is in read-only mode. + */ + + public void writeChar(char value) throws JMSException { + initializeWriting(); + try { + this.dataOut.write(CHAR); + this.dataOut.writeChar(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes an int to the stream message. + * + * @param value + * the int value to be written + * @throws JMSException + * if the JMS provider fails to write the message due to some + * internal error. + * @throws MessageNotWriteableException + * if the message is in read-only mode. + */ + + public void writeInt(int value) throws JMSException { + initializeWriting(); + try { + this.dataOut.write(INT); + this.dataOut.writeInt(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a long to the stream message. + * + * @param value + * the long value to be written + * @throws JMSException + * if the JMS provider fails to write the message due to some + * internal error. + * @throws MessageNotWriteableException + * if the message is in read-only mode. + */ + + public void writeLong(long value) throws JMSException { + initializeWriting(); + try { + this.dataOut.write(LONG); + this.dataOut.writeLong(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a float to the stream message. + * + * @param value + * the float value to be written + * @throws JMSException + * if the JMS provider fails to write the message due to some + * internal error. + * @throws MessageNotWriteableException + * if the message is in read-only mode. + */ + + public void writeFloat(float value) throws JMSException { + initializeWriting(); + try { + this.dataOut.write(FLOAT); + this.dataOut.writeFloat(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a double to the stream message. + * + * @param value + * the double value to be written + * @throws JMSException + * if the JMS provider fails to write the message due to some + * internal error. + * @throws MessageNotWriteableException + * if the message is in read-only mode. + */ + + public void writeDouble(double value) throws JMSException { + initializeWriting(); + try { + this.dataOut.write(DOUBLE); + this.dataOut.writeDouble(value); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a String to the stream message. + * + * @param value + * the String value to be written + * @throws JMSException + * if the JMS provider fails to write the message due to some + * internal error. + * @throws MessageNotWriteableException + * if the message is in read-only mode. + */ + + public void writeString(String value) throws JMSException { + initializeWriting(); + try { + if (value == null) { + this.dataOut.write(NULL); + } else { + this.dataOut.write(STRING); + this.dataOut.writeUTF(value); + } + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes a byte array field to the stream message.

+ *

+ * The byte array value is written to the message as a byte + * array field. Consecutively written byte array fields are treated as two + * distinct fields when the fields are read. + * + * @param value + * the byte array value to be written + * @throws JMSException + * if the JMS provider fails to write the message due to some + * internal error. + * @throws MessageNotWriteableException + * if the message is in read-only mode. + */ + + public void writeBytes(byte[] value) throws JMSException { + writeBytes(value, 0, value.length); + } + + /** + * Writes a portion of a byte array as a byte array field to the stream + * message.

+ *

+ * The a portion of the byte array value is written to the + * message as a byte array field. Consecutively written byte array fields + * are treated as two distinct fields when the fields are read. + * + * @param value + * the byte array value to be written + * @param offset + * the initial offset within the byte array + * @param length + * the number of bytes to use + * @throws JMSException + * if the JMS provider fails to write the message due to some + * internal error. + * @throws MessageNotWriteableException + * if the message is in read-only mode. + */ + + public void writeBytes(byte[] value, int offset, int length) throws JMSException { + initializeWriting(); + try { + this.dataOut.write(BYTES); + this.dataOut.writeInt(length); + this.dataOut.write(value, offset, length); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + /** + * Writes an object to the stream message.

+ *

+ * This method works only for the objectified primitive object types (Integer, + * Double, Long ...), + * String objects, and byte arrays. + * + * @param value + * the Java object to be written + * @throws JMSException + * if the JMS provider fails to write the message due to some + * internal error. + * @throws MessageFormatException + * if the object is invalid. + * @throws MessageNotWriteableException + * if the message is in read-only mode. + */ + + public void writeObject(Object value) throws JMSException { + initializeWriting(); + if (value == null) { + try { + this.dataOut.write(NULL); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } else if (value instanceof String) { + writeString(value.toString()); + } else if (value instanceof Character) { + writeChar(((Character) value).charValue()); + } else if (value instanceof Boolean) { + writeBoolean(((Boolean) value).booleanValue()); + } else if (value instanceof Byte) { + writeByte(((Byte) value).byteValue()); + } else if (value instanceof Short) { + writeShort(((Short) value).shortValue()); + } else if (value instanceof Integer) { + writeInt(((Integer) value).intValue()); + } else if (value instanceof Float) { + writeFloat(((Float) value).floatValue()); + } else if (value instanceof Double) { + writeDouble(((Double) value).doubleValue()); + } else if (value instanceof byte[]) { + writeBytes((byte[]) value); + } + } + + /** + * Puts the message body in read-only mode and repositions the stream of + * bytes to the beginning. + * + * @throws JMSException + * if an internal error occurs + */ + + public void reset() throws JMSException { + storeContent(); + this.bytesOut = null; + this.dataIn = null; + this.dataOut = null; + this.remainingBytes=-1; + setReadOnlyBody(true); + } + + private void initializeWriting() throws MessageNotWriteableException { + checkReadOnlyBody(); + if (this.dataOut == null) { + this.bytesOut = new ByteArrayOutputStream(); + OutputStream os = bytesOut; + ActiveMQConnection connection = getConnection(); + if (connection!=null && connection.isUseCompression()) { + compressed = true; + os = new DeflaterOutputStream(os); + } + this.dataOut = new DataOutputStream(os); + } + } + + protected void checkWriteOnlyBody() throws MessageNotReadableException { + if (!readOnlyBody) { + throw new MessageNotReadableException("Message body is write-only"); + } + } + + private void initializeReading() throws MessageNotReadableException { + checkWriteOnlyBody(); + if (this.dataIn == null) { + ByteSequence data = getContent(); + if (data == null) + data = new ByteSequence(new byte[] {}, 0 ,0); + InputStream is = new ByteArrayInputStream(data); + if (isCompressed()) { + is = new InflaterInputStream(is); + is = new BufferedInputStream(is); + } + this.dataIn = new DataInputStream(is); + } + } + + public String toString() { + return super.toString() + " ActiveMQStreamMessage{ " + "bytesOut = " + bytesOut + ", dataOut = " + dataOut + + ", dataIn = " + dataIn + " }"; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/ActiveMQTempDestination.java b/activemq-core/src/main/java/org/activemq/command/ActiveMQTempDestination.java new file mode 100755 index 0000000000..343f7c81b5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ActiveMQTempDestination.java @@ -0,0 +1,80 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import javax.jms.JMSException; + +import org.activemq.ActiveMQConnection; + +/** + * @openwire:marshaller + * @version $Revision: 1.5 $ + */ +abstract public class ActiveMQTempDestination extends ActiveMQDestination { + + protected transient ActiveMQConnection connection; + protected transient String connectionId; + protected transient int sequenceId; + + public ActiveMQTempDestination() { + } + + public ActiveMQTempDestination(String name) { + super(name); + } + + public ActiveMQTempDestination(String connectionId, long sequenceId) { + super(connectionId+":"+sequenceId); + } + + public boolean isTemporary() { + return true; + } + + public void delete() throws JMSException { + connection.deleteTempDestination(this); + } + + public ActiveMQConnection getConnection() { + return connection; + } + + public void setConnection(ActiveMQConnection connection) { + this.connection = connection; + } + + public void setPhysicalName(String physicalName) { + super.setPhysicalName(physicalName); + if( !isComposite() ) { + // Parse off the sequenceId off the end. + int p = this.physicalName.lastIndexOf(":"); + sequenceId = Integer.parseInt(this.physicalName.substring(p+1)); + // The rest should be the connection id. + connectionId = this.physicalName.substring(0,p); + } + } + + public String getConnectionId() { + return connectionId; + } + + public int getSequenceId() { + return sequenceId; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/ActiveMQTempQueue.java b/activemq-core/src/main/java/org/activemq/command/ActiveMQTempQueue.java new file mode 100755 index 0000000000..16709f68b0 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ActiveMQTempQueue.java @@ -0,0 +1,65 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import javax.jms.JMSException; +import javax.jms.TemporaryQueue; + +/** + * @openwire:marshaller + * @version $Revision: 1.6 $ + */ +public class ActiveMQTempQueue extends ActiveMQTempDestination implements TemporaryQueue { + + private static final long serialVersionUID = 6683049467527633867L; + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.ACTIVEMQ_TEMP_QUEUE; + + public ActiveMQTempQueue() { + } + + public ActiveMQTempQueue(String name) { + super(name); + } + + public ActiveMQTempQueue(ConnectionId connectionId, long sequenceId) { + super(connectionId.getConnectionId(), sequenceId); + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + + public boolean isQueue() { + return true; + } + + public String getQueueName() throws JMSException { + return getPhysicalName(); + } + + public byte getDestinationType() { + return TEMP_QUEUE_TYPE; + } + + protected String getQualifiedPrefix() { + return TEMP_QUEUE_QUALIFED_PREFIX; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/ActiveMQTempTopic.java b/activemq-core/src/main/java/org/activemq/command/ActiveMQTempTopic.java new file mode 100755 index 0000000000..f173bedf02 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ActiveMQTempTopic.java @@ -0,0 +1,64 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import javax.jms.JMSException; +import javax.jms.TemporaryTopic; + +/** + * @openwire:marshaller + * @version $Revision: 1.6 $ + */ +public class ActiveMQTempTopic extends ActiveMQTempDestination implements TemporaryTopic { + + private static final long serialVersionUID = -4325596784597300253L; + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.ACTIVEMQ_TEMP_TOPIC; + + public ActiveMQTempTopic() { + } + + public ActiveMQTempTopic(String name) { + super(name); + } + + public ActiveMQTempTopic(ConnectionId connectionId, long sequenceId) { + super(connectionId.getConnectionId(), sequenceId); + } + + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public boolean isTopic() { + return true; + } + + public String getTopicName() throws JMSException { + return getPhysicalName(); + } + + public byte getDestinationType() { + return TEMP_TOPIC_TYPE; + } + + protected String getQualifiedPrefix() { + return TEMP_TOPIC_QUALIFED_PREFIX; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/ActiveMQTextMessage.java b/activemq-core/src/main/java/org/activemq/command/ActiveMQTextMessage.java new file mode 100755 index 0000000000..e823018a7f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ActiveMQTextMessage.java @@ -0,0 +1,135 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +import javax.jms.JMSException; +import javax.jms.MessageNotWriteableException; +import javax.jms.TextMessage; + +import org.activeio.ByteArrayInputStream; +import org.activeio.ByteArrayOutputStream; +import org.activeio.ByteSequence; +import org.activeio.command.WireFormat; +import org.activemq.ActiveMQConnection; +import org.activemq.util.JMSExceptionSupport; +import org.activemq.util.MarshallingSupport; + +/** + * + * @openwire:marshaller + * @version $Revision$ + */ +public class ActiveMQTextMessage extends ActiveMQMessage implements TextMessage { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.ACTIVEMQ_TEXT_MESSAGE; + + protected String text; + + public Message copy() { + ActiveMQTextMessage copy = new ActiveMQTextMessage(); + copy(copy); + return copy; + } + + private void copy(ActiveMQTextMessage copy) { + super.copy(copy); + copy.text = text; + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public void setText(String text) throws MessageNotWriteableException { + checkReadOnlyBody(); + this.text = text; + setContent(null); + } + + public String getText() throws JMSException { + if (text == null && getContent() != null) { + try { + ByteSequence bodyAsBytes = getContent(); + if (bodyAsBytes != null) { + InputStream is = new ByteArrayInputStream(bodyAsBytes); + if( isCompressed() ) { + is = new InflaterInputStream(is); + } + DataInputStream dataIn = new DataInputStream(is); + text = MarshallingSupport.readUTF8(dataIn); + dataIn.close(); + setContent(null); + } + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + return text; + } + + public void beforeMarshall(WireFormat wireFormat) throws IOException { + super.beforeMarshall(wireFormat); + + ByteSequence content = getContent(); + if (content == null && text!=null ) { + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + OutputStream os = bytesOut; + ActiveMQConnection connection = getConnection(); + if( connection!=null && connection.isUseCompression() ) { + compressed = true; + os = new DeflaterOutputStream(os); + } + DataOutputStream dataOut = new DataOutputStream(os); + MarshallingSupport.writeUTF8(dataOut, text); + dataOut.close(); + setContent(bytesOut.toByteSequence()); + } + } + + /** + * Clears out the message body. Clearing a message's body does not clear its header values or property entries. + *

+ *

If this message body was read-only, calling this method leaves the message body in the same state as an empty + * body in a newly created message. + * + * @throws JMSException if the JMS provider fails to clear the message body due to some internal error. + */ + public void clearBody() throws JMSException { + super.clearBody(); + this.text = null; + } + + public int getSize() { + if( size == 0 && content==null && text!=null ) { + size = AVERAGE_MESSAGE_SIZE_OVERHEAD; + if( marshalledProperties!=null ) + size += marshalledProperties.getLength(); + size = text.length()*2; + } + return super.getSize(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/ActiveMQTopic.java b/activemq-core/src/main/java/org/activemq/command/ActiveMQTopic.java new file mode 100755 index 0000000000..b7acb95b41 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ActiveMQTopic.java @@ -0,0 +1,60 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import javax.jms.JMSException; +import javax.jms.Topic; + +/** + * @openwire:marshaller + * @version $Revision: 1.5 $ + */ +public class ActiveMQTopic extends ActiveMQDestination implements Topic { + + private static final long serialVersionUID = 7300307405896488588L; + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.ACTIVEMQ_TOPIC; + + public ActiveMQTopic() { + } + + public ActiveMQTopic(String name) { + super(name); + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public boolean isTopic() { + return true; + } + + public String getTopicName() throws JMSException { + return getPhysicalName(); + } + + public byte getDestinationType() { + return TOPIC_TYPE; + } + + protected String getQualifiedPrefix() { + return TOPIC_QUALIFIED_PREFIX; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/BaseCommand.java b/activemq-core/src/main/java/org/activemq/command/BaseCommand.java new file mode 100755 index 0000000000..e0bdcb66b8 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/BaseCommand.java @@ -0,0 +1,126 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.LinkedHashMap; + +/** + * + * @openwire:marshaller + * @version $Revision: 1.11 $ + */ +abstract public class BaseCommand implements Command { + + protected short commandId; + protected boolean responseRequired; + + public void copy(BaseCommand copy) { + copy.commandId = commandId; + copy.responseRequired = responseRequired; + } + + public boolean isWireFormatInfo() { + return false; + } + + public boolean isBrokerInfo() { + return false; + } + + public boolean isResponse() { + return false; + } + + public boolean isMessageDispatch() { + return false; + } + + public boolean isMessage() { + return false; + } + + public boolean isMarshallAware() { + return false; + } + + /** + * @openwire:property version=1 + */ + public short getCommandId() { + return commandId; + } + + public void setCommandId(short commandId) { + this.commandId = commandId; + } + + /** + * @openwire:property version=1 + */ + public boolean isResponseRequired() { + return responseRequired; + } + + public void setResponseRequired(boolean responseRequired) { + this.responseRequired = responseRequired; + } + + public String toString() { + LinkedHashMap map = new LinkedHashMap(); + addFields(map, getClass()); + return simpleName(getClass())+" "+map; + } + + public static String simpleName(Class clazz) { + String name = clazz.getName(); + int p = name.lastIndexOf("."); + if( p >= 0 ) { + name = name.substring(p+1); + } + return name; + } + + + private void addFields(LinkedHashMap map, Class clazz) { + + if( clazz!=BaseCommand.class ) + addFields( map, clazz.getSuperclass() ); + + Field[] fields = clazz.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + if( Modifier.isStatic(field.getModifiers()) || + Modifier.isTransient(field.getModifiers()) || + Modifier.isPrivate(field.getModifiers()) ) { + continue; + } + + try { + map.put(field.getName(), field.get(this)); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/command/BrokerId.java b/activemq-core/src/main/java/org/activemq/command/BrokerId.java new file mode 100755 index 0000000000..1de9f5bcc6 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/BrokerId.java @@ -0,0 +1,73 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + + +/** + * + * @openwire:marshaller + * @version $Revision$ + */ +public class BrokerId implements DataStructure { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.BROKER_ID; + protected String brokerId; + + public BrokerId() { + } + + public BrokerId(String brokerId) { + this.brokerId = brokerId; + } + + public int hashCode() { + return brokerId.hashCode(); + } + + public boolean equals(Object o) { + if( this == o ) + return true; + if( o == null || o.getClass()!=BrokerId.class ) + return false; + BrokerId id = (BrokerId) o; + return brokerId.equals(id.brokerId); + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public String toString() { + return brokerId; + } + + /** + * @openwire:property version=1 + */ + public String getBrokerId() { + return brokerId; + } + public void setBrokerId(String brokerId) { + this.brokerId = brokerId; + } + + public boolean isMarshallAware() { + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/BrokerInfo.java b/activemq-core/src/main/java/org/activemq/command/BrokerInfo.java new file mode 100755 index 0000000000..2aaec223ca --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/BrokerInfo.java @@ -0,0 +1,105 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import org.activemq.state.CommandVisitor; + + +/** + * When a client connects to a broker, the broker send the client a BrokerInfo + * so that the client knows which broker node he's talking to and also any peers + * that the node has in his cluster. This is the broker helping the client out + * in discovering other nodes in the cluster. + * + * @openwire:marshaller + * @version $Revision: 1.7 $ + */ +public class BrokerInfo extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.BROKER_INFO; + BrokerId brokerId; + String brokerURL; + RedeliveryPolicy redeliveryPolicy; + + BrokerInfo peerBrokerInfos[]; + String brokerName; + + public boolean isBrokerInfo() { + return true; + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 cache=true + */ + public BrokerId getBrokerId() { + return brokerId; + } + public void setBrokerId(BrokerId brokerId) { + this.brokerId = brokerId; + } + + /** + * @openwire:property version=1 + */ + public String getBrokerURL() { + return brokerURL; + } + public void setBrokerURL(String brokerURL) { + this.brokerURL = brokerURL; + } + + /** + * @openwire:property version=1 + */ + public BrokerInfo[] getPeerBrokerInfos() { + return peerBrokerInfos; + } + public void setPeerBrokerInfos(BrokerInfo[] peerBrokerInfos) { + this.peerBrokerInfos = peerBrokerInfos; + } + + /** + * @openwire:property version=1 + */ + public RedeliveryPolicy getRedeliveryPolicy() { + return redeliveryPolicy; + } + public void setRedeliveryPolicy(RedeliveryPolicy redeliveryPolicy) { + this.redeliveryPolicy = redeliveryPolicy; + } + + /** + * @openwire:property version=1 + */ + public String getBrokerName() { + return brokerName; + } + public void setBrokerName(String brokerName) { + this.brokerName = brokerName; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + return visitor.processBrokerInfo( this ); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/Command.java b/activemq-core/src/main/java/org/activemq/command/Command.java new file mode 100755 index 0000000000..ae03e0880c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/Command.java @@ -0,0 +1,48 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import org.activemq.state.CommandVisitor; + +/** + * The Command Pattern so that we can send and receive commands + * on the different transports + * + * @version $Revision: 1.7 $ + */ +public interface Command extends DataStructure { + + void setCommandId(short value); + + /** + * @return the unique ID of this request used to map responses to requests + */ + short getCommandId(); + + void setResponseRequired(boolean responseRequired); + boolean isResponseRequired(); + + boolean isResponse(); + boolean isMessageDispatch(); + boolean isBrokerInfo(); + boolean isWireFormatInfo(); + boolean isMessage(); + + Response visit( CommandVisitor visitor) throws Throwable; +} diff --git a/activemq-core/src/main/java/org/activemq/command/CommandTypes.java b/activemq-core/src/main/java/org/activemq/command/CommandTypes.java new file mode 100755 index 0000000000..96f3dc58ee --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/CommandTypes.java @@ -0,0 +1,136 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +/** + * Holds the command id constants used by the command objects. + * + * @version $Revision: 1.21 $ + */ +public interface CommandTypes { + + // A marshaling layer can use this type to specify a null object. + byte NULL = 0; + + /////////////////////////////////////////////////// + // + // Info objects sent back and forth client/server when + // setting up a client connection. + // + /////////////////////////////////////////////////// + byte WIREFORMAT_INFO = 1; + byte BROKER_INFO = 2; + byte CONNECTION_INFO = 3; + byte SESSION_INFO = 4; + byte CONSUMER_INFO = 5; + byte PRODUCER_INFO = 6; + byte TRANSACTION_INFO = 7; + byte DESTINATION_INFO = 8; + byte REMOVE_SUBSCRIPTION_INFO = 9; + byte KEEP_ALIVE_INFO = 10; + byte SHUTDOWN_INFO = 11; + byte REMOVE_INFO = 12; + byte REDELIVERY_POLICY = 13; + byte CONTROL_COMMAND = 14; + byte FLUSH_COMMAND = 15; + + /////////////////////////////////////////////////// + // + // Messages that go back and forth between the client + // and the server. + // + /////////////////////////////////////////////////// + byte MESSAGE_DISPATCH = 21; + byte MESSAGE_ACK = 22; + + byte ACTIVEMQ_MESSAGE = 23; + byte ACTIVEMQ_BYTES_MESSAGE = 24; + byte ACTIVEMQ_MAP_MESSAGE = 25; + byte ACTIVEMQ_OBJECT_MESSAGE = 26; + byte ACTIVEMQ_STREAM_MESSAGE = 27; + byte ACTIVEMQ_TEXT_MESSAGE = 28; + + /////////////////////////////////////////////////// + // + // Command Response messages + // + /////////////////////////////////////////////////// + byte RESPONSE = 30; + byte EXCEPTION_RESPONSE = 31; + byte DATA_RESPONSE = 32; + byte DATA_ARRAY_RESPONSE = 33; + byte INTEGER_RESPONSE = 34; + + + /////////////////////////////////////////////////// + // + // Used by discovery + // + /////////////////////////////////////////////////// + byte DISCOVERY_EVENT = 40; + + /////////////////////////////////////////////////// + // + // Command object used by the Journal + // + /////////////////////////////////////////////////// + byte JOURNAL_ACK = 50; + byte JOURNAL_REMOVE = 52; + byte JOURNAL_TRACE = 53; + byte JOURNAL_TRANSACTION = 54; + byte DURABLE_SUBSCRIPTION_INFO = 55; + + /////////////////////////////////////////////////// + // + // Types used represent basic Java types. + // + /////////////////////////////////////////////////// + byte BYTE_TYPE = 70; + byte CHAR_TYPE = 71; + byte SHORT_TYPE = 72; + byte INTEGER_TYPE = 73; + byte LONG_TYPE = 74; + byte DOUBLE_TYPE = 75; + byte FLOAT_TYPE = 76; + byte STRING_TYPE = 77; + byte BOOLEAN_TYPE = 78; + byte BYTE_ARRAY_TYPE = 79; + + /////////////////////////////////////////////////// + // + // Data structures contained in the command objects. + // + /////////////////////////////////////////////////// + byte ACTIVEMQ_QUEUE = 100; + byte ACTIVEMQ_TOPIC = 101; + byte ACTIVEMQ_TEMP_QUEUE = 102; + byte ACTIVEMQ_TEMP_TOPIC = 103; + + byte MESSAGE_ID = 110; + byte ACTIVEMQ_LOCAL_TRANSACTION_ID = 111; + byte ACTIVEMQ_XA_TRANSACTION_ID = 112; + + byte CONNECTION_ID = 120; + byte SESSION_ID = 121; + byte CONSUMER_ID = 122; + byte PRODUCER_ID = 123; + byte BROKER_ID = 124; + + +} diff --git a/activemq-core/src/main/java/org/activemq/command/ConnectionId.java b/activemq-core/src/main/java/org/activemq/command/ConnectionId.java new file mode 100755 index 0000000000..42d5c29c4b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ConnectionId.java @@ -0,0 +1,90 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + + +/** + * + * @openwire:marshaller + * @version $Revision$ + */ +public class ConnectionId implements DataStructure { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.CONNECTION_ID; + + protected String connectionId; + + public ConnectionId() { + } + + public ConnectionId(String connectionId) { + this.connectionId = connectionId; + } + + public ConnectionId(ConnectionId id) { + this.connectionId = id.getConnectionId(); + } + + public ConnectionId(SessionId id) { + this.connectionId = id.getConnectionId(); + } + + public ConnectionId(ProducerId id) { + this.connectionId = id.getConnectionId(); + } + + public ConnectionId(ConsumerId id) { + this.connectionId = id.getConnectionId(); + } + + public int hashCode() { + return connectionId.hashCode(); + } + + public boolean equals(Object o) { + if( this == o ) + return true; + if( o == null || o.getClass()!=ConnectionId.class ) + return false; + ConnectionId id = (ConnectionId) o; + return connectionId.equals(id.connectionId); + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public String toString() { + return connectionId; + } + + /** + * @openwire:property version=1 + */ + public String getConnectionId() { + return connectionId; + } + public void setConnectionId(String connectionId) { + this.connectionId = connectionId; + } + + public boolean isMarshallAware() { + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/ConnectionInfo.java b/activemq-core/src/main/java/org/activemq/command/ConnectionInfo.java new file mode 100755 index 0000000000..178c6e01c4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ConnectionInfo.java @@ -0,0 +1,111 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import org.activemq.state.CommandVisitor; + + +/** + * + * @openwire:marshaller + * @version $Revision: 1.11 $ + */ +public class ConnectionInfo extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.CONNECTION_INFO; + + protected ConnectionId connectionId; + protected String clientId; + protected String userName; + protected String password; + protected BrokerId[] brokerPath; + + public ConnectionInfo() { + } + public ConnectionInfo(ConnectionId connectionId) { + this.connectionId=connectionId; + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 cache=true + */ + public ConnectionId getConnectionId() { + return connectionId; + } + public void setConnectionId(ConnectionId connectionId) { + this.connectionId = connectionId; + } + + /** + * @openwire:property version=1 + */ + public String getClientId() { + return clientId; + } + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public RemoveInfo createRemoveCommand() { + RemoveInfo command = new RemoveInfo(getConnectionId()); + command.setResponseRequired(isResponseRequired()); + return command; + } + + /** + * @openwire:property version=1 + */ + public String getPassword() { + return password; + } + public void setPassword(String password) { + this.password = password; + } + + /** + * @openwire:property version=1 + */ + public String getUserName() { + return userName; + } + public void setUserName(String userName) { + this.userName = userName; + } + + /** + * The route of brokers the command has moved through. + * + * @openwire:property version=1 cache=true + */ + public BrokerId[] getBrokerPath() { + return brokerPath; + } + public void setBrokerPath(BrokerId[] brokerPath) { + this.brokerPath = brokerPath; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + return visitor.processAddConnection( this ); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/ConsumerId.java b/activemq-core/src/main/java/org/activemq/command/ConsumerId.java new file mode 100755 index 0000000000..6830a873ba --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ConsumerId.java @@ -0,0 +1,124 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + + +/** + * + * @openwire:marshaller + * @version $Revision$ + */ +public class ConsumerId implements DataStructure { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.CONSUMER_ID; + + protected String connectionId; + protected long sessionId; + protected long consumerId; + + protected transient int hashCode; + protected transient String key; + protected transient SessionId parentId; + + public ConsumerId() { + } + + public ConsumerId(SessionId sessionId, long consumerId) { + this.connectionId = sessionId.getConnectionId(); + this.sessionId = sessionId.getSessionId(); + this.consumerId=consumerId; + } + + public ConsumerId(ConsumerId id) { + this.connectionId = id.getConnectionId(); + this.sessionId = id.getSessionId(); + this.consumerId=id.getConsumerId(); + } + + public SessionId getParentId() { + if( parentId == null ) { + parentId = new SessionId(this); + } + return parentId; + } + + public int hashCode() { + if( hashCode == 0 ) { + hashCode = connectionId.hashCode() ^ (int)sessionId ^ (int)consumerId; + } + return hashCode; + } + + public boolean equals(Object o) { + if( this == o ) + return true; + if( o == null || o.getClass()!=ConsumerId.class ) + return false; + ConsumerId id = (ConsumerId) o; + return sessionId==id.sessionId + && consumerId==id.consumerId + && connectionId.equals(id.connectionId); + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public String toString() { + if( key==null ) { + key = connectionId+":"+sessionId+":"+consumerId; + } + return key; + } + + /** + * @openwire:property version=1 + */ + public String getConnectionId() { + return connectionId; + } + public void setConnectionId(String connectionId) { + this.connectionId = connectionId; + } + + /** + * @openwire:property version=1 + */ + public long getSessionId() { + return sessionId; + } + public void setSessionId(long sessionId) { + this.sessionId = sessionId; + } + + + /** + * @openwire:property version=1 + */ + public long getConsumerId() { + return consumerId; + } + public void setConsumerId(long consumerId) { + this.consumerId = consumerId; + } + + public boolean isMarshallAware() { + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/ConsumerInfo.java b/activemq-core/src/main/java/org/activemq/command/ConsumerInfo.java new file mode 100755 index 0000000000..05b82565b3 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ConsumerInfo.java @@ -0,0 +1,294 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import org.activemq.filter.BooleanExpression; +import org.activemq.state.CommandVisitor; + + +/** + * + * @openwire:marshaller + * @version $Revision: 1.20 $ + */ +public class ConsumerInfo extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.CONSUMER_INFO; + + public static final byte HIGH_PRIORITY = 10; + public static final byte NORMAL_PRIORITY = 0; + public static final byte NETWORK_CONSUMER_PRIORITY = -5; + public static final byte LOW_PRIORITY = -10; + + + protected ConsumerId consumerId; + protected ActiveMQDestination destination; + protected int prefetchSize; + protected boolean browser; + protected boolean dispatchAsync; + protected String selector; + protected String subcriptionName; + protected boolean noLocal; + protected boolean exclusive; + protected boolean retroactive; + protected byte priority; + protected BrokerId[] brokerPath; + + protected transient BooleanExpression additionalPredicate; + protected transient boolean networkSubscription; //this subscription orginated from a network connection + + public ConsumerInfo() { + } + + public ConsumerInfo(ConsumerId consumerId) { + this.consumerId=consumerId; + } + + public ConsumerInfo(SessionInfo sessionInfo, long consumerId) { + this.consumerId = new ConsumerId(sessionInfo.getSessionId(), consumerId); + } + + public ConsumerInfo copy() { + ConsumerInfo info = new ConsumerInfo(); + copy(info); + return info; + } + + public void copy(ConsumerInfo info) { + super.copy(info); + info.consumerId = consumerId; + info.destination = destination; + info.prefetchSize = prefetchSize; + info.browser = browser; + info.dispatchAsync = dispatchAsync; + info.selector = selector; + info.subcriptionName=subcriptionName; + info.noLocal = noLocal; + info.exclusive = exclusive; + info.retroactive = retroactive; + info.priority = priority; + info.brokerPath=brokerPath; + } + + public boolean isDurable() { + return subcriptionName!=null; + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * Is used to uniquely identify the consumer to the broker. + * + * @openwire:property version=1 cache=true + */ + public ConsumerId getConsumerId() { + return consumerId; + } + public void setConsumerId(ConsumerId consumerId) { + this.consumerId = consumerId; + } + + /** + * Is this consumer a queue browser? + * + * @openwire:property version=1 + */ + public boolean isBrowser() { + return browser; + } + public void setBrowser(boolean browser) { + this.browser = browser; + } + + /** + * The destination that the consumer is interested in receiving messages from. + * This destination could be a composite destination. + * + * @openwire:property version=1 cache=true + */ + public ActiveMQDestination getDestination() { + return destination; + } + public void setDestination(ActiveMQDestination destination) { + this.destination = destination; + } + + /** + * How many messages a broker will send to the client without receiving an ack before + * he stops dispatching messages to the client. + * + * @openwire:property version=1 + */ + public int getPrefetchSize() { + return prefetchSize; + } + + public void setPrefetchSize(int prefetchSize) { + this.prefetchSize = prefetchSize; + } + + /** + * Should the broker dispatch a message to the consumer async? If he does it async, then + * he uses a more SEDA style of processing while if it is not done async, then he broker + * use a STP style of processing. STP is more appropriate in high bandwidth situations or when + * being used by and in vm transport. + * + * @openwire:property version=1 + */ + public boolean isDispatchAsync() { + return dispatchAsync; + } + public void setDispatchAsync(boolean dispatchAsync) { + this.dispatchAsync = dispatchAsync; + } + + /** + * The JMS selector used to filter out messages that this consumer + * is interested in. + * + * @openwire:property version=1 + */ + public String getSelector() { + return selector; + } + public void setSelector(String selector) { + this.selector = selector; + } + + /** + * Used to identify the name of a durable subscription. + * + * @openwire:property version=1 + */ + public String getSubcriptionName() { + return subcriptionName; + } + public void setSubcriptionName(String durableSubscriptionId) { + this.subcriptionName = durableSubscriptionId; + } + + /** + * Set noLocal to true to avoid receiving messages that were published locally on the same connection. + * + * @openwire:property version=1 + */ + public boolean isNoLocal() { + return noLocal; + } + public void setNoLocal(boolean noLocal) { + this.noLocal = noLocal; + } + + /** + * An exclusive consumer locks out other consumers from being able to receive messages + * from the destination. If there are multiple exclusive consumers for a destination, the first one + * created will be the exclusive consumer of the destination. + * + * @openwire:property version=1 + */ + public boolean isExclusive() { + return exclusive; + } + public void setExclusive(boolean exclusive) { + this.exclusive = exclusive; + } + + /** + * A retroactive consumer only has meaning for Topics. It allows a consumer + * to retroactively see messages sent prior to the consumer being created. If the + * consumer is not durable, it will be delivered the last message published to the topic. + * If the consumer is durable then it will receive all persistent messages that are + * still stored in persistent storage for that topic. + * + * @openwire:property version=1 + */ + public boolean isRetroactive() { + return retroactive; + } + public void setRetroactive(boolean retroactive) { + this.retroactive = retroactive; + } + + public RemoveInfo createRemoveCommand() { + RemoveInfo command = new RemoveInfo(getConsumerId()); + command.setResponseRequired(isResponseRequired()); + return command; + } + + /** + * The broker will avoid dispatching to a lower priority consumer if there are other higher priority + * consumers available to dispatch to. This allows letting the broker to have an affinity to + * higher priority consumers. Default priority is 0. + * + * @openwire:property version=1 + */ + public byte getPriority() { + return priority; + } + public void setPriority(byte priority) { + this.priority = priority; + } + + /** + * The route of brokers the command has moved through. + * + * @openwire:property version=1 cache=true + */ + public BrokerId[] getBrokerPath() { + return brokerPath; + } + public void setBrokerPath(BrokerId[] brokerPath) { + this.brokerPath = brokerPath; + } + + /** + * A transient additional predicate that can be used it inject additional predicates + * into the selector on the fly. Handy if if say a Security Broker interceptor wants to + * filter out messages based on security level of the consumer. + * + * @return + */ + public BooleanExpression getAdditionalPredicate() { + return additionalPredicate; + } + public void setAdditionalPredicate(BooleanExpression additionalPredicate) { + this.additionalPredicate = additionalPredicate; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + return visitor.processAddConsumer( this ); + } + + /** + * @return Returns the networkSubscription. + */ + public boolean isNetworkSubscription(){ + return networkSubscription; + } + + /** + * @param networkSubscription The networkSubscription to set. + */ + public void setNetworkSubscription(boolean networkSubscription){ + this.networkSubscription=networkSubscription; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/ControlCommand.java b/activemq-core/src/main/java/org/activemq/command/ControlCommand.java new file mode 100644 index 0000000000..1a8e12b94b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ControlCommand.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.command; + +import org.activemq.state.CommandVisitor; + +/** + * Used to start and stop transports as well as terminating clients. + * + * @openwire:marshaller + * + * @version $Revision: 1.1 $ + */ +public class ControlCommand extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE = CommandTypes.CONTROL_COMMAND; + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + private String command; + + /** + * @openwire:property version=1 + */ + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + if (command.equals("shutdown")) + System.exit(0); + return null; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/DataArrayResponse.java b/activemq-core/src/main/java/org/activemq/command/DataArrayResponse.java new file mode 100755 index 0000000000..2142f20ec5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/DataArrayResponse.java @@ -0,0 +1,52 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +/** + * + * @openwire:marshaller + * @version $Revision$ + */ +public class DataArrayResponse extends Response { + + DataStructure data[]; + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.DATA_ARRAY_RESPONSE; + + public DataArrayResponse() { + } + public DataArrayResponse(DataStructure data[]) { + this.data=data; + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 + */ + public DataStructure[] getData() { + return data; + } + public void setData(DataStructure[] data) { + this.data = data; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/DataResponse.java b/activemq-core/src/main/java/org/activemq/command/DataResponse.java new file mode 100755 index 0000000000..1c948c08f0 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/DataResponse.java @@ -0,0 +1,52 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +/** + * + * @openwire:marshaller + * @version $Revision$ + */ +public class DataResponse extends Response { + + DataStructure data; + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.DATA_RESPONSE; + + public DataResponse() { + } + public DataResponse(DataStructure data) { + this.data=data; + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 + */ + public DataStructure getData() { + return data; + } + public void setData(DataStructure data) { + this.data = data; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/DataStructure.java b/activemq-core/src/main/java/org/activemq/command/DataStructure.java new file mode 100755 index 0000000000..149922ed01 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/DataStructure.java @@ -0,0 +1,32 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +/** + * @version $Revision$ + */ +public interface DataStructure { + + /** + * @return The type of the data structure + */ + byte getDataStructureType(); + boolean isMarshallAware(); + +} diff --git a/activemq-core/src/main/java/org/activemq/command/DestinationInfo.java b/activemq-core/src/main/java/org/activemq/command/DestinationInfo.java new file mode 100755 index 0000000000..ba27356ed0 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/DestinationInfo.java @@ -0,0 +1,129 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.IOException; + +import org.activemq.state.CommandVisitor; + +/** + * Used to create and destroy destinations on the broker. + * + * @openwire:marshaller + * @version $Revision: 1.9 $ + */ +public class DestinationInfo extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.DESTINATION_INFO; + + public static final byte ADD_OPERATION_TYPE=0; + public static final byte REMOVE_OPERATION_TYPE=1; + + protected ConnectionId connectionId; + protected ActiveMQDestination destination; + protected byte operationType; + protected long timeout; + protected BrokerId[] brokerPath; + + public DestinationInfo() { + } + + public DestinationInfo(ConnectionId connectionId, byte operationType, ActiveMQDestination destination) { + this.connectionId=connectionId; + this.operationType=operationType; + this.destination=destination; + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public boolean isAddOperation() { + return operationType == ADD_OPERATION_TYPE; + } + + public boolean isRemoveOperation() { + return operationType == REMOVE_OPERATION_TYPE; + } + + /** + * @openwire:property version=1 cache=true + */ + public ConnectionId getConnectionId() { + return connectionId; + } + public void setConnectionId(ConnectionId connectionId) { + this.connectionId = connectionId; + } + + /** + * @openwire:property version=1 cache=true + */ + public ActiveMQDestination getDestination() { + return destination; + } + + public void setDestination(ActiveMQDestination destination) { + this.destination = destination; + } + + /** + * @openwire:property version=1 + */ + public byte getOperationType() { + return operationType; + } + + public void setOperationType(byte operationType) { + this.operationType = operationType; + } + + /** + * @openwire:property version=1 + */ + public long getTimeout() { + return timeout; + } + + public void setTimeout(long timeout) { + this.timeout = timeout; + } + + /** + * The route of brokers the command has moved through. + * + * @openwire:property version=1 cache=true + */ + public BrokerId[] getBrokerPath() { + return brokerPath; + } + public void setBrokerPath(BrokerId[] brokerPath) { + this.brokerPath = brokerPath; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + if( isAddOperation() ) { + return visitor.processAddDestination( this ); + } else if( isRemoveOperation() ) { + return visitor.processRemoveDestination( this ); + } + throw new IOException("Unknown operation type: "+getOperationType()); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/DiscoveryEvent.java b/activemq-core/src/main/java/org/activemq/command/DiscoveryEvent.java new file mode 100755 index 0000000000..0d932c6e1e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/DiscoveryEvent.java @@ -0,0 +1,64 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + + +/** + * Represents a discovery event containing the details of the service + * + * @version $Revision$ + */ +public class DiscoveryEvent implements DataStructure { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.DISCOVERY_EVENT; + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + protected String serviceName; + protected String brokerName; + + public DiscoveryEvent(String serviceName) { + this.serviceName = serviceName; + } + + /** + * @openwire:property version=1 + */ + public String getServiceName() { + return serviceName; + } + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getBrokerName(){ + return brokerName; + } + + public void setBrokerName(String name){ + this.brokerName = name; + } + + public boolean isMarshallAware() { + return false; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/ExceptionResponse.java b/activemq-core/src/main/java/org/activemq/command/ExceptionResponse.java new file mode 100755 index 0000000000..29537d6fab --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ExceptionResponse.java @@ -0,0 +1,56 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +/** + * + * @openwire:marshaller + * @version $Revision: 1.4 $ + */ +public class ExceptionResponse extends Response { + + Throwable exception; + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.EXCEPTION_RESPONSE; + + public ExceptionResponse() { + } + public ExceptionResponse(Throwable e) { + setException(e); + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 + */ + public Throwable getException() { + return exception; + } + + public void setException(Throwable exception) { + this.exception = exception; + } + + public boolean isException() { + return true; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/FlushCommand.java b/activemq-core/src/main/java/org/activemq/command/FlushCommand.java new file mode 100644 index 0000000000..32b78c8d2a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/FlushCommand.java @@ -0,0 +1,43 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import org.activemq.state.CommandVisitor; + +/** + * An indication to the transport layer that a flush is required. + * + * @openwire:marshaller + * @version $Revision$ + */ +public class FlushCommand extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.FLUSH_COMMAND; + public static final Command COMMAND = new FlushCommand(); + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + return visitor.processFlush( this ); + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/command/IntegerResponse.java b/activemq-core/src/main/java/org/activemq/command/IntegerResponse.java new file mode 100755 index 0000000000..ccd97cd7de --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/IntegerResponse.java @@ -0,0 +1,52 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +/** + * + * @openwire:marshaller + * @version $Revision$ + */ +public class IntegerResponse extends Response { + + int result; + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.INTEGER_RESPONSE; + + public IntegerResponse() { + } + public IntegerResponse(int result) { + this.result=result; + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 + */ + public int getResult() { + return result; + } + public void setResult(int result) { + this.result = result; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/JournalQueueAck.java b/activemq-core/src/main/java/org/activemq/command/JournalQueueAck.java new file mode 100755 index 0000000000..66f9664ff6 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/JournalQueueAck.java @@ -0,0 +1,65 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +/** + * + * @openwire:marshaller + * @version $Revision$ + */ +public class JournalQueueAck implements DataStructure { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.JOURNAL_REMOVE; + + ActiveMQDestination destination; + MessageAck messageAck; + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 + */ + public ActiveMQDestination getDestination() { + return destination; + } + + + public void setDestination(ActiveMQDestination destination) { + this.destination = destination; + } + + + /** + * @openwire:property version=1 + */ + public MessageAck getMessageAck() { + return messageAck; + } + + + public void setMessageAck(MessageAck messageAck) { + this.messageAck = messageAck; + } + + public boolean isMarshallAware() { + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/JournalTopicAck.java b/activemq-core/src/main/java/org/activemq/command/JournalTopicAck.java new file mode 100755 index 0000000000..de01dd5906 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/JournalTopicAck.java @@ -0,0 +1,111 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +/** + * + * @openwire:marshaller + * @version $Revision$ + */ +public class JournalTopicAck implements DataStructure { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.JOURNAL_ACK; + + ActiveMQDestination destination; + String clientId; + String subscritionName; + MessageId messageId; + long messageSequenceId; + TransactionId transactionId; + + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 + */ + public ActiveMQDestination getDestination() { + return destination; + } + + public void setDestination(ActiveMQDestination destination) { + this.destination = destination; + } + + /** + * @openwire:property version=1 + */ + public MessageId getMessageId() { + return messageId; + } + + public void setMessageId(MessageId messageId) { + this.messageId = messageId; + } + + /** + * @openwire:property version=1 + */ + public long getMessageSequenceId() { + return messageSequenceId; + } + + public void setMessageSequenceId(long messageSequenceId) { + this.messageSequenceId = messageSequenceId; + } + + /** + * @openwire:property version=1 + */ + public String getSubscritionName() { + return subscritionName; + } + + public void setSubscritionName(String subscritionName) { + this.subscritionName = subscritionName; + } + + /** + * @openwire:property version=1 + */ + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + /** + * @openwire:property version=1 + */ + public TransactionId getTransactionId() { + return transactionId; + } + + public void setTransactionId(TransactionId transaction) { + this.transactionId = transaction; + } + + public boolean isMarshallAware() { + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/JournalTrace.java b/activemq-core/src/main/java/org/activemq/command/JournalTrace.java new file mode 100755 index 0000000000..8edb2f6b4d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/JournalTrace.java @@ -0,0 +1,53 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +/** + * + * @openwire:marshaller + * @version $Revision: 1.6 $ + */ +public class JournalTrace implements DataStructure { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.JOURNAL_TRACE; + + private String message; + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 + */ + public String getMessage() { + return message; + } + + /** + * @openwire:property version=1 + */ + public void setMessage(String message) { + this.message = message; + } + + public boolean isMarshallAware() { + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/JournalTransaction.java b/activemq-core/src/main/java/org/activemq/command/JournalTransaction.java new file mode 100755 index 0000000000..0629078e22 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/JournalTransaction.java @@ -0,0 +1,87 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + + +/** + * @openwire:marshaller + */ +public class JournalTransaction implements DataStructure { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.JOURNAL_TRANSACTION; + + public static final byte XA_PREPARE=1; + public static final byte XA_COMMIT=2; + public static final byte XA_ROLLBACK=3; + public static final byte LOCAL_COMMIT=4; + public static final byte LOCAL_ROLLBACK=5; + + public byte type; + public boolean wasPrepared; + public TransactionId transactionId; + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public JournalTransaction(byte type, TransactionId transactionId, boolean wasPrepared) { + this.type = type; + this.transactionId = transactionId; + this.wasPrepared=wasPrepared; + } + + public JournalTransaction() { + } + + /** + * @openwire:property version=1 + */ + public TransactionId getTransactionId() { + return transactionId; + } + + public void setTransactionId(TransactionId transactionId) { + this.transactionId = transactionId; + } + + /** + * @openwire:property version=1 + */ + public byte getType() { + return type; + } + public void setType(byte type) { + this.type = type; + } + + /** + * @openwire:property version=1 + */ + public boolean getWasPrepared() { + return wasPrepared; + } + + public void setWasPrepared(boolean wasPrepared) { + this.wasPrepared = wasPrepared; + } + + public boolean isMarshallAware() { + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/KeepAliveInfo.java b/activemq-core/src/main/java/org/activemq/command/KeepAliveInfo.java new file mode 100755 index 0000000000..b197e7c728 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/KeepAliveInfo.java @@ -0,0 +1,77 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import org.activemq.state.CommandVisitor; + +/** + * @openwire:marshaller + * @version $Revision$ + */ +public class KeepAliveInfo implements Command { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.KEEP_ALIVE_INFO; + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public void setCommandId(short value) { + } + + public short getCommandId() { + return 0; + } + + public void setResponseRequired(boolean responseRequired) { + } + + public boolean isResponseRequired() { + return false; + } + + public boolean isResponse() { + return false; + } + + public boolean isMessageDispatch() { + return false; + } + + public boolean isMessage() { + return false; + } + + public boolean isBrokerInfo() { + return false; + } + + public boolean isWireFormatInfo() { + return false; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + return visitor.processKeepAlive( this ); + } + + public boolean isMarshallAware() { + return false; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/LocalTransactionId.java b/activemq-core/src/main/java/org/activemq/command/LocalTransactionId.java new file mode 100755 index 0000000000..09ed3b2307 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/LocalTransactionId.java @@ -0,0 +1,104 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +/** + * @openwire:marshaller + * @version $Revision: 1.11 $ + */ +public class LocalTransactionId extends TransactionId { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.ACTIVEMQ_LOCAL_TRANSACTION_ID; + + protected ConnectionId connectionId; + protected long transactionId; + + private transient String transactionKey; + private transient int hashCode; + + public LocalTransactionId() { + } + + public LocalTransactionId(ConnectionId connectionId, long transactionId) { + this.connectionId=connectionId; + this.transactionId=transactionId; + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public boolean isXATransaction() { + return false; + } + + public boolean isLocalTransaction() { + return true; + } + + public String getTransactionKey() { + if( transactionKey==null ) { + transactionKey = "TX:"+connectionId+":"+transactionId; + } + return transactionKey; + } + + public String toString() { + return getTransactionKey(); + } + + public int hashCode() { + if( hashCode == 0 ) { + hashCode = connectionId.hashCode() ^ (int)transactionId; + } + return hashCode; + } + + public boolean equals(Object o) { + if( this == o ) + return true; + if( o == null || o.getClass()!=LocalTransactionId.class ) + return false; + LocalTransactionId tx = (LocalTransactionId) o; + return transactionId==tx.transactionId + && connectionId.equals(tx.connectionId); + } + + /** + * @openwire:property version=1 + */ + public long getTransactionId() { + return transactionId; + } + public void setTransactionId(long transactionId) { + this.transactionId = transactionId; + } + + /** + * @openwire:property version=1 cache=true + */ + public ConnectionId getConnectionId() { + return connectionId; + } + public void setConnectionId(ConnectionId connectionId) { + this.connectionId = connectionId; + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/command/MarshallAware.java b/activemq-core/src/main/java/org/activemq/command/MarshallAware.java new file mode 100755 index 0000000000..6ce3c91c07 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/MarshallAware.java @@ -0,0 +1,36 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.IOException; + +import org.activeio.ByteSequence; +import org.activeio.command.WireFormat; + +public interface MarshallAware { + + public void beforeMarshall(WireFormat wireFormat) throws IOException; + public void afterMarshall(WireFormat wireFormat) throws IOException; + + public void beforeUnmarshall(WireFormat wireFormat) throws IOException; + public void afterUnmarshall(WireFormat wireFormat) throws IOException; + + public void setCachedMarshalledForm(WireFormat wireFormat, ByteSequence data); + public ByteSequence getCachedMarshalledForm(WireFormat wireFormat); +} diff --git a/activemq-core/src/main/java/org/activemq/command/Message.java b/activemq-core/src/main/java/org/activemq/command/Message.java new file mode 100755 index 0000000000..2b892bc08a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/Message.java @@ -0,0 +1,620 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.activeio.ByteArrayInputStream; +import org.activeio.ByteArrayOutputStream; +import org.activeio.ByteSequence; +import org.activeio.command.WireFormat; +import org.activemq.ActiveMQConnection; +import org.activemq.advisory.AdvisorySupport; +import org.activemq.broker.region.MessageReference; +import org.activemq.util.MarshallingSupport; + +/** + * Represents an ActiveMQ message + * + * @openwire:marshaller + * @version $Revision$ + */ +abstract public class Message extends BaseCommand implements MarshallAware, MessageReference { + + public static final int AVERAGE_MESSAGE_SIZE_OVERHEAD = 300; + + protected MessageId messageId; + protected ActiveMQDestination originalDestination; + protected TransactionId originalTransactionId; + + protected ProducerId producerId; + protected ActiveMQDestination destination; + protected TransactionId transactionId; + + protected long expiration; + protected long timestamp; + protected long arrival; + protected String correlationId; + protected ActiveMQDestination replyTo; + protected boolean persistent; + protected String type; + protected byte priority; + protected String groupID; + protected int groupSequence; + protected ConsumerId targetConsumerId; + protected boolean compressed = false; + protected String userID; + + protected ByteSequence content; + protected ByteSequence marshalledProperties; + protected DataStructure dataStructure; + protected int redeliveryCounter; + + protected transient int size; + protected transient HashMap properties; + protected transient boolean readOnlyProperties = false; + protected transient boolean readOnlyBody = false; + protected transient boolean recievedByDFBridge = false; + + private BrokerId [] brokerPath; + private transient short referenceCount; + private transient ActiveMQConnection connection; + private transient org.activemq.broker.region.Destination regionDestination; + private WireFormat cachedWireFormat; + private ByteSequence cachedWireFormatData; + + abstract public Message copy(); + + protected void copy(Message copy) { + super.copy(copy); + copy.producerId = producerId; + copy.transactionId = transactionId; + copy.destination = destination; + copy.messageId = messageId; + copy.originalDestination = originalDestination; + copy.originalTransactionId = originalTransactionId; + copy.expiration = expiration; + copy.timestamp = timestamp; + copy.correlationId = correlationId; + copy.replyTo = replyTo; + copy.persistent = persistent; + copy.redeliveryCounter = redeliveryCounter; + copy.type = type; + copy.priority = priority; + copy.size = size; + copy.groupID = groupID; + copy.userID = userID; + copy.groupSequence = groupSequence; + if( properties!=null ) + copy.properties = new HashMap(properties); + else + copy.properties = properties; + copy.content = content; + copy.marshalledProperties = marshalledProperties; + copy.dataStructure = dataStructure; + copy.readOnlyProperties = readOnlyProperties; + copy.readOnlyBody = readOnlyBody; + copy.compressed = compressed; + copy.recievedByDFBridge = recievedByDFBridge; + } + + public Object getProperty(String name) throws IOException { + if( properties == null ) { + if( marshalledProperties ==null ) + return null; + properties = unmarsallProperties(marshalledProperties); + } + return properties.get(name); + } + + public Map getProperties() throws IOException { + if( properties == null ) { + if( marshalledProperties==null ) + return Collections.EMPTY_MAP; + properties = unmarsallProperties(marshalledProperties); + } + return Collections.unmodifiableMap(properties); + } + + public void clearProperties() { + marshalledProperties = null; + properties=null; + } + + public void setProperties(Map properties) throws IOException { + lazyCreateProperties(); + properties.putAll(properties); + } + + public void setProperty(String name, Object value) throws IOException { + lazyCreateProperties(); + properties.put(name, value); + } + + protected void lazyCreateProperties() throws IOException { + if( properties == null ) { + if( marshalledProperties == null ) { + properties = new HashMap(); + } else { + properties = unmarsallProperties(marshalledProperties); + marshalledProperties = null; + } + } + } + + private HashMap unmarsallProperties(ByteSequence marshalledProperties) throws IOException { + return MarshallingSupport.unmarshalPrimitiveMap(new DataInputStream(new ByteArrayInputStream(marshalledProperties))); + } + + public void beforeMarshall(WireFormat wireFormat) throws IOException { + // Need to marshal the properties. + if( marshalledProperties==null && properties!=null ) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream os = new DataOutputStream(baos); + MarshallingSupport.marshalPrimitiveMap(properties, os); + os.close(); + marshalledProperties = baos.toByteSequence(); + } + } + + public void afterMarshall(WireFormat wireFormat) throws IOException { + } + + public void beforeUnmarshall(WireFormat wireFormat) throws IOException { + } + + public void afterUnmarshall(WireFormat wireFormat) throws IOException { + } + + + /////////////////////////////////////////////////////////////////// + // + // Simple Field accessors + // + /////////////////////////////////////////////////////////////////// + + /** + * @openwire:property version=1 cache=true + */ + public ProducerId getProducerId() { + return producerId; + } + public void setProducerId(ProducerId producerId) { + this.producerId = producerId; + } + + /** + * @openwire:property version=1 cache=true + */ + public ActiveMQDestination getDestination() { + return destination; + } + public void setDestination(ActiveMQDestination destination) { + this.destination = destination; + } + + /** + * @openwire:property version=1 cache=true + */ + public TransactionId getTransactionId() { + return transactionId; + } + public void setTransactionId(TransactionId transactionId) { + this.transactionId = transactionId; + } + + public boolean isInTransaction() { + return transactionId!=null; + } + + + /** + * @openwire:property version=1 cache=true + */ + public ActiveMQDestination getOriginalDestination() { + return originalDestination; + } + public void setOriginalDestination(ActiveMQDestination destination) { + this.originalDestination = destination; + } + + /** + * @openwire:property version=1 + */ + public MessageId getMessageId() { + return messageId; + } + + public void setMessageId(MessageId messageId) { + this.messageId = messageId; + } + + /** + * @openwire:property version=1 cache=true + */ + public TransactionId getOriginalTransactionId() { + return originalTransactionId; + } + public void setOriginalTransactionId(TransactionId transactionId) { + this.originalTransactionId = transactionId; + } + + /** + * @openwire:property version=1 + */ + public String getGroupID() { + return groupID; + } + public void setGroupID(String groupID) { + this.groupID = groupID; + } + + /** + * @openwire:property version=1 + */ + public int getGroupSequence() { + return groupSequence; + } + public void setGroupSequence(int groupSequence) { + this.groupSequence = groupSequence; + } + + /** + * @openwire:property version=1 + */ + public String getCorrelationId() { + return correlationId; + } + public void setCorrelationId(String correlationId) { + this.correlationId = correlationId; + } + + /** + * @openwire:property version=1 + */ + public boolean isPersistent() { + return persistent; + } + public void setPersistent(boolean deliveryMode) { + this.persistent = deliveryMode; + } + + /** + * @openwire:property version=1 + */ + public long getExpiration() { + return expiration; + } + public void setExpiration(long expiration) { + this.expiration = expiration; + } + + /** + * @openwire:property version=1 + */ + public byte getPriority() { + return priority; + } + public void setPriority(byte priority) { + this.priority = priority; + } + + /** + * @openwire:property version=1 + */ + public ActiveMQDestination getReplyTo() { + return replyTo; + } + public void setReplyTo(ActiveMQDestination replyTo) { + this.replyTo = replyTo; + } + + /** + * @openwire:property version=1 + */ + public long getTimestamp() { + return timestamp; + } + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + /** + * @openwire:property version=1 + */ + public String getType() { + return type; + } + public void setType(String type) { + this.type = type; + } + + /** + * @openwire:property version=1 + */ + public ByteSequence getContent() { + return content; + } + public void setContent(ByteSequence content) { + this.content = content; + } + + /** + * @openwire:property version=1 + */ + public ByteSequence getMarshalledProperties() { + return marshalledProperties; + } + public void setMarshalledProperties(ByteSequence marshalledProperties) { + this.marshalledProperties = marshalledProperties; + } + + /** + * @openwire:property version=1 + */ + public DataStructure getDataStructure() { + return dataStructure; + } + public void setDataStructure(DataStructure data) { + this.dataStructure = data; + } + + /** + * Can be used to route the message to a specific consumer. Should + * be null to allow the broker use normal JMS routing semantics. If + * the target consumer id is an active consumer on the broker, the message + * is dropped. Used by the AdvisoryBroker to replay advisory messages + * to a specific consumer. + * + * @openwire:property version=1 cache=true + */ + public ConsumerId getTargetConsumerId() { + return targetConsumerId; + } + public void setTargetConsumerId(ConsumerId targetConsumerId) { + this.targetConsumerId = targetConsumerId; + } + + public boolean isExpired() { + // TODO: need to be implemented. + return false; + } + + public boolean isAdvisory(){ + return type != null && type.equals(AdvisorySupport.ADIVSORY_MESSAGE_TYPE); + } + + /** + * @openwire:property version=1 + */ + public boolean isCompressed() { + return compressed; + } + public void setCompressed(boolean compressed) { + this.compressed = compressed; + } + + public boolean isRedelivered() { + return redeliveryCounter>0; + } + + public void setRedelivered(boolean redelivered) { + if( redelivered ) { + if( !isRedelivered() ) { + setRedeliveryCounter(1); + } + } else { + if( isRedelivered() ) { + setRedeliveryCounter(0); + } + } + } + + public void incrementRedeliveryCounter() { + redeliveryCounter++; + } + + /** + * @openwire:property version=1 + */ + public int getRedeliveryCounter() { + return redeliveryCounter; + } + public void setRedeliveryCounter(int deliveryCounter) { + this.redeliveryCounter = deliveryCounter; + } + + /** + * The route of brokers the command has moved through. + * + * @openwire:property version=1 cache=true + */ + public BrokerId[] getBrokerPath() { + return brokerPath; + } + public void setBrokerPath(BrokerId[] brokerPath) { + this.brokerPath = brokerPath; + } + + public boolean isReadOnlyProperties() { + return readOnlyProperties; + } + public void setReadOnlyProperties(boolean readOnlyProperties) { + this.readOnlyProperties = readOnlyProperties; + } + public boolean isReadOnlyBody() { + return readOnlyBody; + } + public void setReadOnlyBody(boolean readOnlyBody) { + this.readOnlyBody = readOnlyBody; + } + + public ActiveMQConnection getConnection() { + return this.connection; + } + public void setConnection(ActiveMQConnection connection) { + this.connection = connection; + } + + /** + * Used to schedule the arrival time of a message to a broker. The broker will + * not dispatch a message to a consumer until it's arrival time has elapsed. + * + * @openwire:property version=1 + */ + public long getArrival() { + return arrival; + } + public void setArrival(long arrival) { + this.arrival = arrival; + } + + + /** + * Only set by the broker and defines the userID of the producer connection who + * sent this message. This is an optional field, it needs to be enabled on the + * broker to have this field populated. + * + * @openwire:property version=1 + */ + public String getUserID() { + return userID; + } + + public void setUserID(String jmsxUserID) { + this.userID = jmsxUserID; + } + + public int getReferenceCount() { + return referenceCount; + } + + public Message getMessageHardRef() { + return this; + } + + public Message getMessage() throws IOException { + return this; + } + + public org.activemq.broker.region.Destination getRegionDestination() { + return regionDestination; + } + + public void setRegionDestination(org.activemq.broker.region.Destination destination) { + this.regionDestination = destination; + } + + public boolean isMarshallAware() { + return true; + } + + synchronized public ByteSequence getCachedMarshalledForm(WireFormat wireFormat) { + if( cachedWireFormat == null || !cachedWireFormat.equals(wireFormat) ) { + return null; + } + return cachedWireFormatData; + } + + synchronized public void evictMarshlledForm() { + cachedWireFormat = null; + cachedWireFormatData = null; + } + + synchronized public void setCachedMarshalledForm(WireFormat wireFormat, ByteSequence data) { + cachedWireFormat = wireFormat; + cachedWireFormatData = data; + + int sizeChange=0; + synchronized (this) { + if( referenceCount > 0 ) { + sizeChange = getSize(); + this.size=0; + sizeChange -= getSize(); + } + } + + if( sizeChange!=0 && regionDestination!=null ) + regionDestination.getUsageManager().decreaseUsage(sizeChange); + + } + + public int incrementReferenceCount() { + int rc; + int size; + synchronized (this) { + rc = ++referenceCount; + size = getSize(); + } + + if( rc==1 && regionDestination!=null ) + regionDestination.getUsageManager().increaseUsage(size); + + return rc; + } + + synchronized public int decrementReferenceCount() { + int rc; + int size; + synchronized (this) { + rc = --referenceCount; + size = getSize(); + } + + if( rc==0 && regionDestination!=null ) + regionDestination.getUsageManager().decreaseUsage(size); + + return rc; + } + + public int getSize() { + if( size == 0 ) { + size = AVERAGE_MESSAGE_SIZE_OVERHEAD; + if( marshalledProperties!=null ) + size += marshalledProperties.getLength(); + if( content!=null ) + size += content.getLength(); + if( cachedWireFormatData !=null ) + size += cachedWireFormatData.getLength() + 12; + else + size *= 2; // Estimate what the cached data will add. + } + return size; + } + + /** + * @return Returns the recievedByDFBridge. + */ + public boolean isRecievedByDFBridge(){ + return recievedByDFBridge; + } + + /** + * @param recievedByDFBridge The recievedByDFBridge to set. + */ + public void setRecievedByDFBridge(boolean recievedByDFBridge){ + this.recievedByDFBridge=recievedByDFBridge; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/MessageAck.java b/activemq-core/src/main/java/org/activemq/command/MessageAck.java new file mode 100755 index 0000000000..861357bfb4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/MessageAck.java @@ -0,0 +1,188 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import org.activemq.state.CommandVisitor; + + +/** + * + * @openwire:marshaller + * @version $Revision: 1.11 $ + */ +public class MessageAck extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.MESSAGE_ACK; + + /** + * Used to let the broker know that the message has been delivered to the + * client. Message will still be retained until an standard ack is received. + * This is used get the broker to send more messages past prefetch limits when + * an standard ack has not been sent. + */ + public static final byte DELIVERED_ACK_TYPE=0; + + /** + * The standard ack case where a client wants the message to be discarded. + */ + public static final byte STANDARD_ACK_TYPE=2; + + /** + * In case the client want's to explicitly let the broker know that a + * message was not processed and the message was considered a poison message. + */ + public static final byte POSION_ACK_TYPE=1; + + protected byte ackType; + protected ConsumerId consumerId; + protected MessageId firstMessageId; + protected MessageId lastMessageId; + protected ActiveMQDestination destination; + protected TransactionId transactionId; + protected int messageCount; + + protected transient String consumerKey; + + public MessageAck() { + } + + public MessageAck(MessageDispatch md, byte ackType, int messageCount) { + this.ackType = ackType; + this.consumerId = md.getConsumerId(); + this.destination = md.getDestination(); + this.lastMessageId = md.getMessage().getMessageId(); + this.messageCount=messageCount; + } + + public void copy(MessageAck copy) { + super.copy(copy); + copy.firstMessageId = firstMessageId; + copy.lastMessageId = lastMessageId; + copy.destination = destination; + copy.transactionId = transactionId; + copy.ackType = ackType; + copy.consumerId = consumerId; + } + + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public boolean isPoisonAck() { + return ackType==POSION_ACK_TYPE; + } + + public boolean isStandardAck() { + return ackType==STANDARD_ACK_TYPE; + } + + public boolean isDeliveredAck() { + return ackType==DELIVERED_ACK_TYPE; + } + + /** + * @openwire:property version=1 cache=true + */ + public ActiveMQDestination getDestination() { + return destination; + } + public void setDestination(ActiveMQDestination destination) { + this.destination = destination; + } + + /** + * @openwire:property version=1 cache=true + */ + public TransactionId getTransactionId() { + return transactionId; + } + public void setTransactionId(TransactionId transactionId) { + this.transactionId = transactionId; + } + + public boolean isInTransaction() { + return transactionId!=null; + } + + /** + * @openwire:property version=1 cache=true + */ + public ConsumerId getConsumerId() { + return consumerId; + } + public void setConsumerId(ConsumerId consumerId) { + this.consumerId = consumerId; + } + + /** + * @openwire:property version=1 + */ + public byte getAckType() { + return ackType; + } + public void setAckType(byte ackType) { + this.ackType = ackType; + } + + /** + * @openwire:property version=1 + */ + public MessageId getFirstMessageId() { + return firstMessageId; + } + public void setFirstMessageId(MessageId firstMessageId) { + this.firstMessageId = firstMessageId; + } + + /** + * @openwire:property version=1 + */ + public MessageId getLastMessageId() { + return lastMessageId; + } + public void setLastMessageId(MessageId lastMessageId) { + this.lastMessageId = lastMessageId; + } + + /** + * The number of messages being acknowledged in the range. + * @openwire:property version=1 + */ + public int getMessageCount() { + return messageCount; + } + public void setMessageCount(int messageCount) { + this.messageCount = messageCount; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + return visitor.processMessageAck( this ); + } + + /** + * A helper method to allow a single message ID to be acknowledged + */ + public void setMessageID(MessageId messageID) { + setFirstMessageId(messageID); + setLastMessageId(messageID); + setMessageCount(1); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/MessageDispatch.java b/activemq-core/src/main/java/org/activemq/command/MessageDispatch.java new file mode 100755 index 0000000000..7d56c56cc2 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/MessageDispatch.java @@ -0,0 +1,108 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import org.activemq.state.CommandVisitor; + + +/** + * + * @openwire:marshaller + * @version $Revision$ + */ +public class MessageDispatch extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.MESSAGE_DISPATCH; + + protected ConsumerId consumerId; + protected ActiveMQDestination destination; + protected Message message; + protected int redeliveryCounter; + + transient protected long deliverySequenceId; + transient protected Object consumer; + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public boolean isMessageDispatch() { + return true; + } + + /** + * @openwire:property version=1 cache=true + */ + public ConsumerId getConsumerId() { + return consumerId; + } + public void setConsumerId(ConsumerId consumerId) { + this.consumerId = consumerId; + } + + /** + * @openwire:property version=1 cache=true + */ + public ActiveMQDestination getDestination() { + return destination; + } + public void setDestination(ActiveMQDestination destination) { + this.destination = destination; + } + + /** + * @openwire:property version=1 + */ + public Message getMessage() { + return message; + } + public void setMessage(Message message) { + this.message = message; + } + + public long getDeliverySequenceId() { + return deliverySequenceId; + } + public void setDeliverySequenceId(long deliverySequenceId) { + this.deliverySequenceId = deliverySequenceId; + } + + /** + * @openwire:property version=1 + */ + public int getRedeliveryCounter() { + return redeliveryCounter; + } + public void setRedeliveryCounter(int deliveryCounter) { + this.redeliveryCounter = deliveryCounter; + } + + public Object getConsumer() { + return consumer; + } + + public void setConsumer(Object consumer) { + this.consumer = consumer; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + return null; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/MessageId.java b/activemq-core/src/main/java/org/activemq/command/MessageId.java new file mode 100755 index 0000000000..d5301fe67b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/MessageId.java @@ -0,0 +1,132 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + + +/** + * @openwire:marshaller + * @version $Revision: 1.12 $ + */ +public class MessageId implements DataStructure { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.MESSAGE_ID; + + protected ProducerId producerId; + protected long producerSequenceId; + protected long brokerSequenceId; + + transient private String key; + transient private int hashCode; + + public MessageId() { + this.producerId = new ProducerId(); + } + + public MessageId(ProducerInfo producerInfo, long producerSequenceId) { + this.producerId = producerInfo.getProducerId(); + this.producerSequenceId = producerSequenceId; + } + + public MessageId(String messageKey) { + setMessageKey(messageKey); + } + + public MessageId(String producerId, long producerSequenceId) { + this( new ProducerId(producerId), producerSequenceId); + } + + public MessageId(ProducerId producerId, long producerSequenceId) { + this.producerId=producerId; + this.producerSequenceId = producerSequenceId; + } + + public void setMessageKey(String messageKey) { + key = messageKey; + // Parse off the sequenceId + int p = messageKey.lastIndexOf(":"); + if( p >= 0 ) { + producerSequenceId = Long.parseLong(messageKey.substring(p+1)); + messageKey = messageKey.substring(0,p); + } + producerId = new ProducerId(messageKey); + } + + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public boolean equals(Object o) { + if( this == o ) + return true; + if( o==null || o.getClass() != getClass() ) + return false; + + MessageId id = (MessageId) o; + return producerSequenceId==id.producerSequenceId && producerId.equals(id.producerId); + } + + public int hashCode() { + if( hashCode == 0 ) { + hashCode = producerId.hashCode() ^ (int)producerSequenceId; + } + return hashCode; + } + + public String toString() { + if(key==null) { + key = producerId.toString()+":"+producerSequenceId; + } + return key; + } + + /** + * @openwire:property version=1 cache=true + */ + public ProducerId getProducerId() { + return producerId; + } + public void setProducerId(ProducerId producerId) { + this.producerId = producerId; + } + + /** + * @openwire:property version=1 + */ + public long getProducerSequenceId() { + return producerSequenceId; + } + public void setProducerSequenceId(long producerSequenceId) { + this.producerSequenceId = producerSequenceId; + } + + /** + * @openwire:property version=1 + */ + public long getBrokerSequenceId() { + return brokerSequenceId; + } + public void setBrokerSequenceId(long brokerSequenceId) { + this.brokerSequenceId = brokerSequenceId; + } + + public boolean isMarshallAware() { + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/ProducerId.java b/activemq-core/src/main/java/org/activemq/command/ProducerId.java new file mode 100755 index 0000000000..a62b95aa66 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ProducerId.java @@ -0,0 +1,145 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +/** + * + * @openwire:marshaller + * @version $Revision$ + */ +public class ProducerId implements DataStructure { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.PRODUCER_ID; + + protected String connectionId; + protected long sessionId; + protected long producerId; + + protected transient int hashCode; + protected transient String key; + protected transient SessionId parentId; + + public ProducerId() { + } + + public ProducerId(SessionId sessionId, long producerId) { + this.connectionId = sessionId.getConnectionId(); + this.sessionId = sessionId.getSessionId(); + this.producerId=producerId; + } + + public ProducerId(ProducerId id) { + this.connectionId = id.getConnectionId(); + this.sessionId = id.getSessionId(); + this.producerId=id.getProducerId(); + } + + public ProducerId(String producerKey) { + // Parse off the producerId + int p = producerKey.lastIndexOf(":"); + if( p >= 0 ) { + producerId = Long.parseLong(producerKey.substring(p+1)); + producerKey = producerKey.substring(0,p); + } + setProducerSessionKey(producerKey); + } + + public SessionId getParentId() { + if( parentId == null ) { + parentId = new SessionId(this); + } + return parentId; + } + + public int hashCode() { + if( hashCode == 0 ) { + hashCode = connectionId.hashCode() ^ (int)sessionId ^ (int)producerId; + } + return hashCode; + } + + public boolean equals(Object o) { + if( this == o ) + return true; + if( o == null || o.getClass()!=ProducerId.class ) + return false; + ProducerId id = (ProducerId) o; + return sessionId==id.sessionId + && producerId==id.producerId + && connectionId.equals(id.connectionId); + } + + + /** + * @param sessionKey + */ + private void setProducerSessionKey(String sessionKey) { + // Parse off the sessionId + int p = sessionKey.lastIndexOf(":"); + if( p >= 0 ) { + sessionId = Long.parseLong(sessionKey.substring(p+1)); + sessionKey = sessionKey.substring(0,p); + } + // The rest is the connectionId + connectionId = sessionKey; + } + + public String toString() { + if( key == null ) { + key=connectionId+":"+sessionId+":"+producerId; + } + return key; + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + /** + * @openwire:property version=1 cache=true + */ + public String getConnectionId() { + return connectionId; + } + public void setConnectionId(String connectionId) { + this.connectionId = connectionId; + } + /** + * @openwire:property version=1 + */ + public long getProducerId() { + return producerId; + } + public void setProducerId(long producerId) { + this.producerId = producerId; + } + + /** + * @openwire:property version=1 + */ + public long getSessionId() { + return sessionId; + } + public void setSessionId(long sessionId) { + this.sessionId = sessionId; + } + + public boolean isMarshallAware() { + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/ProducerInfo.java b/activemq-core/src/main/java/org/activemq/command/ProducerInfo.java new file mode 100755 index 0000000000..0e59793d42 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ProducerInfo.java @@ -0,0 +1,105 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import org.activemq.state.CommandVisitor; + +/** + * + * @openwire:marshaller + * @version $Revision: 1.13 $ + */ +public class ProducerInfo extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.PRODUCER_INFO; + + protected ProducerId producerId; + protected ActiveMQDestination destination; + protected BrokerId[] brokerPath; + + public ProducerInfo() { + } + + public ProducerInfo(ProducerId producerId) { + this.producerId = producerId; + } + + public ProducerInfo(SessionInfo sessionInfo, long producerId) { + this.producerId = new ProducerId(sessionInfo.getSessionId(), producerId); + } + + public ProducerInfo copy() { + ProducerInfo info = new ProducerInfo(); + copy(info); + return info; + } + + public void copy(ProducerInfo info) { + super.copy(info); + info.producerId = producerId; + info.destination = destination; + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 cache=true + */ + public ProducerId getProducerId() { + return producerId; + } + public void setProducerId(ProducerId producerId) { + this.producerId = producerId; + } + + /** + * @openwire:property version=1 cache=true + */ + public ActiveMQDestination getDestination() { + return destination; + } + public void setDestination(ActiveMQDestination destination) { + this.destination = destination; + } + + public RemoveInfo createRemoveCommand() { + RemoveInfo command = new RemoveInfo(getProducerId()); + command.setResponseRequired(isResponseRequired()); + return command; + } + + /** + * The route of brokers the command has moved through. + * + * @openwire:property version=1 cache=true + */ + public BrokerId[] getBrokerPath() { + return brokerPath; + } + public void setBrokerPath(BrokerId[] brokerPath) { + this.brokerPath = brokerPath; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + return visitor.processAddProducer( this ); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/RedeliveryPolicy.java b/activemq-core/src/main/java/org/activemq/command/RedeliveryPolicy.java new file mode 100644 index 0000000000..0c62c88172 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/RedeliveryPolicy.java @@ -0,0 +1,87 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +/** + * Configuration options used to control how messages are re-delivered when + * they are rolled back. + * + * @openwire:marshaller + * @version $Revision: 1.11 $ + */ +public class RedeliveryPolicy implements DataStructure { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.REDELIVERY_POLICY; + + protected int maximumRedeliveries = 5; + protected long initialRedeliveryDelay = 1000L; + protected boolean useExponentialBackOff = false; + protected short backOffMultiplier = 5; + + public RedeliveryPolicy() { + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 cache=false + */ + public short getBackOffMultiplier() { + return backOffMultiplier; + } + public void setBackOffMultiplier(short backOffMultiplier) { + this.backOffMultiplier = backOffMultiplier; + } + + /** + * @openwire:property version=1 cache=false + */ + public long getInitialRedeliveryDelay() { + return initialRedeliveryDelay; + } + public void setInitialRedeliveryDelay(long initialRedeliveryDelay) { + this.initialRedeliveryDelay = initialRedeliveryDelay; + } + + /** + * @openwire:property version=1 cache=false + */ + public int getMaximumRedeliveries() { + return maximumRedeliveries; + } + public void setMaximumRedeliveries(int maximumRedeliveries) { + this.maximumRedeliveries = maximumRedeliveries; + } + + /** + * @openwire:property version=1 cache=false + */ + public boolean isUseExponentialBackOff() { + return useExponentialBackOff; + } + public void setUseExponentialBackOff(boolean useExponentialBackOff) { + this.useExponentialBackOff = useExponentialBackOff; + } + + public boolean isMarshallAware() { + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/RemoveInfo.java b/activemq-core/src/main/java/org/activemq/command/RemoveInfo.java new file mode 100755 index 0000000000..0b3f62f8a5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/RemoveInfo.java @@ -0,0 +1,73 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.IOException; + +import org.activemq.state.CommandVisitor; + +/** + * Removes a consumer, producer, session or connection. + * + * @openwire:marshaller + * @version $Revision$ + */ +public class RemoveInfo extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.REMOVE_INFO; + + protected DataStructure objectId; + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public RemoveInfo() { + } + public RemoveInfo(DataStructure objectId) { + this.objectId=objectId; + } + + /** + * @openwire:property version=1 cache=true + */ + public DataStructure getObjectId() { + return objectId; + } + + public void setObjectId(DataStructure objectId) { + this.objectId = objectId; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + switch (objectId.getDataStructureType()) { + case ConnectionId.DATA_STRUCTURE_TYPE: + return visitor.processRemoveConnection((ConnectionId) objectId); + case SessionId.DATA_STRUCTURE_TYPE: + return visitor.processRemoveSession((SessionId) objectId); + case ConsumerId.DATA_STRUCTURE_TYPE: + return visitor.processRemoveConsumer((ConsumerId) objectId); + case ProducerId.DATA_STRUCTURE_TYPE: + return visitor.processRemoveProducer((ProducerId) objectId); + default: + throw new IOException("Unknown remove command type: "+ objectId.getDataStructureType()); + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/RemoveSubscriptionInfo.java b/activemq-core/src/main/java/org/activemq/command/RemoveSubscriptionInfo.java new file mode 100755 index 0000000000..bd1e268f73 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/RemoveSubscriptionInfo.java @@ -0,0 +1,77 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import org.activemq.state.CommandVisitor; + +/** + * + * @openwire:marshaller + * @version $Revision: 1.7 $ + */ +public class RemoveSubscriptionInfo extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.REMOVE_SUBSCRIPTION_INFO; + + protected ConnectionId connectionId; + protected String clientId; + protected String subcriptionName; + + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 cache=true + */ + public ConnectionId getConnectionId() { + return connectionId; + } + public void setConnectionId(ConnectionId connectionId) { + this.connectionId = connectionId; + } + + /** + * @openwire:property version=1 + */ + public String getSubcriptionName() { + return subcriptionName; + } + + public void setSubcriptionName(String subcriptionName) { + this.subcriptionName = subcriptionName; + } + + /** + * @openwire:property version=1 + */ + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + return visitor.processRemoveSubscription( this ); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/Response.java b/activemq-core/src/main/java/org/activemq/command/Response.java new file mode 100755 index 0000000000..cda8a4c5dd --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/Response.java @@ -0,0 +1,58 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import org.activemq.state.CommandVisitor; + +/** + * @openwire:marshaller + * @version $Revision: 1.6 $ + */ +public class Response extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.RESPONSE; + short correlationId; + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 + */ + public short getCorrelationId() { + return correlationId; + } + + public void setCorrelationId(short responseId) { + this.correlationId = responseId; + } + + public boolean isResponse() { + return true; + } + + public boolean isException() { + return false; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + return null; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/SessionId.java b/activemq-core/src/main/java/org/activemq/command/SessionId.java new file mode 100755 index 0000000000..4f0bc24a2d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/SessionId.java @@ -0,0 +1,118 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +/** + * + * @openwire:marshaller + * @version $Revision$ + */ +public class SessionId implements DataStructure { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.SESSION_ID; + + protected String connectionId; + protected long sessionId; + + protected transient int hashCode; + protected transient String key; + protected transient ConnectionId parentId; + + public SessionId() { + } + + public SessionId(ConnectionId connectionId, long sessionId) { + this.connectionId = connectionId.getConnectionId(); + this.sessionId=sessionId; + } + + public SessionId(SessionId id) { + this.connectionId = id.getConnectionId(); + this.sessionId=id.getSessionId(); + } + + public SessionId(ProducerId id) { + this.connectionId = id.getConnectionId(); + this.sessionId=id.getSessionId(); + } + + public SessionId(ConsumerId id) { + this.connectionId = id.getConnectionId(); + this.sessionId=id.getSessionId(); + } + + public ConnectionId getParentId() { + if( parentId == null ) { + parentId = new ConnectionId(this); + } + return parentId; + } + + public int hashCode() { + if( hashCode == 0 ) { + hashCode = connectionId.hashCode() ^ (int)sessionId; + } + return hashCode; + } + + public boolean equals(Object o) { + if( this == o ) + return true; + if( o == null || o.getClass()!=SessionId.class ) + return false; + SessionId id = (SessionId) o; + return sessionId==id.sessionId + && connectionId.equals(id.connectionId); + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 cache=true + */ + public String getConnectionId() { + return connectionId; + } + public void setConnectionId(String connectionId) { + this.connectionId = connectionId; + } + + /** + * @openwire:property version=1 + */ + public long getSessionId() { + return sessionId; + } + public void setSessionId(long sessionId) { + this.sessionId = sessionId; + } + + public String toString() { + if( key==null ) { + key = connectionId+":"+sessionId; + } + return key; + } + + public boolean isMarshallAware() { + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/SessionInfo.java b/activemq-core/src/main/java/org/activemq/command/SessionInfo.java new file mode 100755 index 0000000000..24fa271a6c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/SessionInfo.java @@ -0,0 +1,70 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import org.activemq.state.CommandVisitor; + +/** + * + * @openwire:marshaller + * @version $Revision: 1.13 $ + */ +public class SessionInfo extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.SESSION_INFO; + + protected SessionId sessionId; + + public SessionInfo() { + sessionId = new SessionId(); + } + + public SessionInfo(ConnectionInfo connectionInfo, long sessionId) { + this.sessionId = new SessionId(connectionInfo.getConnectionId(), sessionId); + } + + public SessionInfo(SessionId sessionId) { + this.sessionId = sessionId; + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 cache=true + */ + public SessionId getSessionId() { + return sessionId; + } + public void setSessionId(SessionId sessionId) { + this.sessionId = sessionId; + } + + public RemoveInfo createRemoveCommand() { + RemoveInfo command = new RemoveInfo(getSessionId()); + command.setResponseRequired(isResponseRequired()); + return command; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + return visitor.processAddSession( this); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/ShutdownInfo.java b/activemq-core/src/main/java/org/activemq/command/ShutdownInfo.java new file mode 100755 index 0000000000..14c66aed63 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/ShutdownInfo.java @@ -0,0 +1,41 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import org.activemq.state.CommandVisitor; + +/** + * + * @openwire:marshaller + * @version $Revision$ + */ +public class ShutdownInfo extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.SHUTDOWN_INFO; + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + return visitor.processShutdown( this ); + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/command/SubscriptionInfo.java b/activemq-core/src/main/java/org/activemq/command/SubscriptionInfo.java new file mode 100755 index 0000000000..ebdfb3b8a5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/SubscriptionInfo.java @@ -0,0 +1,87 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + + +/** + * + * @openwire:marshaller + * @version $Revision: 1.6 $ + */ +public class SubscriptionInfo implements DataStructure { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.DURABLE_SUBSCRIPTION_INFO; + + protected ActiveMQDestination destination; + protected String clientId; + protected String subcriptionName; + protected String selector; + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 + */ + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + /** + * @openwire:property version=1 cache=true + */ + public ActiveMQDestination getDestination() { + return destination; + } + + public void setDestination(ActiveMQDestination destination) { + this.destination = destination; + } + + /** + * @openwire:property version=1 + */ + public String getSelector() { + return selector; + } + + public void setSelector(String selector) { + this.selector = selector; + } + + /** + * @openwire:property version=1 + */ + public String getSubcriptionName() { + return subcriptionName; + } + + public void setSubcriptionName(String subcriptionName) { + this.subcriptionName = subcriptionName; + } + + public boolean isMarshallAware() { + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/TransactionId.java b/activemq-core/src/main/java/org/activemq/command/TransactionId.java new file mode 100755 index 0000000000..502a1059a0 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/TransactionId.java @@ -0,0 +1,34 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +/** + * @openwire:marshaller + * @version $Revision: 1.6 $ + */ +abstract public class TransactionId implements DataStructure { + + abstract public boolean isXATransaction(); + abstract public boolean isLocalTransaction(); + abstract public String getTransactionKey(); + + public boolean isMarshallAware() { + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/command/TransactionInfo.java b/activemq-core/src/main/java/org/activemq/command/TransactionInfo.java new file mode 100755 index 0000000000..d491f84e4f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/TransactionInfo.java @@ -0,0 +1,114 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.IOException; + +import org.activemq.state.CommandVisitor; + +/** + * + * @openwire:marshaller + * @version $Revision: 1.10 $ + */ +public class TransactionInfo extends BaseCommand { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.TRANSACTION_INFO; + + + public static final byte BEGIN = 0; + public static final byte PREPARE=1; + public static final byte COMMIT_ONE_PHASE=2; + public static final byte COMMIT_TWO_PHASE=3; + public static final byte ROLLBACK=4; + public static final byte RECOVER=5; + public static final byte FORGET=6; + public static final byte END=7; + + protected byte type; + protected ConnectionId connectionId; + protected TransactionId transactionId; + + public TransactionInfo() { + } + + public TransactionInfo(ConnectionId connectionId, TransactionId transactionId, byte type) { + this.connectionId=connectionId; + this.transactionId=transactionId; + this.type=type; + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + /** + * @openwire:property version=1 cache=true + */ + public ConnectionId getConnectionId() { + return connectionId; + } + public void setConnectionId(ConnectionId connectionId) { + this.connectionId = connectionId; + } + + /** + * @openwire:property version=1 cache=true + */ + public TransactionId getTransactionId() { + return transactionId; + } + public void setTransactionId(TransactionId transactionId) { + this.transactionId = transactionId; + } + + /** + * @openwire:property version=1 + */ + public byte getType() { + return type; + } + public void setType(byte type) { + this.type = type; + } + + public Response visit(CommandVisitor visitor) throws Throwable { + switch( type ) { + case TransactionInfo.BEGIN: + return visitor.processBeginTransaction(this); + case TransactionInfo.END: + return visitor.processEndTransaction(this); + case TransactionInfo.PREPARE: + return visitor.processPrepareTransaction(this); + case TransactionInfo.COMMIT_ONE_PHASE: + return visitor.processCommitTransactionOnePhase(this); + case TransactionInfo.COMMIT_TWO_PHASE: + return visitor.processCommitTransactionTwoPhase(this); + case TransactionInfo.ROLLBACK: + return visitor.processRollbackTransaction(this); + case TransactionInfo.RECOVER: + return visitor.processRecoverTransactions(this); + case TransactionInfo.FORGET: + return visitor.processForgetTransaction(this); + default: + throw new IOException("Transaction info type unknown: "+type); + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/WireFormatInfo.java b/activemq-core/src/main/java/org/activemq/command/WireFormatInfo.java new file mode 100755 index 0000000000..8c699a24de --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/WireFormatInfo.java @@ -0,0 +1,164 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.util.Arrays; + +import org.activemq.state.CommandVisitor; + +/** + * + * @openwire:marshaller + * @version $Revision$ + */ +public class WireFormatInfo implements Command { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.WIREFORMAT_INFO; + static final private byte MAGIC[] = new byte[]{'A','c','t','i','v','e','M','Q'}; + + static final public long STACK_TRACE_MASK = 0x00000001; + static final public long TCP_NO_DELAY_MASK = 0x00000002; + static final public long CACHE_MASK = 0x00000004; + static final public long COMPRESSION_MASK = 0x00000008; + + protected int version; + protected byte magic[] = MAGIC; + protected int options; + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public boolean isWireFormatInfo() { + return true; + } + + /** + * @openwire:property version=1 size=8 + */ + public byte[] getMagic() { + return magic; + } + public void setMagic(byte[] magic) { + this.magic = magic; + } + + /** + * @openwire:property version=1 + */ + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public boolean isValid() { + return magic!=null && Arrays.equals(magic, MAGIC); + } + + public void setCommandId(short value) { + } + + public short getCommandId() { + return 0; + } + + public boolean isResponseRequired() { + return false; + } + + public boolean isResponse() { + return false; + } + + public boolean isBrokerInfo() { + return false; + } + + public boolean isMessageDispatch() { + return false; + } + public boolean isMessage() { + return false; + } + + + public void setResponseRequired(boolean responseRequired) { + } + + public String toString() { + return "WireFormatInfo {version="+version+"}"; + } + + /** + * @openwire:property version=1 + */ + public int getOptions() { + return options; + } + + public void setOptions(int options) { + this.options = options; + } + + + public boolean isStackTraceEnabled() { + return (options & STACK_TRACE_MASK)!=0; + } + public void setStackTraceEnabled(boolean enable) { + if( enable ) { + options |= STACK_TRACE_MASK; + } else { + options &= ~STACK_TRACE_MASK; + } + } + + public boolean isTcpNoDelayEnabled() { + return (options & TCP_NO_DELAY_MASK)!=0; + } + public void setTcpNoDelayEnabled(boolean enable) { + if( enable ) { + options |= TCP_NO_DELAY_MASK; + } else { + options &= ~TCP_NO_DELAY_MASK; + } + } + + public boolean isCacheEnabled() { + return (options & CACHE_MASK)!=0; + } + public void setCacheEnabled(boolean enable) { + if( enable ) { + options |= CACHE_MASK; + } else { + options &= ~CACHE_MASK; + } + } + + public Response visit(CommandVisitor visitor) throws Throwable { + return visitor.processWireFormat( this ); + } + + public boolean isMarshallAware() { + return false; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/XATransactionId.java b/activemq-core/src/main/java/org/activemq/command/XATransactionId.java new file mode 100755 index 0000000000..189526c07e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/XATransactionId.java @@ -0,0 +1,134 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.util.Arrays; + +import javax.transaction.xa.Xid; + +import org.activemq.util.HexSupport; + + +/** + * @openwire:marshaller + * @version $Revision: 1.6 $ + */ +public class XATransactionId extends TransactionId implements Xid { + + public static final byte DATA_STRUCTURE_TYPE=CommandTypes.ACTIVEMQ_XA_TRANSACTION_ID; + + private int formatId; + private byte[] branchQualifier; + private byte[] globalTransactionId; + + private transient int hash; + + public XATransactionId() { + } + + public XATransactionId(Xid xid) { + this.formatId = xid.getFormatId(); + this.globalTransactionId = xid.getGlobalTransactionId(); + this.branchQualifier = xid.getBranchQualifier(); + } + + public byte getDataStructureType() { + return DATA_STRUCTURE_TYPE; + } + + public String getTransactionKey() { + return "XID:"+formatId+":"+HexSupport.toHexFromBytes(globalTransactionId)+":"+HexSupport.toHexFromBytes(branchQualifier); + } + + public String toString() { + return getTransactionKey(); + } + + public boolean isXATransaction() { + return true; + } + + public boolean isLocalTransaction() { + return false; + } + + /** + * @openwire:property version=1 + */ + public int getFormatId() { + return formatId; + } + + /** + * @openwire:property version=1 + */ + public byte[] getGlobalTransactionId() { + return globalTransactionId; + } + + /** + * @openwire:property version=1 + */ + public byte[] getBranchQualifier() { + return branchQualifier; + } + + public void setBranchQualifier(byte[] branchQualifier) { + this.branchQualifier = branchQualifier; + this.hash=0; + } + + public void setFormatId(int formatId) { + this.formatId = formatId; + this.hash=0; + } + + public void setGlobalTransactionId(byte[] globalTransactionId) { + this.globalTransactionId = globalTransactionId; + this.hash=0; + } + + public int hashCode() { + if( hash==0 ) { + hash = formatId; + hash = hash(globalTransactionId, hash); + hash = hash(branchQualifier, hash); + if (hash == 0) { + hash = 0xaceace; + } + } + return hash; + } + + private static int hash(byte[] bytes, int hash) { + for (int i = 0, size = bytes.length; i < size; i++) { + hash ^= bytes[i] << ((i % 4) * 8); + } + return hash; + } + + public boolean equals(Object o) { + if( o==null || o.getClass()!=XATransactionId.class ) + return false; + XATransactionId xid = (XATransactionId)o; + return xid.formatId==formatId && Arrays.equals(xid.globalTransactionId,globalTransactionId) + && Arrays.equals(xid.branchQualifier, branchQualifier); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/command/package.html b/activemq-core/src/main/java/org/activemq/command/package.html new file mode 100755 index 0000000000..55946ecfe3 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/command/package.html @@ -0,0 +1,11 @@ + + + + + +

+Command objects used via the Command Pattern to communicate among nodes +

+ + + diff --git a/activemq-core/src/main/java/org/activemq/filter/ArithmeticExpression.java b/activemq-core/src/main/java/org/activemq/filter/ArithmeticExpression.java new file mode 100755 index 0000000000..9bf2625da2 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/ArithmeticExpression.java @@ -0,0 +1,212 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import javax.jms.JMSException; + +/** + * An expression which performs an operation on two expression values + * + * @version $Revision: 1.2 $ + */ +public abstract class ArithmeticExpression extends BinaryExpression { + + protected static final int INTEGER = 1; + protected static final int LONG = 2; + protected static final int DOUBLE = 3; + + /** + * @param left + * @param right + */ + public ArithmeticExpression(Expression left, Expression right) { + super(left, right); + } + + public static Expression createPlus(Expression left, Expression right) { + return new ArithmeticExpression(left, right) { + protected Object evaluate(Object lvalue, Object rvalue) { + if (lvalue instanceof String) { + String text = (String) lvalue; + String answer = text + rvalue; + System.out.println("lvalue: " + lvalue + " rvalue: " + rvalue + " result: " + answer); + return answer; + } + else if (lvalue instanceof Number) { + return plus((Number) lvalue, asNumber(rvalue)); + } + throw new RuntimeException("Cannot call plus operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() { + return "+"; + } + }; + } + + public static Expression createMinus(Expression left, Expression right) { + return new ArithmeticExpression(left, right) { + protected Object evaluate(Object lvalue, Object rvalue) { + if (lvalue instanceof Number) { + return minus((Number) lvalue, asNumber(rvalue)); + } + throw new RuntimeException("Cannot call minus operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() { + return "-"; + } + }; + } + + public static Expression createMultiply(Expression left, Expression right) { + return new ArithmeticExpression(left, right) { + + protected Object evaluate(Object lvalue, Object rvalue) { + if (lvalue instanceof Number) { + return multiply((Number) lvalue, asNumber(rvalue)); + } + throw new RuntimeException("Cannot call multiply operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() { + return "*"; + } + }; + } + + public static Expression createDivide(Expression left, Expression right) { + return new ArithmeticExpression(left, right) { + + protected Object evaluate(Object lvalue, Object rvalue) { + if (lvalue instanceof Number) { + return divide((Number) lvalue, asNumber(rvalue)); + } + throw new RuntimeException("Cannot call divide operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() { + return "/"; + } + }; + } + + public static Expression createMod(Expression left, Expression right) { + return new ArithmeticExpression(left, right) { + + protected Object evaluate(Object lvalue, Object rvalue) { + if (lvalue instanceof Number) { + return mod((Number) lvalue, asNumber(rvalue)); + } + throw new RuntimeException("Cannot call mod operation on: " + lvalue + " and: " + rvalue); + } + + public String getExpressionSymbol() { + return "%"; + } + }; + } + + protected Number plus(Number left, Number right) { + switch (numberType(left, right)) { + case INTEGER: + return new Integer(left.intValue() + right.intValue()); + case LONG: + return new Long(left.longValue() + right.longValue()); + default: + return new Double(left.doubleValue() + right.doubleValue()); + } + } + + protected Number minus(Number left, Number right) { + switch (numberType(left, right)) { + case INTEGER: + return new Integer(left.intValue() - right.intValue()); + case LONG: + return new Long(left.longValue() - right.longValue()); + default: + return new Double(left.doubleValue() - right.doubleValue()); + } + } + + protected Number multiply(Number left, Number right) { + switch (numberType(left, right)) { + case INTEGER: + return new Integer(left.intValue() * right.intValue()); + case LONG: + return new Long(left.longValue() * right.longValue()); + default: + return new Double(left.doubleValue() * right.doubleValue()); + } + } + + protected Number divide(Number left, Number right) { + return new Double(left.doubleValue() / right.doubleValue()); + } + + protected Number mod(Number left, Number right) { + return new Double(left.doubleValue() % right.doubleValue()); + } + + private int numberType(Number left, Number right) { + if (isDouble(left) || isDouble(right)) { + return DOUBLE; + } + else if (left instanceof Long || right instanceof Long) { + return LONG; + } + else { + return INTEGER; + } + } + + private boolean isDouble(Number n) { + return n instanceof Float || n instanceof Double; + } + + protected Number asNumber(Object value) { + if (value instanceof Number) { + return (Number) value; + } + else { + throw new RuntimeException("Cannot convert value: " + value + " into a number"); + } + } + + public Object evaluate(MessageEvaluationContext message) throws JMSException { + Object lvalue = left.evaluate(message); + if (lvalue == null) { + return null; + } + Object rvalue = right.evaluate(message); + if (rvalue == null) { + return null; + } + return evaluate(lvalue, rvalue); + } + + + /** + * @param lvalue + * @param rvalue + * @return + */ + abstract protected Object evaluate(Object lvalue, Object rvalue); + +} diff --git a/activemq-core/src/main/java/org/activemq/filter/BinaryExpression.java b/activemq-core/src/main/java/org/activemq/filter/BinaryExpression.java new file mode 100755 index 0000000000..889bae5e0d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/BinaryExpression.java @@ -0,0 +1,98 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + + + +/** + * An expression which performs an operation on two expression values. + * + * @version $Revision: 1.2 $ + */ +abstract public class BinaryExpression implements Expression { + protected Expression left; + protected Expression right; + + public BinaryExpression(Expression left, Expression right) { + this.left = left; + this.right = right; + } + + public Expression getLeft() { + return left; + } + + public Expression getRight() { + return right; + } + + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + return "(" + left.toString() + " " + getExpressionSymbol() + " " + right.toString() + ")"; + } + + /** + * TODO: more efficient hashCode() + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return toString().hashCode(); + } + + /** + * TODO: more efficient hashCode() + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object o) { + + if (o == null || !this.getClass().equals(o.getClass())) { + return false; + } + return toString().equals(o.toString()); + + } + + /** + * Returns the symbol that represents this binary expression. For example, addition is + * represented by "+" + * + * @return + */ + abstract public String getExpressionSymbol(); + + /** + * @param expression + */ + public void setRight(Expression expression) { + right = expression; + } + + /** + * @param expression + */ + public void setLeft(Expression expression) { + left = expression; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/filter/BooleanExpression.java b/activemq-core/src/main/java/org/activemq/filter/BooleanExpression.java new file mode 100755 index 0000000000..3272c829a7 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/BooleanExpression.java @@ -0,0 +1,39 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import javax.jms.JMSException; + + +/** + * A BooleanExpression is an expression that always + * produces a Boolean result. + * + * @version $Revision: 1.2 $ + */ +public interface BooleanExpression extends Expression { + + /** + * @param message + * @return true if the expression evaluates to Boolean.TRUE. + * @throws JMSException + */ + public boolean matches(MessageEvaluationContext message) throws JMSException; + +} diff --git a/activemq-core/src/main/java/org/activemq/filter/ComparisonExpression.java b/activemq-core/src/main/java/org/activemq/filter/ComparisonExpression.java new file mode 100755 index 0000000000..371b84b672 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/ComparisonExpression.java @@ -0,0 +1,426 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import java.util.HashSet; +import java.util.List; +import java.util.regex.Pattern; + +import javax.jms.JMSException; + +/** + * A filter performing a comparison of two objects + * + * @version $Revision: 1.2 $ + */ +public abstract class ComparisonExpression extends BinaryExpression implements BooleanExpression { + + public static BooleanExpression createBetween(Expression value, Expression left, Expression right) { + return LogicExpression.createAND(createGreaterThanEqual(value, left), createLessThanEqual(value, right)); + } + + public static BooleanExpression createNotBetween(Expression value, Expression left, Expression right) { + return LogicExpression.createOR(createLessThan(value, left), createGreaterThan(value, right)); + } + + static final private HashSet REGEXP_CONTROL_CHARS = new HashSet(); + + static { + REGEXP_CONTROL_CHARS.add(new Character('.')); + REGEXP_CONTROL_CHARS.add(new Character('\\')); + REGEXP_CONTROL_CHARS.add(new Character('[')); + REGEXP_CONTROL_CHARS.add(new Character(']')); + REGEXP_CONTROL_CHARS.add(new Character('^')); + REGEXP_CONTROL_CHARS.add(new Character('$')); + REGEXP_CONTROL_CHARS.add(new Character('?')); + REGEXP_CONTROL_CHARS.add(new Character('*')); + REGEXP_CONTROL_CHARS.add(new Character('+')); + REGEXP_CONTROL_CHARS.add(new Character('{')); + REGEXP_CONTROL_CHARS.add(new Character('}')); + REGEXP_CONTROL_CHARS.add(new Character('|')); + REGEXP_CONTROL_CHARS.add(new Character('(')); + REGEXP_CONTROL_CHARS.add(new Character(')')); + REGEXP_CONTROL_CHARS.add(new Character(':')); + REGEXP_CONTROL_CHARS.add(new Character('&')); + REGEXP_CONTROL_CHARS.add(new Character('<')); + REGEXP_CONTROL_CHARS.add(new Character('>')); + REGEXP_CONTROL_CHARS.add(new Character('=')); + REGEXP_CONTROL_CHARS.add(new Character('!')); + } + + static class LikeExpression extends UnaryExpression implements BooleanExpression { + + Pattern likePattern; + + /** + * @param left + */ + public LikeExpression(Expression right, String like, int escape) { + super(right); + + StringBuffer regexp = new StringBuffer(like.length() * 2); + regexp.append("\\A"); // The beginning of the input + for (int i = 0; i < like.length(); i++) { + char c = like.charAt(i); + if (escape == (0xFFFF & c)) { + i++; + if (i >= like.length()) { + // nothing left to escape... + break; + } + + char t = like.charAt(i); + regexp.append("\\x"); + regexp.append(Integer.toHexString(0xFFFF & t)); + } + else if (c == '%') { + regexp.append(".*?"); // Do a non-greedy match + } + else if (c == '_') { + regexp.append("."); // match one + } + else if (REGEXP_CONTROL_CHARS.contains(new Character(c))) { + regexp.append("\\x"); + regexp.append(Integer.toHexString(0xFFFF & c)); + } + else { + regexp.append(c); + } + } + regexp.append("\\z"); // The end of the input + + System.out.println("regexp: " + like + ": " + regexp); + likePattern = Pattern.compile(regexp.toString(), Pattern.DOTALL); + } + + /** + * @see org.activemq.filter.UnaryExpression#getExpressionSymbol() + */ + public String getExpressionSymbol() { + return "LIKE"; + } + + /** + * @see org.activemq.filter.Expression#evaluate(MessageEvaluationContext) + */ + public Object evaluate(MessageEvaluationContext message) throws JMSException { + + Object rv = this.getRight().evaluate(message); + + if (rv == null) { + return null; + } + + if (!(rv instanceof String)) { + return Boolean.FALSE; + //throw new RuntimeException("LIKE can only operate on String identifiers. LIKE attemped on: '" + rv.getClass()); + } + + return likePattern.matcher((String) rv).matches() ? Boolean.TRUE : Boolean.FALSE; + } + + public boolean matches(MessageEvaluationContext message) throws JMSException { + Object object = evaluate(message); + return object!=null && object==Boolean.TRUE; + } + } + + public static BooleanExpression createLike(Expression left, String right, String escape) { + if (escape != null && escape.length() != 1) { + throw new RuntimeException("The ESCAPE string litteral is invalid. It can only be one character. Litteral used: " + escape); + } + int c = -1; + if (escape != null) { + c = 0xFFFF & escape.charAt(0); + } + + return new LikeExpression(left, right, c); + } + + public static BooleanExpression createNotLike(Expression left, String right, String escape) { + return UnaryExpression.createNOT(createLike(left, right, escape)); + } + + public static BooleanExpression createInFilter(Expression left, List elements) { + + if( !(left instanceof PropertyExpression) ) + throw new RuntimeException("Expected a property for In expression, got: "+left); + return UnaryExpression.createInExpression((PropertyExpression)left, elements, false); + + } + + public static BooleanExpression createNotInFilter(Expression left, List elements) { + + if( !(left instanceof PropertyExpression) ) + throw new RuntimeException("Expected a property for In expression, got: "+left); + return UnaryExpression.createInExpression((PropertyExpression)left, elements, true); + + } + + public static BooleanExpression createIsNull(Expression left) { + return doCreateEqual(left, ConstantExpression.NULL); + } + + public static BooleanExpression createIsNotNull(Expression left) { + return UnaryExpression.createNOT(doCreateEqual(left, ConstantExpression.NULL)); + } + + public static BooleanExpression createNotEqual(Expression left, Expression right) { + return UnaryExpression.createNOT(createEqual(left, right)); + } + + public static BooleanExpression createEqual(Expression left, Expression right) { + checkEqualOperand(left); + checkEqualOperand(right); + checkEqualOperandCompatability(left, right); + return doCreateEqual(left, right); + } + + private static BooleanExpression doCreateEqual(Expression left, Expression right) { + return new ComparisonExpression(left, right) { + + public Object evaluate(MessageEvaluationContext message) throws JMSException { + Object lv = left.evaluate(message); + Object rv = right.evaluate(message); + + // Iff one of the values is null + if (lv == null ^ rv == null) { + return Boolean.FALSE; + } + if (lv == rv || lv.equals(rv)) { + return Boolean.TRUE; + } + if( lv instanceof Comparable && rv instanceof Comparable ) { + return compare((Comparable)lv, (Comparable)rv); + } + return Boolean.FALSE; + } + + protected boolean asBoolean(int answer) { + return answer == 0; + } + + public String getExpressionSymbol() { + return "="; + } + }; + } + + public static BooleanExpression createGreaterThan(final Expression left, final Expression right) { + checkLessThanOperand(left); + checkLessThanOperand(right); + return new ComparisonExpression(left, right) { + protected boolean asBoolean(int answer) { + return answer > 0; + } + + public String getExpressionSymbol() { + return ">"; + } + }; + } + + public static BooleanExpression createGreaterThanEqual(final Expression left, final Expression right) { + checkLessThanOperand(left); + checkLessThanOperand(right); + return new ComparisonExpression(left, right) { + protected boolean asBoolean(int answer) { + return answer >= 0; + } + + public String getExpressionSymbol() { + return ">="; + } + }; + } + + public static BooleanExpression createLessThan(final Expression left, final Expression right) { + checkLessThanOperand(left); + checkLessThanOperand(right); + return new ComparisonExpression(left, right) { + + protected boolean asBoolean(int answer) { + return answer < 0; + } + + public String getExpressionSymbol() { + return "<"; + } + + }; + } + + public static BooleanExpression createLessThanEqual(final Expression left, final Expression right) { + checkLessThanOperand(left); + checkLessThanOperand(right); + return new ComparisonExpression(left, right) { + + protected boolean asBoolean(int answer) { + return answer <= 0; + } + + public String getExpressionSymbol() { + return "<="; + } + }; + } + + /** + * Only Numeric expressions can be used in >, >=, < or <= expressions.s + * + * @param expr + */ + public static void checkLessThanOperand(Expression expr ) { + if( expr instanceof ConstantExpression ) { + Object value = ((ConstantExpression)expr).getValue(); + if( value instanceof Number ) + return; + + // Else it's boolean or a String.. + throw new RuntimeException("Value '"+expr+"' cannot be compared."); + } + if( expr instanceof BooleanExpression ) { + throw new RuntimeException("Value '"+expr+"' cannot be compared."); + } + } + + /** + * Validates that the expression can be used in == or <> expression. + * Cannot not be NULL TRUE or FALSE litterals. + * + * @param expr + */ + public static void checkEqualOperand(Expression expr ) { + if( expr instanceof ConstantExpression ) { + Object value = ((ConstantExpression)expr).getValue(); + if( value == null ) + throw new RuntimeException("'"+expr+"' cannot be compared."); + } + } + + /** + * + * @param left + * @param right + */ + private static void checkEqualOperandCompatability(Expression left, Expression right) { + if( left instanceof ConstantExpression && right instanceof ConstantExpression ) { + if( left instanceof BooleanExpression && !(right instanceof BooleanExpression) ) + throw new RuntimeException("'"+left+"' cannot be compared with '"+right+"'"); + } + } + + + + /** + * @param left + * @param right + */ + public ComparisonExpression(Expression left, Expression right) { + super(left, right); + } + + public Object evaluate(MessageEvaluationContext message) throws JMSException { + Comparable lv = (Comparable) left.evaluate(message); + if (lv == null) { + return null; + } + Comparable rv = (Comparable) right.evaluate(message); + if (rv == null) { + return null; + } + return compare(lv, rv); + } + + protected Boolean compare(Comparable lv, Comparable rv) { + Class lc = lv.getClass(); + Class rc = rv.getClass(); + // If the the objects are not of the same type, + // try to convert up to allow the comparison. + if (lc != rc) { + if (lc == Integer.class) { + if (rc == Long.class) { + lv = new Long(((Number) lv).longValue()); + } + else if (rc == Float.class) { + lv = new Float(((Number) lv).floatValue()); + } + else if (rc == Double.class) { + lv = new Double(((Number) lv).doubleValue()); + } + else { + return Boolean.FALSE; + } + } + else if (lc == Long.class) { + if (rc == Integer.class) { + rv = new Long(((Number) rv).longValue()); + } + else if (rc == Float.class) { + lv = new Float(((Number) lv).floatValue()); + } + else if (rc == Double.class) { + lv = new Double(((Number) lv).doubleValue()); + } + else { + return Boolean.FALSE; + } + } + else if (lc == Float.class) { + if (rc == Integer.class) { + rv = new Float(((Number) rv).floatValue()); + } + else if (rc == Long.class) { + rv = new Float(((Number) rv).floatValue()); + } + else if (rc == Double.class) { + lv = new Double(((Number) lv).doubleValue()); + } + else { + return Boolean.FALSE; + } + } + else if (lc == Double.class) { + if (rc == Integer.class) { + rv = new Double(((Number) rv).doubleValue()); + } + else if (rc == Long.class) { + rv = new Double(((Number) rv).doubleValue()); + } + else if (rc == Float.class) { + rv = new Float(((Number) rv).doubleValue()); + } + else { + return Boolean.FALSE; + } + } + else + return Boolean.FALSE; + } + return asBoolean(lv.compareTo(rv)) ? Boolean.TRUE : Boolean.FALSE; + } + + protected abstract boolean asBoolean(int answer); + + public boolean matches(MessageEvaluationContext message) throws JMSException { + Object object = evaluate(message); + return object!=null && object==Boolean.TRUE; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/filter/CompositeDestinationFilter.java b/activemq-core/src/main/java/org/activemq/filter/CompositeDestinationFilter.java new file mode 100755 index 0000000000..641400f6d3 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/CompositeDestinationFilter.java @@ -0,0 +1,53 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import org.activemq.command.ActiveMQDestination; + +/** + * A {@link DestinationFilter} used for composite destinations + * + * @version $Revision: 1.3 $ + */ +public class CompositeDestinationFilter extends DestinationFilter { + + private DestinationFilter filters[]; + + public CompositeDestinationFilter(ActiveMQDestination destination) { + ActiveMQDestination[] destinations = destination.getCompositeDestinations(); + filters = new DestinationFilter[destinations.length]; + for (int i = 0; i < destinations.length; i++) { + ActiveMQDestination childDestination = destinations[i]; + filters[i]= DestinationFilter.parseFilter(childDestination); + } + } + + public boolean matches(ActiveMQDestination destination) { + for (int i = 0; i < filters.length; i++) { + if (filters[i].matches(destination)) { + return true; + } + } + return false; + } + + public boolean isWildcard() { + return true; + } +} diff --git a/activemq-core/src/main/java/org/activemq/filter/ConstantExpression.java b/activemq-core/src/main/java/org/activemq/filter/ConstantExpression.java new file mode 100755 index 0000000000..1efed266eb --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/ConstantExpression.java @@ -0,0 +1,165 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import java.math.BigDecimal; + +import javax.jms.JMSException; + +/** + * Represents a constant expression + * + * @version $Revision: 1.2 $ + */ +public class ConstantExpression implements Expression { + + static class BooleanConstantExpression extends ConstantExpression implements BooleanExpression { + public BooleanConstantExpression(Object value) { + super(value); + } + public boolean matches(MessageEvaluationContext message) throws JMSException { + Object object = evaluate(message); + return object!=null && object==Boolean.TRUE; + } + } + + public static final BooleanConstantExpression NULL = new BooleanConstantExpression(null); + public static final BooleanConstantExpression TRUE = new BooleanConstantExpression(Boolean.TRUE); + public static final BooleanConstantExpression FALSE = new BooleanConstantExpression(Boolean.FALSE); + + private Object value; + + public static ConstantExpression createFromDecimal(String text) { + + // Strip off the 'l' or 'L' if needed. + if( text.endsWith("l") || text.endsWith("L") ) + text = text.substring(0, text.length()-1); + + Number value; + try { + value = new Long(text); + } catch ( NumberFormatException e) { + // The number may be too big to fit in a long. + value = new BigDecimal(text); + } + + long l = value.longValue(); + if (Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE) { + value = new Integer(value.intValue()); + } + return new ConstantExpression(value); + } + + public static ConstantExpression createFromHex(String text) { + Number value = new Long(Long.parseLong(text.substring(2), 16)); + long l = value.longValue(); + if (Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE) { + value = new Integer(value.intValue()); + } + return new ConstantExpression(value); + } + + public static ConstantExpression createFromOctal(String text) { + Number value = new Long(Long.parseLong(text, 8)); + long l = value.longValue(); + if (Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE) { + value = new Integer(value.intValue()); + } + return new ConstantExpression(value); + } + + public static ConstantExpression createFloat(String text) { + Number value = new Double(text); + return new ConstantExpression(value); + } + + public ConstantExpression(Object value) { + this.value = value; + } + + public Object evaluate(MessageEvaluationContext message) throws JMSException { + return value; + } + + public Object getValue() { + return value; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + if (value == null) { + return "NULL"; + } + if (value instanceof Boolean) { + return ((Boolean) value).booleanValue() ? "TRUE" : "FALSE"; + } + if (value instanceof String) { + return encodeString((String) value); + } + return value.toString(); + } + + /** + * TODO: more efficient hashCode() + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return toString().hashCode(); + } + + /** + * TODO: more efficient hashCode() + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object o) { + + if (o == null || !this.getClass().equals(o.getClass())) { + return false; + } + return toString().equals(o.toString()); + + } + + + /** + * Encodes the value of string so that it looks like it would look like + * when it was provided in a selector. + * + * @param string + * @return + */ + public static String encodeString(String s) { + StringBuffer b = new StringBuffer(); + b.append('\''); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\'') { + b.append(c); + } + b.append(c); + } + b.append('\''); + return b.toString(); + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/filter/DestinationFilter.java b/activemq-core/src/main/java/org/activemq/filter/DestinationFilter.java new file mode 100755 index 0000000000..9ae9e8f152 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/DestinationFilter.java @@ -0,0 +1,80 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import java.io.IOException; + +import javax.jms.JMSException; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.util.JMSExceptionSupport; + + +/** + * Represents a filter which only operates on Destinations + * + * @version $Revision: 1.3 $ + */ +public abstract class DestinationFilter implements BooleanExpression { + + public static final String ANY_DESCENDENT = ">"; + public static final String ANY_CHILD = "*"; + + public Object evaluate(MessageEvaluationContext message) throws JMSException { + return matches(message) ? Boolean.TRUE : Boolean.FALSE; + } + + public boolean matches(MessageEvaluationContext message) throws JMSException { + try { + if( message.isDropped() ) + return false; + return matches(message.getMessage().getDestination()); + } catch (IOException e) { + throw JMSExceptionSupport.create(e); + } + } + + public abstract boolean matches(ActiveMQDestination destination); + + public static DestinationFilter parseFilter(ActiveMQDestination destination) { + if (destination.isComposite()) { + return new CompositeDestinationFilter(destination); + } + String[] paths = DestinationPath.getDestinationPaths(destination); + int idx = paths.length - 1; + if (idx >= 0) { + String lastPath = paths[idx]; + if (lastPath.equals(ANY_DESCENDENT)) { + return new PrefixDestinationFilter(paths); + } + else { + while (idx >= 0) { + lastPath = paths[idx--]; + if (lastPath.equals(ANY_CHILD)) { + return new WildcardDestinationFilter(paths); + } + } + } + } + + // if none of the paths contain a wildcard then use equality + return new SimpleDestinationFilter(destination); + } +} diff --git a/activemq-core/src/main/java/org/activemq/filter/DestinationMap.java b/activemq-core/src/main/java/org/activemq/filter/DestinationMap.java new file mode 100755 index 0000000000..f540088da5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/DestinationMap.java @@ -0,0 +1,179 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.activemq.command.ActiveMQDestination; + +/** + * A Map-like data structure allowing values to be indexed by {@link ActiveMQDestination} + * and retrieved by destination - supporting both * and > style of wildcard + * as well as composite destinations. + *
+ * This class assumes that the index changes rarely but that fast lookup into the index is required. + * So this class maintains a pre-calculated index for destination steps. So looking up the values + * for "TEST.*" or "*.TEST" will be pretty fast. + *
+ * Looking up of a value could return a single value or a List of matching values if a wildcard or + * composite destination is used. + * + * @version $Revision: 1.3 $ + */ +public class DestinationMap { + private DestinationMapNode rootNode = new DestinationMapNode(null); + protected static final String ANY_DESCENDENT = DestinationFilter.ANY_DESCENDENT; + protected static final String ANY_CHILD = DestinationFilter.ANY_CHILD; + + /** + * Looks up the value(s) matching the given Destination key. For simple destinations + * this is typically a List of one single value, for wildcards or composite destinations this will typically be + * a List of matching values. + * + * @param key the destination to lookup + * @return a List of matching values or an empty list if there are no matching values. + */ + public synchronized Set get(ActiveMQDestination key) { + if (key.isComposite()) { + ActiveMQDestination[] destinations = key.getCompositeDestinations(); + Set answer = new HashSet(destinations.length); + for (int i = 0; i < destinations.length; i++) { + ActiveMQDestination childDestination = destinations[i]; + Object value = get(childDestination); + if (value instanceof Set) { + answer.addAll((Set) value); + } + else if (value != null) { + answer.add(value); + } + } + return answer; + } + return findWildcardMatches(key); + } + + public synchronized void put(ActiveMQDestination key, Object value) { + if (key.isComposite()) { + ActiveMQDestination[] destinations = key.getCompositeDestinations(); + for (int i = 0; i < destinations.length; i++) { + ActiveMQDestination childDestination = destinations[i]; + put(childDestination, value); + } + return; + } + String[] paths = key.getDestinationPaths(); + rootNode.add(paths, 0, value); + } + + /** + * Removes the value from the associated destination + */ + public synchronized void remove(ActiveMQDestination key, Object value) { + if (key.isComposite()) { + ActiveMQDestination[] destinations = key.getCompositeDestinations(); + for (int i = 0; i < destinations.length; i++) { + ActiveMQDestination childDestination = destinations[i]; + remove(childDestination, value); + } + return; + } + String[] paths = key.getDestinationPaths(); + rootNode.remove(paths, 0, value); + + } + + public int getRootChildCount() { + return rootNode.getChildCount(); + } + + // Implementation methods + //------------------------------------------------------------------------- + + /** + * A helper method to allow the destination map to be populated from a dependency injection + * framework such as Spring + */ + protected void setEntries(List entries) { + for (Iterator iter = entries.iterator(); iter.hasNext();) { + Object element = (Object) iter.next(); + Class type = getEntryClass(); + if (type.isInstance(element)) { + DestinationMapEntry entry = (DestinationMapEntry) element; + put(entry.getDestination(), entry.getValue()); + } + else { + throw new IllegalArgumentException("Each entry must be an instance of type: " + type.getName() + " but was: " + element); + } + } + } + + /** + * Returns the type of the allowed entries which can be set via the {@link #setEntries(List)} method. + * This allows derived classes to further restrict the type of allowed entries to make a type safe + * destination map for custom policies. + */ + protected Class getEntryClass() { + return DestinationMapEntry.class; + } + + protected Set findWildcardMatches(ActiveMQDestination key) { + String[] paths = key.getDestinationPaths(); + Set answer = new HashSet(); + rootNode.appendMatchingValues(answer, paths, 0); + return answer; + } + + /** + * @param dest + */ + public void removeAll(ActiveMQDestination key) { + if (key.isComposite()) { + ActiveMQDestination[] destinations = key.getCompositeDestinations(); + for (int i = 0; i < destinations.length; i++) { + removeAll(destinations[i]); + } + return; + } + String[] paths = key.getDestinationPaths(); + rootNode.removeAll(paths, 0); + } + + /** + * Returns the value which matches the given destination or null if there is no matching + * value. If there are multiple values, the results are sorted and the last item (the biggest) + * is returned. + * + * @param destination the destination to find the value for + * @return the largest matching value or null if no value matches + */ + public Object chooseValue(ActiveMQDestination destination) { + Set set = get(destination); + if (set == null || set.isEmpty()) { + return null; + } + SortedSet sortedSet = new TreeSet(set); + return sortedSet.last(); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/filter/DestinationMapEntry.java b/activemq-core/src/main/java/org/activemq/filter/DestinationMapEntry.java new file mode 100644 index 0000000000..ad64b402c1 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/DestinationMapEntry.java @@ -0,0 +1,66 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.filter; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.springframework.beans.factory.InitializingBean; + +/** + * A base class for entry objects used to construct a destination based policy + * map. + * + * @version $Revision: 1.1 $ + */ +public abstract class DestinationMapEntry implements InitializingBean { + + private ActiveMQDestination destination; + + /** + * A helper method to set the destination from a configuration file + */ + public void setQueue(String name) { + setDestination(new ActiveMQQueue(name)); + } + + /** + * A helper method to set the destination from a configuration file + */ + public void setTopic(String name) { + setDestination(new ActiveMQTopic(name)); + } + + public ActiveMQDestination getDestination() { + return destination; + } + + public void setDestination(ActiveMQDestination destination) { + this.destination = destination; + } + + public void afterPropertiesSet() throws Exception { + if (destination == null) { + throw new IllegalArgumentException("You must specify the 'destination' property"); + } + } + + public Object getValue() { + return this; + } +} diff --git a/activemq-core/src/main/java/org/activemq/filter/DestinationMapNode.java b/activemq-core/src/main/java/org/activemq/filter/DestinationMapNode.java new file mode 100755 index 0000000000..00f97805de --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/DestinationMapNode.java @@ -0,0 +1,210 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * An implementation class used to implement {@link DestinationMap} + * + * @version $Revision: 1.2 $ + */ +public class DestinationMapNode { + // we synchornize at the DestinationMap level + private DestinationMapNode parent; + private List values = new ArrayList(); + private Map childNodes = new HashMap(); + private String path = "*"; + private DestinationMapNode anyChild; + protected static final String ANY_CHILD = DestinationMap.ANY_CHILD; + protected static final String ANY_DESCENDENT = DestinationMap.ANY_DESCENDENT; + + + public DestinationMapNode(DestinationMapNode parent) { + this.parent = parent; + } + + + /** + * Returns the child node for the given named path or null if it does not exist + */ + public DestinationMapNode getChild(String path) { + return (DestinationMapNode) childNodes.get(path); + } + + public int getChildCount() { + return childNodes.size(); + } + + /** + * Returns the child node for the given named path, lazily creating one if it does + * not yet exist + */ + public DestinationMapNode getChildOrCreate(String path) { + DestinationMapNode answer = (DestinationMapNode) childNodes.get(path); + if (answer == null) { + answer = createChildNode(); + answer.path = path; + childNodes.put(path, answer); + } + return answer; + } + + /** + * Returns the node which represents all children (i.e. the * node) + */ + public DestinationMapNode getAnyChildNode() { + if (anyChild == null) { + anyChild = createChildNode(); + } + return anyChild; + } + + /** + * Returns a mutable List of the values available at this node in the tree + */ + public List getValues() { + return values; + } + + /** + * Returns a list of all the values from this node down the tree + */ + public Set getDesendentValues() { + Set answer = new HashSet(); + appendDescendantValues(answer); + return answer; + } + + public void add(String[] paths, int idx, Object value) { + if (idx >= paths.length) { + values.add(value); + } + else { + if (idx == paths.length - 1) { + getAnyChildNode().getValues().add(value); + } + else { + getAnyChildNode().add(paths, idx + 1, value); + } + getChildOrCreate(paths[idx]).add(paths, idx + 1, value); + } + } + + public void remove(String[] paths, int idx, Object value) { + if (idx >= paths.length) { + values.remove(value); + pruneIfEmpty(); + } + else { + if (idx == paths.length - 1) { + getAnyChildNode().getValues().remove(value); + } + else { + getAnyChildNode().remove(paths, idx + 1, value); + } + getChildOrCreate(paths[idx]).remove(paths, ++idx, value); + } + } + + public void removeAll(String[] paths, int idx) { + if (idx >= paths.length) { + values.clear(); + pruneIfEmpty(); + } + else { + if (idx == paths.length - 1) { + getAnyChildNode().getValues().clear(); + } + else { + getAnyChildNode().removeAll(paths, idx + 1); + } + getChildOrCreate(paths[idx]).removeAll(paths, ++idx); + } + } + + protected void appendDescendantValues(Set answer) { + answer.addAll(values); + if (anyChild != null) { + anyChild.appendDescendantValues(answer); + } + } + + /** + * Factory method to create a child node + */ + protected DestinationMapNode createChildNode() { + return new DestinationMapNode(this); + } + + public void appendMatchingWildcards(Set answer, String[] paths, int idx) { + DestinationMapNode wildCardNode = getChild(ANY_CHILD); + if (wildCardNode != null) { + wildCardNode.appendMatchingValues(answer, paths, idx + 1); + } + wildCardNode = getChild(ANY_DESCENDENT); + if (wildCardNode != null) { + answer.addAll(wildCardNode.getDesendentValues()); + } + } + + public void appendMatchingValues(Set answer, String[] paths, int startIndex) { + DestinationMapNode node = this; + for (int i = startIndex, size = paths.length; i < size && node != null; i++) { + String path = paths[i]; + if (path.equals(ANY_DESCENDENT)) { + answer.addAll(node.getDesendentValues()); + break; + } + + node.appendMatchingWildcards(answer, paths, i); + if (path.equals(ANY_CHILD)) { + node = node.getAnyChildNode(); + } + else { + node = node.getChild(path); + } + } + if (node != null) { + answer.addAll(node.getValues()); + } + } + + + public String getPath() { + return path; + } + + protected void pruneIfEmpty() { + if (parent != null && childNodes.isEmpty() && values.isEmpty()) { + parent.removeChild(this); + } + } + + + protected void removeChild(DestinationMapNode node) { + childNodes.remove(node.getPath()); + pruneIfEmpty(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/filter/DestinationPath.java b/activemq-core/src/main/java/org/activemq/filter/DestinationPath.java new file mode 100755 index 0000000000..126551d251 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/DestinationPath.java @@ -0,0 +1,86 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import java.util.ArrayList; +import java.util.List; + +import javax.jms.JMSException; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.Message; + +/** + * Helper class for decomposing a Destination into a number of paths + * + * @version $Revision: 1.3 $ + */ +public class DestinationPath { + protected static final char SEPARATOR = '.'; + + public static String[] getDestinationPaths(String subject) { + List list = new ArrayList(); + int previous = 0; + int lastIndex = subject.length() - 1; + while (true) { + int idx = subject.indexOf(SEPARATOR, previous); + if (idx < 0) { + list.add(subject.substring(previous, lastIndex + 1)); + break; + } + list.add(subject.substring(previous, idx)); + previous = idx + 1; + } + String[] answer = new String[list.size()]; + list.toArray(answer); + return answer; + } + + public static String[] getDestinationPaths(Message message) throws JMSException { + return getDestinationPaths(message.getDestination()); + } + + public static String[] getDestinationPaths(ActiveMQDestination destination) { + return getDestinationPaths(destination.getPhysicalName()); + } + + /** + * Converts the paths to a single String seperated by dots. + * + * @param paths + * @return + */ + public static String toString(String[] paths) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < paths.length; i++) { + if (i > 0) { + buffer.append(SEPARATOR); + } + String path = paths[i]; + if (path == null) { + buffer.append("*"); + } + else { + buffer.append(path); + } + } + return buffer.toString(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/filter/Expression.java b/activemq-core/src/main/java/org/activemq/filter/Expression.java new file mode 100755 index 0000000000..9eaf0768e9 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/Expression.java @@ -0,0 +1,37 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import javax.jms.JMSException; + + +/** + * Represents an expression + * + * @version $Revision: 1.2 $ + */ +public interface Expression { + + /** + * @return the value of this expression + */ + public Object evaluate(MessageEvaluationContext message) throws JMSException; + +} diff --git a/activemq-core/src/main/java/org/activemq/filter/JAXPXPathEvaluator.java b/activemq-core/src/main/java/org/activemq/filter/JAXPXPathEvaluator.java new file mode 100755 index 0000000000..fcea876110 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/JAXPXPathEvaluator.java @@ -0,0 +1,79 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import java.io.StringReader; + +import javax.jms.BytesMessage; +import javax.jms.JMSException; +import javax.jms.TextMessage; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.activeio.ByteArrayInputStream; +import org.activemq.command.Message; +import org.xml.sax.InputSource; + +public class JAXPXPathEvaluator implements XPathExpression.XPathEvaluator { + + private static final XPathFactory factory = XPathFactory.newInstance(); + private javax.xml.xpath.XPathExpression expression; + + public JAXPXPathEvaluator(String xpathExpression) { + try { + XPath xpath = factory.newXPath(); + expression = xpath.compile(xpathExpression); + } catch (XPathExpressionException e) { + throw new RuntimeException("Invalid XPath expression: "+xpathExpression); + } + } + + public boolean evaluate(Message message) throws JMSException { + if( message instanceof TextMessage ) { + String text = ((TextMessage)message).getText(); + return evaluate(text); + } else if ( message instanceof BytesMessage ) { + BytesMessage bm = (BytesMessage) message; + byte data[] = new byte[(int) bm.getBodyLength()]; + bm.readBytes(data); + return evaluate(data); + } + return false; + } + + private boolean evaluate(byte[] data) { + try { + InputSource inputSource = new InputSource(new ByteArrayInputStream(data)); + return ((Boolean)expression.evaluate(inputSource, XPathConstants.BOOLEAN)).booleanValue(); + } catch (XPathExpressionException e) { + return false; + } + } + + private boolean evaluate(String text) { + try { + InputSource inputSource = new InputSource(new StringReader(text)); + return ((Boolean)expression.evaluate(inputSource, XPathConstants.BOOLEAN)).booleanValue(); + } catch (XPathExpressionException e) { + return false; + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/filter/LogicExpression.java b/activemq-core/src/main/java/org/activemq/filter/LogicExpression.java new file mode 100755 index 0000000000..94ae59686f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/LogicExpression.java @@ -0,0 +1,90 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import javax.jms.JMSException; + +/** + * A filter performing a comparison of two objects + * + * @version $Revision: 1.2 $ + */ +public abstract class LogicExpression extends BinaryExpression implements BooleanExpression { + + public static BooleanExpression createOR(BooleanExpression lvalue, BooleanExpression rvalue) { + return new LogicExpression(lvalue, rvalue) { + + public Object evaluate(MessageEvaluationContext message) throws JMSException { + + Boolean lv = (Boolean) left.evaluate(message); + // Can we do an OR shortcut?? + if (lv !=null && lv.booleanValue()) { + return Boolean.TRUE; + } + + Boolean rv = (Boolean) right.evaluate(message); + return rv==null ? null : rv; + } + + public String getExpressionSymbol() { + return "OR"; + } + }; + } + + public static BooleanExpression createAND(BooleanExpression lvalue, BooleanExpression rvalue) { + return new LogicExpression(lvalue, rvalue) { + + public Object evaluate(MessageEvaluationContext message) throws JMSException { + + Boolean lv = (Boolean) left.evaluate(message); + + // Can we do an AND shortcut?? + if (lv == null) + return null; + if (!lv.booleanValue()) { + return Boolean.FALSE; + } + + Boolean rv = (Boolean) right.evaluate(message); + return rv == null ? null : rv; + } + + public String getExpressionSymbol() { + return "AND"; + } + }; + } + + /** + * @param left + * @param right + */ + public LogicExpression(BooleanExpression left, BooleanExpression right) { + super(left, right); + } + + abstract public Object evaluate(MessageEvaluationContext message) throws JMSException; + + public boolean matches(MessageEvaluationContext message) throws JMSException { + Object object = evaluate(message); + return object!=null && object==Boolean.TRUE; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/filter/MessageEvaluationContext.java b/activemq-core/src/main/java/org/activemq/filter/MessageEvaluationContext.java new file mode 100755 index 0000000000..8ce9bb6c94 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/MessageEvaluationContext.java @@ -0,0 +1,98 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.filter; + +import java.io.IOException; + +import org.activemq.broker.region.MessageReference; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.Message; + +/** + * MessageEvaluationContext is used to cache selection results. + * + * A message usually has multiple selectors applied against it. Some selector + * have a high cost of evaluating against the message. Those selectors may whish + * to cache evaluation results associated with the message in the + * MessageEvaluationContext. + * + * @version $Revision: 1.4 $ + */ +public class MessageEvaluationContext { + + private MessageReference messageReference; + private boolean loaded=false; + private boolean dropped; + private Message message; + private ActiveMQDestination destination; + + public MessageEvaluationContext() { + } + + public boolean isDropped() throws IOException { + getMessage(); + return dropped; + } + + public Message getMessage() throws IOException { + if( !dropped && !loaded ) { + loaded=true; + messageReference.incrementReferenceCount(); + message = messageReference.getMessage(); + if(message==null) { + messageReference.decrementReferenceCount(); + dropped=true; + loaded=false; + } + } + return message; + } + + public void setMessageReference(MessageReference messageReference) { + if (this.messageReference != messageReference) { + clearMessageCache(); + } + this.messageReference = messageReference; + } + + public void clear() { + clearMessageCache(); + destination = null; + } + + public ActiveMQDestination getDestination() { + return destination; + } + + public void setDestination(ActiveMQDestination destination) { + this.destination = destination; + } + + /** + * A strategy hook to allow per-message caches to be cleared + */ + protected void clearMessageCache() { + if( loaded ) { + messageReference.decrementReferenceCount(); + } + message=null; + dropped=false; + loaded=false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/filter/MultiExpressionEvaluator.java b/activemq-core/src/main/java/org/activemq/filter/MultiExpressionEvaluator.java new file mode 100755 index 0000000000..900bf1fdf5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/MultiExpressionEvaluator.java @@ -0,0 +1,270 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; + +import javax.jms.JMSException; + +/** + * A MultiExpressionEvaluator is used to evaluate multiple expressions in + * single method call. + *

+ * Multiple Expression/ExpressionListener pairs can be added to a MultiExpressionEvaluator object. When + * the MultiExpressionEvaluator object is evaluated, all the registed Expressions are evaluated and then the + * associated ExpressionListener is invoked to inform it of the evaluation result. + *

+ * By evaluating multiple expressions at one time, some optimizations can be made + * to reduce the number of computations normally required to evaluate all the expressions. + *

+ * When this class adds an Expression it wrapps each node in the Expression's AST with a + * CacheExpression object. Then each CacheExpression object (one for each node) is placed + * in the cachedExpressions map. The cachedExpressions map allows us to find the sub expressions + * that are common across two different expressions. When adding an Expression in, if a sub + * Expression of the Expression is allready in the cachedExpressions map, then instead of + * wrapping the sub expression in a new CacheExpression object, we reuse the CacheExpression allready + * int the map. + *

+ * To help illustrate what going on, lets try to give an exmample: + * If we denote the AST of a Expression as follows: [AST-Node-Type,Left-Node,Right-Node], then + * A expression like: "3*5+6" would result in "[*,3,[+,5,6]]" + *

+ * If the [*,3,[+,5,6]] expression is added to the MultiExpressionEvaluator, it would really + * be converted to: [c0,[*,3,[c1,[+,5,6]]]] where c0 and c1 represent the CacheExpression expression + * objects that cache the results of the * and the + operation. Constants and Property nodes are not + * cached. + *

+ * If later on we add the following expression [=,11,[+,5,6]] ("11=5+6") to the MultiExpressionEvaluator + * it would be converted to: [c2,[=,11,[c1,[+,5,6]]]], where c2 is a new CacheExpression object + * but c1 is the same CacheExpression used in the previous expression. + *

+ * When the expressions are evaluated, the c1 CacheExpression object will only evaluate the + * [+,5,6] expression once and cache the resulting value. Hence evauating the second expression + * costs less because that [+,5,6] is not done 2 times. + *

+ * Problems: + * - cacheing the values introduces overhead. It may be possible to be smarter about WHICH + * nodes in the AST are cached and which are not. + * - Current implementation is not thread safe. This is because you need a way to invalidate + * all the cached values so that the next evaluation re-evaluates the nodes. By going single + * threaded, chache invalidation is done quickly by incrementing a 'view' counter. + * When a CacheExpressionnotices it's last cached value was generated in an old 'view', + * it invalidates its cached value. + * + * @version $Revision: 1.2 $ $Date: 2005/08/27 03:52:36 $ + */ +public class MultiExpressionEvaluator { + + HashMap rootExpressions = new HashMap(); + HashMap cachedExpressions = new HashMap(); + + int view = 0; + + /** + * A UnaryExpression that caches the result of the + * nested expression. The cached value is valid + * if the CacheExpression.cview==MultiExpressionEvaluator.view + */ + public class CacheExpression extends UnaryExpression { + short refCount = 0; + int cview = view - 1; + Object cachedValue; + int cachedHashCode; + + public CacheExpression(Expression realExpression) { + super(realExpression); + cachedHashCode = realExpression.hashCode(); + } + + /** + * @see org.activemq.filter.Expression#evaluate(MessageEvaluationContext) + */ + public Object evaluate(MessageEvaluationContext message) throws JMSException { + if (view == cview) { + return cachedValue; + } + cachedValue = right.evaluate(message); + cview = view; + return cachedValue; + } + + public int hashCode() { + return cachedHashCode; + } + + public boolean equals(Object o) { + if( o == null ) + return false; + return ((CacheExpression) o).right.equals(right); + } + + public String getExpressionSymbol() { + return null; + } + + public String toString() { + return right.toString(); + } + + } + + /** + * Multiple listeners my be interested in the results + * of a single expression. + */ + static class ExpressionListenerSet { + Expression expression; + ArrayList listeners = new ArrayList(); + } + + /** + * Objects that are interested in the results of an expression + * should implement this interface. + */ + static interface ExpressionListener { + public void evaluateResultEvent(Expression selector, MessageEvaluationContext message, Object result); + } + + /** + * Adds an ExpressionListener to a given expression. When evaluate is + * called, the ExpressionListener will be provided the results of the + * Expression applied to the evaluated message. + */ + public void addExpressionListner(Expression selector, ExpressionListener c) { + ExpressionListenerSet data = (ExpressionListenerSet) rootExpressions.get(selector.toString()); + if (data == null) { + data = new ExpressionListenerSet(); + data.expression = addToCache(selector); + rootExpressions.put(selector.toString(), data); + } + data.listeners.add(c); + } + + /** + * Removes an ExpressionListener from receiving the results of + * a given evaluation. + */ + public boolean removeEventListner(String selector, ExpressionListener c) { + String expKey = selector; + ExpressionListenerSet d = (ExpressionListenerSet) rootExpressions.get(expKey); + if (d == null) // that selector had not been added. + { + return false; + } + if (!d.listeners.remove(c)) // that selector did not have that listner.. + { + return false; + } + + // If there are no more listners for this expression.... + if (d.listeners.size() == 0) { + // Uncache it... + removeFromCache((CacheExpression) d.expression); + rootExpressions.remove(expKey); + } + return true; + } + + /** + * Finds the CacheExpression that has been associated + * with an expression. If it is the first time the + * Expression is being added to the Cache, a new + * CacheExpression is created and associated with + * the expression. + *

+ * This method updates the reference counters on the + * CacheExpression to know when it is no longer needed. + */ + private CacheExpression addToCache(Expression expr) { + + CacheExpression n = (CacheExpression) cachedExpressions.get(expr); + if (n == null) { + n = new CacheExpression(expr); + cachedExpressions.put(expr, n); + if (expr instanceof UnaryExpression) { + + // Cache the sub expressions too + UnaryExpression un = (UnaryExpression) expr; + un.setRight(addToCache(un.getRight())); + + } + else if (expr instanceof BinaryExpression) { + + // Cache the sub expressions too. + BinaryExpression bn = (BinaryExpression) expr; + bn.setRight(addToCache(bn.getRight())); + bn.setLeft(addToCache(bn.getLeft())); + + } + } + n.refCount++; + return n; + } + + /** + * Removes an expression from the cache. Updates the + * reference counters on the CacheExpression object. When + * the refernce counter goes to zero, the entry + * int the Expression to CacheExpression map is removed. + * + * @param cn + */ + private void removeFromCache(CacheExpression cn) { + cn.refCount--; + Expression realExpr = cn.getRight(); + if (cn.refCount == 0) { + cachedExpressions.remove(realExpr); + } + if (realExpr instanceof UnaryExpression) { + UnaryExpression un = (UnaryExpression) realExpr; + removeFromCache((CacheExpression) un.getRight()); + } + if (realExpr instanceof BinaryExpression) { + BinaryExpression bn = (BinaryExpression) realExpr; + removeFromCache((CacheExpression) bn.getRight()); + } + } + + /** + * Evaluates the message against all the Expressions added to + * this object. The added ExpressionListeners are notified + * of the result of the evaluation. + * + * @param message + */ + public void evaluate(MessageEvaluationContext message) { + Collection expressionListeners = rootExpressions.values(); + for (Iterator iter = expressionListeners.iterator(); iter.hasNext();) { + ExpressionListenerSet els = (ExpressionListenerSet) iter.next(); + try { + Object result = els.expression.evaluate(message); + for (Iterator iterator = els.listeners.iterator(); iterator.hasNext();) { + ExpressionListener l = (ExpressionListener) iterator.next(); + l.evaluateResultEvent(els.expression, message, result); + } + } + catch (Throwable e) { + e.printStackTrace(); + } + } + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/filter/NoLocalExpression.java b/activemq-core/src/main/java/org/activemq/filter/NoLocalExpression.java new file mode 100755 index 0000000000..410216f361 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/NoLocalExpression.java @@ -0,0 +1,49 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import java.io.IOException; + +import javax.jms.JMSException; + +import org.activemq.util.JMSExceptionSupport; + +public class NoLocalExpression implements BooleanExpression { + + private final String connectionId; + + public NoLocalExpression(String connectionId) { + this.connectionId = connectionId; + } + + public boolean matches(MessageEvaluationContext message) throws JMSException { + try { + if( message.isDropped() ) + return false; + return !connectionId.equals(message.getMessage().getMessageId().getProducerId().getConnectionId()); + } catch (IOException e) { + throw JMSExceptionSupport.create(e); + } + } + + public Object evaluate(MessageEvaluationContext message) throws JMSException { + return matches(message) ? Boolean.TRUE : Boolean.FALSE; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/filter/PrefixDestinationFilter.java b/activemq-core/src/main/java/org/activemq/filter/PrefixDestinationFilter.java new file mode 100755 index 0000000000..4547ca763e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/PrefixDestinationFilter.java @@ -0,0 +1,68 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import org.activemq.command.ActiveMQDestination; + + +/** + * Matches messages which match a prefix like "A.B.>" + * + * @version $Revision: 1.2 $ + */ +public class PrefixDestinationFilter extends DestinationFilter { + + private String[] prefixes; + + /** + * An array of paths, the last path is '>' + * + * @param prefixes + */ + public PrefixDestinationFilter(String[] prefixes) { + this.prefixes = prefixes; + } + + public boolean matches(ActiveMQDestination destination) { + String[] path = DestinationPath.getDestinationPaths(destination.getPhysicalName()); + int length = prefixes.length; + if (path.length >= length) { + for (int i = 0, size = length - 1; i < size; i++) { + if (!prefixes[i].equals(path[i])) { + return false; + } + } + return true; + } + return false; + } + + public String getText() { + return DestinationPath.toString(prefixes); + } + + public String toString() { + return super.toString() + "[destination: " + getText() + "]"; + } + + public boolean isWildcard() { + return true; + } +} diff --git a/activemq-core/src/main/java/org/activemq/filter/PropertyExpression.java b/activemq-core/src/main/java/org/activemq/filter/PropertyExpression.java new file mode 100755 index 0000000000..3bdaf8f4f3 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/PropertyExpression.java @@ -0,0 +1,210 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import java.io.IOException; +import java.util.HashMap; + +import javax.jms.DeliveryMode; +import javax.jms.JMSException; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.Message; +import org.activemq.command.TransactionId; +import org.activemq.util.JMSExceptionSupport; + +/** + * Represents a property expression + * + * @version $Revision: 1.5 $ + */ +public class PropertyExpression implements Expression { + + interface SubExpression { + public Object evaluate(Message message); + } + + static final private HashMap JMS_PROPERTY_EXPRESSIONS = new HashMap(); + static { + JMS_PROPERTY_EXPRESSIONS.put("JMSDestination", new SubExpression() { + public Object evaluate(Message message) { + ActiveMQDestination dest = message.getOriginalDestination(); + if( dest == null ) + dest = message.getDestination(); + if( dest == null ) + return null; + return dest.toString(); + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSReplyTo", new SubExpression() { + public Object evaluate(Message message) { + if( message.getReplyTo() == null ) + return null; + return message.getReplyTo().toString(); + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSType", new SubExpression() { + public Object evaluate(Message message) { + return message.getType(); + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSDeliveryMode", new SubExpression() { + public Object evaluate(Message message) { + return new Integer(message.isPersistent() ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT ); + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSPriority", new SubExpression() { + public Object evaluate(Message message) { + return new Integer(message.getPriority()); + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSMessageID", new SubExpression() { + public Object evaluate(Message message) { + if( message.getMessageId() == null ) + return null; + return message.getMessageId().toString(); + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSTimestamp", new SubExpression() { + public Object evaluate(Message message) { + return new Long(message.getTimestamp()); + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSCorrelationID", new SubExpression() { + public Object evaluate(Message message) { + return message.getCorrelationId(); + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSExpiration", new SubExpression() { + public Object evaluate(Message message) { + return new Long(message.getExpiration()); + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSPriority", new SubExpression() { + public Object evaluate(Message message) { + return new Integer(message.getPriority()); + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSTimestamp", new SubExpression() { + public Object evaluate(Message message) { + return new Long(message.getTimestamp()); + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSRedelivered", new SubExpression() { + public Object evaluate(Message message) { + return new Boolean(message.isRedelivered()); + } + }); + + JMS_PROPERTY_EXPRESSIONS.put("JMSXDeliveryCount", new SubExpression() { + public Object evaluate(Message message) { + return new Integer(message.getRedeliveryCounter()+1); + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSXGroupID", new SubExpression() { + public Object evaluate(Message message) { + return message.getGroupID(); + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSXGroupSeq", new SubExpression() { + public Object evaluate(Message message) { + return new Integer(message.getGroupSequence()); + } + }); + JMS_PROPERTY_EXPRESSIONS.put("JMSXProducerTXID", new SubExpression() { + public Object evaluate(Message message) { + TransactionId txId = message.getOriginalTransactionId(); + if( txId==null) + txId = message.getTransactionId(); + if( txId==null ) + return null; + return new Integer(txId.toString()); + } + }); + } + + private final String name; + private final SubExpression jmsPropertyExpression; + + public PropertyExpression(String name) { + this.name = name; + jmsPropertyExpression = (SubExpression) JMS_PROPERTY_EXPRESSIONS.get(name); + } + + public Object evaluate(MessageEvaluationContext message) throws JMSException { + try { + if( message.isDropped() ) + return null; + + if( jmsPropertyExpression!=null ) + return jmsPropertyExpression.evaluate(message.getMessage()); + try { + return message.getMessage().getProperty(name); + } catch (IOException ioe) { + throw JMSExceptionSupport.create("Could not get property: "+name+" reason: "+ioe.getMessage(), ioe); + } + } catch (IOException e) { + throw JMSExceptionSupport.create(e); + } + + } + + public Object evaluate(Message message) throws JMSException { + if( jmsPropertyExpression!=null ) + return jmsPropertyExpression.evaluate(message); + try { + return message.getProperty(name); + } catch (IOException ioe) { + throw JMSExceptionSupport.create(ioe); + } + } + + public String getName() { + return name; + } + + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + return name; + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return name.hashCode(); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object o) { + + if (o == null || !this.getClass().equals(o.getClass())) { + return false; + } + return name.equals(((PropertyExpression) o).name); + + } + +} diff --git a/activemq-core/src/main/java/org/activemq/filter/SimpleDestinationFilter.java b/activemq-core/src/main/java/org/activemq/filter/SimpleDestinationFilter.java new file mode 100755 index 0000000000..1c7951892a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/SimpleDestinationFilter.java @@ -0,0 +1,45 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import org.activemq.command.ActiveMQDestination; + + +/** + * Matches messages sent to an exact destination + * + * @version $Revision: 1.3 $ + */ +public class SimpleDestinationFilter extends DestinationFilter { + + private ActiveMQDestination destination; + + public SimpleDestinationFilter(ActiveMQDestination destination) { + this.destination = destination; + } + + public boolean matches(ActiveMQDestination destination) { + return this.destination.equals(destination); + } + + public boolean isWildcard() { + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/filter/UnaryExpression.java b/activemq-core/src/main/java/org/activemq/filter/UnaryExpression.java new file mode 100755 index 0000000000..e9dbe30725 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/UnaryExpression.java @@ -0,0 +1,258 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +import javax.jms.JMSException; + +/** + * An expression which performs an operation on two expression values + * + * @version $Revision: 1.3 $ + */ +public abstract class UnaryExpression implements Expression { + + private static final BigDecimal BD_LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE); + protected Expression right; + + public static Expression createNegate(Expression left) { + return new UnaryExpression(left) { + public Object evaluate(MessageEvaluationContext message) throws JMSException { + Object rvalue = right.evaluate(message); + if (rvalue == null) { + return null; + } + if (rvalue instanceof Number) { + return negate((Number) rvalue); + } + return null; + } + + public String getExpressionSymbol() { + return "-"; + } + }; + } + + public static BooleanExpression createInExpression(PropertyExpression right, List elements, final boolean not) { + + // Use a HashSet if there are many elements. + Collection t; + if( elements.size()==0 ) + t=null; + else if( elements.size() < 5 ) + t = elements; + else { + t = new HashSet(elements); + } + final Collection inList = t; + + return new BooleanUnaryExpression(right) { + public Object evaluate(MessageEvaluationContext message) throws JMSException { + + Object rvalue = right.evaluate(message); + if (rvalue == null) { + return null; + } + if( rvalue.getClass()!=String.class ) + return null; + + if( (inList!=null && inList.contains(rvalue)) ^ not ) { + return Boolean.TRUE; + } else { + return Boolean.FALSE; + } + + } + + public String toString() { + StringBuffer answer = new StringBuffer(); + answer.append(right); + answer.append(" "); + answer.append(getExpressionSymbol()); + answer.append(" ( "); + + int count=0; + for (Iterator i = inList.iterator(); i.hasNext();) { + Object o = (Object) i.next(); + if( count!=0 ) { + answer.append(", "); + } + answer.append(o); + count++; + } + + answer.append(" )"); + return answer.toString(); + } + + public String getExpressionSymbol() { + if( not ) + return "NOT IN"; + else + return "IN"; + } + }; + } + + abstract static class BooleanUnaryExpression extends UnaryExpression implements BooleanExpression { + public BooleanUnaryExpression(Expression left) { + super(left); + } + + public boolean matches(MessageEvaluationContext message) throws JMSException { + Object object = evaluate(message); + return object!=null && object==Boolean.TRUE; + } + }; + + + public static BooleanExpression createNOT(BooleanExpression left) { + return new BooleanUnaryExpression(left) { + public Object evaluate(MessageEvaluationContext message) throws JMSException { + Boolean lvalue = (Boolean) right.evaluate(message); + if (lvalue == null) { + return null; + } + return lvalue.booleanValue() ? Boolean.FALSE : Boolean.TRUE; + } + + public String getExpressionSymbol() { + return "NOT"; + } + }; + } + + public static BooleanExpression createXPath(final String xpath) { + return new XPathExpression(xpath); + } + + public static BooleanExpression createXQuery(final String xpath) { + return new XQueryExpression(xpath); + } + + public static BooleanExpression createBooleanCast(Expression left) { + return new BooleanUnaryExpression(left) { + public Object evaluate(MessageEvaluationContext message) throws JMSException { + Object rvalue = right.evaluate(message); + if (rvalue == null) + return null; + if (!rvalue.getClass().equals(Boolean.class)) + return Boolean.FALSE; + return ((Boolean)rvalue).booleanValue() ? Boolean.TRUE : Boolean.FALSE; + } + + public String toString() { + return right.toString(); + } + + public String getExpressionSymbol() { + return ""; + } + }; + } + + private static Number negate(Number left) { + Class clazz = left.getClass(); + if (clazz == Integer.class) { + return new Integer(-left.intValue()); + } + else if (clazz == Long.class) { + return new Long(-left.longValue()); + } + else if (clazz == Float.class) { + return new Float(-left.floatValue()); + } + else if (clazz == Double.class) { + return new Double(-left.doubleValue()); + } + else if (clazz == BigDecimal.class) { + // We ussually get a big deciamal when we have Long.MIN_VALUE constant in the + // Selector. Long.MIN_VALUE is too big to store in a Long as a positive so we store it + // as a Big decimal. But it gets Negated right away.. to here we try to covert it back + // to a Long. + BigDecimal bd = (BigDecimal)left; + bd = bd.negate(); + + if( BD_LONG_MIN_VALUE.compareTo(bd)==0 ) { + return new Long(Long.MIN_VALUE); + } + return bd; + } + else { + throw new RuntimeException("Don't know how to negate: "+left); + } + } + + public UnaryExpression(Expression left) { + this.right = left; + } + + public Expression getRight() { + return right; + } + + public void setRight(Expression expression) { + right = expression; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + return "(" + getExpressionSymbol() + " " + right.toString() + ")"; + } + + /** + * TODO: more efficient hashCode() + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return toString().hashCode(); + } + + /** + * TODO: more efficient hashCode() + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object o) { + + if (o == null || !this.getClass().equals(o.getClass())) { + return false; + } + return toString().equals(o.toString()); + + } + + /** + * Returns the symbol that represents this binary expression. For example, addition is + * represented by "+" + * + * @return + */ + abstract public String getExpressionSymbol(); + +} diff --git a/activemq-core/src/main/java/org/activemq/filter/WildcardDestinationFilter.java b/activemq-core/src/main/java/org/activemq/filter/WildcardDestinationFilter.java new file mode 100755 index 0000000000..f3cf51484c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/WildcardDestinationFilter.java @@ -0,0 +1,76 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import org.activemq.command.ActiveMQDestination; + + +/** + * Matches messages which contain wildcards like "A.B.*.*" + * + * @version $Revision: 1.2 $ + */ +public class WildcardDestinationFilter extends DestinationFilter { + + private String[] prefixes; + + /** + * An array of paths containing * characters + * + * @param prefixes + */ + public WildcardDestinationFilter(String[] prefixes) { + this.prefixes = new String[prefixes.length]; + for (int i = 0; i < prefixes.length; i++) { + String prefix = prefixes[i]; + if (!prefix.equals("*")) { + this.prefixes[i] = prefix; + } + } + } + + public boolean matches(ActiveMQDestination destination) { + String[] path = DestinationPath.getDestinationPaths(destination); + int length = prefixes.length; + if (path.length == length) { + for (int i = 0, size = length; i < size; i++) { + String prefix = prefixes[i]; + if (prefix != null && !prefix.equals(path[i])) { + return false; + } + } + return true; + } + return false; + } + + + public String getText() { + return DestinationPath.toString(prefixes); + } + + public String toString() { + return super.toString() + "[destination: " + getText() + "]"; + } + + public boolean isWildcard() { + return true; + } +} diff --git a/activemq-core/src/main/java/org/activemq/filter/XMLBeansXPathEvaluator.java b/activemq-core/src/main/java/org/activemq/filter/XMLBeansXPathEvaluator.java new file mode 100755 index 0000000000..3e5a8d80b4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/XMLBeansXPathEvaluator.java @@ -0,0 +1,63 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import javax.jms.BytesMessage; +import javax.jms.JMSException; +import javax.jms.TextMessage; + +import org.activeio.ByteArrayInputStream; +import org.activemq.command.Message; +import org.apache.xmlbeans.XmlObject; + +public class XMLBeansXPathEvaluator implements XPathExpression.XPathEvaluator { + + private final String xpath; + + public XMLBeansXPathEvaluator(String xpath) { + this.xpath = xpath; + } + + public boolean evaluate(Message message) throws JMSException { + if( message instanceof TextMessage ) { + String text = ((TextMessage)message).getText(); + try { + XmlObject object = XmlObject.Factory.parse(text); + XmlObject[] objects = object.selectPath(xpath); + return object!=null && objects.length>0; + } catch (Throwable e) { + return false; + } + + } else if ( message instanceof BytesMessage ) { + BytesMessage bm = (BytesMessage) message; + byte data[] = new byte[(int) bm.getBodyLength()]; + bm.readBytes(data); + try { + XmlObject object = XmlObject.Factory.parse(new ByteArrayInputStream(data)); + XmlObject[] objects = object.selectPath(xpath); + return object!=null && objects.length>0; + } catch (Throwable e) { + return false; + } + } + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/filter/XPathExpression.java b/activemq-core/src/main/java/org/activemq/filter/XPathExpression.java new file mode 100755 index 0000000000..be241e02d6 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/XPathExpression.java @@ -0,0 +1,122 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import javax.jms.JMSException; + +import org.activemq.command.Message; +import org.activemq.util.JMSExceptionSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Used to evaluate an XPath Expression in a JMS selector. + */ +public final class XPathExpression implements BooleanExpression { + + private static final Log log = LogFactory.getLog(XPathExpression.class); + private static final String EVALUATOR_SYSTEM_PROPERTY = "org.activemq.XPathEvaluatorClassName"; + private static final String DEFAULT_EVALUATOR_CLASS_NAME=XalanXPathEvaluator.class.getName(); + + private static final Constructor EVALUATOR_CONSTRUCTOR; + + static { + String cn = System.getProperty(EVALUATOR_SYSTEM_PROPERTY, DEFAULT_EVALUATOR_CLASS_NAME); + Constructor m = null; + try { + try { + m = getXPathEvaluatorConstructor(cn); + } catch (Throwable e) { + log.warn("Invalid "+XPathEvaluator.class.getName()+" implementation: "+cn+", reason: "+e,e); + cn = DEFAULT_EVALUATOR_CLASS_NAME; + try { + m = getXPathEvaluatorConstructor(cn); + } catch (Throwable e2) { + log.error("Default XPath evaluator could not be loaded",e); + } + } + } finally { + EVALUATOR_CONSTRUCTOR = m; + } + } + + private static Constructor getXPathEvaluatorConstructor(String cn) throws ClassNotFoundException, SecurityException, NoSuchMethodException { + Class c = XPathExpression.class.getClassLoader().loadClass(cn); + if( !XPathEvaluator.class.isAssignableFrom(c) ) { + throw new ClassCastException(""+c+" is not an instance of "+XPathEvaluator.class); + } + return c.getConstructor(new Class[]{String.class}); + } + + private final String xpath; + private final XPathEvaluator evaluator; + + static public interface XPathEvaluator { + public boolean evaluate(Message message) throws JMSException; + } + + XPathExpression(String xpath) { + this.xpath = xpath; + this.evaluator = createEvaluator(xpath); + } + + private XPathEvaluator createEvaluator(String xpath2) { + try { + return (XPathEvaluator)EVALUATOR_CONSTRUCTOR.newInstance(new Object[]{xpath}); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if( cause instanceof RuntimeException ) { + throw (RuntimeException)cause; + } + throw new RuntimeException("Invalid XPath Expression: "+xpath+" reason: "+e.getMessage(), e); + } catch (Throwable e) { + throw new RuntimeException("Invalid XPath Expression: "+xpath+" reason: "+e.getMessage(), e); + } + } + + public Object evaluate(MessageEvaluationContext message) throws JMSException { + try { + if( message.isDropped() ) + return null; + return evaluator.evaluate(message.getMessage()) ? Boolean.TRUE : Boolean.FALSE; + } catch (IOException e) { + throw JMSExceptionSupport.create(e); + } + + } + + public String toString() { + return "XPATH "+ConstantExpression.encodeString(xpath); + } + + /** + * @param message + * @return true if the expression evaluates to Boolean.TRUE. + * @throws JMSException + */ + public boolean matches(MessageEvaluationContext message) throws JMSException { + Object object = evaluate(message); + return object!=null && object==Boolean.TRUE; + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/filter/XQueryExpression.java b/activemq-core/src/main/java/org/activemq/filter/XQueryExpression.java new file mode 100755 index 0000000000..330bdf0f70 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/XQueryExpression.java @@ -0,0 +1,52 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import javax.jms.JMSException; + +/** + * Used to evaluate an XQuery Expression in a JMS selector. + */ +public final class XQueryExpression implements BooleanExpression { + private final String xpath; + + XQueryExpression(String xpath) { + super(); + this.xpath = xpath; + } + + public Object evaluate(MessageEvaluationContext message) throws JMSException { + return Boolean.FALSE; + } + + public String toString() { + return "XQUERY "+ConstantExpression.encodeString(xpath); + } + + /** + * @param message + * @return true if the expression evaluates to Boolean.TRUE. + * @throws JMSException + */ + public boolean matches(MessageEvaluationContext message) throws JMSException { + Object object = evaluate(message); + return object!=null && object==Boolean.TRUE; + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/filter/XalanXPathEvaluator.java b/activemq-core/src/main/java/org/activemq/filter/XalanXPathEvaluator.java new file mode 100755 index 0000000000..e3be77539f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/XalanXPathEvaluator.java @@ -0,0 +1,95 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import java.io.StringReader; + +import javax.jms.BytesMessage; +import javax.jms.JMSException; +import javax.jms.TextMessage; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.activeio.ByteArrayInputStream; +import org.activemq.command.Message; +import org.apache.xpath.CachedXPathAPI; +import org.w3c.dom.Document; +import org.w3c.dom.traversal.NodeIterator; +import org.xml.sax.InputSource; + +public class XalanXPathEvaluator implements XPathExpression.XPathEvaluator { + + private final String xpath; + + public XalanXPathEvaluator(String xpath) { + this.xpath = xpath; + } + + public boolean evaluate(Message m) throws JMSException { + if( m instanceof TextMessage ) { + String text = ((TextMessage)m).getText(); + return evaluate(text); + } else if ( m instanceof BytesMessage ) { + BytesMessage bm = (BytesMessage) m; + byte data[] = new byte[(int) bm.getBodyLength()]; + bm.readBytes(data); + return evaluate(data); + } + return false; + } + + private boolean evaluate(byte[] data) { + try { + + InputSource inputSource = new InputSource(new ByteArrayInputStream(data)); + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder dbuilder = factory.newDocumentBuilder(); + Document doc = dbuilder.parse(inputSource); + + CachedXPathAPI cachedXPathAPI = new CachedXPathAPI(); + NodeIterator iterator = cachedXPathAPI.selectNodeIterator(doc,xpath); + return iterator.nextNode()!=null; + + } catch (Throwable e) { + return false; + } + } + + private boolean evaluate(String text) { + try { + InputSource inputSource = new InputSource(new StringReader(text)); + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder dbuilder = factory.newDocumentBuilder(); + Document doc = dbuilder.parse(inputSource); + + // We should associated the cachedXPathAPI object with the message being evaluated + // since that should speedup subsequent xpath expressions. + CachedXPathAPI cachedXPathAPI = new CachedXPathAPI(); + NodeIterator iterator = cachedXPathAPI.selectNodeIterator(doc,xpath); + return iterator.nextNode()!=null; + } catch (Throwable e) { + return false; + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/filter/package.html b/activemq-core/src/main/java/org/activemq/filter/package.html new file mode 100755 index 0000000000..5b83cf6244 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/filter/package.html @@ -0,0 +1,11 @@ + + + + + +

+ Filter implementations for wildcards & JMS selectors +

+ + + diff --git a/activemq-core/src/main/java/org/activemq/jndi/ActiveMQInitialContextFactory.java b/activemq-core/src/main/java/org/activemq/jndi/ActiveMQInitialContextFactory.java new file mode 100755 index 0000000000..0ad4f9ec50 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/jndi/ActiveMQInitialContextFactory.java @@ -0,0 +1,224 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.jndi; + +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +import javax.jms.ConnectionFactory; +import javax.jms.Queue; +import javax.jms.Topic; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.spi.InitialContextFactory; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.broker.Broker; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * A factory of the ActiveMQ InitialContext which contains {@link ConnectionFactory} + * instances as well as a child context called destinations which contain all of the + * current active destinations, in child context depending on the QoS such as + * transient or durable and queue or topic. + * + * @version $Revision: 1.2 $ + */ +public class ActiveMQInitialContextFactory implements InitialContextFactory { + + private static final String[] defaultConnectionFactoryNames = { + "ConnectionFactory", "QueueConnectionFactory", "TopicConnectionFactory" + }; + + private String connectionPrefix = "connection."; + private String queuePrefix = "queue."; + private String topicPrefix = "topic."; + + public Context getInitialContext(Hashtable environment) throws NamingException { + // lets create a factory + Map data = new ConcurrentHashMap(); + Broker broker = null; + + String[] names = getConnectionFactoryNames(environment); + for (int i = 0; i < names.length; i++) { + ActiveMQConnectionFactory factory =null; + String name = names[i]; + + try{ + factory = createConnectionFactory(name, environment); + }catch(Exception e){ + throw new NamingException("Invalid broker URL"); + + } + /* if( broker==null ) { + try { + broker = factory.getEmbeddedBroker(); + } + catch (JMSException e) { + log.warn("Failed to get embedded broker", e); + } + } + */ + data.put(name,factory); + } + + createQueues(data, environment); + createTopics(data, environment); + /* + if (broker != null) { + data.put("destinations", broker.getDestinationContext(environment)); + } + */ + data.put("dynamicQueues", new LazyCreateContext() { + private static final long serialVersionUID = 6503881346214855588L; + + protected Object createEntry(String name) { + return new ActiveMQQueue(name); + } + }); + data.put("dynamicTopics", new LazyCreateContext() { + private static final long serialVersionUID = 2019166796234979615L; + + protected Object createEntry(String name) { + return new ActiveMQTopic(name); + } + }); + + return new ReadOnlyContext(environment, data); + } + + private ActiveMQConnectionFactory createConnectionFactory(String name, Hashtable environment) throws URISyntaxException { + Hashtable temp = new Hashtable(environment); + String prefix = connectionPrefix+name+"."; + for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry = (Map.Entry) iter.next(); + String key = (String) entry.getKey(); + if( key.startsWith(prefix) ) { + // Rename the key... + temp.remove(key); + key = key.substring(prefix.length()); + temp.put(key, entry.getValue()); + } + } + return createConnectionFactory(temp); + } + + // Properties + //------------------------------------------------------------------------- + public String getTopicPrefix() { + return topicPrefix; + } + + public void setTopicPrefix(String topicPrefix) { + this.topicPrefix = topicPrefix; + } + + public String getQueuePrefix() { + return queuePrefix; + } + + public void setQueuePrefix(String queuePrefix) { + this.queuePrefix = queuePrefix; + } + + // Implementation methods + //------------------------------------------------------------------------- + protected String[] getConnectionFactoryNames(Map environment) { + String factoryNames = (String) environment.get("connectionFactoryNames"); + if (factoryNames != null) { + List list = new ArrayList(); + for (StringTokenizer enumeration = new StringTokenizer(factoryNames, ","); enumeration.hasMoreTokens();) { + list.add(enumeration.nextToken().trim()); + } + int size = list.size(); + if (size > 0) { + String[] answer = new String[size]; + list.toArray(answer); + return answer; + } + } + return defaultConnectionFactoryNames; + } + + protected void createQueues(Map data, Hashtable environment) { + for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry = (Map.Entry) iter.next(); + String key = entry.getKey().toString(); + if (key.startsWith(queuePrefix)) { + String jndiName = key.substring(queuePrefix.length()); + data.put(jndiName, createQueue(entry.getValue().toString())); + } + } + } + + protected void createTopics(Map data, Hashtable environment) { + for (Iterator iter = environment.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry = (Map.Entry) iter.next(); + String key = entry.getKey().toString(); + if (key.startsWith(topicPrefix)) { + String jndiName = key.substring(topicPrefix.length()); + data.put(jndiName, createTopic(entry.getValue().toString())); + } + } + } + + /** + * Factory method to create new Queue instances + */ + protected Queue createQueue(String name) { + return new ActiveMQQueue(name); + } + + /** + * Factory method to create new Topic instances + */ + protected Topic createTopic(String name) { + return new ActiveMQTopic(name); + } + + /** + * Factory method to create a new connection factory from the given environment + */ + protected ActiveMQConnectionFactory createConnectionFactory(Hashtable environment) throws URISyntaxException { + ActiveMQConnectionFactory answer = new ActiveMQConnectionFactory(); + Properties properties = new Properties(); + properties.putAll(environment); + answer.setProperties(properties); + return answer; + } + + public String getConnectionPrefix() { + return connectionPrefix; + } + + + public void setConnectionPrefix(String connectionPrefix) { + this.connectionPrefix = connectionPrefix; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/jndi/LazyCreateContext.java b/activemq-core/src/main/java/org/activemq/jndi/LazyCreateContext.java new file mode 100755 index 0000000000..b1c4ff648d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/jndi/LazyCreateContext.java @@ -0,0 +1,45 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.jndi; + +import javax.naming.NameNotFoundException; +import javax.naming.NamingException; + +/** + * Allows users to dynamically create items + * + * @version $Revision: 1.2 $ + */ +public abstract class LazyCreateContext extends ReadOnlyContext { + public Object lookup(String name) throws NamingException { + try { + return super.lookup(name); + } + catch (NameNotFoundException e) { + Object answer = createEntry(name); + if (answer == null) { + throw e; + } + internalBind(name, answer); + return answer; + } + } + + protected abstract Object createEntry(String name); +} diff --git a/activemq-core/src/main/java/org/activemq/jndi/NameParserImpl.java b/activemq-core/src/main/java/org/activemq/jndi/NameParserImpl.java new file mode 100755 index 0000000000..82fab553bb --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/jndi/NameParserImpl.java @@ -0,0 +1,36 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.jndi; + +import javax.naming.CompositeName; +import javax.naming.Name; +import javax.naming.NameParser; +import javax.naming.NamingException; + +/** + * A default implementation of {@link NameParser} + * + * @version $Revision: 1.2 $ + */ +public class NameParserImpl implements NameParser { + public Name parse(String name) throws NamingException { + return new CompositeName(name); + } +} diff --git a/activemq-core/src/main/java/org/activemq/jndi/ReadOnlyContext.java b/activemq-core/src/main/java/org/activemq/jndi/ReadOnlyContext.java new file mode 100755 index 0000000000..a61dab681f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/jndi/ReadOnlyContext.java @@ -0,0 +1,413 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.jndi; + +import javax.naming.*; +import javax.naming.spi.NamingManager; +import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; + +/** + * A read-only Context + *

+ * This version assumes it and all its subcontext are read-only and any attempt + * to modify (e.g. through bind) will result in an OperationNotSupportedException. + * Each Context in the tree builds a cache of the entries in all sub-contexts + * to optimise the performance of lookup. + *

+ *

This implementation is intended to optimise the performance of lookup(String) + * to about the level of a HashMap get. It has been observed that the scheme + * resolution phase performed by the JVM takes considerably longer, so for + * optimum performance lookups should be coded like:

+ * + * Context componentContext = (Context)new InitialContext().lookup("java:comp"); + * String envEntry = (String) componentContext.lookup("env/myEntry"); + * String envEntry2 = (String) componentContext.lookup("env/myEntry2"); + * + * + * @version $Revision: 1.2 $ $Date: 2005/08/27 03:52:39 $ + */ +public class ReadOnlyContext implements Context, Serializable { + private static final long serialVersionUID = -5754338187296859149L; + protected static final NameParser nameParser = new NameParserImpl(); + + protected final Hashtable environment; // environment for this context + protected final Map bindings; // bindings at my level + protected final Map treeBindings; // all bindings under me + + private boolean frozen = false; + private String nameInNamespace = ""; + public static final String SEPARATOR = "/"; + + public ReadOnlyContext() { + environment = new Hashtable(); + bindings = new HashMap(); + treeBindings = new HashMap(); + } + + public ReadOnlyContext(Hashtable env) { + if (env == null) { + this.environment = new Hashtable(); + } + else { + this.environment = new Hashtable(env); + } + this.bindings = Collections.EMPTY_MAP; + this.treeBindings = Collections.EMPTY_MAP; + } + + public ReadOnlyContext(Hashtable environment, Map bindings) { + if (environment == null) { + this.environment = new Hashtable(); + } + else { + this.environment = new Hashtable(environment); + } + this.bindings = bindings; + treeBindings = new HashMap(); + frozen = true; + } + + public ReadOnlyContext(Hashtable environment, Map bindings, String nameInNamespace) { + this(environment, bindings); + this.nameInNamespace = nameInNamespace; + } + + protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env) { + this.bindings = clone.bindings; + this.treeBindings = clone.treeBindings; + this.environment = new Hashtable(env); + } + + protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env, String nameInNamespace) { + this(clone, env); + this.nameInNamespace = nameInNamespace; + } + + public void freeze() { + frozen = true; + } + + boolean isFrozen() { + return frozen; + } + + /** + * internalBind is intended for use only during setup or possibly by suitably synchronized superclasses. + * It binds every possible lookup into a map in each context. To do this, each context + * strips off one name segment and if necessary creates a new context for it. Then it asks that context + * to bind the remaining name. It returns a map containing all the bindings from the next context, plus + * the context it just created (if it in fact created it). (the names are suitably extended by the segment + * originally lopped off). + * + * @param name + * @param value + * @return + * @throws javax.naming.NamingException + */ + protected Map internalBind(String name, Object value) throws NamingException { + assert name != null && name.length() > 0; + assert !frozen; + + Map newBindings = new HashMap(); + int pos = name.indexOf('/'); + if (pos == -1) { + if (treeBindings.put(name, value) != null) { + throw new NamingException("Something already bound at " + name); + } + bindings.put(name, value); + newBindings.put(name, value); + } + else { + String segment = name.substring(0, pos); + assert segment != null; + assert !segment.equals(""); + Object o = treeBindings.get(segment); + if (o == null) { + o = newContext(); + treeBindings.put(segment, o); + bindings.put(segment, o); + newBindings.put(segment, o); + } + else if (!(o instanceof ReadOnlyContext)) { + throw new NamingException("Something already bound where a subcontext should go"); + } + ReadOnlyContext readOnlyContext = (ReadOnlyContext) o; + String remainder = name.substring(pos + 1); + Map subBindings = readOnlyContext.internalBind(remainder, value); + for (Iterator iterator = subBindings.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + String subName = segment + "/" + (String) entry.getKey(); + Object bound = entry.getValue(); + treeBindings.put(subName, bound); + newBindings.put(subName, bound); + } + } + return newBindings; + } + + protected ReadOnlyContext newContext() { + return new ReadOnlyContext(); + } + + public Object addToEnvironment(String propName, Object propVal) throws NamingException { + return environment.put(propName, propVal); + } + + public Hashtable getEnvironment() throws NamingException { + return (Hashtable) environment.clone(); + } + + public Object removeFromEnvironment(String propName) throws NamingException { + return environment.remove(propName); + } + + public Object lookup(String name) throws NamingException { + if (name.length() == 0) { + return this; + } + Object result = treeBindings.get(name); + if (result == null) { + result = bindings.get(name); + } + if (result == null) { + int pos = name.indexOf(':'); + if (pos > 0) { + String scheme = name.substring(0, pos); + Context ctx = NamingManager.getURLContext(scheme, environment); + if (ctx == null) { + throw new NamingException("scheme " + scheme + " not recognized"); + } + return ctx.lookup(name); + } + else { + // Split out the first name of the path + // and look for it in the bindings map. + CompositeName path = new CompositeName(name); + + if (path.size() == 0) { + return this; + } + else { + String first = path.get(0); + Object obj = bindings.get(first); + if (obj == null) { + throw new NameNotFoundException(name); + } + else if (obj instanceof Context && path.size() > 1) { + Context subContext = (Context) obj; + obj = subContext.lookup(path.getSuffix(1)); + } + return obj; + } + } + } + if (result instanceof LinkRef) { + LinkRef ref = (LinkRef) result; + result = lookup(ref.getLinkName()); + } + if (result instanceof Reference) { + try { + result = NamingManager.getObjectInstance(result, null, null, this.environment); + } + catch (NamingException e) { + throw e; + } + catch (Exception e) { + throw (NamingException) new NamingException("could not look up : " + name).initCause(e); + } + } + if (result instanceof ReadOnlyContext) { + String prefix = getNameInNamespace(); + if (prefix.length() > 0) { + prefix = prefix + SEPARATOR; + } + result = new ReadOnlyContext((ReadOnlyContext) result, environment, prefix + name); + } + return result; + } + + public Object lookup(Name name) throws NamingException { + return lookup(name.toString()); + } + + public Object lookupLink(String name) throws NamingException { + return lookup(name); + } + + public Name composeName(Name name, Name prefix) throws NamingException { + Name result = (Name) prefix.clone(); + result.addAll(name); + return result; + } + + public String composeName(String name, String prefix) throws NamingException { + CompositeName result = new CompositeName(prefix); + result.addAll(new CompositeName(name)); + return result.toString(); + } + + public NamingEnumeration list(String name) throws NamingException { + Object o = lookup(name); + if (o == this) { + return new ListEnumeration(); + } + else if (o instanceof Context) { + return ((Context) o).list(""); + } + else { + throw new NotContextException(); + } + } + + public NamingEnumeration listBindings(String name) throws NamingException { + Object o = lookup(name); + if (o == this) { + return new ListBindingEnumeration(); + } + else if (o instanceof Context) { + return ((Context) o).listBindings(""); + } + else { + throw new NotContextException(); + } + } + + public Object lookupLink(Name name) throws NamingException { + return lookupLink(name.toString()); + } + + public NamingEnumeration list(Name name) throws NamingException { + return list(name.toString()); + } + + public NamingEnumeration listBindings(Name name) throws NamingException { + return listBindings(name.toString()); + } + + public void bind(Name name, Object obj) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void bind(String name, Object obj) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void close() throws NamingException { + // ignore + } + + public Context createSubcontext(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public Context createSubcontext(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void destroySubcontext(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void destroySubcontext(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public String getNameInNamespace() throws NamingException { + return nameInNamespace; + } + + public NameParser getNameParser(Name name) throws NamingException { + return nameParser; + } + + public NameParser getNameParser(String name) throws NamingException { + return nameParser; + } + + public void rebind(Name name, Object obj) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void rebind(String name, Object obj) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void rename(Name oldName, Name newName) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void rename(String oldName, String newName) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void unbind(Name name) throws NamingException { + throw new OperationNotSupportedException(); + } + + public void unbind(String name) throws NamingException { + throw new OperationNotSupportedException(); + } + + private abstract class LocalNamingEnumeration implements NamingEnumeration { + private Iterator i = bindings.entrySet().iterator(); + + public boolean hasMore() throws NamingException { + return i.hasNext(); + } + + public boolean hasMoreElements() { + return i.hasNext(); + } + + protected Map.Entry getNext() { + return (Map.Entry) i.next(); + } + + public void close() throws NamingException { + } + } + + private class ListEnumeration extends LocalNamingEnumeration { + public Object next() throws NamingException { + return nextElement(); + } + + public Object nextElement() { + Map.Entry entry = getNext(); + return new NameClassPair((String) entry.getKey(), entry.getValue().getClass().getName()); + } + } + + private class ListBindingEnumeration extends LocalNamingEnumeration { + public Object next() throws NamingException { + return nextElement(); + } + + public Object nextElement() { + Map.Entry entry = getNext(); + return new Binding((String) entry.getKey(), entry.getValue()); + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/jndi/package.html b/activemq-core/src/main/java/org/activemq/jndi/package.html new file mode 100755 index 0000000000..e43d380da8 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/jndi/package.html @@ -0,0 +1,9 @@ + + + + + +JNDI support classes + + + diff --git a/activemq-core/src/main/java/org/activemq/management/BoundaryStatisticImpl.java b/activemq-core/src/main/java/org/activemq/management/BoundaryStatisticImpl.java new file mode 100755 index 0000000000..49e82bec35 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/BoundaryStatisticImpl.java @@ -0,0 +1,52 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + + +/** + * A boundary statistic implementation + * + * @version $Revision: 1.2 $ + */ +public class BoundaryStatisticImpl extends StatisticImpl { + private long lowerBound; + private long upperBound; + + public BoundaryStatisticImpl(String name, String unit, String description, long lowerBound, long upperBound) { + super(name, unit, description); + this.lowerBound = lowerBound; + this.upperBound = upperBound; + } + + public long getLowerBound() { + return lowerBound; + } + + public long getUpperBound() { + return upperBound; + } + + protected void appendFieldDescription(StringBuffer buffer) { + buffer.append(" lowerBound: "); + buffer.append(Long.toString(lowerBound)); + buffer.append(" upperBound: "); + buffer.append(Long.toString(upperBound)); + super.appendFieldDescription(buffer); + } +} diff --git a/activemq-core/src/main/java/org/activemq/management/BoundedRangeStatisticImpl.java b/activemq-core/src/main/java/org/activemq/management/BoundedRangeStatisticImpl.java new file mode 100755 index 0000000000..a6bcbef5b2 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/BoundedRangeStatisticImpl.java @@ -0,0 +1,52 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + + +/** + * A bounded range statistic implementation + * + * @version $Revision: 1.2 $ + */ +public class BoundedRangeStatisticImpl extends RangeStatisticImpl { + private long lowerBound; + private long upperBound; + + public BoundedRangeStatisticImpl(String name, String unit, String description, long lowerBound, long upperBound) { + super(name, unit, description); + this.lowerBound = lowerBound; + this.upperBound = upperBound; + } + + public long getLowerBound() { + return lowerBound; + } + + public long getUpperBound() { + return upperBound; + } + + protected void appendFieldDescription(StringBuffer buffer) { + buffer.append(" lowerBound: "); + buffer.append(Long.toString(lowerBound)); + buffer.append(" upperBound: "); + buffer.append(Long.toString(upperBound)); + super.appendFieldDescription(buffer); + } +} diff --git a/activemq-core/src/main/java/org/activemq/management/CountStatisticImpl.java b/activemq-core/src/main/java/org/activemq/management/CountStatisticImpl.java new file mode 100755 index 0000000000..4c2a6fad6c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/CountStatisticImpl.java @@ -0,0 +1,127 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +import javax.management.j2ee.statistics.CountStatistic; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicLong; + +/** + * A count statistic implementation + * + * @version $Revision: 1.3 $ + */ +public class CountStatisticImpl extends StatisticImpl implements CountStatistic { + + private final AtomicLong counter = new AtomicLong(0); + private CountStatisticImpl parent; + + public CountStatisticImpl(CountStatisticImpl parent, String name, String description) { + this(name, description); + this.parent = parent; + } + + public CountStatisticImpl(String name, String description) { + this(name, "count", description); + } + + public CountStatisticImpl(String name, String unit, String description) { + super(name, unit, description); + } + + public void reset() { + super.reset(); + counter.set(0); + } + + public long getCount() { + return counter.get(); + } + + public void setCount(long count) { + counter.set(count); + } + + public void add(long amount) { + counter.addAndGet(amount); + updateSampleTime(); + if (parent != null) { + parent.add(amount); + } + } + + public void increment() { + counter.incrementAndGet(); + updateSampleTime(); + if (parent != null) { + parent.increment(); + } + } + + public void subtract(long amount) { + counter.addAndGet(-amount); + updateSampleTime(); + if (parent != null) { + parent.subtract(amount); + } + } + + public void decrement() { + counter.decrementAndGet(); + updateSampleTime(); + if (parent != null) { + parent.decrement(); + } + } + + public CountStatisticImpl getParent() { + return parent; + } + + public void setParent(CountStatisticImpl parent) { + this.parent = parent; + } + + protected void appendFieldDescription(StringBuffer buffer) { + buffer.append(" count: "); + buffer.append(Long.toString(counter.get())); + super.appendFieldDescription(buffer); + } + + /** + * @return the average time period that elapses between counter increments since the last reset. + */ + public double getPeriod() { + double count = counter.get(); + if( count == 0 ) + return 0; + double time = (System.currentTimeMillis() - getStartTime()); + return (time/(count*1000.0)); + } + + /** + * @return the number of times per second that the counter is incrementing since the last reset. + */ + public double getFrequency() { + double count = counter.get(); + double time = (System.currentTimeMillis() - getStartTime()); + return (count*1000.0/time); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/management/JCAConnectionPoolStatsImpl.java b/activemq-core/src/main/java/org/activemq/management/JCAConnectionPoolStatsImpl.java new file mode 100755 index 0000000000..cb7794832b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/JCAConnectionPoolStatsImpl.java @@ -0,0 +1,68 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + + +/** + * Statistics for a JCA connection pool + * + * @version $Revision: 1.2 $ + */ +public class JCAConnectionPoolStatsImpl extends JCAConnectionStatsImpl { + private CountStatisticImpl closeCount; + private CountStatisticImpl createCount; + private BoundedRangeStatisticImpl freePoolSize; + private BoundedRangeStatisticImpl poolSize; + private RangeStatisticImpl waitingThreadCount; + + public JCAConnectionPoolStatsImpl(String connectionFactory, String managedConnectionFactory, TimeStatisticImpl waitTime, TimeStatisticImpl useTime, CountStatisticImpl closeCount, CountStatisticImpl createCount, BoundedRangeStatisticImpl freePoolSize, BoundedRangeStatisticImpl poolSize, RangeStatisticImpl waitingThreadCount) { + super(connectionFactory, managedConnectionFactory, waitTime, useTime); + this.closeCount = closeCount; + this.createCount = createCount; + this.freePoolSize = freePoolSize; + this.poolSize = poolSize; + this.waitingThreadCount = waitingThreadCount; + + // lets add named stats + addStatistic("freePoolSize", freePoolSize); + addStatistic("poolSize", poolSize); + addStatistic("waitingThreadCount", waitingThreadCount); + } + + public CountStatisticImpl getCloseCount() { + return closeCount; + } + + public CountStatisticImpl getCreateCount() { + return createCount; + } + + public BoundedRangeStatisticImpl getFreePoolSize() { + return freePoolSize; + } + + public BoundedRangeStatisticImpl getPoolSize() { + return poolSize; + } + + public RangeStatisticImpl getWaitingThreadCount() { + return waitingThreadCount; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/management/JCAConnectionStatsImpl.java b/activemq-core/src/main/java/org/activemq/management/JCAConnectionStatsImpl.java new file mode 100755 index 0000000000..10699bb009 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/JCAConnectionStatsImpl.java @@ -0,0 +1,60 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + + +/** + * Statistics for a JCA connection + * + * @version $Revision: 1.2 $ + */ +public class JCAConnectionStatsImpl extends StatsImpl { + private String connectionFactory; + private String managedConnectionFactory; + private TimeStatisticImpl waitTime; + private TimeStatisticImpl useTime; + + public JCAConnectionStatsImpl(String connectionFactory, String managedConnectionFactory, TimeStatisticImpl waitTime, TimeStatisticImpl useTime) { + this.connectionFactory = connectionFactory; + this.managedConnectionFactory = managedConnectionFactory; + this.waitTime = waitTime; + this.useTime = useTime; + + // lets add named stats + addStatistic("waitTime", waitTime); + addStatistic("useTime", useTime); + } + + public String getConnectionFactory() { + return connectionFactory; + } + + public String getManagedConnectionFactory() { + return managedConnectionFactory; + } + + public TimeStatisticImpl getWaitTime() { + return waitTime; + } + + public TimeStatisticImpl getUseTime() { + return useTime; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/management/JCAStatsImpl.java b/activemq-core/src/main/java/org/activemq/management/JCAStatsImpl.java new file mode 100755 index 0000000000..eb1761b6cf --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/JCAStatsImpl.java @@ -0,0 +1,45 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + + +/** + * Statistics for a number of JCA connections and connection pools + * + * @version $Revision: 1.2 $ + */ +public class JCAStatsImpl extends StatsImpl { + private JCAConnectionStatsImpl[] connectionStats; + private JCAConnectionPoolStatsImpl[] connectionPoolStats; + + public JCAStatsImpl(JCAConnectionStatsImpl[] connectionStats, JCAConnectionPoolStatsImpl[] connectionPoolStats) { + this.connectionStats = connectionStats; + this.connectionPoolStats = connectionPoolStats; + } + + public JCAConnectionStatsImpl[] getConnections() { + return connectionStats; + } + + public JCAConnectionPoolStatsImpl[] getConnectionPools() { + return connectionPoolStats; + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/management/JMSConnectionStatsImpl.java b/activemq-core/src/main/java/org/activemq/management/JMSConnectionStatsImpl.java new file mode 100755 index 0000000000..3dbe744b5b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/JMSConnectionStatsImpl.java @@ -0,0 +1,100 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +import java.util.List; + +import org.activemq.ActiveMQSession; +import org.activemq.util.IndentPrinter; +import javax.management.j2ee.statistics.Statistic; +/** + * Statistics for a JMS connection + * + * @version $Revision: 1.2 $ + */ +public class JMSConnectionStatsImpl extends StatsImpl { + private List sessions; + private boolean transactional; + + public JMSConnectionStatsImpl(List sessions, boolean transactional) { + this.sessions = sessions; + this.transactional = transactional; + } + + public JMSSessionStatsImpl[] getSessions() { + // lets make a snapshot before we process them + Object[] sessionArray = sessions.toArray(); + int size = sessionArray.length; + JMSSessionStatsImpl[] answer = new JMSSessionStatsImpl[size]; + for (int i = 0; i < size; i++) { + ActiveMQSession session = (ActiveMQSession) sessionArray[i]; + answer[i] = session.getSessionStats(); + } + return answer; + } + + public void reset() { + super.reset(); + JMSSessionStatsImpl[] stats = getSessions(); + for (int i = 0, size = stats.length; i < size; i++) { + stats[i].reset(); + } + } + + + public boolean isTransactional() { + return transactional; + } + + public String toString() { + StringBuffer buffer = new StringBuffer("connection{ "); + JMSSessionStatsImpl[] array = getSessions(); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(", "); + } + buffer.append(Integer.toString(i)); + buffer.append(" = "); + buffer.append(array[i]); + } + buffer.append(" }"); + return buffer.toString(); + } + + public void dump(IndentPrinter out) { + out.printIndent(); + out.println("connection {"); + out.incrementIndent(); + JMSSessionStatsImpl[] array = getSessions(); + for (int i = 0; i < array.length; i++) { + JMSSessionStatsImpl sessionStat = (JMSSessionStatsImpl) array[i]; + out.printIndent(); + out.println("session {"); + out.incrementIndent(); + sessionStat.dump(out); + out.decrementIndent(); + out.printIndent(); + out.println("}"); + } + out.decrementIndent(); + out.printIndent(); + out.println("}"); + out.flush(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/management/JMSConsumerStatsImpl.java b/activemq-core/src/main/java/org/activemq/management/JMSConsumerStatsImpl.java new file mode 100755 index 0000000000..88d6fbefbb --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/JMSConsumerStatsImpl.java @@ -0,0 +1,72 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +import javax.jms.Destination; + +import org.activemq.util.IndentPrinter; + +/** + * Statistics for a JMS consumer + * + * @version $Revision: 1.2 $ + */ +public class JMSConsumerStatsImpl extends JMSEndpointStatsImpl { + private String origin; + + public JMSConsumerStatsImpl(JMSSessionStatsImpl sessionStats, Destination destination) { + super(sessionStats); + if (destination != null) { + this.origin = destination.toString(); + } + } + + public JMSConsumerStatsImpl(CountStatisticImpl messageCount, CountStatisticImpl pendingMessageCount, CountStatisticImpl expiredMessageCount, TimeStatisticImpl messageWaitTime, TimeStatisticImpl messageRateTime, String origin) { + super(messageCount, pendingMessageCount, expiredMessageCount, messageWaitTime, messageRateTime); + this.origin = origin; + } + + public String getOrigin() { + return origin; + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append("consumer "); + buffer.append(origin); + buffer.append(" { "); + buffer.append(super.toString()); + buffer.append(" }"); + return buffer.toString(); + } + + public void dump(IndentPrinter out) { + out.printIndent(); + out.print("consumer "); + out.print(origin); + out.println(" {"); + out.incrementIndent(); + + super.dump(out); + + out.decrementIndent(); + out.printIndent(); + out.println("}"); + } +} diff --git a/activemq-core/src/main/java/org/activemq/management/JMSDestinationStats.java b/activemq-core/src/main/java/org/activemq/management/JMSDestinationStats.java new file mode 100755 index 0000000000..b0774dafab --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/JMSDestinationStats.java @@ -0,0 +1,48 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +import javax.jms.Message; + +/** + * A simple interface to allow statistics gathering to be easily switched out + * for performance reasons. + * + * @version $Revision: 1.2 $ + */ +public interface JMSDestinationStats { + /** + * On startup sets the pending message count + * + * @param count + */ + public void setPendingMessageCountOnStartup(long count); + + /** + * On a message send to this destination, update the producing stats + * + * @param message + */ + public void onMessageSend(Message message); + + /** + * On a consume from this destination, updates the consumed states + */ + public void onMessageAck(); +} diff --git a/activemq-core/src/main/java/org/activemq/management/JMSEndpointStatsImpl.java b/activemq-core/src/main/java/org/activemq/management/JMSEndpointStatsImpl.java new file mode 100755 index 0000000000..16f5deb927 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/JMSEndpointStatsImpl.java @@ -0,0 +1,174 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +import javax.jms.Destination; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import org.activemq.util.IndentPrinter; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Statistics for a JMS endpoint, typically a MessageProducer or MessageConsumer + * but this class can also be used to represent statistics on a {@link Destination} as well. + * + * @version $Revision: 1.3 $ + */ +public class JMSEndpointStatsImpl extends StatsImpl { + private static final Log log = LogFactory.getLog(JMSEndpointStatsImpl.class); + + protected CountStatisticImpl messageCount; + protected CountStatisticImpl pendingMessageCount; + protected CountStatisticImpl expiredMessageCount; + protected TimeStatisticImpl messageWaitTime; + protected TimeStatisticImpl messageRateTime; + + /** + * This constructor is used to create statistics for a + * {@link MessageProducer} or {@link MessageConsumer} as it passes in a + * {@link Session} parent statistic. + * + * @param sessionStats + */ + public JMSEndpointStatsImpl(JMSSessionStatsImpl sessionStats) { + this(); + setParent(messageCount, sessionStats.getMessageCount()); + setParent(pendingMessageCount, sessionStats.getPendingMessageCount()); + setParent(expiredMessageCount, sessionStats.getExpiredMessageCount()); + setParent(messageWaitTime, sessionStats.getMessageWaitTime()); + setParent(messageRateTime, sessionStats.getMessageRateTime()); + } + + /** + * This constructor is typically used to create a statistics object for a + * {@link Destination} + */ + public JMSEndpointStatsImpl() { + this(new CountStatisticImpl("messageCount", "Number of messages processed"), + new CountStatisticImpl("pendingMessageCount", "Number of pending messages"), + new CountStatisticImpl("expiredMessageCount", "Number of expired messages"), + new TimeStatisticImpl("messageWaitTime", "Time spent by a message before being delivered"), + new TimeStatisticImpl("messageRateTime", "Time taken to process a message (thoughtput rate)")); + } + + public JMSEndpointStatsImpl(CountStatisticImpl messageCount, CountStatisticImpl pendingMessageCount, CountStatisticImpl expiredMessageCount, TimeStatisticImpl messageWaitTime, TimeStatisticImpl messageRateTime) { + this.messageCount = messageCount; + this.pendingMessageCount = pendingMessageCount; + this.expiredMessageCount = expiredMessageCount; + this.messageWaitTime = messageWaitTime; + this.messageRateTime = messageRateTime; + + // lets add named stats + addStatistic("messageCount", messageCount); + addStatistic("pendingMessageCount", pendingMessageCount); + addStatistic("expiredMessageCount", expiredMessageCount); + addStatistic("messageWaitTime", messageWaitTime); + addStatistic("messageRateTime", messageRateTime); + } + + public synchronized void reset() { + super.reset(); + messageCount.reset(); + messageRateTime.reset(); + pendingMessageCount.reset(); + expiredMessageCount.reset(); + messageWaitTime.reset(); + } + + public CountStatisticImpl getMessageCount() { + return messageCount; + } + + public CountStatisticImpl getPendingMessageCount() { + return pendingMessageCount; + } + + public CountStatisticImpl getExpiredMessageCount() { + return expiredMessageCount; + } + + public TimeStatisticImpl getMessageRateTime() { + return messageRateTime; + } + + public TimeStatisticImpl getMessageWaitTime() { + return messageWaitTime; + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(messageCount); + buffer.append(" "); + buffer.append(messageRateTime); + buffer.append(" "); + buffer.append(pendingMessageCount); + buffer.append(" "); + buffer.append(expiredMessageCount); + buffer.append(" "); + buffer.append(messageWaitTime); + return buffer.toString(); + } + + public void onMessage() { + long start = messageCount.getLastSampleTime(); + messageCount.increment(); + long end = messageCount.getLastSampleTime(); + messageRateTime.addTime(end - start); + } + + public void dump(IndentPrinter out) { + out.printIndent(); + out.println(messageCount); + out.printIndent(); + out.println(messageRateTime); + out.printIndent(); + out.println(pendingMessageCount); + out.printIndent(); + out.println(messageRateTime); + out.printIndent(); + out.println(expiredMessageCount); + out.printIndent(); + out.println(messageWaitTime); + } + + // Implementation methods + //------------------------------------------------------------------------- + protected void setParent(CountStatisticImpl child, CountStatisticImpl parent) { + if (child instanceof CountStatisticImpl && parent instanceof CountStatisticImpl) { + CountStatisticImpl c = (CountStatisticImpl) child; + c.setParent((CountStatisticImpl) parent); + } + else { + log.warn("Cannot associate endpoint counters with session level counters as they are not both CountStatisticImpl clases. Endpoint: " + child + " session: " + parent); + } + } + + protected void setParent(TimeStatisticImpl child, TimeStatisticImpl parent) { + if (child instanceof TimeStatisticImpl && parent instanceof TimeStatisticImpl) { + TimeStatisticImpl c = (TimeStatisticImpl) child; + c.setParent((TimeStatisticImpl) parent); + } + else { + log.warn("Cannot associate endpoint counters with session level counters as they are not both TimeStatisticImpl clases. Endpoint: " + child + " session: " + parent); + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/management/JMSProducerStatsImpl.java b/activemq-core/src/main/java/org/activemq/management/JMSProducerStatsImpl.java new file mode 100755 index 0000000000..e70203bb34 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/JMSProducerStatsImpl.java @@ -0,0 +1,72 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +import javax.jms.Destination; + +import org.activemq.util.IndentPrinter; + +/** + * Statistics for a JMS producer + * + * @version $Revision: 1.2 $ + */ +public class JMSProducerStatsImpl extends JMSEndpointStatsImpl { + private String destination; + + public JMSProducerStatsImpl(JMSSessionStatsImpl sessionStats, Destination destination) { + super(sessionStats); + if (destination != null) { + this.destination = destination.toString(); + } + } + + public JMSProducerStatsImpl(CountStatisticImpl messageCount, CountStatisticImpl pendingMessageCount, CountStatisticImpl expiredMessageCount, TimeStatisticImpl messageWaitTime, TimeStatisticImpl messageRateTime, String destination) { + super(messageCount, pendingMessageCount, expiredMessageCount, messageWaitTime, messageRateTime); + this.destination = destination; + } + + public String getDestination() { + return destination; + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append("producer "); + buffer.append(destination); + buffer.append(" { "); + buffer.append(super.toString()); + buffer.append(" }"); + return buffer.toString(); + } + + public void dump(IndentPrinter out) { + out.printIndent(); + out.print("producer "); + out.print(destination); + out.println(" {"); + out.incrementIndent(); + + super.dump(out); + + out.decrementIndent(); + out.printIndent(); + out.println("}"); + } +} diff --git a/activemq-core/src/main/java/org/activemq/management/JMSQueueStatsImpl.java b/activemq-core/src/main/java/org/activemq/management/JMSQueueStatsImpl.java new file mode 100755 index 0000000000..c5a8019f58 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/JMSQueueStatsImpl.java @@ -0,0 +1,60 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +import javax.jms.Message; + +/** + * Statistics for a {@link javax.jms.Queue} + * + * @version $Revision: 1.2 $ + */ +public class JMSQueueStatsImpl extends JMSEndpointStatsImpl implements JMSDestinationStats { + protected TimeStatisticImpl sendMessageRateTime; + + public JMSQueueStatsImpl() { + this.sendMessageRateTime = new TimeStatisticImpl("sendMessageRateTime", "Time taken to send a message (publish thoughtput rate)"); + addStatistic("sendMessageRateTime", sendMessageRateTime); + } + + public JMSQueueStatsImpl(CountStatisticImpl messageCount, CountStatisticImpl pendingMessageCount, CountStatisticImpl expiredMessageCount, TimeStatisticImpl messageWaitTime, TimeStatisticImpl messageRateTime, TimeStatisticImpl sendMessageRateTime) { + super(messageCount, pendingMessageCount, expiredMessageCount, messageWaitTime, messageRateTime); + this.sendMessageRateTime = sendMessageRateTime; + addStatistic("sendMessageRateTime", sendMessageRateTime); + } + + public void setPendingMessageCountOnStartup(long count) { + CountStatisticImpl messageCount = (CountStatisticImpl) getPendingMessageCount(); + messageCount.setCount(count); + } + + public void onMessageSend(Message message) { + long start = pendingMessageCount.getLastSampleTime(); + pendingMessageCount.increment(); + long end = pendingMessageCount.getLastSampleTime(); + sendMessageRateTime.addTime(end - start); + } + + public void onMessageAck() { + long start = messageCount.getLastSampleTime(); + messageCount.increment(); + long end = messageCount.getLastSampleTime(); + messageRateTime.addTime(end - start); + } +} diff --git a/activemq-core/src/main/java/org/activemq/management/JMSSessionStatsImpl.java b/activemq-core/src/main/java/org/activemq/management/JMSSessionStatsImpl.java new file mode 100755 index 0000000000..9bdb116d52 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/JMSSessionStatsImpl.java @@ -0,0 +1,208 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +import java.util.List; +import javax.management.j2ee.statistics.*; +import org.activemq.ActiveMQMessageConsumer; +import org.activemq.ActiveMQMessageProducer; +import org.activemq.util.IndentPrinter; + +/** + * Statistics for a JMS session + * + * @version $Revision: 1.2 $ + */ +public class JMSSessionStatsImpl extends StatsImpl { + private List producers; + private List consumers; + private CountStatisticImpl messageCount; + private CountStatisticImpl pendingMessageCount; + private CountStatisticImpl expiredMessageCount; + private TimeStatisticImpl messageWaitTime; + private CountStatisticImpl durableSubscriptionCount; + + private TimeStatisticImpl messageRateTime; + + public JMSSessionStatsImpl(List producers, List consumers) { + this.producers = producers; + this.consumers = consumers; + this.messageCount = new CountStatisticImpl("messageCount", "Number of messages exchanged"); + this.pendingMessageCount = new CountStatisticImpl("pendingMessageCount", "Number of pending messages"); + this.expiredMessageCount = new CountStatisticImpl("expiredMessageCount", "Number of expired messages"); + this.messageWaitTime = new TimeStatisticImpl("messageWaitTime", "Time spent by a message before being delivered"); + this.durableSubscriptionCount = new CountStatisticImpl("durableSubscriptionCount", "The number of durable subscriptions"); + this.messageWaitTime = new TimeStatisticImpl("messageWaitTime", "Time spent by a message before being delivered"); + this.messageRateTime = new TimeStatisticImpl("messageRateTime", "Time taken to process a message (thoughtput rate)"); + + // lets add named stats + addStatistic("messageCount", messageCount); + addStatistic("pendingMessageCount", pendingMessageCount); + addStatistic("expiredMessageCount", expiredMessageCount); + addStatistic("messageWaitTime", messageWaitTime); + addStatistic("durableSubscriptionCount", durableSubscriptionCount); + addStatistic("messageRateTime", messageRateTime); + } + + public JMSProducerStatsImpl[] getProducers() { + // lets make a snapshot before we process them + Object[] producerArray = producers.toArray(); + int size = producerArray.length; + JMSProducerStatsImpl[] answer = new JMSProducerStatsImpl[size]; + for (int i = 0; i < size; i++) { + ActiveMQMessageProducer producer = (ActiveMQMessageProducer) producerArray[i]; + answer[i] = producer.getProducerStats(); + } + return answer; + } + + public JMSConsumerStatsImpl[] getConsumers() { + // lets make a snapshot before we process them + Object[] consumerArray = consumers.toArray(); + int size = consumerArray.length; + JMSConsumerStatsImpl[] answer = new JMSConsumerStatsImpl[size]; + for (int i = 0; i < size; i++) { + ActiveMQMessageConsumer consumer = (ActiveMQMessageConsumer) consumerArray[i]; + answer[i] = consumer.getConsumerStats(); + } + return answer; + } + + public void reset() { + super.reset(); + JMSConsumerStatsImpl[] cstats = getConsumers(); + for (int i = 0, size = cstats.length; i < size; i++) { + cstats[i].reset(); + } + JMSProducerStatsImpl[] pstats = getProducers(); + for (int i = 0, size = pstats.length; i < size; i++) { + pstats[i].reset(); + } + } + + public CountStatisticImpl getMessageCount() { + return messageCount; + } + + public CountStatisticImpl getPendingMessageCount() { + return pendingMessageCount; + } + + public CountStatisticImpl getExpiredMessageCount() { + return expiredMessageCount; + } + + public TimeStatisticImpl getMessageWaitTime() { + return messageWaitTime; + } + + public CountStatisticImpl getDurableSubscriptionCount() { + return durableSubscriptionCount; + } + + public TimeStatisticImpl getMessageRateTime() { + return messageRateTime; + } + + public String toString() { + StringBuffer buffer = new StringBuffer(" "); + buffer.append(messageCount); + buffer.append(" "); + buffer.append(messageRateTime); + buffer.append(" "); + buffer.append(pendingMessageCount); + buffer.append(" "); + buffer.append(expiredMessageCount); + buffer.append(" "); + buffer.append(messageWaitTime); + buffer.append(" "); + buffer.append(durableSubscriptionCount); + + buffer.append(" producers{ "); + JMSProducerStatsImpl[] producerArray = getProducers(); + for (int i = 0; i < producerArray.length; i++) { + if (i > 0) { + buffer.append(", "); + } + buffer.append(Integer.toString(i)); + buffer.append(" = "); + buffer.append(producerArray[i]); + } + buffer.append(" } consumers{ "); + JMSConsumerStatsImpl[] consumerArray = getConsumers(); + for (int i = 0; i < consumerArray.length; i++) { + if (i > 0) { + buffer.append(", "); + } + buffer.append(Integer.toString(i)); + buffer.append(" = "); + buffer.append(consumerArray[i]); + } + buffer.append(" }"); + return buffer.toString(); + } + + public void dump(IndentPrinter out) { + out.printIndent(); + out.println(messageCount); + out.printIndent(); + out.println(messageRateTime); + out.printIndent(); + out.println(pendingMessageCount); + out.printIndent(); + out.println(expiredMessageCount); + out.printIndent(); + out.println(messageWaitTime); + out.printIndent(); + out.println(durableSubscriptionCount); + out.println(); + + out.printIndent(); + out.println("producers {"); + out.incrementIndent(); + JMSProducerStatsImpl[] producerArray = getProducers(); + for (int i = 0; i < producerArray.length; i++) { + JMSProducerStatsImpl producer = (JMSProducerStatsImpl) producerArray[i]; + producer.dump(out); + } + out.decrementIndent(); + out.printIndent(); + out.println("}"); + + out.printIndent(); + out.println("consumers {"); + out.incrementIndent(); + JMSConsumerStatsImpl[] consumerArray = getConsumers(); + for (int i = 0; i < consumerArray.length; i++) { + JMSConsumerStatsImpl consumer = (JMSConsumerStatsImpl) consumerArray[i]; + consumer.dump(out); + } + out.decrementIndent(); + out.printIndent(); + out.println("}"); + } + + public void onCreateDurableSubscriber() { + durableSubscriptionCount.increment(); + } + + public void onRemoveDurableSubscriber() { + durableSubscriptionCount.decrement(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/management/JMSStatsImpl.java b/activemq-core/src/main/java/org/activemq/management/JMSStatsImpl.java new file mode 100755 index 0000000000..1ae6e447f9 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/JMSStatsImpl.java @@ -0,0 +1,72 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +import java.util.List; + +import org.activemq.ActiveMQConnection; +import org.activemq.util.IndentPrinter; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + +/** + * Statistics for a number of JMS connections + * + * @version $Revision: 1.2 $ + */ +public class JMSStatsImpl extends StatsImpl { + private List connections = new CopyOnWriteArrayList(); + + public JMSStatsImpl() { + } + + public JMSConnectionStatsImpl[] getConnections() { + Object[] connectionArray = connections.toArray(); + int size = connectionArray.length; + JMSConnectionStatsImpl[] answer = new JMSConnectionStatsImpl[size]; + for (int i = 0; i < size; i++) { + ActiveMQConnection connection = (ActiveMQConnection) connectionArray[i]; + answer[i] = connection.getConnectionStats(); + } + return answer; + } + + public void addConnection(ActiveMQConnection connection) { + connections.add(connection); + } + + public void removeConnection(ActiveMQConnection connection) { + connections.remove(connection); + } + + public void dump(IndentPrinter out) { + out.printIndent(); + out.println("factory {"); + out.incrementIndent(); + JMSConnectionStatsImpl[] array = getConnections(); + for (int i = 0; i < array.length; i++) { + JMSConnectionStatsImpl connectionStat = (JMSConnectionStatsImpl) array[i]; + connectionStat.dump(out); + } + out.decrementIndent(); + out.printIndent(); + out.println("}"); + out.flush(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/management/JMSTopicStatsImpl.java b/activemq-core/src/main/java/org/activemq/management/JMSTopicStatsImpl.java new file mode 100755 index 0000000000..d3580d470d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/JMSTopicStatsImpl.java @@ -0,0 +1,43 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +import javax.jms.Message; + +/** + * Statistics for a {@link javax.jms.Topic} + * + * @version $Revision: 1.3 $ + */ +public class JMSTopicStatsImpl extends JMSEndpointStatsImpl implements JMSDestinationStats { + public JMSTopicStatsImpl() { + } + + public void setPendingMessageCountOnStartup(long count) { + // we don't calculate pending counts for topics + } + + public void onMessageSend(Message message) { + onMessage(); + } + + public void onMessageAck() { + // we don't calculate pending counts for topics + } +} diff --git a/activemq-core/src/main/java/org/activemq/management/PollCountStatisticImpl.java b/activemq-core/src/main/java/org/activemq/management/PollCountStatisticImpl.java new file mode 100755 index 0000000000..4ca4b8c773 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/PollCountStatisticImpl.java @@ -0,0 +1,111 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +import java.util.ArrayList; +import java.util.Iterator; + +import javax.management.j2ee.statistics.CountStatistic; + +/** + * A count statistic implementation + * + * @version $Revision$ + */ +public class PollCountStatisticImpl extends StatisticImpl implements CountStatistic { + + private PollCountStatisticImpl parent; + private ArrayList children; + + public PollCountStatisticImpl(PollCountStatisticImpl parent, String name, String description) { + this(name, description); + setParent(parent); + } + + public PollCountStatisticImpl(String name, String description) { + this(name, "count", description); + } + + public PollCountStatisticImpl(String name, String unit, String description) { + super(name, unit, description); + } + + public PollCountStatisticImpl getParent() { + return parent; + } + + public void setParent(PollCountStatisticImpl parent) { + if( this.parent !=null ) { + this.parent.removeChild(this); + } + this.parent = parent; + if( this.parent !=null ) { + this.parent.addChild(this); + } + } + + synchronized private void removeChild(PollCountStatisticImpl child) { + if( children!=null ) + children.remove(child); + } + + synchronized private void addChild(PollCountStatisticImpl child) { + if( children==null ) + children = new ArrayList(); + children.add(child); + } + + synchronized public long getCount() { + if ( children == null ) + return 0; + long count=0; + for (Iterator iter = children.iterator(); iter.hasNext();) { + PollCountStatisticImpl child = (PollCountStatisticImpl) iter.next(); + count += child.getCount(); + } + return count; + } + + protected void appendFieldDescription(StringBuffer buffer) { + buffer.append(" count: "); + buffer.append(Long.toString(getCount())); + super.appendFieldDescription(buffer); + } + + /** + * @return the average time period that elapses between counter increments since the last reset. + */ + public double getPeriod() { + double count = getCount(); + if( count == 0 ) + return 0; + double time = (System.currentTimeMillis() - getStartTime()); + return (time/(count*1000.0)); + } + + /** + * @return the number of times per second that the counter is incrementing since the last reset. + */ + public double getFrequency() { + double count = getCount(); + double time = (System.currentTimeMillis() - getStartTime()); + return (count*1000.0/time); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/management/RangeStatisticImpl.java b/activemq-core/src/main/java/org/activemq/management/RangeStatisticImpl.java new file mode 100755 index 0000000000..5d5acbb6eb --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/RangeStatisticImpl.java @@ -0,0 +1,75 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + + +/** + * A range statistic implementation + * + * @version $Revision: 1.2 $ + */ +public class RangeStatisticImpl extends StatisticImpl { + private long highWaterMark; + private long lowWaterMark; + private long current; + + public RangeStatisticImpl(String name, String unit, String description) { + super(name, unit, description); + } + + public void reset() { + super.reset(); + current = 0; + lowWaterMark = 0; + highWaterMark = 0; + } + + public long getHighWaterMark() { + return highWaterMark; + } + + public long getLowWaterMark() { + return lowWaterMark; + } + + public long getCurrent() { + return current; + } + + public void setCurrent(long current) { + this.current = current; + if (current > highWaterMark) { + highWaterMark = current; + } + if (current < lowWaterMark || lowWaterMark == 0) { + lowWaterMark = current; + } + updateSampleTime(); + } + + protected void appendFieldDescription(StringBuffer buffer) { + buffer.append(" current: "); + buffer.append(Long.toString(current)); + buffer.append(" lowWaterMark: "); + buffer.append(Long.toString(lowWaterMark)); + buffer.append(" highWaterMark: "); + buffer.append(Long.toString(highWaterMark)); + super.appendFieldDescription(buffer); + } +} diff --git a/activemq-core/src/main/java/org/activemq/management/Resettable.java b/activemq-core/src/main/java/org/activemq/management/Resettable.java new file mode 100755 index 0000000000..65a8b2874d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/Resettable.java @@ -0,0 +1,32 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +/** + * Represents some statistic that is capable of being reset + * + * @version $Revision: 1.2 $ + */ +public interface Resettable { + + /** + * Reset the statistic + */ + public void reset(); +} diff --git a/activemq-core/src/main/java/org/activemq/management/StatisticImpl.java b/activemq-core/src/main/java/org/activemq/management/StatisticImpl.java new file mode 100755 index 0000000000..965453ba33 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/StatisticImpl.java @@ -0,0 +1,92 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +import javax.management.j2ee.statistics.Statistic; +/** + * Base class for a Statistic implementation + * + * @version $Revision: 1.2 $ + */ +public class StatisticImpl implements Statistic, Resettable { + private String name; + private String unit; + private String description; + private long startTime; + private long lastSampleTime; + + public StatisticImpl(String name, String unit, String description) { + this.name = name; + this.unit = unit; + this.description = description; + startTime = System.currentTimeMillis(); + lastSampleTime = startTime; + } + + public synchronized void reset() { + startTime = System.currentTimeMillis(); + lastSampleTime = startTime; + } + + protected synchronized void updateSampleTime() { + lastSampleTime = System.currentTimeMillis(); + } + + public synchronized String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(name); + buffer.append("{"); + appendFieldDescription(buffer); + buffer.append(" }"); + return buffer.toString(); + } + + public String getName() { + return name; + } + + public String getUnit() { + return unit; + } + + public String getDescription() { + return description; + } + + public synchronized long getStartTime() { + return startTime; + } + + public synchronized long getLastSampleTime() { + return lastSampleTime; + } + + protected synchronized void appendFieldDescription(StringBuffer buffer) { + buffer.append(" unit: "); + buffer.append(unit); + buffer.append(" startTime: "); + //buffer.append(new Date(startTime)); + buffer.append(startTime); + buffer.append(" lastSampleTime: "); + //buffer.append(new Date(lastSampleTime)); + buffer.append(lastSampleTime); + buffer.append(" description: "); + buffer.append(description); + } +} diff --git a/activemq-core/src/main/java/org/activemq/management/StatsCapable.java b/activemq-core/src/main/java/org/activemq/management/StatsCapable.java new file mode 100755 index 0000000000..01c053b8e3 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/StatsCapable.java @@ -0,0 +1,33 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + + +/** + * Represents an object which is capable of providing some stats + * + * @version $Revision: 1.2 $ + */ +public interface StatsCapable { + + /** + * @return the Stats for this object + */ + public StatsImpl getStats(); +} diff --git a/activemq-core/src/main/java/org/activemq/management/StatsImpl.java b/activemq-core/src/main/java/org/activemq/management/StatsImpl.java new file mode 100755 index 0000000000..1f60cc18b6 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/StatsImpl.java @@ -0,0 +1,75 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +import java.util.*; +import javax.management.j2ee.statistics.Statistic; +import javax.management.j2ee.statistics.Stats; + + +/** + * Base class for a Stats implementation + * + * @version $Revision: 1.2 $ + */ +public class StatsImpl extends StatisticImpl implements Stats, Resettable{ + private Map map; + + public StatsImpl() { + this(new HashMap()); + } + + public StatsImpl(Map map) { + super("stats", "many", "Used only as container, not Statistic"); + this.map = map; + } + + public void reset() { + Statistic[] stats = getStatistics(); + for (int i = 0, size = stats.length; i < size; i++) { + Statistic stat = stats[i]; + if (stat instanceof Resettable) { + Resettable r = (Resettable) stat; + r.reset(); + } + } + } + + public Statistic getStatistic(String name) { + return (Statistic) map.get(name); + } + + public String[] getStatisticNames() { + Set keys = map.keySet(); + String[] answer = new String[keys.size()]; + keys.toArray(answer); + return answer; + } + + public Statistic[] getStatistics() { + Collection values = map.values(); + Statistic[] answer = new Statistic[values.size()]; + values.toArray(answer); + return answer; + } + + protected void addStatistic(String name, StatisticImpl statistic) { + map.put(name, statistic); + } +} diff --git a/activemq-core/src/main/java/org/activemq/management/TimeStatisticImpl.java b/activemq-core/src/main/java/org/activemq/management/TimeStatisticImpl.java new file mode 100755 index 0000000000..f66cb5e3db --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/TimeStatisticImpl.java @@ -0,0 +1,174 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + + +/** + * A time statistic implementation + * + * @version $Revision: 1.2 $ + */ +public class TimeStatisticImpl extends StatisticImpl { + private long count; + private long maxTime; + private long minTime; + private long totalTime; + private TimeStatisticImpl parent; + + public TimeStatisticImpl(String name, String description) { + this(name, "millis", description); + } + + public TimeStatisticImpl(TimeStatisticImpl parent, String name, String description) { + this(name, description); + this.parent = parent; + } + + public TimeStatisticImpl(String name, String unit, String description) { + super(name, unit, description); + } + + public synchronized void reset() { + super.reset(); + count = 0; + maxTime = 0; + minTime = 0; + totalTime = 0; + } + + public synchronized long getCount() { + return count; + } + + public synchronized void addTime(long time) { + count++; + totalTime += time; + if (time > maxTime) { + maxTime = time; + } + if (time < minTime || minTime == 0) { + minTime = time; + } + updateSampleTime(); + if (parent != null) { + parent.addTime(time); + } + } + + /** + * @return the maximum time of any step + */ + public long getMaxTime() { + return maxTime; + } + + /** + * @return the minimum time of any step + */ + public synchronized long getMinTime() { + return minTime; + } + + /** + * @return the total time of all the steps added together + */ + public synchronized long getTotalTime() { + return totalTime; + } + + /** + * @return the average time calculated by dividing the + * total time by the number of counts + */ + public synchronized double getAverageTime() { + if (count == 0) { + return 0; + } + double d = totalTime; + return d / count; + } + + + /** + * @return the average time calculated by dividing the + * total time by the number of counts but excluding the + * minimum and maximum times. + */ + public synchronized double getAverageTimeExcludingMinMax() { + if (count <= 2) { + return 0; + } + double d = totalTime - minTime - maxTime; + return d / (count - 2); + } + + + /** + * @return the average number of steps per second + */ + public double getAveragePerSecond() { + double d = 1000; + double averageTime = getAverageTime(); + if (averageTime == 0) { + return 0; + } + return d / averageTime; + } + + /** + * @return the average number of steps per second excluding the min & max values + */ + public double getAveragePerSecondExcludingMinMax() { + double d = 1000; + double average = getAverageTimeExcludingMinMax(); + if (average == 0) { + return 0; + } + return d / average; + } + + public TimeStatisticImpl getParent() { + return parent; + } + + public void setParent(TimeStatisticImpl parent) { + this.parent = parent; + } + + protected synchronized void appendFieldDescription(StringBuffer buffer) { + buffer.append(" count: "); + buffer.append(Long.toString(count)); + buffer.append(" maxTime: "); + buffer.append(Long.toString(maxTime)); + buffer.append(" minTime: "); + buffer.append(Long.toString(minTime)); + buffer.append(" totalTime: "); + buffer.append(Long.toString(totalTime)); + buffer.append(" averageTime: "); + buffer.append(Double.toString(getAverageTime())); + buffer.append(" averageTimeExMinMax: "); + buffer.append(Double.toString(getAverageTimeExcludingMinMax())); + buffer.append(" averagePerSecond: "); + buffer.append(Double.toString(getAveragePerSecond())); + buffer.append(" averagePerSecondExMinMax: "); + buffer.append(Double.toString(getAveragePerSecondExcludingMinMax())); + super.appendFieldDescription(buffer); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/management/package.html b/activemq-core/src/main/java/org/activemq/management/package.html new file mode 100755 index 0000000000..5fea3f624a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/management/package.html @@ -0,0 +1,11 @@ + + + + + +

+ An implementation of the J2EE Management API +

+ + + diff --git a/activemq-core/src/main/java/org/activemq/memory/Cache.java b/activemq-core/src/main/java/org/activemq/memory/Cache.java new file mode 100755 index 0000000000..219f81ed9f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/Cache.java @@ -0,0 +1,65 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.memory; + + +/** + * Defines the interface used to cache messages. + * + * @version $Revision$ + */ +public interface Cache { + + /** + * Gets an object that was previously put into this object. + * + * @param msgid + * @return null if the object was not previously put or if the object has expired out of the cache. + */ + public Object get(Object key); + + /** + * Puts an object into the cache. + * + * @param messageID + * @param message + */ + public Object put(Object key, Object value); + + /** + * Removes an object from the cache. + * + * @param messageID + * @return the object associated with the key if it was still in the cache. + */ + public Object remove(Object key); + + /** + * Lets a cache know it will not be used any further and that it can release + * acquired resources + */ + public void close(); + + /** + * How big is the cache right now? + * @return + */ + public int size(); + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/memory/CacheEntry.java b/activemq-core/src/main/java/org/activemq/memory/CacheEntry.java new file mode 100755 index 0000000000..dc60524d6e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/CacheEntry.java @@ -0,0 +1,58 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * + * Licensed 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.activemq.memory; + +public class CacheEntry { + + public final Object key; + public final Object value; + + public CacheEntry next; + public CacheEntry previous; + public CacheEntryList owner; + + public CacheEntry(Object key, Object value) { + this.key=key; + this.value = value; + } + + /** + * + * @param entry + * @return false if you are trying to remove the tail pointer. + */ + public boolean remove() { + + // Cannot remove if this is a tail pointer. + // Or not linked. + if( owner==null || this.key==null || this.next==null ) + return false; + + synchronized( owner.tail ) { + this.next.previous = this.previous; + this.previous.next = this.next; + this.owner = null; + this.next = this.previous = null; + } + + return true; + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/memory/CacheEntryList.java b/activemq-core/src/main/java/org/activemq/memory/CacheEntryList.java new file mode 100755 index 0000000000..71299bd5cc --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/CacheEntryList.java @@ -0,0 +1,63 @@ +package org.activemq.memory; + +/** + * Maintains a simple linked list of CacheEntry objects. It is thread safe. + * + * @version $Revision$ + */ +public class CacheEntryList { + + // Points at the tail of the CacheEntry list + public final CacheEntry tail = new CacheEntry(null, null); + + public CacheEntryList() { + tail.next = tail.previous = tail; + } + + public void add(CacheEntry ce) { + addEntryBefore(tail, ce); + } + + private void addEntryBefore(CacheEntry position, CacheEntry ce) { + assert ce.key!=null && ce.next==null && ce.owner==null; + + synchronized( tail ) { + ce.owner=this; + ce.next = position; + ce.previous = position.previous; + ce.previous.next = ce; + ce.next.previous = ce; + } + } + + public void clear() { + synchronized( tail ) { + tail.next = tail.previous = tail; + } + } + + public CacheEvictor createFIFOCacheEvictor() { + return new CacheEvictor() { + public CacheEntry evictCacheEntry() { + CacheEntry rc; + synchronized( tail ) { + rc = tail.next; + } + return rc.remove() ? rc : null; + } + }; + } + + public CacheEvictor createLIFOCacheEvictor() { + return new CacheEvictor() { + public CacheEntry evictCacheEntry() { + CacheEntry rc; + synchronized( tail ) { + rc = tail.previous; + } + return rc.remove() ? rc : null; + } + }; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/memory/CacheEvictionUsageListener.java b/activemq-core/src/main/java/org/activemq/memory/CacheEvictionUsageListener.java new file mode 100755 index 0000000000..1e7e07a585 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/CacheEvictionUsageListener.java @@ -0,0 +1,75 @@ +package org.activemq.memory; + +import java.util.Iterator; +import java.util.LinkedList; + +import org.activemq.thread.Task; +import org.activemq.thread.TaskRunner; +import org.activemq.thread.TaskRunnerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + +public class CacheEvictionUsageListener implements UsageListener { + + private final static Log log = LogFactory.getLog(CacheEvictionUsageListener.class); + + private final CopyOnWriteArrayList evictors = new CopyOnWriteArrayList(); + private final int usageHighMark; + private final int usageLowMark; + + private final TaskRunner evictionTask; + private final UsageManager usageManager; + + public CacheEvictionUsageListener(UsageManager usageManager, int usageHighMark, int usageLowMark, TaskRunnerFactory taskRunnerFactory) { + this.usageManager = usageManager; + this.usageHighMark = usageHighMark; + this.usageLowMark = usageLowMark; + evictionTask = taskRunnerFactory.createTaskRunner(new Task(){ + public boolean iterate() { + return evictMessages(); + } + }); + } + + private boolean evictMessages() { + // Try to take the memory usage down below the low mark. + try { + log.debug("Evicting cache memory usage: "+usageManager.getPercentUsage()); + + LinkedList list = new LinkedList(evictors); + while (list.size()>0 && usageManager.getPercentUsage() > usageLowMark) { + + // Evenly evict messages from all evictors + for (Iterator iter = list.iterator(); iter.hasNext();) { + CacheEvictor evictor = (CacheEvictor) iter.next(); + if( evictor.evictCacheEntry() == null ) + iter.remove(); + } + } + } finally { + } + return false; + } + + public void onMemoryUseChanged(UsageManager memoryManager, int oldPercentUsage, int newPercentUsage) { + // Do we need to start evicting cache entries? Usage > than the + // high mark + if (oldPercentUsage < newPercentUsage && memoryManager.getPercentUsage() >= usageHighMark) { + try { + evictionTask.wakeup(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + public void add(CacheEvictor evictor) { + evictors.add(evictor); + } + + public void remove(CacheEvictor evictor) { + evictors.remove(evictor); + } +} diff --git a/activemq-core/src/main/java/org/activemq/memory/CacheEvictor.java b/activemq-core/src/main/java/org/activemq/memory/CacheEvictor.java new file mode 100755 index 0000000000..110138a33e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/CacheEvictor.java @@ -0,0 +1,7 @@ +package org.activemq.memory; + +public interface CacheEvictor { + + CacheEntry evictCacheEntry(); + +} diff --git a/activemq-core/src/main/java/org/activemq/memory/CacheFilter.java b/activemq-core/src/main/java/org/activemq/memory/CacheFilter.java new file mode 100755 index 0000000000..148e94b510 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/CacheFilter.java @@ -0,0 +1,36 @@ +package org.activemq.memory; + + +/** + * Filters another Cache implementation. + * + * @version $Revision$ + */ +public class CacheFilter implements Cache { + + protected final Cache next; + + public CacheFilter(Cache next) { + this.next = next; + } + + public Object put(Object key, Object value) { + return next.put(key, value); + } + + public Object get(Object key) { + return next.get(key); + } + + public Object remove(Object key) { + return next.remove(key); + } + + public void close() { + next.close(); + } + + public int size() { + return next.size(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/memory/LRUMap.java b/activemq-core/src/main/java/org/activemq/memory/LRUMap.java new file mode 100755 index 0000000000..64b18ba5d0 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/LRUMap.java @@ -0,0 +1,52 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.memory; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A simple least-recently-used cache of a fixed size. + * + * @version $Revision:$ + */ +public class LRUMap extends LinkedHashMap { + private static final long serialVersionUID = -9179676638408888162L; + + protected static final float DEFAULT_LOAD_FACTOR = (float) 0.75; + protected static final int DEFAULT_INITIAL_CAPACITY = 5000; + + private int maximumSize; + + public LRUMap(int maximumSize) { + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, true, maximumSize); + } + + public LRUMap(int maximumSize, boolean accessOrder) { + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, accessOrder, maximumSize); + } + + public LRUMap(int initialCapacity, float loadFactor, boolean accessOrder, int maximumSize) { + super(initialCapacity, loadFactor, accessOrder); + this.maximumSize = maximumSize; + } + + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > maximumSize; + } +} diff --git a/activemq-core/src/main/java/org/activemq/memory/MapCache.java b/activemq-core/src/main/java/org/activemq/memory/MapCache.java new file mode 100755 index 0000000000..f2e03a53bb --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/MapCache.java @@ -0,0 +1,44 @@ +package org.activemq.memory; + +import java.util.Map; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * Use any Map to implement the Cache. No cache eviction going on here. Just gives + * a Map a Cache interface. + * + * @version $Revision$ + */ +public class MapCache implements Cache { + + protected final Map map; + + public MapCache() { + this(new ConcurrentHashMap()); + } + + public MapCache(Map map) { + this.map = map; + } + + public Object put(Object key, Object value) { + return map.put(key, value); + } + + public Object get(Object key) { + return map.get(key); + } + + public Object remove(Object key) { + return map.remove(key); + } + + public void close() { + map.clear(); + } + + public int size() { + return map.size(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/memory/UsageListener.java b/activemq-core/src/main/java/org/activemq/memory/UsageListener.java new file mode 100755 index 0000000000..c8dab588f9 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/UsageListener.java @@ -0,0 +1,23 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.memory; + +public interface UsageListener { + public void onMemoryUseChanged( UsageManager memoryManager, int oldPercentUsage, int newPercentUsage ); +} diff --git a/activemq-core/src/main/java/org/activemq/memory/UsageManager.java b/activemq-core/src/main/java/org/activemq/memory/UsageManager.java new file mode 100755 index 0000000000..776223aa1a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/UsageManager.java @@ -0,0 +1,224 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.memory; + +import java.util.Iterator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + + +/** + * Used to keep track of how much of something is being used so that + * a productive working set usage can be controlled. + * + * Main use case is manage memory usage. + * + * @org.xbean.XBean + * + * @version $Revision: 1.3 $ + */ +public class UsageManager { + + private static final Log log = LogFactory.getLog(UsageManager.class); + + private final UsageManager parent; + private long limit; + private long usage; + + private int percentUsage; + private int percentUsageMinDelta=10; + + private final Object usageMutex = new Object(); + + private final CopyOnWriteArrayList listeners = new CopyOnWriteArrayList(); + + public UsageManager() { + this(null); + } + + /** + * Create the memory manager linked to a parent. When the memory manager is linked to + * a parent then when usage increased or decreased, the parent's usage is also increased + * or decreased. + * + * @param parent + */ + public UsageManager(UsageManager parent) { + this.parent = parent; + } + + /** + * Tries to increase the usage by value amount but blocks if this object + * is currently full. + * @throws InterruptedException + */ + public void enqueueUsage(long value) throws InterruptedException { + waitForSpace(); + increaseUsage(value); + } + + /** + * @throws InterruptedException + */ + public void waitForSpace() throws InterruptedException { + synchronized (usageMutex) { + for( int i=0; percentUsage >= 100 ; i++) { + usageMutex.wait(); + } + } + } + + /** + * Increases the usage by the value amount. + * + * @param value + */ + public void increaseUsage(long value) { + if( value == 0 ) + return; + if(parent!=null) + parent.increaseUsage(value); + synchronized(usageMutex) { + usage+=value; + setPercentUsage(caclPercentUsage()); + } + } + + /** + * Decreases the usage by the value amount. + * + * @param value + */ + public void decreaseUsage(long value) { + if( value == 0 ) + return; + if(parent!=null) + parent.decreaseUsage(value); + synchronized(usageMutex) { + usage-=value; + setPercentUsage(caclPercentUsage()); + } + } + + public boolean isFull() { + synchronized (usageMutex) { + return percentUsage >= 100; + } + } + + public void addUsageListener(UsageListener listener) { + listeners.add(listener); + } + public void removeUsageListener(UsageListener listener) { + listeners.remove(listener); + } + + public long getLimit() { + synchronized (usageMutex) { + return limit; + } + } + + public void setLimit(long limit) { + if(percentUsageMinDelta < 0 ) { + throw new IllegalArgumentException("percentUsageMinDelta must be greater or equal to 0"); + } + synchronized (usageMutex) { + this.limit = limit; + setPercentUsage(caclPercentUsage()); + } + } + + /* + * Sets the minimum number of percentage points the usage has to change before a UsageListener + * event is fired by the manager. + */ + public int getPercentUsage() { + synchronized (usageMutex) { + return percentUsage; + } + } + + public int getPercentUsageMinDelta() { + synchronized (usageMutex) { + return percentUsageMinDelta; + } + } + + /** + * Sets the minimum number of percentage points the usage has to change before a UsageListener + * event is fired by the manager. + * + * @param percentUsageMinDelta + */ + public void setPercentUsageMinDelta(int percentUsageMinDelta) { + if(percentUsageMinDelta < 1) { + throw new IllegalArgumentException("percentUsageMinDelta must be greater than 0"); + } + synchronized (usageMutex) { + this.percentUsageMinDelta = percentUsageMinDelta; + setPercentUsage(caclPercentUsage()); + } + } + + public long getUsage() { + synchronized (usageMutex) { + return usage; + } + } + + + private void setPercentUsage(int value) { + int oldValue = percentUsage; + percentUsage = value; + if( oldValue!=value ) { + fireEvent(oldValue, value); + } + } + + private int caclPercentUsage() { + if( limit==0 ) return 0; + return (int)((((usage*100)/limit)/percentUsageMinDelta)*percentUsageMinDelta); + } + + private void fireEvent(int oldPercentUsage, int newPercentUsage) { + + log.debug("Memory usage change. from: "+oldPercentUsage+", to: "+newPercentUsage); + + // Switching from being full to not being full.. + if( oldPercentUsage >= 100 && newPercentUsage < 100 ) { + synchronized (usageMutex) { + usageMutex.notifyAll(); + } + } + + // Let the listeners know + for (Iterator iter = listeners.iterator(); iter.hasNext();) { + UsageListener l = (UsageListener) iter.next(); + l.onMemoryUseChanged(this, oldPercentUsage, newPercentUsage); + } + } + + public String toString() { + return "UsageManager: percentUsage="+percentUsage+"%, usage="+usage+" limit="+limit+" percentUsageMinDelta="+percentUsageMinDelta+"%"; + } +} diff --git a/activemq-core/src/main/java/org/activemq/memory/UsageManagerCacheFilter.java b/activemq-core/src/main/java/org/activemq/memory/UsageManagerCacheFilter.java new file mode 100755 index 0000000000..5de1795f3a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/UsageManagerCacheFilter.java @@ -0,0 +1,54 @@ +package org.activemq.memory; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicLong; + +/** + * Simple CacheFilter that increases/decreases usage on a UsageManager as + * objects are added/removed from the Cache. + * + * @version $Revision$ + */ +public class UsageManagerCacheFilter extends CacheFilter { + + private final AtomicLong totalUsage = new AtomicLong(0); + private final UsageManager um; + + public UsageManagerCacheFilter(Cache next, UsageManager um) { + super(next); + this.um = um; + } + + public Object put(Object key, Object value) { + long usage = getUsageOfAddedObject(value); + Object rc = super.put(key, value); + if( rc !=null ) { + usage -= getUsageOfRemovedObject(rc); + } + totalUsage.addAndGet(usage); + um.increaseUsage(usage); + return rc; + } + + public Object remove(Object key) { + Object rc = super.remove(key); + if( rc !=null ) { + long usage = getUsageOfRemovedObject(rc); + totalUsage.addAndGet(-usage); + um.decreaseUsage(usage); + } + return rc; + } + + + protected long getUsageOfAddedObject(Object value) { + return 1; + } + + protected long getUsageOfRemovedObject(Object value) { + return 1; + } + + public void close() { + um.decreaseUsage(totalUsage.get()); + } +} diff --git a/activemq-core/src/main/java/org/activemq/memory/buffer/MessageBuffer.java b/activemq-core/src/main/java/org/activemq/memory/buffer/MessageBuffer.java new file mode 100644 index 0000000000..6700d4784d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/buffer/MessageBuffer.java @@ -0,0 +1,46 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.memory.buffer; + +/** + * Represents a collection of MessageQueue instances which are all bound by the + * same memory buffer to fix the amount of RAM used to some uppper bound. + * + * @version $Revision: 1.1 $ + */ +public interface MessageBuffer { + + public int getSize(); + + /** + * Creates a new message queue instance + */ + public MessageQueue createMessageQueue(); + + /** + * After a message queue has changed we may need to perform some evictions + * + * @param delta + * @param queueSize + */ + public void onSizeChanged(MessageQueue queue, int delta, int queueSize); + + public void clear(); + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/memory/buffer/MessageQueue.java b/activemq-core/src/main/java/org/activemq/memory/buffer/MessageQueue.java new file mode 100644 index 0000000000..377a62b9ee --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/buffer/MessageQueue.java @@ -0,0 +1,120 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.memory.buffer; + +import org.activemq.broker.region.MessageReference; +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.Message; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * Allows messages to be added to the end of the buffer such that they are kept + * around and evicted in a FIFO manner. + * + * @version $Revision: 1.1 $ + */ +public class MessageQueue { + + private MessageBuffer buffer; + private LinkedList list = new LinkedList(); + private int size; + private Object lock = new Object(); + private int position; + + public MessageQueue(MessageBuffer buffer) { + this.buffer = buffer; + } + + public void add(MessageReference messageRef) { + Message message = messageRef.getMessageHardRef(); + int delta = message.getSize(); + int newSize = 0; + synchronized (lock) { + list.add(messageRef); + size += delta; + newSize = size; + } + buffer.onSizeChanged(this, delta, newSize); + } + + public void add(ActiveMQMessage message) { + int delta = message.getSize(); + int newSize = 0; + synchronized (lock) { + list.add(message); + size += delta; + newSize = size; + } + buffer.onSizeChanged(this, delta, newSize); + } + + public int evictMessage() { + synchronized (lock) { + if (!list.isEmpty()) { + ActiveMQMessage message = (ActiveMQMessage) list.removeFirst(); + int messageSize = message.getSize(); + size -= messageSize; + return messageSize; + } + } + return 0; + } + + /** + * Returns a copy of the list + */ + public List getList() { + synchronized (lock) { + return new ArrayList(list); + } + } + + public void appendMessages(List answer) { + synchronized (lock) { + for (Iterator iter = list.iterator(); iter.hasNext();) { + answer.add(iter.next()); + } + } + } + + public int getSize() { + synchronized (lock) { + return size; + } + } + + public int getPosition() { + return position; + } + + public void setPosition(int position) { + this.position = position; + } + + public void clear() { + synchronized (lock) { + list.clear(); + size = 0; + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/memory/buffer/OrderBasedMessageBuffer.java b/activemq-core/src/main/java/org/activemq/memory/buffer/OrderBasedMessageBuffer.java new file mode 100644 index 0000000000..fd822e947a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/buffer/OrderBasedMessageBuffer.java @@ -0,0 +1,83 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.memory.buffer; + +import java.util.Iterator; +import java.util.LinkedList; + +/** + * A {@link MessageBuffer} which evicts messages in arrival order so the oldest + * messages are removed first. + * + * @version $Revision: 1.1 $ + */ +public class OrderBasedMessageBuffer implements MessageBuffer { + + private int limit = 100 * 64 * 1024; + private LinkedList list = new LinkedList(); + private int size; + private Object lock = new Object(); + + public OrderBasedMessageBuffer() { + } + + public OrderBasedMessageBuffer(int limit) { + this.limit = limit; + } + + public int getSize() { + synchronized (lock) { + return size; + } + } + + /** + * Creates a new message queue instance + */ + public MessageQueue createMessageQueue() { + return new MessageQueue(this); + } + + /** + * After a message queue has changed we may need to perform some evictions + * + * @param delta + * @param queueSize + */ + public void onSizeChanged(MessageQueue queue, int delta, int queueSize) { + synchronized (lock) { + list.addLast(queue); + size += delta; + while (size > limit) { + MessageQueue biggest = (MessageQueue) list.removeFirst(); + size -= biggest.evictMessage(); + } + } + } + + public void clear() { + synchronized (lock) { + for (Iterator iter = list.iterator(); iter.hasNext();) { + MessageQueue queue = (MessageQueue) iter.next(); + queue.clear(); + } + size = 0; + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/memory/buffer/SizeBasedMessageBuffer.java b/activemq-core/src/main/java/org/activemq/memory/buffer/SizeBasedMessageBuffer.java new file mode 100644 index 0000000000..04cb099af2 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/buffer/SizeBasedMessageBuffer.java @@ -0,0 +1,125 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.memory.buffer; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * A {@link MessageBuffer} which evicts from the largest buffers first. + * + * @version $Revision: 1.1 $ + */ +public class SizeBasedMessageBuffer implements MessageBuffer { + + private int limit = 100 * 64 * 1024; + private List bubbleList = new ArrayList(); + private int size; + private Object lock = new Object(); + + public SizeBasedMessageBuffer() { + } + + public SizeBasedMessageBuffer(int limit) { + this.limit = limit; + } + + public int getSize() { + synchronized (lock) { + return size; + } + } + + /** + * Creates a new message queue instance + */ + public MessageQueue createMessageQueue() { + MessageQueue queue = new MessageQueue(this); + synchronized (lock) { + queue.setPosition(bubbleList.size()); + bubbleList.add(queue); + } + return queue; + } + + /** + * After a message queue has changed we may need to perform some evictions + * + * @param delta + * @param queueSize + */ + public void onSizeChanged(MessageQueue queue, int delta, int queueSize) { + synchronized (lock) { + bubbleUp(queue, queueSize); + + size += delta; + while (size > limit) { + MessageQueue biggest = (MessageQueue) bubbleList.get(0); + size -= biggest.evictMessage(); + + bubbleDown(biggest, 0); + } + } + } + + public void clear() { + synchronized (lock) { + for (Iterator iter = bubbleList.iterator(); iter.hasNext();) { + MessageQueue queue = (MessageQueue) iter.next(); + queue.clear(); + } + size = 0; + } + } + + protected void bubbleUp(MessageQueue queue, int queueSize) { + // lets bubble up to head of queueif we need to + int position = queue.getPosition(); + while (--position >= 0) { + MessageQueue pivot = (MessageQueue) bubbleList.get(position); + if (pivot.getSize() < queueSize) { + swap(position, pivot, position + 1, queue); + } + else { + break; + } + } + } + + protected void bubbleDown(MessageQueue biggest, int position) { + int queueSize = biggest.getSize(); + for (int second = position + 1, end = bubbleList.size(); second < end; second++) { + MessageQueue pivot = (MessageQueue) bubbleList.get(second); + if (pivot.getSize() > queueSize) { + swap(position, biggest, second, pivot); + } + else { + break; + } + position = second; + } + } + + protected void swap(int firstPosition, MessageQueue first, int secondPosition, MessageQueue second) { + bubbleList.set(firstPosition, second); + bubbleList.set(secondPosition, first); + first.setPosition(secondPosition); + second.setPosition(firstPosition); + } +} diff --git a/activemq-core/src/main/java/org/activemq/memory/list/DestinationBasedMessageList.java b/activemq-core/src/main/java/org/activemq/memory/list/DestinationBasedMessageList.java new file mode 100644 index 0000000000..44a339e12c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/list/DestinationBasedMessageList.java @@ -0,0 +1,89 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.memory.list; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.activemq.broker.region.MessageReference; +import org.activemq.broker.region.Subscription; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQMessage; +import org.activemq.filter.DestinationMap; +import org.activemq.memory.buffer.MessageBuffer; +import org.activemq.memory.buffer.MessageQueue; +import org.activemq.memory.buffer.OrderBasedMessageBuffer; + +/** + * An implementation of {@link MessageList} which maintains a separate message + * list for each destination to reduce contention on the list and to speed up + * recovery times by only recovering the interested topics. + * + * @version $Revision: 1.1 $ + */ +public class DestinationBasedMessageList implements MessageList { + + private MessageBuffer messageBuffer; + private Map queueIndex = new HashMap(); + private DestinationMap subscriptionIndex = new DestinationMap(); + private Object lock = new Object(); + + public DestinationBasedMessageList(int maximumSize) { + this(new OrderBasedMessageBuffer(maximumSize)); + } + + public DestinationBasedMessageList(MessageBuffer buffer) { + messageBuffer = buffer; + } + + public void add(MessageReference node) { + ActiveMQMessage message = (ActiveMQMessage) node.getMessageHardRef(); + ActiveMQDestination destination = message.getDestination(); + MessageQueue queue = null; + synchronized (lock) { + queue = (MessageQueue) queueIndex.get(destination); + if (queue == null) { + queue = messageBuffer.createMessageQueue(); + queueIndex.put(destination, queue); + subscriptionIndex.put(destination, queue); + } + } + queue.add(node); + } + + public List getMessages(Subscription sub) { + Set set = null; + synchronized (lock) { + set = subscriptionIndex.get(sub.getConsumerInfo().getDestination()); + } + List answer = new ArrayList(); + for (Iterator iter = set.iterator(); iter.hasNext();) { + MessageQueue queue = (MessageQueue) iter.next(); + queue.appendMessages(answer); + } + return answer; + } + + public void clear() { + messageBuffer.clear(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/memory/list/MessageList.java b/activemq-core/src/main/java/org/activemq/memory/list/MessageList.java new file mode 100644 index 0000000000..84d1aa873a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/list/MessageList.java @@ -0,0 +1,41 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.memory.list; + +import org.activemq.broker.region.MessageReference; +import org.activemq.broker.region.Subscription; + +import java.util.List; + +/** + * A container of messages which is used to store messages and then + * replay them later for a given subscription. + * + * @version $Revision: 1.1 $ + */ +public interface MessageList { + + void add(MessageReference node); + + /** + * Returns the current list of MessageReference objects for the given subscription + */ + List getMessages(Subscription sub); + + void clear(); +} diff --git a/activemq-core/src/main/java/org/activemq/memory/list/SimpleMessageList.java b/activemq-core/src/main/java/org/activemq/memory/list/SimpleMessageList.java new file mode 100644 index 0000000000..7f90aecd9b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/memory/list/SimpleMessageList.java @@ -0,0 +1,87 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.memory.list; + +import org.activemq.broker.region.MessageReference; +import org.activemq.broker.region.Subscription; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * A simple fixed size {@link MessageList} where there is a single, fixed size + * list that all messages are added to for simplicity. Though this + * will lead to possibly slow recovery times as many more messages + * than is necessary will have to be iterated through for each subscription. + * + * @version $Revision: 1.1 $ + */ +public class SimpleMessageList implements MessageList { + + private LinkedList list = new LinkedList(); + private int maximumSize = 100 * 64 * 1024; + private int size; + private Object lock = new Object(); + + public SimpleMessageList() { + } + + public SimpleMessageList(int maximumSize) { + this.maximumSize = maximumSize; + } + + public void add(MessageReference node) { + int delta = node.getMessageHardRef().getSize(); + synchronized (lock) { + list.add(node); + size += delta; + while (size > maximumSize) { + MessageReference evicted = (MessageReference) list.removeFirst(); + size -= evicted.getMessageHardRef().getSize(); + } + } + } + + public List getMessages(Subscription sub) { + return getList(); + } + + /** + * Returns a copy of the list + */ + public List getList() { + synchronized (lock) { + return new ArrayList(list); + } + } + + public int getSize() { + synchronized (lock) { + return size; + } + } + + public void clear() { + synchronized (lock) { + list.clear(); + size = 0; + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/network/Bridge.java b/activemq-core/src/main/java/org/activemq/network/Bridge.java new file mode 100644 index 0000000000..52a23e3bf6 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/Bridge.java @@ -0,0 +1,31 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.network; + +import org.activemq.Service; + + +/** + * Represents a network bridge interface + * + * @version $Revision: 1.1 $ + */ +public interface Bridge extends Service { + +} diff --git a/activemq-core/src/main/java/org/activemq/network/DemandForwardingBridge.java b/activemq-core/src/main/java/org/activemq/network/DemandForwardingBridge.java new file mode 100755 index 0000000000..cbf82878b0 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/DemandForwardingBridge.java @@ -0,0 +1,391 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.network; + +import java.io.IOException; +import org.activemq.advisory.AdvisorySupport; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.BrokerId; +import org.activemq.command.BrokerInfo; +import org.activemq.command.Command; +import org.activemq.command.CommandTypes; +import org.activemq.command.ConnectionId; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerId; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.DataStructure; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageDispatch; +import org.activemq.command.ProducerInfo; +import org.activemq.command.RemoveInfo; +import org.activemq.command.SessionInfo; +import org.activemq.command.ShutdownInfo; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportListener; +import org.activemq.util.IdGenerator; +import org.activemq.util.LongSequenceGenerator; +import org.activemq.util.ServiceStopper; +import org.activemq.util.ServiceSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * Forwards messages from the local broker to the remote broker based on + * demand. + * + * @org.xbean.XBean + * + * @version $Revision$ + */ +public class DemandForwardingBridge implements Bridge { + + static final private Log log = LogFactory.getLog(DemandForwardingBridge.class); + + private final Transport localBroker; + private final Transport remoteBroker; + + IdGenerator idGenerator = new IdGenerator(); + LongSequenceGenerator consumerIdGenerator = new LongSequenceGenerator(); + + ConnectionInfo connectionInfo; + SessionInfo sessionInfo; + ProducerInfo producerInfo; + + private String clientId; + private int prefetchSize=1000; + private boolean dispatchAsync; + private String destinationFilter = ">"; + + private ConsumerInfo demandConsumerInfo; + private int demandConsumerDispatched; + + BrokerId localBrokerId; + BrokerId remoteBrokerId; + + private static class DemandSubscription { + ConsumerInfo remoteInfo; + ConsumerInfo localInfo; + int dispatched; + + public DemandSubscription(ConsumerInfo info) { + remoteInfo = info; + localInfo = info.copy(); + } + } + + ConcurrentHashMap subscriptionMapByLocalId = new ConcurrentHashMap(); + ConcurrentHashMap subscriptionMapByRemoteId = new ConcurrentHashMap(); + + protected final BrokerId localBrokerPath[] = new BrokerId[] {null}; + protected final BrokerId remoteBrokerPath[] = new BrokerId[] {null}; + + public DemandForwardingBridge(Transport localBroker, Transport remoteBroker) { + this.localBroker = localBroker; + this.remoteBroker = remoteBroker; + } + + public void start() throws Exception { + log.info("Starting a network connection between " + localBroker + " and " + remoteBroker + " has been established."); + + localBroker.setTransportListener(new TransportListener(){ + public void onCommand(Command command) { + serviceLocalCommand(command); + } + public void onException(IOException error) { + serviceLocalException(error); + } + }); + + remoteBroker.setTransportListener(new TransportListener(){ + public void onCommand(Command command) { + serviceRemoteCommand(command); + } + public void onException(IOException error) { + serviceRemoteException(error); + } + }); + + localBroker.start(); + remoteBroker.start(); + + } + + protected void triggerStartBridge() throws IOException { + Thread thead = new Thread() { + public void run() { + try { + startBridge(); + } + catch (IOException e) { + log.error("Failed to start network bridge: " + e, e); + } + } + }; + thead.start(); + } + + protected void startBridge() throws IOException { + BrokerInfo brokerInfo = new BrokerInfo(); + remoteBroker.oneway(brokerInfo); + connectionInfo = new ConnectionInfo(); + connectionInfo.setConnectionId(new ConnectionId(idGenerator.generateId())); + connectionInfo.setClientId(clientId); + localBroker.oneway(connectionInfo); + remoteBroker.oneway(connectionInfo); + + sessionInfo=new SessionInfo(connectionInfo, 1); + localBroker.oneway(sessionInfo); + remoteBroker.oneway(sessionInfo); + + producerInfo = new ProducerInfo(sessionInfo, 1); + producerInfo.setResponseRequired(false); + remoteBroker.oneway(producerInfo); + + // Listen to consumer advisory messages on the remote broker to determine demand. + demandConsumerInfo = new ConsumerInfo(sessionInfo, 1); + demandConsumerInfo.setDispatchAsync(dispatchAsync); + demandConsumerInfo.setDestination(new ActiveMQTopic(AdvisorySupport.CONSUMER_ADVISORY_TOPIC_PREFIX+destinationFilter)); + demandConsumerInfo.setPrefetchSize(prefetchSize); + remoteBroker.oneway(demandConsumerInfo); + + log.info("Network connection between " + localBroker + " and " + remoteBroker + " has been established."); + } + + public void stop() throws Exception{ + try { + if( connectionInfo!=null ) { + localBroker.request(connectionInfo.createRemoveCommand()); + remoteBroker.request(connectionInfo.createRemoveCommand()); + } + localBroker.setTransportListener(null); + remoteBroker.setTransportListener(null); + remoteBroker.oneway(new ShutdownInfo()); + localBroker.oneway(new ShutdownInfo()); + }catch(IOException e){ + log.debug("Caught exception stopping",e); + } finally { + ServiceStopper ss = new ServiceStopper(); + ss.stop(localBroker); + ss.stop(remoteBroker); + ss.throwFirstException(); + } + } + + protected void serviceRemoteException(IOException error) { + log.info("Network connection between " + localBroker + " and " + remoteBroker + " shutdown: "+error.getMessage(), error); + ServiceSupport.dispose(this); + } + + protected void serviceRemoteCommand(Command command) { + try { + if( command.isMessageDispatch() ) { + MessageDispatch md = (MessageDispatch) command; + serviceRemoteConsumerAdvisory(md.getMessage().getDataStructure()); + demandConsumerDispatched++; + if( demandConsumerDispatched > (demandConsumerInfo.getPrefetchSize()*.75) ) { + remoteBroker.oneway(new MessageAck(md, MessageAck.STANDARD_ACK_TYPE, demandConsumerDispatched)); + demandConsumerDispatched=0; + } + } else if ( command.isBrokerInfo() ) { + synchronized( this ) { + remoteBrokerId = ((BrokerInfo)command).getBrokerId(); + remoteBrokerPath[0] = remoteBrokerId; + if( localBrokerId !=null) { + if( localBrokerId.equals(remoteBrokerId) ) { + log.info("Disconnecting loop back connection."); + ServiceSupport.dispose(this); + } else { + triggerStartBridge(); + } + } + } + } else { + log.warn("Unexpected remote command: "+command); + } + } catch (IOException e) { + serviceRemoteException(e); + } + } + + private void serviceRemoteConsumerAdvisory(DataStructure data) throws IOException { + if( data.getClass() == ConsumerInfo.class ) { + + // Create a new local subscription + ConsumerInfo info = (ConsumerInfo) data; + BrokerId[] path = info.getBrokerPath(); + String pathStr = "{"; + for (int i =0; path != null && i < path.length; i++){ + pathStr += path[i] + " , "; + } + pathStr += "}"; + if( contains(info.getBrokerPath(), localBrokerPath[0]) ) { + // Ignore this consumer as it's a consumer we locally sent to the broker. + return; + } + + + // Update the packet to show where it came from. + info.setBrokerPath( appendToBrokerPath(info.getBrokerPath(), remoteBrokerPath) ); + + DemandSubscription sub = new DemandSubscription(info); + sub.localInfo.setConsumerId( new ConsumerId(sessionInfo.getSessionId(), consumerIdGenerator.getNextSequenceId()) ); + sub.localInfo.setDispatchAsync(dispatchAsync); + sub.localInfo.setPrefetchSize(prefetchSize); + byte priority = ConsumerInfo.NETWORK_CONSUMER_PRIORITY; + if( priority > Byte.MIN_VALUE && info.getBrokerPath()!=null && info.getBrokerPath().length>1 ) { + // The longer the path to the consumer, the less it's consumer priority. + priority -= info.getBrokerPath().length+1; + } + sub.localInfo.setPriority(priority); + subscriptionMapByLocalId.put(sub.localInfo.getConsumerId(), sub); + subscriptionMapByRemoteId.put(sub.remoteInfo.getConsumerId(), sub); + sub.localInfo.setBrokerPath(info.getBrokerPath()); + sub.localInfo.setNetworkSubscription(true); + localBroker.oneway(sub.localInfo); + } + if( data.getClass() == RemoveInfo.class ) { + ConsumerId id = (ConsumerId) ((RemoveInfo)data).getObjectId(); + DemandSubscription sub = (DemandSubscription)subscriptionMapByRemoteId.remove(id); + if( sub !=null ) { + subscriptionMapByLocalId.remove(sub.localInfo.getConsumerId()); + localBroker.oneway(sub.localInfo.createRemoveCommand()); + } + } + } + + protected void serviceLocalException(IOException error) { + log.info("Network connection between " + localBroker + " and " + remoteBroker + " shutdown: "+error.getMessage(), error); + ServiceSupport.dispose(this); + } + + protected void serviceLocalCommand(Command command) { + try { + if( command.isMessageDispatch() ) { + MessageDispatch md = (MessageDispatch) command; + Message message = md.getMessage(); + //only allow one network hop for this type of bridge + if (message.isRecievedByDFBridge()){ + return; + } + if (message.isAdvisory() && message.getDataStructure() != null && message.getDataStructure().getDataStructureType()==CommandTypes.CONSUMER_INFO){ + ConsumerInfo info = (ConsumerInfo)message.getDataStructure(); + if (info.isNetworkSubscription()){ + //don't want to forward these + return; + } + } + DemandSubscription sub = (DemandSubscription)subscriptionMapByLocalId.get(md.getConsumerId()); + if( sub!=null ) { + + if( contains(message.getBrokerPath(), remoteBrokerPath[0]) ) { + // Don't send the message back to the originator + return; + } + // Update the packet to show where it came from. + message.setBrokerPath( appendToBrokerPath(message.getBrokerPath(), localBrokerPath) ); + + message.setProducerId(producerInfo.getProducerId()); + message.setDestination( md.getDestination() ); + + if( message.getOriginalTransactionId()==null ) + message.setOriginalTransactionId(message.getTransactionId()); + message.setTransactionId(null); + message.evictMarshlledForm(); + + remoteBroker.oneway( message ); + sub.dispatched++; + if( sub.dispatched > (sub.localInfo.getPrefetchSize()*.75) ) { + localBroker.oneway(new MessageAck(md, MessageAck.STANDARD_ACK_TYPE, demandConsumerDispatched)); + sub.dispatched=0; + } + } + } else if ( command.isBrokerInfo() ) { + synchronized( this ) { + localBrokerId = ((BrokerInfo)command).getBrokerId(); + localBrokerPath[0] = localBrokerId; + if( remoteBrokerId !=null ) { + if( remoteBrokerId.equals(localBrokerId) ) { + log.info("Disconnecting loop back connection."); + ServiceSupport.dispose(this); + } else { + triggerStartBridge(); + } + } + } + } else { + log.warn("Unexpected local command: "+command); + } + } catch (IOException e) { + serviceLocalException(e); + } + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public int getPrefetchSize() { + return prefetchSize; + } + + public void setPrefetchSize(int prefetchSize) { + this.prefetchSize = prefetchSize; + } + + public boolean isDispatchAsync() { + return dispatchAsync; + } + + public void setDispatchAsync(boolean dispatchAsync) { + this.dispatchAsync = dispatchAsync; + } + + public String getDestinationFilter() { + return destinationFilter; + } + public void setDestinationFilter(String destinationFilter) { + this.destinationFilter = destinationFilter; + } + + private boolean contains(BrokerId[] brokerPath, BrokerId brokerId) { + if( brokerPath!=null ) { + for (int i = 0; i < brokerPath.length; i++) { + if( brokerId.equals(brokerPath[i]) ) + return true; + } + } + return false; + } + private BrokerId[] appendToBrokerPath(BrokerId[] brokerPath, BrokerId pathsToAppend[]) { + if( brokerPath == null || brokerPath.length==0 ) + return pathsToAppend; + + BrokerId rc[] = new BrokerId[brokerPath.length+pathsToAppend.length]; + System.arraycopy(brokerPath,0,rc,0,brokerPath.length); + System.arraycopy(pathsToAppend,0,rc,brokerPath.length,pathsToAppend.length); + return rc; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/network/ForwardingBridge.java b/activemq-core/src/main/java/org/activemq/network/ForwardingBridge.java new file mode 100755 index 0000000000..32ff75ffee --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/ForwardingBridge.java @@ -0,0 +1,283 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.network; + +import java.io.IOException; + +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.BrokerId; +import org.activemq.command.BrokerInfo; +import org.activemq.command.Command; +import org.activemq.command.ConnectionId; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageDispatch; +import org.activemq.command.ProducerInfo; +import org.activemq.command.SessionInfo; +import org.activemq.command.ShutdownInfo; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportListener; +import org.activemq.util.IdGenerator; +import org.activemq.util.ServiceStopper; +import org.activemq.util.ServiceSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Forwards all messages from the local broker to the remote broker. + * + * @org.xbean.XBean + * + * @version $Revision$ + */ +public class ForwardingBridge implements Bridge { + + static final private Log log = LogFactory.getLog(ForwardingBridge.class); + + private final Transport localBroker; + private final Transport remoteBroker; + + IdGenerator idGenerator = new IdGenerator(); + ConnectionInfo connectionInfo; + SessionInfo sessionInfo; + ProducerInfo producerInfo; + ConsumerInfo queueConsumerInfo; + ConsumerInfo topicConsumerInfo; + + private String clientId; + private int prefetchSize=1000; + private boolean dispatchAsync; + private String destinationFilter = ">"; + + private int queueDispatched; + private int topicDispatched; + + BrokerId localBrokerId; + BrokerId remoteBrokerId; + + public ForwardingBridge(Transport localBroker, Transport remoteBroker) { + this.localBroker = localBroker; + this.remoteBroker = remoteBroker; + } + + public void start() throws Exception { + log.info("Starting a network connection between " + localBroker + " and " + remoteBroker + " has been established."); + + localBroker.setTransportListener(new TransportListener(){ + public void onCommand(Command command) { + serviceLocalCommand(command); + } + public void onException(IOException error) { + serviceLocalException(error); + } + }); + + remoteBroker.setTransportListener(new TransportListener(){ + public void onCommand(Command command) { + serviceRemoteCommand(command); + } + public void onException(IOException error) { + serviceRemoteException(error); + } + }); + + localBroker.start(); + remoteBroker.start(); + } + + protected void triggerStartBridge() throws IOException { + Thread thead = new Thread() { + public void run() { + try { + startBridge(); + } + catch (IOException e) { + log.error("Failed to start network bridge: " + e, e); + } + } + }; + thead.start(); + } + + /** + * @throws IOException + */ + private void startBridge() throws IOException { + connectionInfo = new ConnectionInfo(); + connectionInfo.setConnectionId(new ConnectionId(idGenerator.generateId())); + connectionInfo.setClientId(clientId); + localBroker.oneway(connectionInfo); + remoteBroker.oneway(connectionInfo); + + sessionInfo=new SessionInfo(connectionInfo, 1); + localBroker.oneway(sessionInfo); + remoteBroker.oneway(sessionInfo); + + queueConsumerInfo = new ConsumerInfo(sessionInfo, 1); + queueConsumerInfo.setDispatchAsync(dispatchAsync); + queueConsumerInfo.setDestination(new ActiveMQQueue(destinationFilter)); + queueConsumerInfo.setPrefetchSize(prefetchSize); + queueConsumerInfo.setPriority(ConsumerInfo.NETWORK_CONSUMER_PRIORITY); + localBroker.oneway(queueConsumerInfo); + + producerInfo = new ProducerInfo(sessionInfo, 1); + producerInfo.setResponseRequired(false); + remoteBroker.oneway(producerInfo); + + if( connectionInfo.getClientId()!=null ) { + topicConsumerInfo = new ConsumerInfo(sessionInfo, 2); + topicConsumerInfo.setDispatchAsync(dispatchAsync); + topicConsumerInfo.setSubcriptionName("topic-bridge"); + topicConsumerInfo.setRetroactive(true); + topicConsumerInfo.setDestination(new ActiveMQTopic(destinationFilter)); + topicConsumerInfo.setPrefetchSize(prefetchSize); + topicConsumerInfo.setPriority(ConsumerInfo.NETWORK_CONSUMER_PRIORITY); + localBroker.oneway(topicConsumerInfo); + } + log.info("Network connection between " + localBroker + " and " + remoteBroker + " has been established."); + } + + public void stop() throws Exception { + try { + if( connectionInfo!=null ) { + localBroker.request(connectionInfo.createRemoveCommand()); + remoteBroker.request(connectionInfo.createRemoveCommand()); + } + localBroker.setTransportListener(null); + remoteBroker.setTransportListener(null); + localBroker.oneway(new ShutdownInfo()); + remoteBroker.oneway(new ShutdownInfo()); + } finally { + ServiceStopper ss = new ServiceStopper(); + ss.stop(localBroker); + ss.stop(remoteBroker); + ss.throwFirstException(); + } + } + + protected void serviceRemoteException(IOException error) { + System.out.println("Unexpected remote exception: "+error); + error.printStackTrace(); + } + + protected void serviceRemoteCommand(Command command) { + try { + if(command.isBrokerInfo() ) { + synchronized( this ) { + remoteBrokerId = ((BrokerInfo)command).getBrokerId(); + if( localBrokerId !=null) { + if( localBrokerId.equals(remoteBrokerId) ) { + log.info("Disconnecting loop back connection."); + ServiceSupport.dispose(this); + } else { + triggerStartBridge(); + } + } + } + } else { + System.out.println("Unexpected remote command: "+command); + } + } catch (IOException e) { + serviceLocalException(e); + } + } + + protected void serviceLocalException(IOException error) { + System.out.println("Unexpected local exception: "+error); + error.printStackTrace(); + } + protected void serviceLocalCommand(Command command) { + try { + if( command.isMessageDispatch() ) { + MessageDispatch md = (MessageDispatch) command; + Message message = md.getMessage(); + message.setProducerId(producerInfo.getProducerId()); + message.setDestination( md.getDestination() ); + + if( message.getOriginalTransactionId()==null ) + message.setOriginalTransactionId(message.getTransactionId()); + message.setTransactionId(null); + message.evictMarshlledForm(); + + remoteBroker.oneway( message ); + + if( md.getConsumerId().equals(queueConsumerInfo.getConsumerId()) ) { + queueDispatched++; + if( queueDispatched > (queueConsumerInfo.getPrefetchSize()/2) ) { + localBroker.oneway(new MessageAck(md, MessageAck.STANDARD_ACK_TYPE, queueDispatched)); + queueDispatched=0; + } + } else { + topicDispatched++; + if( topicDispatched > (topicConsumerInfo.getPrefetchSize()/2) ) { + localBroker.oneway(new MessageAck(md, MessageAck.STANDARD_ACK_TYPE, topicDispatched)); + topicDispatched=0; + } + } + } else if(command.isBrokerInfo() ) { + synchronized( this ) { + localBrokerId = ((BrokerInfo)command).getBrokerId(); + if( remoteBrokerId !=null) { + if( remoteBrokerId.equals(localBrokerId) ) { + log.info("Disconnecting loop back connection."); + ServiceSupport.dispose(this); + } else { + triggerStartBridge(); + } + } + } + } else { + System.out.println("Unexpected local command: "+command); + } + } catch (IOException e) { + serviceLocalException(e); + } + } + + public String getClientId() { + return clientId; + } + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public int getPrefetchSize() { + return prefetchSize; + } + public void setPrefetchSize(int prefetchSize) { + this.prefetchSize = prefetchSize; + } + + public boolean isDispatchAsync() { + return dispatchAsync; + } + public void setDispatchAsync(boolean dispatchAsync) { + this.dispatchAsync = dispatchAsync; + } + + public String getDestinationFilter() { + return destinationFilter; + } + public void setDestinationFilter(String destinationFilter) { + this.destinationFilter = destinationFilter; + } +} diff --git a/activemq-core/src/main/java/org/activemq/network/NetworkConnector.java b/activemq-core/src/main/java/org/activemq/network/NetworkConnector.java new file mode 100644 index 0000000000..d9c6d07255 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/NetworkConnector.java @@ -0,0 +1,184 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.network; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activemq.Service; +import org.activemq.command.DiscoveryEvent; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.discovery.DiscoveryAgent; +import org.activemq.transport.discovery.DiscoveryAgentFactory; +import org.activemq.transport.discovery.DiscoveryListener; +import org.activemq.util.ServiceSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * @org.xbean.XBean + * + * @version $Revision$ + */ +public class NetworkConnector implements Service, DiscoveryListener { + + private static final Log log = LogFactory.getLog(NetworkConnector.class); + private DiscoveryAgent discoveryAgent; + private URI localURI; + + private ConcurrentHashMap bridges = new ConcurrentHashMap(); + private String brokerName; + + public NetworkConnector() { + } + + public NetworkConnector(URI localURI, DiscoveryAgent discoveryAgent) throws IOException { + this.localURI = localURI; + setDiscoveryAgent(discoveryAgent); + } + + public void start() throws Exception { + if (discoveryAgent == null) { + throw new IllegalStateException("You must configure the 'discoveryAgent' property"); + } + if (localURI == null) { + throw new IllegalStateException("You must configure the 'localURI' property"); + } + this.discoveryAgent.start(); + } + + public void stop() throws Exception { + this.discoveryAgent.stop(); + } + + public void onServiceAdd(DiscoveryEvent event) { + String url = event.getServiceName(); + if (url != null) { + + URI uri; + try { + uri = new URI(url); + } + catch (URISyntaxException e) { + log.warn("Could not connect to remote URI: " + url + " due to bad URI syntax: " + e, e); + return; + } + + // Has it allready been added? + if (bridges.containsKey(uri) || localURI.equals(uri)) + return; + + log.info("Establishing network connection between " + localURI + " and " + event.getBrokerName() + " at " + uri); + + Transport localTransport; + try { + localTransport = TransportFactory.connect(localURI); + } + catch (Exception e) { + log.warn("Could not connect to local URI: " + localURI + ": " + e, e); + return; + } + + Transport remoteTransport; + try { + remoteTransport = TransportFactory.connect(uri); + } + catch (Exception e) { + ServiceSupport.dispose(localTransport); + log.warn("Could not connect to remote URI: " + uri + ": " + e, e); + return; + } + + Bridge bridge = createBridge(localTransport, remoteTransport); + bridges.put(uri, bridge); + try { + bridge.start(); + } + catch (Exception e) { + ServiceSupport.dispose(localTransport); + ServiceSupport.dispose(remoteTransport); + log.warn("Could not start network bridge between: " + localURI + " and: " + uri + " due to: " + e, e); + return; + } + } + } + + public void onServiceRemove(DiscoveryEvent event) { + String url = event.getServiceName(); + if (url != null) { + URI uri; + try { + uri = new URI(url); + } catch (URISyntaxException e) { + log.warn("Could not connect to remote URI: " + url + " due to bad URI syntax: " + e, e); + return; + } + + Bridge bridge = (Bridge) bridges.get(uri); + if (bridge == null) + return; + + ServiceSupport.dispose(bridge); + } + } + + // Properties + // ------------------------------------------------------------------------- + public DiscoveryAgent getDiscoveryAgent() { + return discoveryAgent; + } + + public void setDiscoveryAgent(DiscoveryAgent discoveryAgent) { + this.discoveryAgent = discoveryAgent; + if (discoveryAgent != null) { + this.discoveryAgent.setDiscoveryListener(this); + this.discoveryAgent.setBrokerName(brokerName); + } + } + + public URI getLocalUri() throws URISyntaxException { + return localURI; + } + + public void setLocalUri(URI localURI) { + this.localURI = localURI; + } + + public void setUri(URI discoveryURI) throws IOException { + setDiscoveryAgent(DiscoveryAgentFactory.createDiscoveryAgent(discoveryURI)); + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected Bridge createBridge(Transport localTransport, Transport remoteTransport) { + return new DemandForwardingBridge(localTransport, remoteTransport); + } + + public void setBrokerName(String brokerName) { + this.brokerName = brokerName; + if( discoveryAgent!=null ) { + discoveryAgent.setBrokerName(brokerName); + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/network/jms/DestinationBridge.java b/activemq-core/src/main/java/org/activemq/network/jms/DestinationBridge.java new file mode 100755 index 0000000000..f5715cafa9 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/jms/DestinationBridge.java @@ -0,0 +1,131 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.network.jms; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import org.activemq.Service; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; +/** + * A Destination bridge is used to bridge between to different JMS systems + * + * @version $Revision: 1.1.1.1 $ + */ +abstract class DestinationBridge implements Service,MessageListener{ + private static final Log log=LogFactory.getLog(DestinationBridge.class); + protected MessageConsumer consumer; + protected AtomicBoolean started=new AtomicBoolean(false); + protected JmsMesageConvertor jmsMessageConvertor; + protected boolean doHandleReplyTo = true; + + /** + * @return Returns the consumer. + */ + public MessageConsumer getConsumer(){ + return consumer; + } + + /** + * @param consumer + * The consumer to set. + */ + public void setConsumer(MessageConsumer consumer){ + this.consumer=consumer; + } + + /** + * @return Returns the jmsMessageConvertor. + */ + public JmsMesageConvertor getJmsMessageConvertor(){ + return jmsMessageConvertor; + } + + /** + * @param jmsMessageConvertor + * The jmsMessageConvertor to set. + */ + public void setJmsMessageConvertor(JmsMesageConvertor jmsMessageConvertor){ + this.jmsMessageConvertor=jmsMessageConvertor; + } + + public void start() throws Exception{ + if(started.compareAndSet(false,true)){ + MessageConsumer consumer=createConsumer(); + consumer.setMessageListener(this); + createProducer(); + } + } + + public void stop() throws Exception{ + started.set(false); + } + + public void onMessage(Message message){ + if(started.get()&&message!=null){ + try{ + if(doHandleReplyTo){ + Destination replyTo=message.getJMSReplyTo(); + if(replyTo!=null){ + replyTo=processReplyToDestination(replyTo); + message.setJMSReplyTo(replyTo); + } + }else { + message.setJMSReplyTo(null); + } + Message converted=jmsMessageConvertor.convert(message); + sendMessage(converted); + message.acknowledge(); + }catch(JMSException e){ + log.error("failed to forward message: "+message,e); + try{ + stop(); + }catch(Exception e1){ + log.warn("Failed to stop cleanly",e1); + } + } + } + } + + /** + * @return Returns the doHandleReplyTo. + */ + protected boolean isDoHandleReplyTo(){ + return doHandleReplyTo; + } + + /** + * @param doHandleReplyTo The doHandleReplyTo to set. + */ + protected void setDoHandleReplyTo(boolean doHandleReplyTo){ + this.doHandleReplyTo=doHandleReplyTo; + } + + protected abstract MessageConsumer createConsumer() throws JMSException; + + protected abstract MessageProducer createProducer() throws JMSException; + + protected abstract void sendMessage(Message message) throws JMSException; + + protected abstract Destination processReplyToDestination(Destination destination); + + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/network/jms/InboundQueueBridge.java b/activemq-core/src/main/java/org/activemq/network/jms/InboundQueueBridge.java new file mode 100755 index 0000000000..d604376983 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/jms/InboundQueueBridge.java @@ -0,0 +1,57 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.network.jms; + + +/** + * Create an Inbound Queue Bridge + * + * @org.xbean.XBean + * + * @version $Revision: 1.1.1.1 $ + */ +public class InboundQueueBridge extends QueueBridge{ + + String inboundQueueName; + /** + * Constructor that takes a foriegn destination as an argument + * @param inboundQueueName + */ + public InboundQueueBridge(String inboundQueueName){ + this.inboundQueueName = inboundQueueName; + } + + /** + * Default Contructor + */ + public InboundQueueBridge(){ + } + + /** + * @return Returns the inboundQueueName. + */ + public String getInboundQueueName(){ + return inboundQueueName; + } + + /** + * @param inboundQueueName The inboundQueueName to set. + */ + public void setInboundQueueName(String inboundQueueName){ + this.inboundQueueName=inboundQueueName; + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/network/jms/InboundTopicBridge.java b/activemq-core/src/main/java/org/activemq/network/jms/InboundTopicBridge.java new file mode 100755 index 0000000000..76da770bd4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/jms/InboundTopicBridge.java @@ -0,0 +1,57 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.network.jms; + + +/** + * Create an Inbound Topic Bridge + * + * @org.xbean.XBean + * + * @version $Revision: 1.1.1.1 $ + */ +public class InboundTopicBridge extends TopicBridge{ + + String inboundTopicName; + /** + * Constructor that takes a foriegn destination as an argument + * @param inboundTopicName + */ + public InboundTopicBridge(String inboundTopicName){ + this.inboundTopicName = inboundTopicName; + } + + /** + * Default Contructor + */ + public InboundTopicBridge(){ + } + + /** + * @return Returns the outboundTopicName. + */ + public String getInboundTopicName(){ + return inboundTopicName; + } + + /** + * @param outboundTopicName The outboundTopicName to set. + */ + public void setInboundTopicName(String inboundTopicName){ + this.inboundTopicName=inboundTopicName; + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/network/jms/JmsConnector.java b/activemq-core/src/main/java/org/activemq/network/jms/JmsConnector.java new file mode 100755 index 0000000000..177dac9ff6 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/jms/JmsConnector.java @@ -0,0 +1,175 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.network.jms; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.Service; +import org.activemq.broker.BrokerService; +import org.activemq.util.LRUCache; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.jndi.JndiTemplate; +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +/** + * This bridge joins the gap between foreign JMS providers and ActiveMQ As some JMS providers are still only 1.0.1 + * compliant, this bridge itself aimed to be JMS 1.0.2 compliant. + * + * @version $Revision: 1.1.1.1 $ + */ +public abstract class JmsConnector implements Service{ + private static final Log log=LogFactory.getLog(JmsConnector.class); + protected JndiTemplate jndiTemplate; + protected JmsMesageConvertor jmsMessageConvertor; + private List inboundBridges = new CopyOnWriteArrayList(); + private List outboundBridges = new CopyOnWriteArrayList(); + protected int replyToDestinationCacheSize=10000; + protected AtomicBoolean initialized = new AtomicBoolean(false); + protected AtomicBoolean started = new AtomicBoolean(false); + protected ActiveMQConnectionFactory embeddedConnectionFactory; + protected LRUCache replyToBridges=new LRUCache(){ + protected boolean removeEldestEntry(Map.Entry enty){ + if(size()>maxCacheSize){ + Iterator iter=entrySet().iterator(); + Map.Entry lru=(Map.Entry) iter.next(); + remove(lru.getKey()); + DestinationBridge bridge=(DestinationBridge) lru.getValue(); + try{ + bridge.stop(); + log.info("Expired bridge: "+bridge); + }catch(Exception e){ + log.warn("stopping expired bridge"+bridge+" caused an exception",e); + } + } + return false; + } + }; + + public boolean init(){ + boolean result=initialized.compareAndSet(false,true); + if(result){ + if(jndiTemplate==null){ + jndiTemplate=new JndiTemplate(); + } + if(jmsMessageConvertor==null){ + jmsMessageConvertor=new SimpleJmsMessageConvertor(); + } + replyToBridges.setMaxCacheSize(getReplyToDestinationCacheSize()); + } + return result; + } + + public void start() throws Exception{ + init(); + if (started.compareAndSet(false, true)){ + for(int i=0;iActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.network.jms; + +import javax.jms.JMSException; +import javax.jms.Message; + +/** + * Converts Message from one JMS to another + * + * @version $Revision: 1.1.1.1 $ + */ +public interface JmsMesageConvertor { + + /** + * Convert a foreign JMS Message to a native ActiveMQ Message + * @param message + * @return the converted message + * @throws JMSException + */ + public Message convert(Message message) throws JMSException; + + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/network/jms/JmsQueueConnector.java b/activemq-core/src/main/java/org/activemq/network/jms/JmsQueueConnector.java new file mode 100755 index 0000000000..19c502ce0b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/jms/JmsQueueConnector.java @@ -0,0 +1,433 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.network.jms; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueConnectionFactory; +import javax.jms.QueueSession; +import javax.jms.Session; +import javax.naming.NamingException; +/** + * A Bridge to other JMS Queue providers + * + * @org.xbean.XBean + * + * @version $Revision: 1.1.1.1 $ + */ +public class JmsQueueConnector extends JmsConnector{ + private static final Log log=LogFactory.getLog(JmsQueueConnector.class); + private String outboundQueueConnectionFactoryName; + private String localConnectionFactoryName; + private QueueConnectionFactory outboundQueueConnectionFactory; + private QueueConnectionFactory localQueueConnectionFactory; + private QueueConnection outboundQueueConnection; + private QueueConnection localQueueConnection; + private InboundQueueBridge[] inboundQueueBridges; + private OutboundQueueBridge[] outboundQueueBridges; + private String outboundUsername; + private String outboundPassword; + private String localUsername; + private String localPassword; + + + + + public boolean init(){ + boolean result=super.init(); + if(result){ + try{ + initializeForeignQueueConnection(); + initializeLocalQueueConnection(); + initializeInboundQueueBridges(); + initializeOutboundQueueBridges(); + }catch(Exception e){ + log.error("Failed to initialize the JMSConnector",e); + } + } + return result; + } + + + + /** + * @return Returns the inboundQueueBridges. + */ + public InboundQueueBridge[] getInboundQueueBridges(){ + return inboundQueueBridges; + } + + /** + * @param inboundQueueBridges + * The inboundQueueBridges to set. + */ + public void setInboundQueueBridges(InboundQueueBridge[] inboundQueueBridges){ + this.inboundQueueBridges=inboundQueueBridges; + } + + /** + * @return Returns the outboundQueueBridges. + */ + public OutboundQueueBridge[] getOutboundQueueBridges(){ + return outboundQueueBridges; + } + + /** + * @param outboundQueueBridges + * The outboundQueueBridges to set. + */ + public void setOutboundQueueBridges(OutboundQueueBridge[] outboundQueueBridges){ + this.outboundQueueBridges=outboundQueueBridges; + } + + /** + * @return Returns the localQueueConnectionFactory. + */ + public QueueConnectionFactory getLocalQueueConnectionFactory(){ + return localQueueConnectionFactory; + } + + /** + * @param localQueueConnectionFactory + * The localQueueConnectionFactory to set. + */ + public void setLocalQueueConnectionFactory(QueueConnectionFactory localConnectionFactory){ + this.localQueueConnectionFactory=localConnectionFactory; + } + + /** + * @return Returns the outboundQueueConnectionFactory. + */ + public QueueConnectionFactory getOutboundQueueConnectionFactory(){ + return outboundQueueConnectionFactory; + } + + /** + * @return Returns the outboundQueueConnectionFactoryName. + */ + public String getOutboundQueueConnectionFactoryName(){ + return outboundQueueConnectionFactoryName; + } + + /** + * @param outboundQueueConnectionFactoryName + * The outboundQueueConnectionFactoryName to set. + */ + public void setOutboundQueueConnectionFactoryName(String foreignQueueConnectionFactoryName){ + this.outboundQueueConnectionFactoryName=foreignQueueConnectionFactoryName; + } + + /** + * @return Returns the localConnectionFactoryName. + */ + public String getLocalConnectionFactoryName(){ + return localConnectionFactoryName; + } + + /** + * @param localConnectionFactoryName + * The localConnectionFactoryName to set. + */ + public void setLocalConnectionFactoryName(String localConnectionFactoryName){ + this.localConnectionFactoryName=localConnectionFactoryName; + } + + /** + * @return Returns the localQueueConnection. + */ + public QueueConnection getLocalQueueConnection(){ + return localQueueConnection; + } + + /** + * @param localQueueConnection + * The localQueueConnection to set. + */ + public void setLocalQueueConnection(QueueConnection localQueueConnection){ + this.localQueueConnection=localQueueConnection; + } + + /** + * @return Returns the outboundQueueConnection. + */ + public QueueConnection getOutboundQueueConnection(){ + return outboundQueueConnection; + } + + /** + * @param outboundQueueConnection + * The outboundQueueConnection to set. + */ + public void setOutboundQueueConnection(QueueConnection foreignQueueConnection){ + this.outboundQueueConnection=foreignQueueConnection; + } + + /** + * @param outboundQueueConnectionFactory + * The outboundQueueConnectionFactory to set. + */ + public void setOutboundQueueConnectionFactory(QueueConnectionFactory foreignQueueConnectionFactory){ + this.outboundQueueConnectionFactory=foreignQueueConnectionFactory; + } + + /** + * @return Returns the outboundPassword. + */ + public String getOutboundPassword(){ + return outboundPassword; + } + + /** + * @param outboundPassword + * The outboundPassword to set. + */ + public void setOutboundPassword(String foreignPassword){ + this.outboundPassword=foreignPassword; + } + + /** + * @return Returns the outboundUsername. + */ + public String getOutboundUsername(){ + return outboundUsername; + } + + /** + * @param outboundUsername + * The outboundUsername to set. + */ + public void setOutboundUsername(String foreignUsername){ + this.outboundUsername=foreignUsername; + } + + /** + * @return Returns the localPassword. + */ + public String getLocalPassword(){ + return localPassword; + } + + /** + * @param localPassword + * The localPassword to set. + */ + public void setLocalPassword(String localPassword){ + this.localPassword=localPassword; + } + + /** + * @return Returns the localUsername. + */ + public String getLocalUsername(){ + return localUsername; + } + + /** + * @param localUsername + * The localUsername to set. + */ + public void setLocalUsername(String localUsername){ + this.localUsername=localUsername; + } + + /** + * @return Returns the replyToDestinationCacheSize. + */ + public int getReplyToDestinationCacheSize(){ + return replyToDestinationCacheSize; + } + + /** + * @param replyToDestinationCacheSize The replyToDestinationCacheSize to set. + */ + public void setReplyToDestinationCacheSize(int temporaryQueueCacheSize){ + this.replyToDestinationCacheSize=temporaryQueueCacheSize; + } + + protected void initializeForeignQueueConnection() throws NamingException,JMSException{ + if(outboundQueueConnection==null){ + // get the connection factories + if(outboundQueueConnectionFactory==null){ + // look it up from JNDI + if(outboundQueueConnectionFactoryName!=null){ + outboundQueueConnectionFactory=(QueueConnectionFactory) jndiTemplate.lookup( + outboundQueueConnectionFactoryName,QueueConnectionFactory.class); + if(outboundUsername!=null){ + outboundQueueConnection=outboundQueueConnectionFactory.createQueueConnection(outboundUsername, + outboundPassword); + }else{ + outboundQueueConnection=outboundQueueConnectionFactory.createQueueConnection(); + } + }else { + throw new JMSException("Cannot create localConnection - no information"); + } + }else { + if(outboundUsername!=null){ + outboundQueueConnection=outboundQueueConnectionFactory.createQueueConnection(outboundUsername, + outboundPassword); + }else{ + outboundQueueConnection=outboundQueueConnectionFactory.createQueueConnection(); + } + } + } + outboundQueueConnection.start(); + } + + protected void initializeLocalQueueConnection() throws NamingException,JMSException{ + if(localQueueConnection==null){ + // get the connection factories + if(localQueueConnectionFactory==null){ + if(embeddedConnectionFactory==null){ + // look it up from JNDI + if(localConnectionFactoryName!=null){ + localQueueConnectionFactory=(QueueConnectionFactory) jndiTemplate.lookup( + localConnectionFactoryName,QueueConnectionFactory.class); + if(localUsername!=null){ + localQueueConnection=localQueueConnectionFactory.createQueueConnection(localUsername, + localPassword); + }else{ + localQueueConnection=localQueueConnectionFactory.createQueueConnection(); + } + }else { + throw new JMSException("Cannot create localConnection - no information"); + } + }else{ + localQueueConnection = embeddedConnectionFactory.createQueueConnection(); + } + }else { + if(localUsername!=null){ + localQueueConnection=localQueueConnectionFactory.createQueueConnection(localUsername, + localPassword); + }else{ + localQueueConnection=localQueueConnectionFactory.createQueueConnection(); + } + } + } + localQueueConnection.start(); + } + + protected void initializeInboundQueueBridges() throws JMSException{ + if(inboundQueueBridges!=null){ + QueueSession outboundSession = outboundQueueConnection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE); + QueueSession localSession = localQueueConnection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE); + for(int i=0;iActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.network.jms; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Session; +import javax.jms.Topic; +import javax.jms.TopicConnection; +import javax.jms.TopicConnectionFactory; +import javax.jms.TopicSession; +import javax.naming.NamingException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A Bridge to other JMS Topic providers + * + * @org.xbean.XBean + * + * @version $Revision: 1.1.1.1 $ + */ +public class JmsTopicConnector extends JmsConnector{ + private static final Log log=LogFactory.getLog(JmsTopicConnector.class); + private String outboundTopicConnectionFactoryName; + private String localConnectionFactoryName; + private TopicConnectionFactory outboundTopicConnectionFactory; + private TopicConnectionFactory localTopicConnectionFactory; + private TopicConnection outboundTopicConnection; + private TopicConnection localTopicConnection; + private InboundTopicBridge[] inboundTopicBridges; + private OutboundTopicBridge[] outboundTopicBridges; + private String outboundUsername; + private String outboundPassword; + private String localUsername; + private String localPassword; + + + + + public boolean init(){ + boolean result=super.init(); + if(result){ + try{ + initializeForeignTopicConnection(); + initializeLocalTopicConnection(); + initializeInboundTopicBridges(); + initializeOutboundTopicBridges(); + }catch(Exception e){ + log.error("Failed to initialize the JMSConnector",e); + } + } + return result; + } + + + + /** + * @return Returns the inboundTopicBridges. + */ + public InboundTopicBridge[] getInboundTopicBridges(){ + return inboundTopicBridges; + } + + /** + * @param inboundTopicBridges + * The inboundTopicBridges to set. + */ + public void setInboundTopicBridges(InboundTopicBridge[] inboundTopicBridges){ + this.inboundTopicBridges=inboundTopicBridges; + } + + /** + * @return Returns the outboundTopicBridges. + */ + public OutboundTopicBridge[] getOutboundTopicBridges(){ + return outboundTopicBridges; + } + + /** + * @param outboundTopicBridges + * The outboundTopicBridges to set. + */ + public void setOutboundTopicBridges(OutboundTopicBridge[] outboundTopicBridges){ + this.outboundTopicBridges=outboundTopicBridges; + } + + /** + * @return Returns the localTopicConnectionFactory. + */ + public TopicConnectionFactory getLocalTopicConnectionFactory(){ + return localTopicConnectionFactory; + } + + /** + * @param localTopicConnectionFactory + * The localTopicConnectionFactory to set. + */ + public void setLocalTopicConnectionFactory(TopicConnectionFactory localConnectionFactory){ + this.localTopicConnectionFactory=localConnectionFactory; + } + + /** + * @return Returns the outboundTopicConnectionFactory. + */ + public TopicConnectionFactory getOutboundTopicConnectionFactory(){ + return outboundTopicConnectionFactory; + } + + /** + * @return Returns the outboundTopicConnectionFactoryName. + */ + public String getOutboundTopicConnectionFactoryName(){ + return outboundTopicConnectionFactoryName; + } + + /** + * @param outboundTopicConnectionFactoryName + * The outboundTopicConnectionFactoryName to set. + */ + public void setOutboundTopicConnectionFactoryName(String foreignTopicConnectionFactoryName){ + this.outboundTopicConnectionFactoryName=foreignTopicConnectionFactoryName; + } + + /** + * @return Returns the localConnectionFactoryName. + */ + public String getLocalConnectionFactoryName(){ + return localConnectionFactoryName; + } + + /** + * @param localConnectionFactoryName + * The localConnectionFactoryName to set. + */ + public void setLocalConnectionFactoryName(String localConnectionFactoryName){ + this.localConnectionFactoryName=localConnectionFactoryName; + } + + /** + * @return Returns the localTopicConnection. + */ + public TopicConnection getLocalTopicConnection(){ + return localTopicConnection; + } + + /** + * @param localTopicConnection + * The localTopicConnection to set. + */ + public void setLocalTopicConnection(TopicConnection localTopicConnection){ + this.localTopicConnection=localTopicConnection; + } + + /** + * @return Returns the outboundTopicConnection. + */ + public TopicConnection getOutboundTopicConnection(){ + return outboundTopicConnection; + } + + /** + * @param outboundTopicConnection + * The outboundTopicConnection to set. + */ + public void setOutboundTopicConnection(TopicConnection foreignTopicConnection){ + this.outboundTopicConnection=foreignTopicConnection; + } + + /** + * @param outboundTopicConnectionFactory + * The outboundTopicConnectionFactory to set. + */ + public void setOutboundTopicConnectionFactory(TopicConnectionFactory foreignTopicConnectionFactory){ + this.outboundTopicConnectionFactory=foreignTopicConnectionFactory; + } + + /** + * @return Returns the outboundPassword. + */ + public String getOutboundPassword(){ + return outboundPassword; + } + + /** + * @param outboundPassword + * The outboundPassword to set. + */ + public void setOutboundPassword(String foreignPassword){ + this.outboundPassword=foreignPassword; + } + + /** + * @return Returns the outboundUsername. + */ + public String getOutboundUsername(){ + return outboundUsername; + } + + /** + * @param outboundUsername + * The outboundUsername to set. + */ + public void setOutboundUsername(String foreignUsername){ + this.outboundUsername=foreignUsername; + } + + /** + * @return Returns the localPassword. + */ + public String getLocalPassword(){ + return localPassword; + } + + /** + * @param localPassword + * The localPassword to set. + */ + public void setLocalPassword(String localPassword){ + this.localPassword=localPassword; + } + + /** + * @return Returns the localUsername. + */ + public String getLocalUsername(){ + return localUsername; + } + + /** + * @param localUsername + * The localUsername to set. + */ + public void setLocalUsername(String localUsername){ + this.localUsername=localUsername; + } + + /** + * @return Returns the replyToDestinationCacheSize. + */ + public int getReplyToDestinationCacheSize(){ + return replyToDestinationCacheSize; + } + + /** + * @param replyToDestinationCacheSize The replyToDestinationCacheSize to set. + */ + public void setReplyToDestinationCacheSize(int temporaryTopicCacheSize){ + this.replyToDestinationCacheSize=temporaryTopicCacheSize; + } + + protected void initializeForeignTopicConnection() throws NamingException,JMSException{ + if(outboundTopicConnection==null){ + // get the connection factories + if(outboundTopicConnectionFactory==null){ + // look it up from JNDI + if(outboundTopicConnectionFactoryName!=null){ + outboundTopicConnectionFactory=(TopicConnectionFactory) jndiTemplate.lookup( + outboundTopicConnectionFactoryName,TopicConnectionFactory.class); + if(outboundUsername!=null){ + outboundTopicConnection=outboundTopicConnectionFactory.createTopicConnection(outboundUsername, + outboundPassword); + }else{ + outboundTopicConnection=outboundTopicConnectionFactory.createTopicConnection(); + } + }else { + throw new JMSException("Cannot create localConnection - no information"); + } + }else { + if(outboundUsername!=null){ + outboundTopicConnection=outboundTopicConnectionFactory.createTopicConnection(outboundUsername, + outboundPassword); + }else{ + outboundTopicConnection=outboundTopicConnectionFactory.createTopicConnection(); + } + } + } + outboundTopicConnection.start(); + } + + protected void initializeLocalTopicConnection() throws NamingException,JMSException{ + if(localTopicConnection==null){ + // get the connection factories + if(localTopicConnectionFactory==null){ + if(embeddedConnectionFactory==null){ + // look it up from JNDI + if(localConnectionFactoryName!=null){ + localTopicConnectionFactory=(TopicConnectionFactory) jndiTemplate.lookup( + localConnectionFactoryName,TopicConnectionFactory.class); + if(localUsername!=null){ + localTopicConnection=localTopicConnectionFactory.createTopicConnection(localUsername, + localPassword); + }else{ + localTopicConnection=localTopicConnectionFactory.createTopicConnection(); + } + }else { + throw new JMSException("Cannot create localConnection - no information"); + } + }else{ + localTopicConnection = embeddedConnectionFactory.createTopicConnection(); + } + }else { + if(localUsername!=null){ + localTopicConnection=localTopicConnectionFactory.createTopicConnection(localUsername, + localPassword); + }else{ + localTopicConnection=localTopicConnectionFactory.createTopicConnection(); + } + } + } + localTopicConnection.start(); + } + + protected void initializeInboundTopicBridges() throws JMSException{ + if(inboundTopicBridges!=null){ + TopicSession outboundSession = outboundTopicConnection.createTopicSession(false,Session.AUTO_ACKNOWLEDGE); + TopicSession localSession = localTopicConnection.createTopicSession(false,Session.AUTO_ACKNOWLEDGE); + for(int i=0;iActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.network.jms; + + +/** + * Create an Outbound Queue Bridge + * + * @org.xbean.XBean + * + * @version $Revision: 1.1.1.1 $ + */ +public class OutboundQueueBridge extends QueueBridge{ + + String outboundQueueName; + /** + * Constructor that takes a foreign destination as an argument + * @param outboundQueueName + */ + public OutboundQueueBridge(String outboundQueueName){ + this.outboundQueueName = outboundQueueName; + } + + /** + * Default Contructor + */ + public OutboundQueueBridge(){ + } + + /** + * @return Returns the outboundQueueName. + */ + public String getOutboundQueueName(){ + return outboundQueueName; + } + + /** + * @param outboundQueueName The outboundQueueName to set. + */ + public void setOutboundQueueName(String outboundQueueName){ + this.outboundQueueName=outboundQueueName; + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/network/jms/OutboundTopicBridge.java b/activemq-core/src/main/java/org/activemq/network/jms/OutboundTopicBridge.java new file mode 100755 index 0000000000..f46f371c6f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/jms/OutboundTopicBridge.java @@ -0,0 +1,57 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.network.jms; + + +/** + * Create an Outbound Topic Bridge + * + * @org.xbean.XBean + * + * @version $Revision: 1.1.1.1 $ + */ +public class OutboundTopicBridge extends TopicBridge{ + + String outboundTopicName; + /** + * Constructor that takes a foreign destination as an argument + * @param outboundTopicName + */ + public OutboundTopicBridge(String outboundTopicName){ + this.outboundTopicName = outboundTopicName; + } + + /** + * Default Contructor + */ + public OutboundTopicBridge(){ + } + + /** + * @return Returns the outboundTopicName. + */ + public String getOutboundTopicName(){ + return outboundTopicName; + } + + /** + * @param outboundTopicName The outboundTopicName to set. + */ + public void setOutboundTopicName(String outboundTopicName){ + this.outboundTopicName=outboundTopicName; + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/network/jms/QueueBridge.java b/activemq-core/src/main/java/org/activemq/network/jms/QueueBridge.java new file mode 100755 index 0000000000..fba4a4e77f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/jms/QueueBridge.java @@ -0,0 +1,163 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.network.jms; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueSender; +import javax.jms.QueueSession; +import javax.jms.Session; +import javax.jms.Topic; +/** + * A Destination bridge is used to bridge between to different JMS systems + * + * @version $Revision: 1.1.1.1 $ + */ +class QueueBridge extends DestinationBridge{ + protected Queue consumerQueue; + protected Queue producerQueue; + protected QueueSession consumerSession; + protected QueueSession producerSession; + + protected String selector; + protected QueueSender producer; + protected QueueConnection consumerConnection; + protected QueueConnection producerConnection; + protected JmsQueueConnector jmsQueueConnector; + + public void stop() throws Exception{ + super.stop(); + if(consumerSession!=null){ + consumerSession.close(); + } + if(producerSession!=null){ + producerSession.close(); + } + } + + protected void setJmsQueueConnector(JmsQueueConnector connector){ + this.jmsQueueConnector = connector; + } + + protected MessageConsumer createConsumer() throws JMSException{ + // set up the consumer + consumerSession=consumerConnection.createQueueSession(false,Session.CLIENT_ACKNOWLEDGE); + producerSession=producerConnection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE); + MessageConsumer consumer=null; + + if(selector!=null&&selector.length()>0){ + consumer=consumerSession.createReceiver(consumerQueue,selector); + }else{ + consumer=consumerSession.createReceiver(consumerQueue); + } + + return consumer; + } + + protected MessageProducer createProducer() throws JMSException{ + producer = producerSession.createSender(null); + return producer; + } + + + protected Destination processReplyToDestination (Destination destination){ + Queue queue = (Queue)destination; + return jmsQueueConnector.createReplyToQueueBridge(queue, getConsumerConnection(), getProducerConnection()); + } + + + + protected void sendMessage(Message message) throws JMSException{ + producer.send(producerQueue,message); + } + + /** + * @return Returns the consumerConnection. + */ + public QueueConnection getConsumerConnection(){ + return consumerConnection; + } + + /** + * @param consumerConnection The consumerConnection to set. + */ + public void setConsumerConnection(QueueConnection consumerConnection){ + this.consumerConnection=consumerConnection; + } + + /** + * @return Returns the consumerQueue. + */ + public Queue getConsumerQueue(){ + return consumerQueue; + } + + /** + * @param consumerQueue The consumerQueue to set. + */ + public void setConsumerQueue(Queue consumerQueue){ + this.consumerQueue=consumerQueue; + } + + /** + * @return Returns the producerConnection. + */ + public QueueConnection getProducerConnection(){ + return producerConnection; + } + + /** + * @param producerConnection The producerConnection to set. + */ + public void setProducerConnection(QueueConnection producerConnection){ + this.producerConnection=producerConnection; + } + + /** + * @return Returns the producerQueue. + */ + public Queue getProducerQueue(){ + return producerQueue; + } + + /** + * @param producerQueue The producerQueue to set. + */ + public void setProducerQueue(Queue producerQueue){ + this.producerQueue=producerQueue; + } + + /** + * @return Returns the selector. + */ + public String getSelector(){ + return selector; + } + + /** + * @param selector The selector to set. + */ + public void setSelector(String selector){ + this.selector=selector; + } + + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/network/jms/SimpleJmsMessageConvertor.java b/activemq-core/src/main/java/org/activemq/network/jms/SimpleJmsMessageConvertor.java new file mode 100755 index 0000000000..38152aa9c9 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/jms/SimpleJmsMessageConvertor.java @@ -0,0 +1,41 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.network.jms; + +import javax.jms.JMSException; +import javax.jms.Message; + +/** + * Converts Message from one JMS to another + * + * @org.xbean.XBean + * + * @version $Revision: 1.1.1.1 $ + */ +public class SimpleJmsMessageConvertor implements JmsMesageConvertor { + + /** + * Convert a foreign JMS Message to a native ActiveMQ Message + * @param message + * @return the converted message + * @throws JMSException + */ + public Message convert(Message message) throws JMSException{ + return message; + } + + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/network/jms/TopicBridge.java b/activemq-core/src/main/java/org/activemq/network/jms/TopicBridge.java new file mode 100755 index 0000000000..1b96202f41 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/jms/TopicBridge.java @@ -0,0 +1,183 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.network.jms; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.Topic; +import javax.jms.TopicConnection; +import javax.jms.TopicPublisher; +import javax.jms.TopicSession; +/** + * A Destination bridge is used to bridge between to different JMS systems + * + * @version $Revision: 1.1.1.1 $ + */ +class TopicBridge extends DestinationBridge{ + protected Topic consumerTopic; + protected Topic producerTopic; + protected TopicSession consumerSession; + protected TopicSession producerSession; + protected String consumerName; + protected String selector; + protected TopicPublisher producer; + protected TopicConnection consumerConnection; + protected TopicConnection producerConnection; + protected JmsTopicConnector jmsTopicConnector; + + public void stop() throws Exception{ + super.stop(); + if(consumerSession!=null){ + consumerSession.close(); + } + if(producerSession!=null){ + producerSession.close(); + } + } + + protected void setJmsTopicConnector(JmsTopicConnector connector){ + this.jmsTopicConnector = connector; + } + + protected MessageConsumer createConsumer() throws JMSException{ + // set up the consumer + consumerSession=consumerConnection.createTopicSession(false,Session.CLIENT_ACKNOWLEDGE); + producerSession=producerConnection.createTopicSession(false,Session.AUTO_ACKNOWLEDGE); + MessageConsumer consumer=null; + if(consumerName!=null&&consumerName.length()>0){ + if(selector!=null&&selector.length()>0){ + consumer=consumerSession.createDurableSubscriber(consumerTopic,consumerName,selector,false); + }else{ + consumer=consumerSession.createDurableSubscriber(consumerTopic,consumerName); + } + }else{ + if(selector!=null&&selector.length()>0){ + consumer=consumerSession.createSubscriber(consumerTopic,selector,false); + }else{ + consumer=consumerSession.createSubscriber(consumerTopic); + } + } + return consumer; + } + + protected Destination processReplyToDestination (Destination destination){ + Topic topic = (Topic)destination; + return jmsTopicConnector.createReplyToTopicBridge(topic, getConsumerConnection(), getProducerConnection()); + } + + protected MessageProducer createProducer() throws JMSException{ + producer = producerSession.createPublisher(null); + return producer; + } + + protected void sendMessage(Message message) throws JMSException{ + producer.publish(producerTopic,message); + } + + /** + * @return Returns the consumerConnection. + */ + public TopicConnection getConsumerConnection(){ + return consumerConnection; + } + + /** + * @param consumerConnection + * The consumerConnection to set. + */ + public void setConsumerConnection(TopicConnection consumerConnection){ + this.consumerConnection=consumerConnection; + } + + /** + * @return Returns the consumerName. + */ + public String getConsumerName(){ + return consumerName; + } + + /** + * @param consumerName + * The consumerName to set. + */ + public void setConsumerName(String consumerName){ + this.consumerName=consumerName; + } + + /** + * @return Returns the consumerTopic. + */ + public Topic getConsumerTopic(){ + return consumerTopic; + } + + /** + * @param consumerTopic + * The consumerTopic to set. + */ + public void setConsumerTopic(Topic consumerTopic){ + this.consumerTopic=consumerTopic; + } + + /** + * @return Returns the producerConnection. + */ + public TopicConnection getProducerConnection(){ + return producerConnection; + } + + /** + * @param producerConnection + * The producerConnection to set. + */ + public void setProducerConnection(TopicConnection producerConnection){ + this.producerConnection=producerConnection; + } + + /** + * @return Returns the producerTopic. + */ + public Topic getProducerTopic(){ + return producerTopic; + } + + /** + * @param producerTopic + * The producerTopic to set. + */ + public void setProducerTopic(Topic producerTopic){ + this.producerTopic=producerTopic; + } + + /** + * @return Returns the selector. + */ + public String getSelector(){ + return selector; + } + + /** + * @param selector + * The selector to set. + */ + public void setSelector(String selector){ + this.selector=selector; + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/network/jms/package.html b/activemq-core/src/main/java/org/activemq/network/jms/package.html new file mode 100755 index 0000000000..73e1a87436 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/jms/package.html @@ -0,0 +1,9 @@ + + + + + +Support for a federated network using a foreign JMS provider. + + + diff --git a/activemq-core/src/main/java/org/activemq/network/package.html b/activemq-core/src/main/java/org/activemq/network/package.html new file mode 100755 index 0000000000..5b5dc0754a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/network/package.html @@ -0,0 +1,8 @@ + + + + + +Support for federated networks of brokers. + + diff --git a/activemq-core/src/main/java/org/activemq/openwire/BooleanStream.java b/activemq-core/src/main/java/org/activemq/openwire/BooleanStream.java new file mode 100755 index 0000000000..67db5a1c85 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/BooleanStream.java @@ -0,0 +1,108 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.openwire; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +final public class BooleanStream { + + byte data[] = new byte[48]; + short arrayLimit; + short arrayPos; + byte bytePos; + + public boolean readBoolean() throws IOException { + assert arrayPos <= arrayLimit; + byte b = data[arrayPos]; + boolean rc = ((b>>bytePos)&0x01)!=0; + bytePos++; + if( bytePos >= 8 ) { + bytePos=0; + arrayPos++; + } + return rc; + } + + public void writeBoolean(boolean value) throws IOException { + if( bytePos == 0 ) { + arrayLimit++; + if( arrayLimit >= data.length ) { + // re-grow the array. + byte d[] = new byte[data.length*2]; + System.arraycopy(data, 0, d, 0, data.length); + data = d; + } + } + if( value ) { + data[arrayPos] |= (0x01 << bytePos); + } + bytePos++; + if( bytePos >= 8 ) { + bytePos=0; + arrayPos++; + } + } + + public void marshal(DataOutputStream dataOut) throws IOException { + if( arrayLimit < 64 ) { + dataOut.writeByte(arrayLimit); + } else if( arrayLimit < 256 ) { // max value of unsigned byte + dataOut.writeByte(0xC0); + dataOut.writeByte(arrayLimit); + } else { + dataOut.writeByte(0xE0); + dataOut.writeShort(arrayLimit); + } + + dataOut.write(data, 0, arrayLimit); + clear(); + } + + public void unmarshal(DataInputStream dataIn) throws IOException { + + arrayLimit = dataIn.readByte(); + if( (arrayLimit & 0xE0)!=0 ) { + arrayLimit = dataIn.readShort(); + } else if ( (arrayLimit & 0xC0)!=0 ) { + arrayLimit = (short)(dataIn.readByte() & 0xFF); + } + if( data.length < arrayLimit ) { + data = new byte[arrayLimit]; + } + dataIn.readFully(data, 0, arrayLimit); + clear(); + } + + public void clear() { + arrayPos=0; + bytePos=0; + } + + public int marshalledSize() { + if( arrayLimit < 64 ) { + return 1+arrayLimit; + } else { + return 2+arrayLimit; + } + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/DataStreamMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/DataStreamMarshaller.java new file mode 100755 index 0000000000..f1bdf7b1db --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/DataStreamMarshaller.java @@ -0,0 +1,338 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.openwire; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Constructor; + +import org.activeio.command.ClassLoading; +import org.activemq.command.DataStructure; + +abstract public class DataStreamMarshaller { + + static final public Constructor STACK_TRACE_ELEMENT_CONSTRUCTOR; + + static { + Constructor constructor=null; + try { + constructor = StackTraceElement.class.getConstructor(new Class[]{String.class, String.class, String.class, int.class}); + } catch (Throwable e) { + } + STACK_TRACE_ELEMENT_CONSTRUCTOR = constructor; + } + + + abstract public byte getDataStructureType(); + abstract public DataStructure createObject(); + + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + return 0; + } + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + } + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + } + + public int marshal1Long(OpenWireFormat wireFormat, long o, BooleanStream bs) throws IOException { + if( o == 0 ) { + bs.writeBoolean(false); + bs.writeBoolean(false); + return 0; + } else if ( (o & 0xFFFFFFFFFFFF0000l ) == 0 ) { + bs.writeBoolean(false); + bs.writeBoolean(true); + return 2; + } else if ( (o & 0xFFFFFFFF00000000l ) == 0) { + bs.writeBoolean(true); + bs.writeBoolean(false); + return 4; + } else { + bs.writeBoolean(true); + bs.writeBoolean(true); + return 8; + } + } + public void marshal2Long(OpenWireFormat wireFormat, long o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + if( bs.readBoolean() ) { + if( bs.readBoolean() ) { + dataOut.writeLong(o); + } else { + dataOut.writeInt((int) o); + } + } else { + if( bs.readBoolean() ) { + dataOut.writeShort((int) o); + } + } + } + public long unmarshalLong(OpenWireFormat wireFormat, DataInputStream dataIn, BooleanStream bs) throws IOException { + if( bs.readBoolean() ) { + if( bs.readBoolean() ) { + return dataIn.readLong(); + } else { + return dataIn.readInt(); + } + } else { + if( bs.readBoolean() ) { + return dataIn.readShort(); + } else { + return 0; + } + } + } + + protected DataStructure unmarsalNestedObject(OpenWireFormat wireFormat, DataInputStream dataIn, BooleanStream bs) throws IOException { + return wireFormat.unmarshalNestedObject(dataIn, bs); + } + protected int marshal1NestedObject(OpenWireFormat wireFormat, DataStructure o, BooleanStream bs) throws IOException { + return wireFormat.marshal1NestedObject(o, bs); + } + + protected void marshal2NestedObject(OpenWireFormat wireFormat, DataStructure o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + wireFormat.marshal2NestedObject(o, dataOut, bs); + } + + protected DataStructure unmarsalCachedObject(OpenWireFormat wireFormat, DataInputStream dataIn, BooleanStream bs) throws IOException { + if( wireFormat.isCacheEnabled() ) { + if( bs.readBoolean() ) { + short index = dataIn.readShort(); + DataStructure object = wireFormat.unmarshalNestedObject(dataIn, bs); + wireFormat.setInUnmarshallCache(index, object); + return object; + } else { + short index = dataIn.readShort(); + return wireFormat.getFromUnmarshallCache(index); + } + } else { + return wireFormat.unmarshalNestedObject(dataIn, bs); + } + } + protected int marshal1CachedObject(OpenWireFormat wireFormat, DataStructure o, BooleanStream bs) throws IOException { + if( wireFormat.isCacheEnabled() ) { + Short index = wireFormat.getMarshallCacheIndex(o); + bs.writeBoolean(index == null); + if( index == null ) { + int rc = wireFormat.marshal1NestedObject(o, bs); + wireFormat.addToMarshallCache(o); + return 2+rc; + } else { + return 2; + } + } else { + return wireFormat.marshal1NestedObject(o, bs); + } + } + protected void marshal2CachedObject(OpenWireFormat wireFormat, DataStructure o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + if( wireFormat.isCacheEnabled() ) { + Short index = wireFormat.getMarshallCacheIndex(o); + if( bs.readBoolean() ) { + dataOut.writeShort(index.shortValue()); + wireFormat.marshal2NestedObject(o, dataOut, bs); + } else { + dataOut.writeShort(index.shortValue()); + } + } else { + wireFormat.marshal2NestedObject(o, dataOut, bs); + } + } + + protected Throwable unmarsalThrowable(OpenWireFormat wireFormat, DataInputStream dataIn, BooleanStream bs) throws IOException { + if( bs.readBoolean() ) { + String clazz = readString(dataIn, bs); + String message = readString(dataIn, bs); + Throwable o = createThrowable(clazz, message); + if( wireFormat.isStackTraceEnabled() ) { + if( STACK_TRACE_ELEMENT_CONSTRUCTOR!=null) { + StackTraceElement ss[] = new StackTraceElement[dataIn.readShort()]; + for (int i = 0; i < ss.length; i++) { + try { + ss[i] = (StackTraceElement) STACK_TRACE_ELEMENT_CONSTRUCTOR.newInstance(new Object[]{ + readString(dataIn, bs), + readString(dataIn, bs), + readString(dataIn, bs), + new Integer(dataIn.readInt()) + }); + } catch (IOException e) { + throw e; + } catch (Throwable e) { + } + } + o.setStackTrace(ss); + } else { + int size = dataIn.readInt(); + for (int i = 0; i < size; i++) { + readString(dataIn, bs); + readString(dataIn, bs); + readString(dataIn, bs); + dataIn.readInt(); + } + } + o.initCause(unmarsalThrowable(wireFormat, dataIn, bs)); + + } + return o; + } else { + return null; + } + } + + private Throwable createThrowable(String className, String message) { + try { + Class clazz = ClassLoading.loadClass(className, DataStreamMarshaller.class.getClassLoader()); + Constructor constructor = clazz.getConstructor(new Class[]{String.class}); + return (Throwable) constructor.newInstance(new Object[]{message}); + } catch (Throwable e) { + return new Throwable(className+": "+message); + } + } + + protected int marshalThrowable(OpenWireFormat wireFormat, Throwable o, BooleanStream bs) throws IOException { + if( o==null ) { + bs.writeBoolean(false); + return 0; + } else { + int rc=0; + bs.writeBoolean(true); + rc += writeString(o.getClass().getName(), bs); + rc += writeString(o.getMessage(), bs); + if( wireFormat.isStackTraceEnabled() ) { + rc += 2; + StackTraceElement[] stackTrace = o.getStackTrace(); + for (int i = 0; i < stackTrace.length; i++) { + StackTraceElement element = stackTrace[i]; + rc += writeString(element.getClassName(), bs); + rc += writeString(element.getMethodName(), bs); + rc += writeString(element.getFileName(), bs); + rc += 4; + } + rc += marshalThrowable(wireFormat, o.getCause(), bs); + } + return rc; + } + } + + protected void marshalThrowable(OpenWireFormat wireFormat, Throwable o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + if( bs.readBoolean() ) { + writeString(o.getClass().getName(), dataOut, bs); + writeString(o.getMessage(), dataOut, bs); + if( wireFormat.isStackTraceEnabled() ) { + StackTraceElement[] stackTrace = o.getStackTrace(); + dataOut.writeShort(stackTrace.length); + for (int i = 0; i < stackTrace.length; i++) { + StackTraceElement element = stackTrace[i]; + writeString(element.getClassName(), dataOut, bs); + writeString(element.getMethodName(), dataOut, bs); + writeString(element.getFileName(), dataOut, bs); + dataOut.writeInt(element.getLineNumber()); + } + marshalThrowable(wireFormat, o.getCause(), dataOut, bs); + } + } + } + + protected String readString(DataInputStream dataIn, BooleanStream bs) throws IOException { + if( bs.readBoolean() ) { + if( bs.readBoolean() ) { + int size = dataIn.readShort(); + byte data[] = new byte[size]; + dataIn.readFully(data); + return new String(data,0); // Yes deprecated, but we know what we are doing. + } else { + return dataIn.readUTF(); + } + } else { + return null; + } + } + + protected int writeString(String value, BooleanStream bs) throws IOException { + bs.writeBoolean(value!=null); + if( value!=null ) { + + int strlen = value.length(); + int utflen = 0; + char[] charr = new char[strlen]; + int c, count = 0; + boolean isOnlyAscii=true; + + value.getChars(0, strlen, charr, 0); + + for (int i = 0; i < strlen; i++) { + c = charr[i]; + if ((c >= 0x0001) && (c <= 0x007F)) { + utflen++; + } else if (c > 0x07FF) { + utflen += 3; + isOnlyAscii=false; + } else { + isOnlyAscii=false; + utflen += 2; + } + } + + if( utflen >= Short.MAX_VALUE ) + throw new IOException("Encountered a String value that is too long to encode."); + + bs.writeBoolean(isOnlyAscii); + return utflen+2; + + } else { + return 0; + } + } + + protected void writeString(String value, DataOutputStream dataOut, BooleanStream bs) throws IOException { + if( bs.readBoolean() ) { + // If we verified it only holds ascii values + if( bs.readBoolean() ) { + dataOut.writeShort(value.length()); + dataOut.writeBytes(value); + } else { + dataOut.writeUTF(value); + } + } + } + + protected int marshalObjectArray(OpenWireFormat wireFormat, DataStructure[] objects, BooleanStream bs) throws IOException { + if( objects != null ) { + int rc=0; + bs.writeBoolean(true); + rc += 2; + for( int i=0; i < objects.length; i++ ) { + rc += marshal1NestedObject(wireFormat,objects[i], bs); + } + return rc; + } else { + bs.writeBoolean(false); + return 0; + } + } + + protected void marshalObjectArray(OpenWireFormat wireFormat, DataStructure[] objects, DataOutputStream dataOut, BooleanStream bs) throws IOException { + if( bs.readBoolean() ) { + dataOut.writeShort(objects.length); + for( int i=0; i < objects.length; i++ ) { + marshal2NestedObject(wireFormat,objects[i], dataOut, bs); + } + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/OpenWireFormat.java b/activemq-core/src/main/java/org/activemq/openwire/OpenWireFormat.java new file mode 100755 index 0000000000..1e900c9be9 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/OpenWireFormat.java @@ -0,0 +1,350 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.openwire; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.HashMap; + +import org.activeio.ByteArrayOutputStream; +import org.activeio.ByteSequence; +import org.activeio.Packet; +import org.activeio.adapter.PacketToInputStream; +import org.activeio.command.ClassLoading; +import org.activeio.command.WireFormat; +import org.activeio.packet.ByteArrayPacket; +import org.activemq.command.CommandTypes; +import org.activemq.command.DataStructure; +import org.activemq.command.MarshallAware; + +/** + * + * @version $Revision$ + */ +final public class OpenWireFormat implements WireFormat { + + static final byte NULL_TYPE = CommandTypes.NULL; + private static final int MARSHAL_CACHE_SIZE = Short.MAX_VALUE/2; + private DataStreamMarshaller dataMarshallers[]; + private int version; + private boolean stackTraceEnabled=true; + private boolean tcpNoDelayEnabled=false; + private boolean cacheEnabled=true; + + private HashMap marshallCacheMap = new HashMap(); + private short nextMarshallCacheIndex=0; + private short lasMarshallCacheEvictionIndex=100; + private DataStructure marshallCache[] = new DataStructure[MARSHAL_CACHE_SIZE]; + private DataStructure unmarshallCache[] = new DataStructure[MARSHAL_CACHE_SIZE]; + + public OpenWireFormat() { + this(true); + } + + public OpenWireFormat(boolean cacheEnabled) { + setVersion(1); + setCacheEnabled(cacheEnabled); + } + + public int hashCode() { + return version + ^ (cacheEnabled?0x10000000:0x20000000) + ^ (stackTraceEnabled?0x30000000:0x40000000); + } + + public boolean equals(Object object) { + if( object == null ) + return false; + OpenWireFormat o = (OpenWireFormat) object; + return o.stackTraceEnabled == stackTraceEnabled && + o.cacheEnabled == cacheEnabled && + o.version == version; + } + + public String toString() { + return "OpenWireFormat{version="+version+", cacheEnabled="+cacheEnabled+", stackTraceEnabled="+stackTraceEnabled+"}"; + } + + public int getVersion() { + return version; + } + + public Packet marshal(Object command) throws IOException { + + MarshallAware ma=null; + // If not using value caching, then the marshaled form is always the same + if( !cacheEnabled && ((DataStructure)command).isMarshallAware() ) { + ma = (MarshallAware) command; + } + + ByteSequence sequence=null; + if( ma!=null ) { + sequence = ma.getCachedMarshalledForm(this); + } + + if( sequence == null ) { + + int size=1; + if( command != null) { + + DataStructure c = (DataStructure) command; + byte type = c.getDataStructureType(); + DataStreamMarshaller dsm = (DataStreamMarshaller) dataMarshallers[type & 0xFF]; + if( dsm == null ) + throw new IOException("Unknown data type: "+type); + BooleanStream bs = new BooleanStream(); + size += dsm.marshal1(this, c, bs); + size += bs.marshalledSize(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(size); + DataOutputStream ds = new DataOutputStream(baos); + ds.writeInt(size); + ds.writeByte(type); + bs.marshal(ds); + dsm.marshal2(this, c, ds, bs); + ds.close(); + + sequence = baos.toByteSequence(); + + } else { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(5); + DataOutputStream daos = new DataOutputStream(baos); + daos.writeInt(size); + daos.writeByte(NULL_TYPE); + daos.close(); + sequence = baos.toByteSequence(); + } + + if( ma!=null ) { + ma.setCachedMarshalledForm(this, sequence); + } + } + return new ByteArrayPacket(sequence); + } + + public Object unmarshal(Packet packet) throws IOException { + ByteSequence sequence = packet.asByteSequence(); + DataInputStream dis = new DataInputStream(new PacketToInputStream(packet)); + int size = dis.readInt(); + if( sequence.getLength() != size+4 ) + System.out.println("Packet size does not match marshaled size: "+size+", "+(sequence.getLength()-4)); +// throw new IOException("Packet size does not match marshaled size"); + Object command = doUnmarshal(dis); + if( !cacheEnabled && ((DataStructure)command).isMarshallAware() ) { + ((MarshallAware) command).setCachedMarshalledForm(this, sequence); + } + return command; + } + + public void marshal(Object o, DataOutputStream ds) throws IOException { + int size=1; + if( o != null) { + DataStructure c = (DataStructure) o; + byte type = c.getDataStructureType(); + DataStreamMarshaller dsm = (DataStreamMarshaller) dataMarshallers[type & 0xFF]; + if( dsm == null ) + throw new IOException("Unknown data type: "+type); + + BooleanStream bs = new BooleanStream(); + size += dsm.marshal1(this, c, bs); + size += bs.marshalledSize(); + + ds.writeInt(size); + ds.writeByte(type); + bs.marshal(ds); + dsm.marshal2(this, c, ds, bs); + } else { + ds.writeInt(size); + ds.writeByte(NULL_TYPE); + } + } + + public Object unmarshal(DataInputStream dis) throws IOException { + dis.readInt(); + return doUnmarshal(dis); + } + + /** + * Allows you to dynamically switch the version of the openwire protocol being used. + * @param version + */ + public void setVersion(int version) { + String mfName = "org.activemq.openwire.v"+version+".MarshallerFactory"; + Class mfClass; + try { + mfClass = ClassLoading.loadClass(mfName, getClass().getClassLoader()); + } catch (ClassNotFoundException e) { + throw (IllegalArgumentException)new IllegalArgumentException("Invalid version: "+version+", could not load "+mfName).initCause(e); + } + try { + Method method = mfClass.getMethod("createMarshallerMap", new Class[]{OpenWireFormat.class}); + dataMarshallers = (DataStreamMarshaller[]) method.invoke(null, new Object[]{this}); + } catch (Throwable e) { + throw (IllegalArgumentException)new IllegalArgumentException("Invalid version: "+version+", "+mfName+" does not properly implement the createMarshallerMap method.").initCause(e); + } + this.version = version; + } + + public Object doUnmarshal(DataInputStream dis) throws IOException { + byte dataType = dis.readByte(); + if( dataType!=NULL_TYPE ) { + DataStreamMarshaller dsm = (DataStreamMarshaller) dataMarshallers[dataType & 0xFF]; + if( dsm == null ) + throw new IOException("Unknown data type: "+dataType); + Object data = dsm.createObject(); + BooleanStream bs = new BooleanStream(); + bs.unmarshal(dis); + dsm.unmarshal(this, data, dis, bs); + return data; + } else { + return null; + } + } + + public int marshal1NestedObject(DataStructure o, BooleanStream bs) throws IOException { + bs.writeBoolean(o != null); + if( o == null ) + return 0; + + if( o.isMarshallAware() ) { + MarshallAware ma = (MarshallAware) o; + ByteSequence sequence=ma.getCachedMarshalledForm(this); + bs.writeBoolean(sequence!=null); + if( sequence!=null ) { + return 1 + sequence.getLength(); + } + } + + byte type = o.getDataStructureType(); + DataStreamMarshaller dsm = (DataStreamMarshaller) dataMarshallers[type & 0xFF]; + if( dsm == null ) + throw new IOException("Unknown data type: "+type); + return 1 + dsm.marshal1(this, o, bs); + } + + public void marshal2NestedObject(DataStructure o, DataOutputStream ds, BooleanStream bs) throws IOException { + if( !bs.readBoolean() ) + return; + + byte type = o.getDataStructureType(); + ds.writeByte(type); + + if( o.isMarshallAware() && bs.readBoolean() ) { + + MarshallAware ma = (MarshallAware) o; + ByteSequence sequence=ma.getCachedMarshalledForm(this); + ds.write(sequence.getData(), sequence.getOffset(), sequence.getLength()); + + } else { + + DataStreamMarshaller dsm = (DataStreamMarshaller) dataMarshallers[type & 0xFF]; + if( dsm == null ) + throw new IOException("Unknown data type: "+type); + dsm.marshal2(this, o, ds, bs); + + } + } + + public DataStructure unmarshalNestedObject(DataInputStream dis, BooleanStream bs) throws IOException { + if( bs.readBoolean() ) { + + byte dataType = dis.readByte(); + DataStreamMarshaller dsm = (DataStreamMarshaller) dataMarshallers[dataType & 0xFF]; + if( dsm == null ) + throw new IOException("Unknown data type: "+dataType); + DataStructure data = dsm.createObject(); + + if( data.isMarshallAware() && bs.readBoolean() ) { + + dis.readInt(); + dis.readByte(); + + BooleanStream bs2 = new BooleanStream(); + bs2.unmarshal(dis); + dsm.unmarshal(this, data, dis, bs2); + + // TODO: extract the sequence from the dis and associate it. +// MarshallAware ma = (MarshallAware)data +// ma.setCachedMarshalledForm(this, sequence); + + } else { + dsm.unmarshal(this, data, dis, bs); + } + + return data; + } else { + return null; + } + } + + public Short getMarshallCacheIndex(Object o) { + return (Short) marshallCacheMap.get(o); + } + + public Short addToMarshallCache(Object o) { + Short index = new Short(nextMarshallCacheIndex++); + if( nextMarshallCacheIndex >= MARSHAL_CACHE_SIZE ) { + nextMarshallCacheIndex=0; + } + lasMarshallCacheEvictionIndex++; + if( lasMarshallCacheEvictionIndex >= MARSHAL_CACHE_SIZE ) { + lasMarshallCacheEvictionIndex=0; + } + if( marshallCache[lasMarshallCacheEvictionIndex]!=null ) { + marshallCacheMap.remove(marshallCache[lasMarshallCacheEvictionIndex]); + marshallCache[lasMarshallCacheEvictionIndex]=null; + } + marshallCacheMap.put(o, index); + return index; + } + + public void setInUnmarshallCache(short index, DataStructure o) { + unmarshallCache[index]=o; + } + + public DataStructure getFromUnmarshallCache(short index) { + return unmarshallCache[index]; + } + + + public void setStackTraceEnabled(boolean b) { + stackTraceEnabled = b; + } + public boolean isStackTraceEnabled() { + return stackTraceEnabled; + } + + public boolean isTcpNoDelayEnabled() { + return tcpNoDelayEnabled; + } + public void setTcpNoDelayEnabled(boolean tcpNoDelayEnabled) { + this.tcpNoDelayEnabled = tcpNoDelayEnabled; + } + + public boolean isCacheEnabled() { + return cacheEnabled; + } + public void setCacheEnabled(boolean cacheEnabled) { + this.cacheEnabled = cacheEnabled; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/OpenWireFormatFactory.java b/activemq-core/src/main/java/org/activemq/openwire/OpenWireFormatFactory.java new file mode 100755 index 0000000000..75206a091c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/OpenWireFormatFactory.java @@ -0,0 +1,74 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.openwire; + +import org.activeio.command.WireFormat; +import org.activeio.command.WireFormatFactory; + +/** + * @version $Revision$ + */ +public class OpenWireFormatFactory implements WireFormatFactory { + + private int version=1; + private boolean stackTraceEnabled=true; + private boolean tcpNoDelayEnabled=false; + private boolean cacheEnabled=true; + + public WireFormat createWireFormat() { + OpenWireFormat format = new OpenWireFormat(); + format.setVersion(version); + format.setStackTraceEnabled(stackTraceEnabled); + format.setCacheEnabled(cacheEnabled); + format.setTcpNoDelayEnabled(tcpNoDelayEnabled); + return format; + } + + public boolean isStackTraceEnabled() { + return stackTraceEnabled; + } + + public void setStackTraceEnabled(boolean stackTraceEnabled) { + this.stackTraceEnabled = stackTraceEnabled; + } + + public boolean isTcpNoDelayEnabled() { + return tcpNoDelayEnabled; + } + + public void setTcpNoDelayEnabled(boolean tcpNoDelayEnabled) { + this.tcpNoDelayEnabled = tcpNoDelayEnabled; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public boolean isCacheEnabled() { + return cacheEnabled; + } + + public void setCacheEnabled(boolean cacheEnabled) { + this.cacheEnabled = cacheEnabled; + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQBytesMessageMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQBytesMessageMarshaller.java new file mode 100755 index 0000000000..d9a7b7706e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQBytesMessageMarshaller.java @@ -0,0 +1,92 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ActiveMQBytesMessage + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ActiveMQBytesMessageMarshaller extends ActiveMQMessageMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ActiveMQBytesMessage.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ActiveMQBytesMessage(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQDestinationMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQDestinationMarshaller.java new file mode 100755 index 0000000000..ad7ec1c817 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQDestinationMarshaller.java @@ -0,0 +1,86 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ActiveMQDestination + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public abstract class ActiveMQDestinationMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + ActiveMQDestination info = (ActiveMQDestination)o; + info.setPhysicalName(readString(dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + ActiveMQDestination info = (ActiveMQDestination)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += writeString(info.getPhysicalName(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + ActiveMQDestination info = (ActiveMQDestination)o; + writeString(info.getPhysicalName(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQMapMessageMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQMapMessageMarshaller.java new file mode 100755 index 0000000000..27b92f6679 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQMapMessageMarshaller.java @@ -0,0 +1,92 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ActiveMQMapMessage + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ActiveMQMapMessageMarshaller extends ActiveMQMessageMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ActiveMQMapMessage.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ActiveMQMapMessage(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQMessageMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQMessageMarshaller.java new file mode 100755 index 0000000000..4eb50c3d7c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQMessageMarshaller.java @@ -0,0 +1,92 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ActiveMQMessage + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ActiveMQMessageMarshaller extends MessageMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ActiveMQMessage.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ActiveMQMessage(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQObjectMessageMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQObjectMessageMarshaller.java new file mode 100755 index 0000000000..f9e5e5756e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQObjectMessageMarshaller.java @@ -0,0 +1,92 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ActiveMQObjectMessage + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ActiveMQObjectMessageMarshaller extends ActiveMQMessageMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ActiveMQObjectMessage.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ActiveMQObjectMessage(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQQueueMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQQueueMarshaller.java new file mode 100755 index 0000000000..f6cabdcb73 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQQueueMarshaller.java @@ -0,0 +1,92 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ActiveMQQueue + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ActiveMQQueueMarshaller extends ActiveMQDestinationMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ActiveMQQueue.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ActiveMQQueue(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQStreamMessageMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQStreamMessageMarshaller.java new file mode 100755 index 0000000000..4933d12554 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQStreamMessageMarshaller.java @@ -0,0 +1,92 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ActiveMQStreamMessage + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ActiveMQStreamMessageMarshaller extends ActiveMQMessageMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ActiveMQStreamMessage.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ActiveMQStreamMessage(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTempDestinationMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTempDestinationMarshaller.java new file mode 100755 index 0000000000..23d2825bc7 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTempDestinationMarshaller.java @@ -0,0 +1,77 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ActiveMQTempDestination + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public abstract class ActiveMQTempDestinationMarshaller extends ActiveMQDestinationMarshaller { + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTempQueueMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTempQueueMarshaller.java new file mode 100755 index 0000000000..9dd53b2f95 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTempQueueMarshaller.java @@ -0,0 +1,92 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ActiveMQTempQueue + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ActiveMQTempQueueMarshaller extends ActiveMQTempDestinationMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ActiveMQTempQueue.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ActiveMQTempQueue(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTempTopicMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTempTopicMarshaller.java new file mode 100755 index 0000000000..4b77ee9f4f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTempTopicMarshaller.java @@ -0,0 +1,92 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ActiveMQTempTopic + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ActiveMQTempTopicMarshaller extends ActiveMQTempDestinationMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ActiveMQTempTopic.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ActiveMQTempTopic(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTextMessageMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTextMessageMarshaller.java new file mode 100755 index 0000000000..f3153784ee --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTextMessageMarshaller.java @@ -0,0 +1,92 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ActiveMQTextMessage + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ActiveMQTextMessageMarshaller extends ActiveMQMessageMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ActiveMQTextMessage.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ActiveMQTextMessage(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTopicMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTopicMarshaller.java new file mode 100755 index 0000000000..0cf7724315 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ActiveMQTopicMarshaller.java @@ -0,0 +1,92 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ActiveMQTopic + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ActiveMQTopicMarshaller extends ActiveMQDestinationMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ActiveMQTopic.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ActiveMQTopic(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/BaseCommandMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/BaseCommandMarshaller.java new file mode 100755 index 0000000000..75844d3d01 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/BaseCommandMarshaller.java @@ -0,0 +1,89 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for BaseCommand + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public abstract class BaseCommandMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + BaseCommand info = (BaseCommand)o; + info.setCommandId(dataIn.readShort()); + info.setResponseRequired(bs.readBoolean()); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + BaseCommand info = (BaseCommand)o; + + int rc = super.marshal1(wireFormat, o, bs); + + bs.writeBoolean(info.isResponseRequired()); + + return rc+2; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + BaseCommand info = (BaseCommand)o; + dataOut.writeShort(info.getCommandId()); + bs.readBoolean(); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/BrokerIdMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/BrokerIdMarshaller.java new file mode 100755 index 0000000000..f8c3e521b6 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/BrokerIdMarshaller.java @@ -0,0 +1,101 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for BrokerId + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class BrokerIdMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return BrokerId.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new BrokerId(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + BrokerId info = (BrokerId)o; + info.setBrokerId(readString(dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + BrokerId info = (BrokerId)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += writeString(info.getBrokerId(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + BrokerId info = (BrokerId)o; + writeString(info.getBrokerId(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/BrokerInfoMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/BrokerInfoMarshaller.java new file mode 100755 index 0000000000..3f3aa27c82 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/BrokerInfoMarshaller.java @@ -0,0 +1,124 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for BrokerInfo + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class BrokerInfoMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return BrokerInfo.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new BrokerInfo(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + BrokerInfo info = (BrokerInfo)o; + info.setBrokerId((org.activemq.command.BrokerId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setBrokerURL(readString(dataIn, bs)); + + if( bs.readBoolean() ) { + short size = dataIn.readShort(); + org.activemq.command.BrokerInfo value[] = new org.activemq.command.BrokerInfo[size]; + for( int i=0; i < size; i++ ) { + value[i] = (org.activemq.command.BrokerInfo)unmarsalNestedObject(wireFormat,dataIn, bs); + } + info.setPeerBrokerInfos(value); + } else { + info.setPeerBrokerInfos(null); + } + + info.setRedeliveryPolicy((org.activemq.command.RedeliveryPolicy) unmarsalNestedObject(wireFormat, dataIn, bs)); + info.setBrokerName(readString(dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + BrokerInfo info = (BrokerInfo)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1CachedObject(wireFormat, info.getBrokerId(), bs); + rc += writeString(info.getBrokerURL(), bs); + rc += marshalObjectArray(wireFormat, info.getPeerBrokerInfos(), bs); + rc += marshal1NestedObject(wireFormat, info.getRedeliveryPolicy(), bs); + rc += writeString(info.getBrokerName(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + BrokerInfo info = (BrokerInfo)o; + marshal2CachedObject(wireFormat, info.getBrokerId(), dataOut, bs); + writeString(info.getBrokerURL(), dataOut, bs); + marshalObjectArray(wireFormat, info.getPeerBrokerInfos(), dataOut, bs); + marshal2NestedObject(wireFormat, info.getRedeliveryPolicy(), dataOut, bs); + writeString(info.getBrokerName(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ConnectionIdMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ConnectionIdMarshaller.java new file mode 100755 index 0000000000..186e892a26 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ConnectionIdMarshaller.java @@ -0,0 +1,101 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ConnectionId + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ConnectionIdMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ConnectionId.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ConnectionId(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + ConnectionId info = (ConnectionId)o; + info.setConnectionId(readString(dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + ConnectionId info = (ConnectionId)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += writeString(info.getConnectionId(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + ConnectionId info = (ConnectionId)o; + writeString(info.getConnectionId(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ConnectionInfoMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ConnectionInfoMarshaller.java new file mode 100755 index 0000000000..099ebef923 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ConnectionInfoMarshaller.java @@ -0,0 +1,124 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ConnectionInfo + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ConnectionInfoMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ConnectionInfo.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ConnectionInfo(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + ConnectionInfo info = (ConnectionInfo)o; + info.setConnectionId((org.activemq.command.ConnectionId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setClientId(readString(dataIn, bs)); + info.setPassword(readString(dataIn, bs)); + info.setUserName(readString(dataIn, bs)); + + if( bs.readBoolean() ) { + short size = dataIn.readShort(); + org.activemq.command.BrokerId value[] = new org.activemq.command.BrokerId[size]; + for( int i=0; i < size; i++ ) { + value[i] = (org.activemq.command.BrokerId)unmarsalNestedObject(wireFormat,dataIn, bs); + } + info.setBrokerPath(value); + } else { + info.setBrokerPath(null); + } + + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + ConnectionInfo info = (ConnectionInfo)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1CachedObject(wireFormat, info.getConnectionId(), bs); + rc += writeString(info.getClientId(), bs); + rc += writeString(info.getPassword(), bs); + rc += writeString(info.getUserName(), bs); + rc += marshalObjectArray(wireFormat, info.getBrokerPath(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + ConnectionInfo info = (ConnectionInfo)o; + marshal2CachedObject(wireFormat, info.getConnectionId(), dataOut, bs); + writeString(info.getClientId(), dataOut, bs); + writeString(info.getPassword(), dataOut, bs); + writeString(info.getUserName(), dataOut, bs); + marshalObjectArray(wireFormat, info.getBrokerPath(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ConsumerIdMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ConsumerIdMarshaller.java new file mode 100755 index 0000000000..c9303a66a5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ConsumerIdMarshaller.java @@ -0,0 +1,107 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ConsumerId + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ConsumerIdMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ConsumerId.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ConsumerId(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + ConsumerId info = (ConsumerId)o; + info.setConnectionId(readString(dataIn, bs)); + info.setSessionId(unmarshalLong(wireFormat, dataIn, bs)); + info.setConsumerId(unmarshalLong(wireFormat, dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + ConsumerId info = (ConsumerId)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += writeString(info.getConnectionId(), bs); + rc+=marshal1Long(wireFormat, info.getSessionId(), bs); + rc+=marshal1Long(wireFormat, info.getConsumerId(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + ConsumerId info = (ConsumerId)o; + writeString(info.getConnectionId(), dataOut, bs); + marshal2Long(wireFormat, info.getSessionId(), dataOut, bs); + marshal2Long(wireFormat, info.getConsumerId(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ConsumerInfoMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ConsumerInfoMarshaller.java new file mode 100755 index 0000000000..f961064552 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ConsumerInfoMarshaller.java @@ -0,0 +1,145 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ConsumerInfo + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ConsumerInfoMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ConsumerInfo.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ConsumerInfo(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + ConsumerInfo info = (ConsumerInfo)o; + info.setConsumerId((org.activemq.command.ConsumerId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setBrowser(bs.readBoolean()); + info.setDestination((org.activemq.command.ActiveMQDestination) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setPrefetchSize(dataIn.readInt()); + info.setDispatchAsync(bs.readBoolean()); + info.setSelector(readString(dataIn, bs)); + info.setSubcriptionName(readString(dataIn, bs)); + info.setNoLocal(bs.readBoolean()); + info.setExclusive(bs.readBoolean()); + info.setRetroactive(bs.readBoolean()); + info.setPriority(dataIn.readByte()); + + if( bs.readBoolean() ) { + short size = dataIn.readShort(); + org.activemq.command.BrokerId value[] = new org.activemq.command.BrokerId[size]; + for( int i=0; i < size; i++ ) { + value[i] = (org.activemq.command.BrokerId)unmarsalNestedObject(wireFormat,dataIn, bs); + } + info.setBrokerPath(value); + } else { + info.setBrokerPath(null); + } + + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + ConsumerInfo info = (ConsumerInfo)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1CachedObject(wireFormat, info.getConsumerId(), bs); + bs.writeBoolean(info.isBrowser()); + rc += marshal1CachedObject(wireFormat, info.getDestination(), bs); + + bs.writeBoolean(info.isDispatchAsync()); + rc += writeString(info.getSelector(), bs); + rc += writeString(info.getSubcriptionName(), bs); + bs.writeBoolean(info.isNoLocal()); + bs.writeBoolean(info.isExclusive()); + bs.writeBoolean(info.isRetroactive()); + + rc += marshalObjectArray(wireFormat, info.getBrokerPath(), bs); + + return rc+5; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + ConsumerInfo info = (ConsumerInfo)o; + marshal2CachedObject(wireFormat, info.getConsumerId(), dataOut, bs); + bs.readBoolean(); + marshal2CachedObject(wireFormat, info.getDestination(), dataOut, bs); + dataOut.writeInt(info.getPrefetchSize()); + bs.readBoolean(); + writeString(info.getSelector(), dataOut, bs); + writeString(info.getSubcriptionName(), dataOut, bs); + bs.readBoolean(); + bs.readBoolean(); + bs.readBoolean(); + dataOut.writeByte(info.getPriority()); + marshalObjectArray(wireFormat, info.getBrokerPath(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ControlCommandMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ControlCommandMarshaller.java new file mode 100644 index 0000000000..d3d9825a11 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ControlCommandMarshaller.java @@ -0,0 +1,101 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ControlCommand + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ControlCommandMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ControlCommand.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ControlCommand(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + ControlCommand info = (ControlCommand)o; + info.setCommand(readString(dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + ControlCommand info = (ControlCommand)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += writeString(info.getCommand(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + ControlCommand info = (ControlCommand)o; + writeString(info.getCommand(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/DataArrayResponseMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/DataArrayResponseMarshaller.java new file mode 100755 index 0000000000..6c911f475f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/DataArrayResponseMarshaller.java @@ -0,0 +1,112 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for DataArrayResponse + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class DataArrayResponseMarshaller extends ResponseMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return DataArrayResponse.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new DataArrayResponse(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + DataArrayResponse info = (DataArrayResponse)o; + + if( bs.readBoolean() ) { + short size = dataIn.readShort(); + org.activemq.command.DataStructure value[] = new org.activemq.command.DataStructure[size]; + for( int i=0; i < size; i++ ) { + value[i] = (org.activemq.command.DataStructure)unmarsalNestedObject(wireFormat,dataIn, bs); + } + info.setData(value); + } else { + info.setData(null); + } + + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + DataArrayResponse info = (DataArrayResponse)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshalObjectArray(wireFormat, info.getData(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + DataArrayResponse info = (DataArrayResponse)o; + marshalObjectArray(wireFormat, info.getData(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/DataResponseMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/DataResponseMarshaller.java new file mode 100755 index 0000000000..d0487bb891 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/DataResponseMarshaller.java @@ -0,0 +1,101 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for DataResponse + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class DataResponseMarshaller extends ResponseMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return DataResponse.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new DataResponse(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + DataResponse info = (DataResponse)o; + info.setData((org.activemq.command.DataStructure) unmarsalNestedObject(wireFormat, dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + DataResponse info = (DataResponse)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1NestedObject(wireFormat, info.getData(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + DataResponse info = (DataResponse)o; + marshal2NestedObject(wireFormat, info.getData(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/DestinationInfoMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/DestinationInfoMarshaller.java new file mode 100755 index 0000000000..9a2c4d8b0b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/DestinationInfoMarshaller.java @@ -0,0 +1,124 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for DestinationInfo + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class DestinationInfoMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return DestinationInfo.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new DestinationInfo(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + DestinationInfo info = (DestinationInfo)o; + info.setConnectionId((org.activemq.command.ConnectionId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setDestination((org.activemq.command.ActiveMQDestination) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setOperationType(dataIn.readByte()); + info.setTimeout(unmarshalLong(wireFormat, dataIn, bs)); + + if( bs.readBoolean() ) { + short size = dataIn.readShort(); + org.activemq.command.BrokerId value[] = new org.activemq.command.BrokerId[size]; + for( int i=0; i < size; i++ ) { + value[i] = (org.activemq.command.BrokerId)unmarsalNestedObject(wireFormat,dataIn, bs); + } + info.setBrokerPath(value); + } else { + info.setBrokerPath(null); + } + + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + DestinationInfo info = (DestinationInfo)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1CachedObject(wireFormat, info.getConnectionId(), bs); + rc += marshal1CachedObject(wireFormat, info.getDestination(), bs); + + rc+=marshal1Long(wireFormat, info.getTimeout(), bs); + rc += marshalObjectArray(wireFormat, info.getBrokerPath(), bs); + + return rc+1; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + DestinationInfo info = (DestinationInfo)o; + marshal2CachedObject(wireFormat, info.getConnectionId(), dataOut, bs); + marshal2CachedObject(wireFormat, info.getDestination(), dataOut, bs); + dataOut.writeByte(info.getOperationType()); + marshal2Long(wireFormat, info.getTimeout(), dataOut, bs); + marshalObjectArray(wireFormat, info.getBrokerPath(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ExceptionResponseMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ExceptionResponseMarshaller.java new file mode 100755 index 0000000000..1abea7fba8 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ExceptionResponseMarshaller.java @@ -0,0 +1,101 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ExceptionResponse + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ExceptionResponseMarshaller extends ResponseMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ExceptionResponse.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ExceptionResponse(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + ExceptionResponse info = (ExceptionResponse)o; + info.setException((java.lang.Throwable) unmarsalThrowable(wireFormat, dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + ExceptionResponse info = (ExceptionResponse)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshalThrowable(wireFormat, info.getException(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + ExceptionResponse info = (ExceptionResponse)o; + marshalThrowable(wireFormat, info.getException(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/FlushCommandMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/FlushCommandMarshaller.java new file mode 100644 index 0000000000..b1c3c186d8 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/FlushCommandMarshaller.java @@ -0,0 +1,92 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for FlushCommand + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class FlushCommandMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return FlushCommand.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new FlushCommand(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/IntegerResponseMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/IntegerResponseMarshaller.java new file mode 100755 index 0000000000..9563c4d0d3 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/IntegerResponseMarshaller.java @@ -0,0 +1,101 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for IntegerResponse + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class IntegerResponseMarshaller extends ResponseMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return IntegerResponse.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new IntegerResponse(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + IntegerResponse info = (IntegerResponse)o; + info.setResult(dataIn.readInt()); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + IntegerResponse info = (IntegerResponse)o; + + int rc = super.marshal1(wireFormat, o, bs); + + + return rc+4; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + IntegerResponse info = (IntegerResponse)o; + dataOut.writeInt(info.getResult()); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/JournalQueueAckMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/JournalQueueAckMarshaller.java new file mode 100755 index 0000000000..448fa6a22e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/JournalQueueAckMarshaller.java @@ -0,0 +1,104 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for JournalQueueAck + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class JournalQueueAckMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return JournalQueueAck.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new JournalQueueAck(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + JournalQueueAck info = (JournalQueueAck)o; + info.setDestination((org.activemq.command.ActiveMQDestination) unmarsalNestedObject(wireFormat, dataIn, bs)); + info.setMessageAck((org.activemq.command.MessageAck) unmarsalNestedObject(wireFormat, dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + JournalQueueAck info = (JournalQueueAck)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1NestedObject(wireFormat, info.getDestination(), bs); + rc += marshal1NestedObject(wireFormat, info.getMessageAck(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + JournalQueueAck info = (JournalQueueAck)o; + marshal2NestedObject(wireFormat, info.getDestination(), dataOut, bs); + marshal2NestedObject(wireFormat, info.getMessageAck(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/JournalTopicAckMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/JournalTopicAckMarshaller.java new file mode 100755 index 0000000000..5cc9c33f98 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/JournalTopicAckMarshaller.java @@ -0,0 +1,116 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for JournalTopicAck + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class JournalTopicAckMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return JournalTopicAck.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new JournalTopicAck(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + JournalTopicAck info = (JournalTopicAck)o; + info.setDestination((org.activemq.command.ActiveMQDestination) unmarsalNestedObject(wireFormat, dataIn, bs)); + info.setMessageId((org.activemq.command.MessageId) unmarsalNestedObject(wireFormat, dataIn, bs)); + info.setMessageSequenceId(unmarshalLong(wireFormat, dataIn, bs)); + info.setSubscritionName(readString(dataIn, bs)); + info.setClientId(readString(dataIn, bs)); + info.setTransactionId((org.activemq.command.TransactionId) unmarsalNestedObject(wireFormat, dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + JournalTopicAck info = (JournalTopicAck)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1NestedObject(wireFormat, info.getDestination(), bs); + rc += marshal1NestedObject(wireFormat, info.getMessageId(), bs); + rc+=marshal1Long(wireFormat, info.getMessageSequenceId(), bs); + rc += writeString(info.getSubscritionName(), bs); + rc += writeString(info.getClientId(), bs); + rc += marshal1NestedObject(wireFormat, info.getTransactionId(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + JournalTopicAck info = (JournalTopicAck)o; + marshal2NestedObject(wireFormat, info.getDestination(), dataOut, bs); + marshal2NestedObject(wireFormat, info.getMessageId(), dataOut, bs); + marshal2Long(wireFormat, info.getMessageSequenceId(), dataOut, bs); + writeString(info.getSubscritionName(), dataOut, bs); + writeString(info.getClientId(), dataOut, bs); + marshal2NestedObject(wireFormat, info.getTransactionId(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/JournalTraceMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/JournalTraceMarshaller.java new file mode 100755 index 0000000000..6ae4471c92 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/JournalTraceMarshaller.java @@ -0,0 +1,101 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for JournalTrace + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class JournalTraceMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return JournalTrace.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new JournalTrace(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + JournalTrace info = (JournalTrace)o; + info.setMessage(readString(dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + JournalTrace info = (JournalTrace)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += writeString(info.getMessage(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + JournalTrace info = (JournalTrace)o; + writeString(info.getMessage(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/JournalTransactionMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/JournalTransactionMarshaller.java new file mode 100755 index 0000000000..c5ccf2e99f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/JournalTransactionMarshaller.java @@ -0,0 +1,107 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for JournalTransaction + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class JournalTransactionMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return JournalTransaction.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new JournalTransaction(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + JournalTransaction info = (JournalTransaction)o; + info.setTransactionId((org.activemq.command.TransactionId) unmarsalNestedObject(wireFormat, dataIn, bs)); + info.setType(dataIn.readByte()); + info.setWasPrepared(bs.readBoolean()); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + JournalTransaction info = (JournalTransaction)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1NestedObject(wireFormat, info.getTransactionId(), bs); + + bs.writeBoolean(info.getWasPrepared()); + + return rc+1; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + JournalTransaction info = (JournalTransaction)o; + marshal2NestedObject(wireFormat, info.getTransactionId(), dataOut, bs); + dataOut.writeByte(info.getType()); + bs.readBoolean(); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/KeepAliveInfoMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/KeepAliveInfoMarshaller.java new file mode 100755 index 0000000000..9014824f20 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/KeepAliveInfoMarshaller.java @@ -0,0 +1,92 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for KeepAliveInfo + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class KeepAliveInfoMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return KeepAliveInfo.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new KeepAliveInfo(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/LocalTransactionIdMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/LocalTransactionIdMarshaller.java new file mode 100755 index 0000000000..2dd65c964a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/LocalTransactionIdMarshaller.java @@ -0,0 +1,104 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for LocalTransactionId + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class LocalTransactionIdMarshaller extends TransactionIdMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return LocalTransactionId.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new LocalTransactionId(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + LocalTransactionId info = (LocalTransactionId)o; + info.setTransactionId(unmarshalLong(wireFormat, dataIn, bs)); + info.setConnectionId((org.activemq.command.ConnectionId) unmarsalCachedObject(wireFormat, dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + LocalTransactionId info = (LocalTransactionId)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc+=marshal1Long(wireFormat, info.getTransactionId(), bs); + rc += marshal1CachedObject(wireFormat, info.getConnectionId(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + LocalTransactionId info = (LocalTransactionId)o; + marshal2Long(wireFormat, info.getTransactionId(), dataOut, bs); + marshal2CachedObject(wireFormat, info.getConnectionId(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/MarshallerFactory.java b/activemq-core/src/main/java/org/activemq/openwire/v1/MarshallerFactory.java new file mode 100755 index 0000000000..d3198eab11 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/MarshallerFactory.java @@ -0,0 +1,99 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import org.activemq.openwire.DataStreamMarshaller; +import org.activemq.openwire.OpenWireFormat; + +/** + * MarshallerFactory for Open Wire Format. + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class MarshallerFactory { + + /** + * Creates a Map of command type -> Marshallers + */ + static final private DataStreamMarshaller marshaller[] = new DataStreamMarshaller[256]; + static { + + add(new ActiveMQMessageMarshaller()); + add(new MessageIdMarshaller()); + add(new ControlCommandMarshaller()); + add(new FlushCommandMarshaller()); + add(new IntegerResponseMarshaller()); + add(new RemoveSubscriptionInfoMarshaller()); + add(new SubscriptionInfoMarshaller()); + add(new DataArrayResponseMarshaller()); + add(new ConnectionIdMarshaller()); + add(new BrokerInfoMarshaller()); + add(new JournalTraceMarshaller()); + add(new MessageDispatchMarshaller()); + add(new KeepAliveInfoMarshaller()); + add(new ActiveMQStreamMessageMarshaller()); + add(new JournalQueueAckMarshaller()); + add(new ActiveMQTempTopicMarshaller()); + add(new ProducerInfoMarshaller()); + add(new BrokerIdMarshaller()); + add(new MessageAckMarshaller()); + add(new ActiveMQBytesMessageMarshaller()); + add(new SessionInfoMarshaller()); + add(new ActiveMQTextMessageMarshaller()); + add(new ActiveMQMapMessageMarshaller()); + add(new ShutdownInfoMarshaller()); + add(new DataResponseMarshaller()); + add(new JournalTopicAckMarshaller()); + add(new DestinationInfoMarshaller()); + add(new XATransactionIdMarshaller()); + add(new ActiveMQObjectMessageMarshaller()); + add(new ConsumerIdMarshaller()); + add(new SessionIdMarshaller()); + add(new ConsumerInfoMarshaller()); + add(new ConnectionInfoMarshaller()); + add(new ActiveMQTopicMarshaller()); + add(new RedeliveryPolicyMarshaller()); + add(new ExceptionResponseMarshaller()); + add(new JournalTransactionMarshaller()); + add(new ProducerIdMarshaller()); + add(new ActiveMQQueueMarshaller()); + add(new ActiveMQTempQueueMarshaller()); + add(new TransactionInfoMarshaller()); + add(new ResponseMarshaller()); + add(new RemoveInfoMarshaller()); + add(new WireFormatInfoMarshaller()); + add(new LocalTransactionIdMarshaller()); + + } + + static private void add(DataStreamMarshaller dsm) { + marshaller[dsm.getDataStructureType()] = dsm; + } + + static public DataStreamMarshaller[] createMarshallerMap(OpenWireFormat wireFormat) { + return marshaller; + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/MessageAckMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/MessageAckMarshaller.java new file mode 100755 index 0000000000..8647c75041 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/MessageAckMarshaller.java @@ -0,0 +1,119 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for MessageAck + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class MessageAckMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return MessageAck.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new MessageAck(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + MessageAck info = (MessageAck)o; + info.setDestination((org.activemq.command.ActiveMQDestination) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setTransactionId((org.activemq.command.TransactionId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setConsumerId((org.activemq.command.ConsumerId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setAckType(dataIn.readByte()); + info.setFirstMessageId((org.activemq.command.MessageId) unmarsalNestedObject(wireFormat, dataIn, bs)); + info.setLastMessageId((org.activemq.command.MessageId) unmarsalNestedObject(wireFormat, dataIn, bs)); + info.setMessageCount(dataIn.readInt()); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + MessageAck info = (MessageAck)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1CachedObject(wireFormat, info.getDestination(), bs); + rc += marshal1CachedObject(wireFormat, info.getTransactionId(), bs); + rc += marshal1CachedObject(wireFormat, info.getConsumerId(), bs); + + rc += marshal1NestedObject(wireFormat, info.getFirstMessageId(), bs); + rc += marshal1NestedObject(wireFormat, info.getLastMessageId(), bs); + + + return rc+5; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + MessageAck info = (MessageAck)o; + marshal2CachedObject(wireFormat, info.getDestination(), dataOut, bs); + marshal2CachedObject(wireFormat, info.getTransactionId(), dataOut, bs); + marshal2CachedObject(wireFormat, info.getConsumerId(), dataOut, bs); + dataOut.writeByte(info.getAckType()); + marshal2NestedObject(wireFormat, info.getFirstMessageId(), dataOut, bs); + marshal2NestedObject(wireFormat, info.getLastMessageId(), dataOut, bs); + dataOut.writeInt(info.getMessageCount()); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/MessageDispatchMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/MessageDispatchMarshaller.java new file mode 100755 index 0000000000..6636c4eee5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/MessageDispatchMarshaller.java @@ -0,0 +1,110 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for MessageDispatch + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class MessageDispatchMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return MessageDispatch.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new MessageDispatch(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + MessageDispatch info = (MessageDispatch)o; + info.setConsumerId((org.activemq.command.ConsumerId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setDestination((org.activemq.command.ActiveMQDestination) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setMessage((org.activemq.command.Message) unmarsalNestedObject(wireFormat, dataIn, bs)); + info.setRedeliveryCounter(dataIn.readInt()); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + MessageDispatch info = (MessageDispatch)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1CachedObject(wireFormat, info.getConsumerId(), bs); + rc += marshal1CachedObject(wireFormat, info.getDestination(), bs); + rc += marshal1NestedObject(wireFormat, info.getMessage(), bs); + + + return rc+4; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + MessageDispatch info = (MessageDispatch)o; + marshal2CachedObject(wireFormat, info.getConsumerId(), dataOut, bs); + marshal2CachedObject(wireFormat, info.getDestination(), dataOut, bs); + marshal2NestedObject(wireFormat, info.getMessage(), dataOut, bs); + dataOut.writeInt(info.getRedeliveryCounter()); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/MessageIdMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/MessageIdMarshaller.java new file mode 100755 index 0000000000..c19c9a840d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/MessageIdMarshaller.java @@ -0,0 +1,107 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for MessageId + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class MessageIdMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return MessageId.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new MessageId(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + MessageId info = (MessageId)o; + info.setProducerId((org.activemq.command.ProducerId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setProducerSequenceId(unmarshalLong(wireFormat, dataIn, bs)); + info.setBrokerSequenceId(unmarshalLong(wireFormat, dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + MessageId info = (MessageId)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1CachedObject(wireFormat, info.getProducerId(), bs); + rc+=marshal1Long(wireFormat, info.getProducerSequenceId(), bs); + rc+=marshal1Long(wireFormat, info.getBrokerSequenceId(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + MessageId info = (MessageId)o; + marshal2CachedObject(wireFormat, info.getProducerId(), dataOut, bs); + marshal2Long(wireFormat, info.getProducerSequenceId(), dataOut, bs); + marshal2Long(wireFormat, info.getBrokerSequenceId(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/MessageMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/MessageMarshaller.java new file mode 100755 index 0000000000..a3499d9a9e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/MessageMarshaller.java @@ -0,0 +1,211 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for Message + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public abstract class MessageMarshaller extends BaseCommandMarshaller { + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + Message info = (Message)o; + + info.beforeUnmarshall(wireFormat); + + info.setProducerId((org.activemq.command.ProducerId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setDestination((org.activemq.command.ActiveMQDestination) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setTransactionId((org.activemq.command.TransactionId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setOriginalDestination((org.activemq.command.ActiveMQDestination) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setMessageId((org.activemq.command.MessageId) unmarsalNestedObject(wireFormat, dataIn, bs)); + info.setOriginalTransactionId((org.activemq.command.TransactionId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setGroupID(readString(dataIn, bs)); + info.setGroupSequence(dataIn.readInt()); + info.setCorrelationId(readString(dataIn, bs)); + info.setPersistent(bs.readBoolean()); + info.setExpiration(unmarshalLong(wireFormat, dataIn, bs)); + info.setPriority(dataIn.readByte()); + info.setReplyTo((org.activemq.command.ActiveMQDestination) unmarsalNestedObject(wireFormat, dataIn, bs)); + info.setTimestamp(unmarshalLong(wireFormat, dataIn, bs)); + info.setType(readString(dataIn, bs)); + + if( bs.readBoolean() ) { + int size = dataIn.readInt(); + byte data[] = new byte[size]; + dataIn.readFully(data); + info.setContent(new org.activeio.ByteSequence(data,0,size)); + } else { + info.setContent(null); + } + + + if( bs.readBoolean() ) { + int size = dataIn.readInt(); + byte data[] = new byte[size]; + dataIn.readFully(data); + info.setMarshalledProperties(new org.activeio.ByteSequence(data,0,size)); + } else { + info.setMarshalledProperties(null); + } + + info.setDataStructure((org.activemq.command.DataStructure) unmarsalNestedObject(wireFormat, dataIn, bs)); + info.setTargetConsumerId((org.activemq.command.ConsumerId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setCompressed(bs.readBoolean()); + info.setRedeliveryCounter(dataIn.readInt()); + + if( bs.readBoolean() ) { + short size = dataIn.readShort(); + org.activemq.command.BrokerId value[] = new org.activemq.command.BrokerId[size]; + for( int i=0; i < size; i++ ) { + value[i] = (org.activemq.command.BrokerId)unmarsalNestedObject(wireFormat,dataIn, bs); + } + info.setBrokerPath(value); + } else { + info.setBrokerPath(null); + } + + info.setArrival(unmarshalLong(wireFormat, dataIn, bs)); + info.setUserID(readString(dataIn, bs)); + + info.afterUnmarshall(wireFormat); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + Message info = (Message)o; + + info.beforeMarshall(wireFormat); + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1CachedObject(wireFormat, info.getProducerId(), bs); + rc += marshal1CachedObject(wireFormat, info.getDestination(), bs); + rc += marshal1CachedObject(wireFormat, info.getTransactionId(), bs); + rc += marshal1CachedObject(wireFormat, info.getOriginalDestination(), bs); + rc += marshal1NestedObject(wireFormat, info.getMessageId(), bs); + rc += marshal1CachedObject(wireFormat, info.getOriginalTransactionId(), bs); + rc += writeString(info.getGroupID(), bs); + + rc += writeString(info.getCorrelationId(), bs); + bs.writeBoolean(info.isPersistent()); + rc+=marshal1Long(wireFormat, info.getExpiration(), bs); + + rc += marshal1NestedObject(wireFormat, info.getReplyTo(), bs); + rc+=marshal1Long(wireFormat, info.getTimestamp(), bs); + rc += writeString(info.getType(), bs); + + bs.writeBoolean(info.getContent()!=null); + rc += info.getContent()==null ? 0 : info.getContent().getLength()+4; + + + bs.writeBoolean(info.getMarshalledProperties()!=null); + rc += info.getMarshalledProperties()==null ? 0 : info.getMarshalledProperties().getLength()+4; + + rc += marshal1NestedObject(wireFormat, info.getDataStructure(), bs); + rc += marshal1CachedObject(wireFormat, info.getTargetConsumerId(), bs); + bs.writeBoolean(info.isCompressed()); + + rc += marshalObjectArray(wireFormat, info.getBrokerPath(), bs); + rc+=marshal1Long(wireFormat, info.getArrival(), bs); + rc += writeString(info.getUserID(), bs); + + return rc+9; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + Message info = (Message)o; + marshal2CachedObject(wireFormat, info.getProducerId(), dataOut, bs); + marshal2CachedObject(wireFormat, info.getDestination(), dataOut, bs); + marshal2CachedObject(wireFormat, info.getTransactionId(), dataOut, bs); + marshal2CachedObject(wireFormat, info.getOriginalDestination(), dataOut, bs); + marshal2NestedObject(wireFormat, info.getMessageId(), dataOut, bs); + marshal2CachedObject(wireFormat, info.getOriginalTransactionId(), dataOut, bs); + writeString(info.getGroupID(), dataOut, bs); + dataOut.writeInt(info.getGroupSequence()); + writeString(info.getCorrelationId(), dataOut, bs); + bs.readBoolean(); + marshal2Long(wireFormat, info.getExpiration(), dataOut, bs); + dataOut.writeByte(info.getPriority()); + marshal2NestedObject(wireFormat, info.getReplyTo(), dataOut, bs); + marshal2Long(wireFormat, info.getTimestamp(), dataOut, bs); + writeString(info.getType(), dataOut, bs); + + if(bs.readBoolean()) { + org.activeio.ByteSequence data = info.getContent(); + dataOut.writeInt(data.getLength()); + dataOut.write(data.getData(), data.getOffset(), data.getLength()); + } + + + if(bs.readBoolean()) { + org.activeio.ByteSequence data = info.getMarshalledProperties(); + dataOut.writeInt(data.getLength()); + dataOut.write(data.getData(), data.getOffset(), data.getLength()); + } + + marshal2NestedObject(wireFormat, info.getDataStructure(), dataOut, bs); + marshal2CachedObject(wireFormat, info.getTargetConsumerId(), dataOut, bs); + bs.readBoolean(); + dataOut.writeInt(info.getRedeliveryCounter()); + marshalObjectArray(wireFormat, info.getBrokerPath(), dataOut, bs); + marshal2Long(wireFormat, info.getArrival(), dataOut, bs); + writeString(info.getUserID(), dataOut, bs); + + info.afterMarshall(wireFormat); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ProducerIdMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ProducerIdMarshaller.java new file mode 100755 index 0000000000..f833f74c3f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ProducerIdMarshaller.java @@ -0,0 +1,107 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ProducerId + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ProducerIdMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ProducerId.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ProducerId(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + ProducerId info = (ProducerId)o; + info.setConnectionId(readString(dataIn, bs)); + info.setProducerId(unmarshalLong(wireFormat, dataIn, bs)); + info.setSessionId(unmarshalLong(wireFormat, dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + ProducerId info = (ProducerId)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += writeString(info.getConnectionId(), bs); + rc+=marshal1Long(wireFormat, info.getProducerId(), bs); + rc+=marshal1Long(wireFormat, info.getSessionId(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + ProducerId info = (ProducerId)o; + writeString(info.getConnectionId(), dataOut, bs); + marshal2Long(wireFormat, info.getProducerId(), dataOut, bs); + marshal2Long(wireFormat, info.getSessionId(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ProducerInfoMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ProducerInfoMarshaller.java new file mode 100755 index 0000000000..92199cfdb2 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ProducerInfoMarshaller.java @@ -0,0 +1,118 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ProducerInfo + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ProducerInfoMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ProducerInfo.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ProducerInfo(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + ProducerInfo info = (ProducerInfo)o; + info.setProducerId((org.activemq.command.ProducerId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setDestination((org.activemq.command.ActiveMQDestination) unmarsalCachedObject(wireFormat, dataIn, bs)); + + if( bs.readBoolean() ) { + short size = dataIn.readShort(); + org.activemq.command.BrokerId value[] = new org.activemq.command.BrokerId[size]; + for( int i=0; i < size; i++ ) { + value[i] = (org.activemq.command.BrokerId)unmarsalNestedObject(wireFormat,dataIn, bs); + } + info.setBrokerPath(value); + } else { + info.setBrokerPath(null); + } + + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + ProducerInfo info = (ProducerInfo)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1CachedObject(wireFormat, info.getProducerId(), bs); + rc += marshal1CachedObject(wireFormat, info.getDestination(), bs); + rc += marshalObjectArray(wireFormat, info.getBrokerPath(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + ProducerInfo info = (ProducerInfo)o; + marshal2CachedObject(wireFormat, info.getProducerId(), dataOut, bs); + marshal2CachedObject(wireFormat, info.getDestination(), dataOut, bs); + marshalObjectArray(wireFormat, info.getBrokerPath(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/RedeliveryPolicyMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/RedeliveryPolicyMarshaller.java new file mode 100644 index 0000000000..4a098526c6 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/RedeliveryPolicyMarshaller.java @@ -0,0 +1,110 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for RedeliveryPolicy + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class RedeliveryPolicyMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return RedeliveryPolicy.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new RedeliveryPolicy(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + RedeliveryPolicy info = (RedeliveryPolicy)o; + info.setBackOffMultiplier(dataIn.readShort()); + info.setInitialRedeliveryDelay(unmarshalLong(wireFormat, dataIn, bs)); + info.setMaximumRedeliveries(dataIn.readInt()); + info.setUseExponentialBackOff(bs.readBoolean()); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + RedeliveryPolicy info = (RedeliveryPolicy)o; + + int rc = super.marshal1(wireFormat, o, bs); + + rc+=marshal1Long(wireFormat, info.getInitialRedeliveryDelay(), bs); + + bs.writeBoolean(info.isUseExponentialBackOff()); + + return rc+6; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + RedeliveryPolicy info = (RedeliveryPolicy)o; + dataOut.writeShort(info.getBackOffMultiplier()); + marshal2Long(wireFormat, info.getInitialRedeliveryDelay(), dataOut, bs); + dataOut.writeInt(info.getMaximumRedeliveries()); + bs.readBoolean(); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/RemoveInfoMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/RemoveInfoMarshaller.java new file mode 100755 index 0000000000..ff86420385 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/RemoveInfoMarshaller.java @@ -0,0 +1,101 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for RemoveInfo + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class RemoveInfoMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return RemoveInfo.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new RemoveInfo(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + RemoveInfo info = (RemoveInfo)o; + info.setObjectId((org.activemq.command.DataStructure) unmarsalCachedObject(wireFormat, dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + RemoveInfo info = (RemoveInfo)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1CachedObject(wireFormat, info.getObjectId(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + RemoveInfo info = (RemoveInfo)o; + marshal2CachedObject(wireFormat, info.getObjectId(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/RemoveSubscriptionInfoMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/RemoveSubscriptionInfoMarshaller.java new file mode 100755 index 0000000000..cca8782453 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/RemoveSubscriptionInfoMarshaller.java @@ -0,0 +1,107 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for RemoveSubscriptionInfo + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class RemoveSubscriptionInfoMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return RemoveSubscriptionInfo.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new RemoveSubscriptionInfo(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + RemoveSubscriptionInfo info = (RemoveSubscriptionInfo)o; + info.setConnectionId((org.activemq.command.ConnectionId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setSubcriptionName(readString(dataIn, bs)); + info.setClientId(readString(dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + RemoveSubscriptionInfo info = (RemoveSubscriptionInfo)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1CachedObject(wireFormat, info.getConnectionId(), bs); + rc += writeString(info.getSubcriptionName(), bs); + rc += writeString(info.getClientId(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + RemoveSubscriptionInfo info = (RemoveSubscriptionInfo)o; + marshal2CachedObject(wireFormat, info.getConnectionId(), dataOut, bs); + writeString(info.getSubcriptionName(), dataOut, bs); + writeString(info.getClientId(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ResponseMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ResponseMarshaller.java new file mode 100755 index 0000000000..84030d368e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ResponseMarshaller.java @@ -0,0 +1,101 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for Response + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ResponseMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return Response.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new Response(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + Response info = (Response)o; + info.setCorrelationId(dataIn.readShort()); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + Response info = (Response)o; + + int rc = super.marshal1(wireFormat, o, bs); + + + return rc+2; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + Response info = (Response)o; + dataOut.writeShort(info.getCorrelationId()); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/SessionIdMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/SessionIdMarshaller.java new file mode 100755 index 0000000000..4397b7d935 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/SessionIdMarshaller.java @@ -0,0 +1,104 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for SessionId + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class SessionIdMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return SessionId.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new SessionId(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + SessionId info = (SessionId)o; + info.setConnectionId(readString(dataIn, bs)); + info.setSessionId(unmarshalLong(wireFormat, dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + SessionId info = (SessionId)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += writeString(info.getConnectionId(), bs); + rc+=marshal1Long(wireFormat, info.getSessionId(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + SessionId info = (SessionId)o; + writeString(info.getConnectionId(), dataOut, bs); + marshal2Long(wireFormat, info.getSessionId(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/SessionInfoMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/SessionInfoMarshaller.java new file mode 100755 index 0000000000..d9a92a5587 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/SessionInfoMarshaller.java @@ -0,0 +1,101 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for SessionInfo + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class SessionInfoMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return SessionInfo.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new SessionInfo(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + SessionInfo info = (SessionInfo)o; + info.setSessionId((org.activemq.command.SessionId) unmarsalCachedObject(wireFormat, dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + SessionInfo info = (SessionInfo)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1CachedObject(wireFormat, info.getSessionId(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + SessionInfo info = (SessionInfo)o; + marshal2CachedObject(wireFormat, info.getSessionId(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/ShutdownInfoMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/ShutdownInfoMarshaller.java new file mode 100755 index 0000000000..5cc01c12cd --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/ShutdownInfoMarshaller.java @@ -0,0 +1,92 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for ShutdownInfo + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class ShutdownInfoMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return ShutdownInfo.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new ShutdownInfo(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/SubscriptionInfoMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/SubscriptionInfoMarshaller.java new file mode 100755 index 0000000000..5201a4171d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/SubscriptionInfoMarshaller.java @@ -0,0 +1,110 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for SubscriptionInfo + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class SubscriptionInfoMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return SubscriptionInfo.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new SubscriptionInfo(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + SubscriptionInfo info = (SubscriptionInfo)o; + info.setClientId(readString(dataIn, bs)); + info.setDestination((org.activemq.command.ActiveMQDestination) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setSelector(readString(dataIn, bs)); + info.setSubcriptionName(readString(dataIn, bs)); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + SubscriptionInfo info = (SubscriptionInfo)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += writeString(info.getClientId(), bs); + rc += marshal1CachedObject(wireFormat, info.getDestination(), bs); + rc += writeString(info.getSelector(), bs); + rc += writeString(info.getSubcriptionName(), bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + SubscriptionInfo info = (SubscriptionInfo)o; + writeString(info.getClientId(), dataOut, bs); + marshal2CachedObject(wireFormat, info.getDestination(), dataOut, bs); + writeString(info.getSelector(), dataOut, bs); + writeString(info.getSubcriptionName(), dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/TransactionIdMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/TransactionIdMarshaller.java new file mode 100755 index 0000000000..d1993e9b79 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/TransactionIdMarshaller.java @@ -0,0 +1,77 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for TransactionId + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public abstract class TransactionIdMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + int rc = super.marshal1(wireFormat, o, bs); + + return rc+0; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/TransactionInfoMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/TransactionInfoMarshaller.java new file mode 100755 index 0000000000..d5b0c74a21 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/TransactionInfoMarshaller.java @@ -0,0 +1,107 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for TransactionInfo + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class TransactionInfoMarshaller extends BaseCommandMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return TransactionInfo.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new TransactionInfo(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + TransactionInfo info = (TransactionInfo)o; + info.setConnectionId((org.activemq.command.ConnectionId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setTransactionId((org.activemq.command.TransactionId) unmarsalCachedObject(wireFormat, dataIn, bs)); + info.setType(dataIn.readByte()); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + TransactionInfo info = (TransactionInfo)o; + + int rc = super.marshal1(wireFormat, o, bs); + rc += marshal1CachedObject(wireFormat, info.getConnectionId(), bs); + rc += marshal1CachedObject(wireFormat, info.getTransactionId(), bs); + + + return rc+1; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + TransactionInfo info = (TransactionInfo)o; + marshal2CachedObject(wireFormat, info.getConnectionId(), dataOut, bs); + marshal2CachedObject(wireFormat, info.getTransactionId(), dataOut, bs); + dataOut.writeByte(info.getType()); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/WireFormatInfoMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/WireFormatInfoMarshaller.java new file mode 100755 index 0000000000..24077d50c1 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/WireFormatInfoMarshaller.java @@ -0,0 +1,112 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for WireFormatInfo + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class WireFormatInfoMarshaller extends org.activemq.openwire.DataStreamMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return WireFormatInfo.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new WireFormatInfo(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + WireFormatInfo info = (WireFormatInfo)o; + { + byte data[] = new byte[8]; + dataIn.readFully(data); + info.setMagic(data); + } + + info.setVersion(dataIn.readInt()); + info.setOptions(dataIn.readInt()); + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + WireFormatInfo info = (WireFormatInfo)o; + + int rc = super.marshal1(wireFormat, o, bs); + + + + + return rc+16; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + WireFormatInfo info = (WireFormatInfo)o; + dataOut.write(info.getMagic(), 0, 8); + dataOut.writeInt(info.getVersion()); + dataOut.writeInt(info.getOptions()); + + } +} diff --git a/activemq-core/src/main/java/org/activemq/openwire/v1/XATransactionIdMarshaller.java b/activemq-core/src/main/java/org/activemq/openwire/v1/XATransactionIdMarshaller.java new file mode 100755 index 0000000000..1e43d6dc1c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/openwire/v1/XATransactionIdMarshaller.java @@ -0,0 +1,143 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.openwire.v1; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.activemq.openwire.*; +import org.activemq.command.*; + + +/** + * Marshalling code for Open Wire Format for XATransactionId + * + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + * @version $Revision$ + */ +public class XATransactionIdMarshaller extends TransactionIdMarshaller { + + /** + * Return the type of Data Structure we marshal + * @return short representation of the type data structure + */ + public byte getDataStructureType() { + return XATransactionId.DATA_STRUCTURE_TYPE; + } + + /** + * @return a new object instance + */ + public DataStructure createObject() { + return new XATransactionId(); + } + + /** + * Un-marshal an object instance from the data input stream + * + * @param o the object to un-marshal + * @param dataIn the data input stream to build the object from + * @throws IOException + */ + public void unmarshal(OpenWireFormat wireFormat, Object o, DataInputStream dataIn, BooleanStream bs) throws IOException { + super.unmarshal(wireFormat, o, dataIn, bs); + + XATransactionId info = (XATransactionId)o; + info.setFormatId(dataIn.readInt()); + { + if( bs.readBoolean() ) { + int size = dataIn.readInt(); + byte data[] = new byte[size]; + dataIn.readFully(data); + info.setGlobalTransactionId(data); + } else { + info.setGlobalTransactionId(null); + } + } + + { + if( bs.readBoolean() ) { + int size = dataIn.readInt(); + byte data[] = new byte[size]; + dataIn.readFully(data); + info.setBranchQualifier(data); + } else { + info.setBranchQualifier(null); + } + } + + + } + + + /** + * Write the booleans that this object uses to a BooleanStream + */ + public int marshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException { + + XATransactionId info = (XATransactionId)o; + + int rc = super.marshal1(wireFormat, o, bs); + + + bs.writeBoolean(info.getGlobalTransactionId()!=null); + rc += info.getGlobalTransactionId()==null ? 0 : info.getGlobalTransactionId().length+4; + + + bs.writeBoolean(info.getBranchQualifier()!=null); + rc += info.getBranchQualifier()==null ? 0 : info.getBranchQualifier().length+4; + + + return rc+4; + } + + /** + * Write a object instance to data output stream + * + * @param o the instance to be marshaled + * @param dataOut the output stream + * @throws IOException thrown if an error occurs + */ + public void marshal2(OpenWireFormat wireFormat, Object o, DataOutputStream dataOut, BooleanStream bs) throws IOException { + super.marshal2(wireFormat, o, dataOut, bs); + + XATransactionId info = (XATransactionId)o; + dataOut.writeInt(info.getFormatId()); + + if(bs.readBoolean()) { + dataOut.writeInt(info.getGlobalTransactionId().length); + dataOut.write(info.getGlobalTransactionId()); + } + + + if(bs.readBoolean()) { + dataOut.writeInt(info.getBranchQualifier().length); + dataOut.write(info.getBranchQualifier()); + } + + + } +} diff --git a/activemq-core/src/main/java/org/activemq/package.html b/activemq-core/src/main/java/org/activemq/package.html new file mode 100755 index 0000000000..32701062c5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/package.html @@ -0,0 +1,9 @@ + + + + + +The core JMS client API implementation classes. + + + diff --git a/activemq-core/src/main/java/org/activemq/pool/ConnectionKey.java b/activemq-core/src/main/java/org/activemq/pool/ConnectionKey.java new file mode 100644 index 0000000000..4115d3217a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/pool/ConnectionKey.java @@ -0,0 +1,78 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.pool; + +/** + * A cache key for the connection details + * + * @version $Revision: 1.1 $ + */ +public class ConnectionKey { + private String userName; + private String password; + private int hash; + + public ConnectionKey(String password, String userName) { + this.password = password; + this.userName = userName; + hash = 31; + if (userName != null) { + hash += userName.hashCode(); + } + hash *= 31; + if (password != null) { + hash += password.hashCode(); + } + } + + public int hashCode() { + return hash; + } + + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that instanceof ConnectionKey) { + return equals((ConnectionKey) that); + } + return false; + } + + public boolean equals(ConnectionKey that) { + return isEqual(this.userName, that.userName) && isEqual(this.password, that.password); + } + + public String getPassword() { + return password; + } + + public String getUserName() { + return userName; + } + + public static boolean isEqual(Object o1, Object o2) { + if (o1 == o2) { + return true; + } + return (o1 != null && o2 != null && o1.equals(o2)); + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/pool/PooledConnection.java b/activemq-core/src/main/java/org/activemq/pool/PooledConnection.java new file mode 100755 index 0000000000..8624006a8a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/pool/PooledConnection.java @@ -0,0 +1,169 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.pool; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.jms.Connection; +import javax.jms.ConnectionConsumer; +import javax.jms.ConnectionMetaData; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueSession; +import javax.jms.ServerSessionPool; +import javax.jms.Session; +import javax.jms.Topic; +import javax.jms.TopicConnection; +import javax.jms.TopicSession; + +import org.activemq.ActiveMQConnection; +import org.activemq.ActiveMQSession; +import org.activemq.AlreadyClosedException; +import org.activemq.util.JMSExceptionSupport; + +/** + * Represents a proxy {@link Connection} which is-a {@link TopicConnection} and + * {@link QueueConnection} which is pooled and on {@link #close()} will return + * itself to the sessionPool. + * + * @version $Revision: 1.1.1.1 $ + */ +public class PooledConnection implements TopicConnection, QueueConnection { + + private ActiveMQConnection connection; + private Map cache; + private boolean stopped; + + public PooledConnection(ActiveMQConnection connection) { + this(connection, new HashMap()); + } + + public PooledConnection(ActiveMQConnection connection, Map cache) { + this.connection = connection; + this.cache = cache; + } + + /** + * Factory method to create a new instance. + */ + public PooledConnection newInstance() { + return new PooledConnection(connection, cache); + } + + public void close() throws JMSException { + connection = null; + Iterator i = cache.values().iterator(); + while (i.hasNext()) { + SessionPool pool = (SessionPool) i.next(); + i.remove(); + try { + pool.close(); + } + catch (Exception e) { + throw JMSExceptionSupport.create(e); + } + } + } + + public void start() throws JMSException { + // TODO should we start connections first before pooling them? + getConnection().start(); + } + + public void stop() throws JMSException { + stopped = true; + } + + public ConnectionConsumer createConnectionConsumer(Destination destination, String selector, ServerSessionPool serverSessionPool, int maxMessages) + throws JMSException { + return getConnection().createConnectionConsumer(destination, selector, serverSessionPool, maxMessages); + } + + public ConnectionConsumer createConnectionConsumer(Topic topic, String s, ServerSessionPool serverSessionPool, int maxMessages) throws JMSException { + return getConnection().createConnectionConsumer(topic, s, serverSessionPool, maxMessages); + } + + public ConnectionConsumer createDurableConnectionConsumer(Topic topic, String selector, String s1, ServerSessionPool serverSessionPool, int i) + throws JMSException { + return getConnection().createDurableConnectionConsumer(topic, selector, s1, serverSessionPool, i); + } + + public String getClientID() throws JMSException { + return getConnection().getClientID(); + } + + public ExceptionListener getExceptionListener() throws JMSException { + return getConnection().getExceptionListener(); + } + + public ConnectionMetaData getMetaData() throws JMSException { + return getConnection().getMetaData(); + } + + public void setExceptionListener(ExceptionListener exceptionListener) throws JMSException { + getConnection().setExceptionListener(exceptionListener); + } + + public void setClientID(String clientID) throws JMSException { + getConnection().setClientID(clientID); + } + + public ConnectionConsumer createConnectionConsumer(Queue queue, String selector, ServerSessionPool serverSessionPool, int maxMessages) throws JMSException { + return getConnection().createConnectionConsumer(queue, selector, serverSessionPool, maxMessages); + } + + // Session factory methods + // ------------------------------------------------------------------------- + public QueueSession createQueueSession(boolean transacted, int ackMode) throws JMSException { + return (QueueSession) createSession(transacted, ackMode); + } + + public TopicSession createTopicSession(boolean transacted, int ackMode) throws JMSException { + return (TopicSession) createSession(transacted, ackMode); + } + + public Session createSession(boolean transacted, int ackMode) throws JMSException { + SessionKey key = new SessionKey(transacted, ackMode); + SessionPool pool = (SessionPool) cache.get(key); + if (pool == null) { + pool = new SessionPool(getConnection(), key); + cache.put(key, pool); + } + return pool.borrowSession(); + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected ActiveMQConnection getConnection() throws JMSException { + if (stopped || connection == null) { + throw new AlreadyClosedException(); + } + return connection; + } + + protected ActiveMQSession createSession(SessionKey key) throws JMSException { + return (ActiveMQSession) getConnection().createSession(key.isTransacted(), key.getAckMode()); + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/pool/PooledConnectionFactory.java b/activemq-core/src/main/java/org/activemq/pool/PooledConnectionFactory.java new file mode 100644 index 0000000000..6cb082439d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/pool/PooledConnectionFactory.java @@ -0,0 +1,115 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.pool; + +import org.activemq.ActiveMQConnection; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.Service; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.ServiceStopper; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * A JMS provider which pools Connection, Session and MessageProducer instances so it can be used with tools like + * Spring's JmsTemplate + * + * @version $Revision: 1.1 $ + */ +public class PooledConnectionFactory implements ConnectionFactory, Service { + private ActiveMQConnectionFactory connectionFactory; + private Map cache = new HashMap(); + + public PooledConnectionFactory() { + this(new ActiveMQConnectionFactory()); + } + + public PooledConnectionFactory(String brokerURL) { + this(new ActiveMQConnectionFactory(brokerURL)); + } + + public PooledConnectionFactory(ActiveMQConnectionFactory connectionFactory) { + this.connectionFactory = connectionFactory; + } + + public ActiveMQConnectionFactory getConnectionFactory() { + return connectionFactory; + } + + public void setConnectionFactory(ActiveMQConnectionFactory connectionFactory) { + this.connectionFactory = connectionFactory; + } + + public Connection createConnection() throws JMSException { + return createConnection(null, null); + } + + public synchronized Connection createConnection(String userName, String password) throws JMSException { + ConnectionKey key = new ConnectionKey(userName, password); + PooledConnection connection = (PooledConnection) cache.get(key); + if (connection == null) { + ActiveMQConnection delegate = createConnection(key); + connection = new PooledConnection(delegate); + cache.put(key, connection); + } + return connection.newInstance(); + } + + protected ActiveMQConnection createConnection(ConnectionKey key) throws JMSException { + if (key.getUserName() == null && key.getPassword() == null) { + return (ActiveMQConnection) connectionFactory.createConnection(); + } + else { + return (ActiveMQConnection) connectionFactory.createConnection(key.getUserName(), key.getPassword()); + } + } + + /** + * @see org.activemq.service.Service#start() + */ + public void start() { + try { + createConnection(); + } + catch (JMSException e) { + IOExceptionSupport.create(e); + } + } + + public void stop() throws Exception { + ServiceStopper stopper = new ServiceStopper(); + for (Iterator iter = cache.values().iterator(); iter.hasNext();) { + PooledConnection connection = (PooledConnection) iter.next(); + try { + connection.getConnection().close(); + connection.close(); + } + catch (JMSException e) { + stopper.onException(this, e); + } + } + stopper.throwFirstException(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/pool/PooledProducer.java b/activemq-core/src/main/java/org/activemq/pool/PooledProducer.java new file mode 100644 index 0000000000..034754b431 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/pool/PooledProducer.java @@ -0,0 +1,129 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.pool; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; + +import org.activemq.ActiveMQMessageProducer; + +/** + * A pooled {@link MessageProducer} + * + * @version $Revision: 1.1 $ + */ +public class PooledProducer implements MessageProducer { + private ActiveMQMessageProducer messageProducer; + private Destination destination; + private int deliveryMode; + private boolean disableMessageID; + private boolean disableMessageTimestamp; + private int priority; + private long timeToLive; + + public PooledProducer(ActiveMQMessageProducer messageProducer, Destination destination) throws JMSException { + this.messageProducer = messageProducer; + this.destination = destination; + + this.deliveryMode = messageProducer.getDeliveryMode(); + this.disableMessageID = messageProducer.getDisableMessageID(); + this.disableMessageTimestamp = messageProducer.getDisableMessageTimestamp(); + this.priority = messageProducer.getPriority(); + this.timeToLive = messageProducer.getTimeToLive(); + } + + public void close() throws JMSException { + } + + public void send(Destination destination, Message message) throws JMSException { + send(destination, message, getDeliveryMode(), getPriority(), getTimeToLive()); + } + + public void send(Message message) throws JMSException { + send(destination, message, getDeliveryMode(), getPriority(), getTimeToLive()); + } + + public void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException { + send(destination, message, deliveryMode, priority, timeToLive); + } + + public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive) throws JMSException { + if (destination == null) { + destination = this.destination; + } + ActiveMQMessageProducer messageProducer = getMessageProducer(); + + // just in case let only one thread send at once + synchronized (messageProducer) { + messageProducer.send(destination, message, deliveryMode, priority, timeToLive); + } + } + + public Destination getDestination() { + return destination; + } + + public int getDeliveryMode() { + return deliveryMode; + } + + public void setDeliveryMode(int deliveryMode) { + this.deliveryMode = deliveryMode; + } + + public boolean getDisableMessageID() { + return disableMessageID; + } + + public void setDisableMessageID(boolean disableMessageID) { + this.disableMessageID = disableMessageID; + } + + public boolean getDisableMessageTimestamp() { + return disableMessageTimestamp; + } + + public void setDisableMessageTimestamp(boolean disableMessageTimestamp) { + this.disableMessageTimestamp = disableMessageTimestamp; + } + + public int getPriority() { + return priority; + } + + public void setPriority(int priority) { + this.priority = priority; + } + + public long getTimeToLive() { + return timeToLive; + } + + public void setTimeToLive(long timeToLive) { + this.timeToLive = timeToLive; + } + + // Implementation methods + //------------------------------------------------------------------------- + protected ActiveMQMessageProducer getMessageProducer() { + return messageProducer; + } +} diff --git a/activemq-core/src/main/java/org/activemq/pool/PooledQueueSender.java b/activemq-core/src/main/java/org/activemq/pool/PooledQueueSender.java new file mode 100644 index 0000000000..4de17d29ca --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/pool/PooledQueueSender.java @@ -0,0 +1,55 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.pool; + +import org.activemq.ActiveMQQueueSender; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Queue; +import javax.jms.QueueSender; + +/** + * @version $Revision: 1.1 $ + */ +public class PooledQueueSender extends PooledProducer implements QueueSender { + + public PooledQueueSender(ActiveMQQueueSender messageProducer, Destination destination) throws JMSException { + super(messageProducer, destination); + } + + public void send(Queue queue, Message message, int i, int i1, long l) throws JMSException { + getQueueSender().send(queue, message, i, i1, l); + } + + public void send(Queue queue, Message message) throws JMSException { + getQueueSender().send(queue, message); + } + + public Queue getQueue() throws JMSException { + return getQueueSender().getQueue(); + } + + + protected ActiveMQQueueSender getQueueSender() { + return (ActiveMQQueueSender) getMessageProducer(); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/pool/PooledSession.java b/activemq-core/src/main/java/org/activemq/pool/PooledSession.java new file mode 100644 index 0000000000..9e898f5064 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/pool/PooledSession.java @@ -0,0 +1,288 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.pool; + +import org.activemq.ActiveMQMessageProducer; +import org.activemq.ActiveMQQueueSender; +import org.activemq.ActiveMQSession; +import org.activemq.ActiveMQTopicPublisher; +import org.activemq.AlreadyClosedException; +import org.activemq.util.JMSExceptionSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.pool.ObjectPool; + +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.QueueBrowser; +import javax.jms.QueueReceiver; +import javax.jms.QueueSender; +import javax.jms.QueueSession; +import javax.jms.StreamMessage; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicPublisher; +import javax.jms.TopicSession; +import javax.jms.TopicSubscriber; + +import java.io.Serializable; + +/** + * @version $Revision: 1.1 $ + */ +public class PooledSession implements TopicSession, QueueSession { + private static final transient Log log = LogFactory.getLog(PooledSession.class); + + private ActiveMQSession session; + private ObjectPool sessionPool; + private ActiveMQMessageProducer messageProducer; + private ActiveMQQueueSender queueSender; + private ActiveMQTopicPublisher topicPublisher; + private boolean transactional = true; + + public PooledSession(ActiveMQSession aSession, ObjectPool sessionPool) { + this.session = aSession; + this.sessionPool = sessionPool; + this.transactional = session.isTransacted(); + } + + + public void close() throws JMSException { + // TODO a cleaner way to reset?? + + // lets reset the session + getSession().setMessageListener(null); + + // maybe do a rollback? + if (transactional) { + try { + getSession().rollback(); + } + catch (JMSException e) { + log.warn("Caught exception trying rollback() when putting session back into the pool: " + e, e); + + // lets close the session and not put the session back into the pool + try { + session.close(); + } + catch (JMSException e1) { + log.trace("Ignoring exception as discarding session: " + e1, e1); + } + session = null; + return; + } + } + + try { + sessionPool.returnObject(this); + } + catch (Exception e) { + throw JMSExceptionSupport.create("Failed to return session to pool: " + e, e); + } + } + + public void commit() throws JMSException { + getSession().commit(); + } + + public BytesMessage createBytesMessage() throws JMSException { + return getSession().createBytesMessage(); + } + + public MapMessage createMapMessage() throws JMSException { + return getSession().createMapMessage(); + } + + public Message createMessage() throws JMSException { + return getSession().createMessage(); + } + + public ObjectMessage createObjectMessage() throws JMSException { + return getSession().createObjectMessage(); + } + + public ObjectMessage createObjectMessage(Serializable serializable) throws JMSException { + return getSession().createObjectMessage(serializable); + } + + public Queue createQueue(String s) throws JMSException { + return getSession().createQueue(s); + } + + public StreamMessage createStreamMessage() throws JMSException { + return getSession().createStreamMessage(); + } + + public TemporaryQueue createTemporaryQueue() throws JMSException { + return getSession().createTemporaryQueue(); + } + + public TemporaryTopic createTemporaryTopic() throws JMSException { + return getSession().createTemporaryTopic(); + } + + public void unsubscribe(String s) throws JMSException { + getSession().unsubscribe(s); + } + + public TextMessage createTextMessage() throws JMSException { + return getSession().createTextMessage(); + } + + public TextMessage createTextMessage(String s) throws JMSException { + return getSession().createTextMessage(s); + } + + public Topic createTopic(String s) throws JMSException { + return getSession().createTopic(s); + } + + public int getAcknowledgeMode() throws JMSException { + return getSession().getAcknowledgeMode(); + } + + public boolean getTransacted() throws JMSException { + return getSession().getTransacted(); + } + + public void recover() throws JMSException { + getSession().recover(); + } + + public void rollback() throws JMSException { + getSession().rollback(); + } + + public void run() { + if (session != null) { + session.run(); + } + } + + + // Consumer related methods + //------------------------------------------------------------------------- + public QueueBrowser createBrowser(Queue queue) throws JMSException { + return getSession().createBrowser(queue); + } + + public QueueBrowser createBrowser(Queue queue, String selector) throws JMSException { + return getSession().createBrowser(queue, selector); + } + + public MessageConsumer createConsumer(Destination destination) throws JMSException { + return getSession().createConsumer(destination); + } + + public MessageConsumer createConsumer(Destination destination, String selector) throws JMSException { + return getSession().createConsumer(destination, selector); + } + + public MessageConsumer createConsumer(Destination destination, String selector, boolean noLocal) throws JMSException { + return getSession().createConsumer(destination, selector, noLocal); + } + + public TopicSubscriber createDurableSubscriber(Topic topic, String selector) throws JMSException { + return getSession().createDurableSubscriber(topic, selector); + } + + public TopicSubscriber createDurableSubscriber(Topic topic, String name, String selector, boolean noLocal) throws JMSException { + return getSession().createDurableSubscriber(topic, name, selector, noLocal); + } + + public MessageListener getMessageListener() throws JMSException { + return getSession().getMessageListener(); + } + + public void setMessageListener(MessageListener messageListener) throws JMSException { + getSession().setMessageListener(messageListener); + } + + public TopicSubscriber createSubscriber(Topic topic) throws JMSException { + return getSession().createSubscriber(topic); + } + + public TopicSubscriber createSubscriber(Topic topic, String selector, boolean local) throws JMSException { + return getSession().createSubscriber(topic, selector, local); + } + + public QueueReceiver createReceiver(Queue queue) throws JMSException { + return getSession().createReceiver(queue); + } + + public QueueReceiver createReceiver(Queue queue, String selector) throws JMSException { + return getSession().createReceiver(queue, selector); + } + + + // Producer related methods + //------------------------------------------------------------------------- + public MessageProducer createProducer(Destination destination) throws JMSException { + return new PooledProducer(getMessageProducer(), destination); + } + + public QueueSender createSender(Queue queue) throws JMSException { + return new PooledQueueSender(getQueueSender(), queue); + } + + public TopicPublisher createPublisher(Topic topic) throws JMSException { + return new PooledTopicPublisher(getTopicPublisher(), topic); + } + + // Implementation methods + //------------------------------------------------------------------------- + protected ActiveMQSession getSession() throws AlreadyClosedException { + if (session == null) { + throw new AlreadyClosedException("The session has already been closed"); + } + return session; + } + + public ActiveMQMessageProducer getMessageProducer() throws JMSException { + if (messageProducer == null) { + messageProducer = (ActiveMQMessageProducer) getSession().createProducer(null); + } + return messageProducer; + } + + public ActiveMQQueueSender getQueueSender() throws JMSException { + if (queueSender == null) { + queueSender = (ActiveMQQueueSender) getSession().createSender(null); + } + return queueSender; + } + + public ActiveMQTopicPublisher getTopicPublisher() throws JMSException { + if (topicPublisher == null) { + topicPublisher = (ActiveMQTopicPublisher) getSession().createPublisher(null); + } + return topicPublisher; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/pool/PooledTopicPublisher.java b/activemq-core/src/main/java/org/activemq/pool/PooledTopicPublisher.java new file mode 100644 index 0000000000..65e949ae93 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/pool/PooledTopicPublisher.java @@ -0,0 +1,61 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.pool; + +import org.activemq.ActiveMQTopicPublisher; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Topic; +import javax.jms.TopicPublisher; + +/** + * @version $Revision: 1.1 $ + */ +public class PooledTopicPublisher extends PooledProducer implements TopicPublisher { + + public PooledTopicPublisher(ActiveMQTopicPublisher messageProducer, Destination destination) throws JMSException { + super(messageProducer, destination); + } + + public Topic getTopic() throws JMSException { + return getTopicPublisher().getTopic(); + } + + public void publish(Message message) throws JMSException { + getTopicPublisher().publish(message); + } + + public void publish(Message message, int i, int i1, long l) throws JMSException { + getTopicPublisher().publish(message, i, i1, l); + } + + public void publish(Topic topic, Message message) throws JMSException { + getTopicPublisher().publish(topic, message); + } + + public void publish(Topic topic, Message message, int i, int i1, long l) throws JMSException { + getTopicPublisher().publish(topic, message, i, i1, l); + } + + protected ActiveMQTopicPublisher getTopicPublisher() { + return (ActiveMQTopicPublisher) getMessageProducer(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/pool/SessionKey.java b/activemq-core/src/main/java/org/activemq/pool/SessionKey.java new file mode 100644 index 0000000000..7f9e48e073 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/pool/SessionKey.java @@ -0,0 +1,65 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.pool; + +/** + * A cache key for the session details + * + * @version $Revision: 1.1 $ + */ +public class SessionKey { + private boolean transacted; + private int ackMode; + private int hash; + + public SessionKey(boolean transacted, int ackMode) { + this.transacted = transacted; + this.ackMode = ackMode; + hash = ackMode; + if (transacted) { + hash = 31 * hash + 1; + } + } + + public int hashCode() { + return hash; + } + + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that instanceof SessionKey) { + return equals((SessionKey) that); + } + return false; + } + + public boolean equals(SessionKey that) { + return this.transacted == that.transacted && this.ackMode == that.ackMode; + } + + public boolean isTransacted() { + return transacted; + } + + public int getAckMode() { + return ackMode; + } +} diff --git a/activemq-core/src/main/java/org/activemq/pool/SessionPool.java b/activemq-core/src/main/java/org/activemq/pool/SessionPool.java new file mode 100644 index 0000000000..90e8c7dea9 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/pool/SessionPool.java @@ -0,0 +1,104 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.pool; + +import org.activemq.ActiveMQConnection; +import org.activemq.ActiveMQSession; +import org.activemq.AlreadyClosedException; +import org.activemq.util.JMSExceptionSupport; +import org.apache.commons.pool.ObjectPool; +import org.apache.commons.pool.PoolableObjectFactory; +import org.apache.commons.pool.impl.GenericObjectPool; + +import javax.jms.JMSException; + +/** + * Represents the session pool for a given JMS connection. + * + * @version $Revision: 1.1 $ + */ +public class SessionPool implements PoolableObjectFactory { + private ActiveMQConnection connection; + private SessionKey key; + private ObjectPool sessionPool; + + public SessionPool(ActiveMQConnection connection, SessionKey key) { + this(connection, key, new GenericObjectPool()); + } + + public SessionPool(ActiveMQConnection connection, SessionKey key, ObjectPool sessionPool) { + this.connection = connection; + this.key = key; + this.sessionPool = sessionPool; + sessionPool.setFactory(this); + } + + public void close() throws Exception { + sessionPool.close(); + } + + public PooledSession borrowSession() throws JMSException { + try { + Object object = sessionPool.borrowObject(); + return (PooledSession) object; + } + catch (JMSException e) { + throw e; + } + catch (Exception e) { + throw JMSExceptionSupport.create(e); + } + } + + // PoolableObjectFactory methods + //------------------------------------------------------------------------- + public Object makeObject() throws Exception { + return new PooledSession(createSession(), sessionPool); + } + + public void destroyObject(Object o) throws Exception { + PooledSession session = (PooledSession) o; + session.getSession().close(); + } + + public boolean validateObject(Object o) { + return true; + } + + public void activateObject(Object o) throws Exception { + } + + public void passivateObject(Object o) throws Exception { + } + + // Implemention methods + //------------------------------------------------------------------------- + protected ActiveMQConnection getConnection() throws JMSException { + if (connection == null) { + throw new AlreadyClosedException(); + } + return connection; + } + + protected ActiveMQSession createSession() throws JMSException { + return (ActiveMQSession) getConnection().createSession(key.isTransacted(), key.getAckMode()); + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/pool/package.html b/activemq-core/src/main/java/org/activemq/pool/package.html new file mode 100755 index 0000000000..70e038ba8e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/pool/package.html @@ -0,0 +1,11 @@ + + + + + +A JMS provider which pools Connection, Session and MessageProducer instances so it can be used with tools like +Spring's JmsTemplate + + + + diff --git a/activemq-core/src/main/java/org/activemq/proxy/ProxyConnection.java b/activemq-core/src/main/java/org/activemq/proxy/ProxyConnection.java new file mode 100644 index 0000000000..382bab4ea4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/proxy/ProxyConnection.java @@ -0,0 +1,115 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * + * Licensed 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.activemq.proxy; + +import java.io.IOException; + +import org.activemq.Service; +import org.activemq.command.Command; +import org.activemq.command.ShutdownInfo; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportListener; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.ServiceStopper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +class ProxyConnection implements Service { + + static final private Log log = LogFactory.getLog(ProxyConnection.class); + + private final Transport localTransport; + private final Transport remoteTransport; + private AtomicBoolean shuttingDown = new AtomicBoolean(false); + private AtomicBoolean running = new AtomicBoolean(false); + + public ProxyConnection(Transport localTransport, Transport remoteTransport) { + this.localTransport = localTransport; + this.remoteTransport = remoteTransport; + } + + public void onFailure(IOException e) { + if( !shuttingDown.get() ) { + log.debug("Transport error: "+e,e); + try { + stop(); + } catch (Exception ignore) { + } + } + } + + public void start() throws Exception { + if( !running.compareAndSet(false, true) ) { + return; + } + + this.localTransport.setTransportListener(new TransportListener() { + public void onCommand(Command command) { + boolean shutdown=false; + if( command.getClass() == ShutdownInfo.class ) { + shuttingDown.set(true); + shutdown=true; + } + try { + remoteTransport.oneway(command); + if( shutdown ) + stop(); + } catch (IOException error) { + onFailure(error); + } catch (Exception error) { + onFailure(IOExceptionSupport.create(error)); + } + } + public void onException(IOException error) { + onFailure(error); + } + }); + + this.remoteTransport.setTransportListener(new TransportListener() { + public void onCommand(Command command) { + try { + localTransport.oneway(command); + } catch (IOException error) { + onFailure(error); + } + } + public void onException(IOException error) { + onFailure(error); + } + }); + + localTransport.start(); + remoteTransport.start(); + } + + public void stop() throws Exception { + if( !running.compareAndSet(true, false) ) { + return; + } + shuttingDown.set(true); + ServiceStopper ss = new ServiceStopper(); + ss.stop(localTransport); + ss.stop(remoteTransport); + ss.throwFirstException(); + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/proxy/ProxyConnector.java b/activemq-core/src/main/java/org/activemq/proxy/ProxyConnector.java new file mode 100644 index 0000000000..cacfcb1a18 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/proxy/ProxyConnector.java @@ -0,0 +1,129 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.proxy; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activemq.Service; +import org.activemq.transport.CompositeTransport; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportAcceptListener; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportServer; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @org.xbean.XBean + * + * @version $Revision$ + */ +public class ProxyConnector implements Service { + + private static final Log log = LogFactory.getLog(ProxyConnector.class); + private TransportServer server; + private URI bind; + private URI remote; + private URI localUri; + + public void start() throws Exception { + + this.getServer().setAcceptListener(new TransportAcceptListener() { + public void onAccept(Transport localTransport) { + try { + Transport remoteTransport = createRemoteTransport(); + ProxyConnection connection = new ProxyConnection(localTransport, remoteTransport); + connection.start(); + } + catch (Exception e) { + onAcceptError(e); + } + } + + public void onAcceptError(Exception error) { + log.error("Could not accept connection: " + error, error); + } + }); + getServer().start(); + + } + + public void stop() throws Exception { + if( this.server!=null ) { + this.server.stop(); + } + } + + // Properties + // ------------------------------------------------------------------------- + + public URI getLocalUri() { + return localUri; + } + + public void setLocalUri(URI localURI) { + this.localUri = localURI; + } + + public URI getBind() { + return bind; + } + + public void setBind(URI bind) { + this.bind = bind; + } + + public URI getRemote() { + return remote; + } + + public void setRemote(URI remote) { + this.remote = remote; + } + + public TransportServer getServer() throws IOException, URISyntaxException { + if (server == null) { + server = createServer(); + } + return server; + } + + public void setServer(TransportServer server) { + this.server = server; + } + + protected TransportServer createServer() throws IOException, URISyntaxException { + if (bind == null) { + throw new IllegalArgumentException("You must specify either a server or the bind property"); + } + return TransportFactory.bind(null, bind); + } + + private Transport createRemoteTransport() throws Exception { + Transport transport = TransportFactory.compositeConnect(remote); + CompositeTransport ct = (CompositeTransport) transport.narrow(CompositeTransport.class); + if( ct !=null && localUri!=null ) { + ct.add(new URI[]{localUri}); + } + return transport; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/security/JaasAuthenticationBroker.java b/activemq-core/src/main/java/org/activemq/security/JaasAuthenticationBroker.java new file mode 100644 index 0000000000..40550af944 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/security/JaasAuthenticationBroker.java @@ -0,0 +1,110 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.security; + +import java.util.Iterator; +import java.util.Set; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; + +import org.activemq.broker.Broker; +import org.activemq.broker.BrokerFilter; +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ConnectionInfo; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + + +/** + * Logs a user in using JAAS. + * + * @version $Revision$ + */ +public class JaasAuthenticationBroker extends BrokerFilter { + + private final String jassConfiguration; + private final CopyOnWriteArrayList securityContexts = new CopyOnWriteArrayList(); + + public JaasAuthenticationBroker(Broker next, String jassConfiguration) { + super(next); + this.jassConfiguration = jassConfiguration; + } + + static class JaasSecurityContext extends SecurityContext { + + private final Subject subject; + + public JaasSecurityContext(String userName, Subject subject) { + super(userName); + this.subject = subject; + } + + public Set getPrincipals() { + return subject.getPrincipals(); + } + + } + + public void addConnection(ConnectionContext context, ConnectionInfo info) throws Throwable { + + if( context.getSecurityContext()==null ) { + // Set the TCCL since it seems JAAS needs it to find the login module classes. + ClassLoader original = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(JaasAuthenticationBroker.class.getClassLoader()); + try { + // Do the login. + try { + JassCredentialCallback callback = new JassCredentialCallback(info.getUserName(), info.getPassword()); + LoginContext lc = new LoginContext(jassConfiguration, callback); + lc.login(); + Subject subject = lc.getSubject(); + + SecurityContext s = new JaasSecurityContext(info.getUserName(), subject); + context.setSecurityContext(s); + securityContexts.add(s); + } catch (Exception e) { + throw (SecurityException)new SecurityException("User name or password is invalid.").initCause(e); + } + } finally { + Thread.currentThread().setContextClassLoader(original); + } + } + super.addConnection(context, info); + } + + public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Throwable { + super.removeConnection(context, info, error); + if( securityContexts.remove(context.getSecurityContext()) ) { + context.setSecurityContext(null); + } + } + + /** + * Previously logged in users may no longer have the same access anymore. Refresh + * all the logged into users. + */ + public void refresh() { + for (Iterator iter = securityContexts.iterator(); iter.hasNext();) { + SecurityContext sc = (SecurityContext) iter.next(); + sc.getAuthorizedReadDests().clear(); + sc.getAuthorizedWriteDests().clear(); + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/security/JassCredentialCallback.java b/activemq-core/src/main/java/org/activemq/security/JassCredentialCallback.java new file mode 100644 index 0000000000..3c3cc85caa --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/security/JassCredentialCallback.java @@ -0,0 +1,49 @@ +/** + * + * Copyright 2004 The Apache Software Foundation + * + * Licensed 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.activemq.security; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import java.io.IOException; + + +/** + * A JASS username password CallbackHandler. + */ +public class JassCredentialCallback implements CallbackHandler { + + private final String username; + private final String password; + + public JassCredentialCallback(String username, String password) { + this.username = username; + this.password = password; + } + + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword(password.toCharArray()); + } else if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName(username); + } + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/security/SecurityAdminMBean.java b/activemq-core/src/main/java/org/activemq/security/SecurityAdminMBean.java new file mode 100644 index 0000000000..1c08123e0e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/security/SecurityAdminMBean.java @@ -0,0 +1,44 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.security; + +/** + * An MBean for adding and removing users, roles + * and destinations. + * + * @version $Revision: 1.1 $ + */ +public interface SecurityAdminMBean { + + public static final String OPERATION_READ = "read"; + public static final String OPERATION_WRITE = "write"; + public static final String OPERATION_ADMIN = "admin"; + + public void addRole(String role); + public void removeRole(String role); + + public void addUserRole(String user, String role); + public void removeUserRole(String user, String role); + + public void addTopicRole(String topic, String operation, String role); + public void removeTopicRole(String topic, String operation, String role); + + public void addQueueRole(String queue, String operation, String role); + public void removeQueueRole(String queue, String operation, String role); + +} diff --git a/activemq-core/src/main/java/org/activemq/security/SecurityContext.java b/activemq-core/src/main/java/org/activemq/security/SecurityContext.java new file mode 100644 index 0000000000..720e8a440d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/security/SecurityContext.java @@ -0,0 +1,43 @@ +package org.activemq.security; + +import java.util.HashSet; +import java.util.Set; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * Used to cache up authorizations so that subsequent requests are faster. + * + * @version $Revision$ + */ +abstract public class SecurityContext { + + final String userName; + + final ConcurrentHashMap authorizedReadDests = new ConcurrentHashMap(); + final ConcurrentHashMap authorizedWriteDests = new ConcurrentHashMap(); + + public SecurityContext(String userName) { + this.userName = userName; + } + + public boolean isInOneOf(Set allowedPrincipals) { + HashSet set = new HashSet(getPrincipals()); + set.retainAll(allowedPrincipals); + return set.size()>0; + } + + abstract public Set getPrincipals(); + + public String getUserName() { + return userName; + } + + public ConcurrentHashMap getAuthorizedReadDests() { + return authorizedReadDests; + } + public ConcurrentHashMap getAuthorizedWriteDests() { + return authorizedWriteDests; + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/security/SimpleAuthenticationBroker.java b/activemq-core/src/main/java/org/activemq/security/SimpleAuthenticationBroker.java new file mode 100644 index 0000000000..306c0f78c7 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/security/SimpleAuthenticationBroker.java @@ -0,0 +1,90 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.security; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.activemq.broker.Broker; +import org.activemq.broker.BrokerFilter; +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ConnectionInfo; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + + +/** + * Handles authenticating a users against a simple user name/password map. + * + * @version $Revision$ + */ +public class SimpleAuthenticationBroker extends BrokerFilter { + + private final Map userPasswords; + private final Map userGroups; + private final CopyOnWriteArrayList securityContexts = new CopyOnWriteArrayList(); + + public SimpleAuthenticationBroker(Broker next, Map userPasswords, Map userGroups) { + super(next); + this.userPasswords = userPasswords; + this.userGroups = userGroups; + } + + public void addConnection(ConnectionContext context, ConnectionInfo info) throws Throwable { + + if( context.getSecurityContext()==null ) { + // Check the username and password. + String pw = (String) userPasswords.get(info.getUserName()); + if( pw == null || !pw.equals(info.getPassword()) ) + throw new SecurityException("User name or password is invalid."); + + final Set groups = (Set)userGroups.get(info.getUserName()); + SecurityContext s = new SecurityContext(info.getUserName()) { + public Set getPrincipals() { + return groups; + } + }; + + context.setSecurityContext(s); + securityContexts.add(s); + } + super.addConnection(context, info); + } + + public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Throwable { + super.removeConnection(context, info, error); + if( securityContexts.remove(context.getSecurityContext()) ) { + context.setSecurityContext(null); + } + } + + /** + * Previously logged in users may no longer have the same access anymore. Refresh + * all the logged into users. + */ + public void refresh() { + for (Iterator iter = securityContexts.iterator(); iter.hasNext();) { + SecurityContext sc = (SecurityContext) iter.next(); + sc.getAuthorizedReadDests().clear(); + sc.getAuthorizedWriteDests().clear(); + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/security/SimpleAuthorizationBroker.java b/activemq-core/src/main/java/org/activemq/security/SimpleAuthorizationBroker.java new file mode 100644 index 0000000000..b7aed2e5ec --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/security/SimpleAuthorizationBroker.java @@ -0,0 +1,208 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.security; + +import org.activemq.broker.Broker; +import org.activemq.broker.BrokerFilter; +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.Destination; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTempDestination; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.ProducerInfo; +import org.activemq.filter.BooleanExpression; +import org.activemq.filter.DestinationMap; +import org.activemq.filter.MessageEvaluationContext; + +import javax.jms.JMSException; + +import java.util.Set; + + +/** + * Verifies if a authenticated user can do an operation against the broker. + * + * @version $Revision$ + */ +public class SimpleAuthorizationBroker extends BrokerFilter implements SecurityAdminMBean { + + private final DestinationMap writeACLs; + private final DestinationMap readACLs; + private final DestinationMap adminACLs; + private boolean filterReads = true; + + public SimpleAuthorizationBroker(Broker next, DestinationMap writeACLs, DestinationMap readACLs, DestinationMap adminACLs) { + super(next); + this.writeACLs = writeACLs; + this.readACLs = readACLs; + this.adminACLs = adminACLs; + } + + public Destination addDestination(ConnectionContext context, ActiveMQDestination destination) throws Throwable { + final SecurityContext securityContext = (SecurityContext) context.getSecurityContext(); + if( securityContext == null ) + throw new SecurityException("User is not authenticated."); + + // You don't need to be an admin to create temp destinations. + if( !destination.isTemporary() + || !((ActiveMQTempDestination)destination).getConnectionId().equals(context.getConnectionId().getConnectionId()) ) { + + Set allowedACLs = adminACLs.get(destination); + if(allowedACLs!=null && !securityContext.isInOneOf(allowedACLs)) + throw new SecurityException("User "+securityContext.getUserName()+" is not authorized to create: "+destination); + } + + return super.addDestination(context, destination); + } + + public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Throwable { + + final SecurityContext securityContext = (SecurityContext) context.getSecurityContext(); + if( securityContext == null ) + throw new SecurityException("User is not authenticated."); + + // You don't need to be an admin to remove temp destinations. + if( !destination.isTemporary() + || !((ActiveMQTempDestination)destination).getConnectionId().equals(context.getConnectionId().getConnectionId()) ) { + + Set allowedACLs = adminACLs.get(destination); + if(allowedACLs!=null && !securityContext.isInOneOf(allowedACLs)) + throw new SecurityException("User "+securityContext.getUserName()+" is not authorized to remove: "+destination); + } + + super.removeDestination(context, destination, timeout); + } + + public void addConsumer(ConnectionContext context, ConsumerInfo info) throws Throwable { + + final SecurityContext subject = (SecurityContext) context.getSecurityContext(); + if( subject == null ) + throw new SecurityException("User is not authenticated."); + + Set allowedACLs = readACLs.get(info.getDestination()); + if(allowedACLs!=null && !subject.isInOneOf(allowedACLs)) + throw new SecurityException("User "+subject.getUserName()+" is not authorized to read from: "+info.getDestination()); + subject.getAuthorizedReadDests().put(info.getDestination(), info.getDestination()); + + // Should we install a additional predicate on the consumer? + // This adds a little more overhead, but is more secure. + if( filterReads ) { + + info.setAdditionalPredicate(new BooleanExpression() { + public boolean matches(MessageEvaluationContext message) throws JMSException { + if( !subject.getAuthorizedReadDests().contains(message.getDestination()) ) { + Set allowedACLs = readACLs.get(message.getDestination()); + if(allowedACLs!=null && !subject.isInOneOf(allowedACLs)) + return false; + subject.getAuthorizedReadDests().put(message.getDestination(), message.getDestination()); + } + return true; + } + public Object evaluate(MessageEvaluationContext message) throws JMSException { + return matches(message) ? Boolean.TRUE : Boolean.FALSE; + } + }); + + } + + super.addConsumer(context, info); + } + + public void addProducer(ConnectionContext context, ProducerInfo info) throws Throwable { + + SecurityContext subject = (SecurityContext) context.getSecurityContext(); + if( subject == null ) + throw new SecurityException("User is not authenticated."); + + if( info.getDestination()!=null ) { + Set allowedACLs = writeACLs.get(info.getDestination()); + if(allowedACLs!=null && !subject.isInOneOf(allowedACLs)) + throw new SecurityException("User "+subject.getUserName()+" is not authorized to write to: "+info.getDestination()); + subject.getAuthorizedWriteDests().put(info.getDestination(), info.getDestination()); + } + + super.addProducer(context, info); + } + + public void send(ConnectionContext context, Message messageSend) throws Throwable { + SecurityContext subject = (SecurityContext) context.getSecurityContext(); + if( subject == null ) + throw new SecurityException("User is not authenticated."); + + if( !subject.getAuthorizedWriteDests().contains(messageSend.getDestination()) ) { + Set allowedACLs = writeACLs.get(messageSend.getDestination()); + if(allowedACLs!=null && !subject.isInOneOf(allowedACLs)) + throw new SecurityException("User "+subject.getUserName()+" is not authorized to write to: "+messageSend.getDestination()); + subject.getAuthorizedWriteDests().put(messageSend.getDestination(), messageSend.getDestination()); + } + + super.send(context, messageSend); + } + + // SecurityAdminMBean interface + // ------------------------------------------------------------------------- + + public void addQueueRole(String queue, String operation, String role) { + addDestinationRole(new ActiveMQQueue(queue), operation, role); + } + + public void addTopicRole(String topic, String operation, String role) { + addDestinationRole(new ActiveMQTopic(topic), operation, role); + } + + public void removeQueueRole(String queue, String operation, String role) { + removeDestinationRole(new ActiveMQQueue(queue), operation, role); + } + + public void removeTopicRole(String topic, String operation, String role) { + removeDestinationRole(new ActiveMQTopic(topic), operation, role); + } + + public void addDestinationRole(javax.jms.Destination destination, String operation, String role) { + } + + public void removeDestinationRole(javax.jms.Destination destination, String operation, String role) { + } + + + public void addRole(String role) { + } + + public void addUserRole(String user, String role) { + } + + public void removeRole(String role) { + } + + public void removeUserRole(String user, String role) { + } + + // Properties + // ------------------------------------------------------------------------- + public boolean isFilterReads() { + return filterReads; + } + + public void setFilterReads(boolean filterReads) { + this.filterReads = filterReads; + } +} diff --git a/activemq-core/src/main/java/org/activemq/security/package.html b/activemq-core/src/main/java/org/activemq/security/package.html new file mode 100644 index 0000000000..c4ec211037 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/security/package.html @@ -0,0 +1,11 @@ + + + + + +

+ Plugable Security Adapter framework along with default implementations such as the JAAS implementation. +

+ + + diff --git a/activemq-core/src/main/java/org/activemq/selector/ParseException.java b/activemq-core/src/main/java/org/activemq/selector/ParseException.java new file mode 100755 index 0000000000..5363682306 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/selector/ParseException.java @@ -0,0 +1,192 @@ +/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 2.1 */ +package org.activemq.selector; + +/** + * This exception is thrown when parse errors are encountered. + * You can explicitly create objects of this exception type by + * calling the method generateParseException in the generated + * parser. + * + * You can modify this class to customize your error reporting + * mechanisms so long as you retain the public fields. + */ +public class ParseException extends Exception { + + /** + * This constructor is used by the method "generateParseException" + * in the generated parser. Calling this constructor generates + * a new object of this type with the fields "currentToken", + * "expectedTokenSequences", and "tokenImage" set. The boolean + * flag "specialConstructor" is also set to true to indicate that + * this constructor was used to create this object. + * This constructor calls its super class with the empty string + * to force the "toString" method of parent class "Throwable" to + * print the error message in the form: + * ParseException: + */ + public ParseException(Token currentTokenVal, + int[][] expectedTokenSequencesVal, + String[] tokenImageVal + ) + { + super(""); + specialConstructor = true; + currentToken = currentTokenVal; + expectedTokenSequences = expectedTokenSequencesVal; + tokenImage = tokenImageVal; + } + + /** + * The following constructors are for use by you for whatever + * purpose you can think of. Constructing the exception in this + * manner makes the exception behave in the normal way - i.e., as + * documented in the class "Throwable". The fields "errorToken", + * "expectedTokenSequences", and "tokenImage" do not contain + * relevant information. The JavaCC generated code does not use + * these constructors. + */ + + public ParseException() { + super(); + specialConstructor = false; + } + + public ParseException(String message) { + super(message); + specialConstructor = false; + } + + /** + * This variable determines which constructor was used to create + * this object and thereby affects the semantics of the + * "getMessage" method (see below). + */ + protected boolean specialConstructor; + + /** + * This is the last token that has been consumed successfully. If + * this object has been created due to a parse error, the token + * followng this token will (therefore) be the first error token. + */ + public Token currentToken; + + /** + * Each entry in this array is an array of integers. Each array + * of integers represents a sequence of tokens (by their ordinal + * values) that is expected at this point of the parse. + */ + public int[][] expectedTokenSequences; + + /** + * This is a reference to the "tokenImage" array of the generated + * parser within which the parse error occurred. This array is + * defined in the generated ...Constants interface. + */ + public String[] tokenImage; + + /** + * This method has the standard behavior when this object has been + * created using the standard constructors. Otherwise, it uses + * "currentToken" and "expectedTokenSequences" to generate a parse + * error message and returns it. If this object has been created + * due to a parse error, and you do not catch it (it gets thrown + * from the parser), then this method is called during the printing + * of the final stack trace, and hence the correct error message + * gets displayed. + */ + public String getMessage() { + if (!specialConstructor) { + return super.getMessage(); + } + String expected = ""; + int maxSize = 0; + for (int i = 0; i < expectedTokenSequences.length; i++) { + if (maxSize < expectedTokenSequences[i].length) { + maxSize = expectedTokenSequences[i].length; + } + for (int j = 0; j < expectedTokenSequences[i].length; j++) { + expected += tokenImage[expectedTokenSequences[i][j]] + " "; + } + if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) { + expected += "..."; + } + expected += eol + " "; + } + String retval = "Encountered \""; + Token tok = currentToken.next; + for (int i = 0; i < maxSize; i++) { + if (i != 0) retval += " "; + if (tok.kind == 0) { + retval += tokenImage[0]; + break; + } + retval += add_escapes(tok.image); + tok = tok.next; + } + retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn; + retval += "." + eol; + if (expectedTokenSequences.length == 1) { + retval += "Was expecting:" + eol + " "; + } else { + retval += "Was expecting one of:" + eol + " "; + } + retval += expected; + return retval; + } + + /** + * The end of line string for this machine. + */ + protected String eol = System.getProperty("line.separator", "\n"); + + /** + * Used to convert raw characters to their escaped version + * when these raw version cannot be used as part of an ASCII + * string literal. + */ + protected String add_escapes(String str) { + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) + { + case 0 : + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/selector/SelectorParser.java b/activemq-core/src/main/java/org/activemq/selector/SelectorParser.java new file mode 100755 index 0000000000..d48c2fef71 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/selector/SelectorParser.java @@ -0,0 +1,1280 @@ +/* Generated By:JavaCC: Do not edit this line. SelectorParser.java */ +package org.activemq.selector; + +import java.io.*; +import java.util.*; + +import javax.jms.InvalidSelectorException; + +import org.activemq.filter.*; + +/** + * JMS Selector Parser generated by JavaCC + * + * Do not edit this .java file directly - it is autogenerated from SelectorParser.jj + */ +public class SelectorParser implements SelectorParserConstants { + + public SelectorParser() { + this(new StringReader("")); + } + + public BooleanExpression parse(String sql) throws InvalidSelectorException { + this.ReInit(new StringReader(sql)); + + try { + return this.JmsSelector(); + } + catch (Throwable e) { + throw (InvalidSelectorException)new InvalidSelectorException(sql).initCause(e); + } + + } + + private BooleanExpression asBooleanExpression(Expression value) throws ParseException { + if (value instanceof BooleanExpression) { + return (BooleanExpression) value; + } + if (value instanceof PropertyExpression) { + return UnaryExpression.createBooleanCast( value ); + } + throw new ParseException("Expression will not result in a boolean value: " + value); + } + +// ---------------------------------------------------------------------------- +// Grammer +// ---------------------------------------------------------------------------- + final public BooleanExpression JmsSelector() throws ParseException { + Expression left=null; + left = orExpression(); + {if (true) return asBooleanExpression(left);} + throw new Error("Missing return statement in function"); + } + + final public Expression orExpression() throws ParseException { + Expression left; + Expression right; + left = andExpression(); + label_1: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case OR: + ; + break; + default: + break label_1; + } + jj_consume_token(OR); + right = andExpression(); + left = LogicExpression.createOR(asBooleanExpression(left), asBooleanExpression(right)); + } + {if (true) return left;} + throw new Error("Missing return statement in function"); + } + + final public Expression andExpression() throws ParseException { + Expression left; + Expression right; + left = equalityExpression(); + label_2: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case AND: + ; + break; + default: + break label_2; + } + jj_consume_token(AND); + right = equalityExpression(); + left = LogicExpression.createAND(asBooleanExpression(left), asBooleanExpression(right)); + } + {if (true) return left;} + throw new Error("Missing return statement in function"); + } + + final public Expression equalityExpression() throws ParseException { + Expression left; + Expression right; + left = comparisonExpression(); + label_3: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IS: + case 28: + case 29: + ; + break; + default: + break label_3; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case 28: + jj_consume_token(28); + right = comparisonExpression(); + left = ComparisonExpression.createEqual(left, right); + break; + case 29: + jj_consume_token(29); + right = comparisonExpression(); + left = ComparisonExpression.createNotEqual(left, right); + break; + default: + if (jj_2_1(2)) { + jj_consume_token(IS); + jj_consume_token(NULL); + left = ComparisonExpression.createIsNull(left); + } else { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IS: + jj_consume_token(IS); + jj_consume_token(NOT); + jj_consume_token(NULL); + left = ComparisonExpression.createIsNotNull(left); + break; + default: + jj_consume_token(-1); + throw new ParseException(); + } + } + } + } + {if (true) return left;} + throw new Error("Missing return statement in function"); + } + + final public Expression comparisonExpression() throws ParseException { + Expression left; + Expression right; + Expression low; + Expression high; + String t, u; + boolean not; + ArrayList list; + left = addExpression(); + label_4: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case NOT: + case BETWEEN: + case LIKE: + case IN: + case 30: + case 31: + case 32: + case 33: + ; + break; + default: + break label_4; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case 30: + jj_consume_token(30); + right = addExpression(); + left = ComparisonExpression.createGreaterThan(left, right); + break; + case 31: + jj_consume_token(31); + right = addExpression(); + left = ComparisonExpression.createGreaterThanEqual(left, right); + break; + case 32: + jj_consume_token(32); + right = addExpression(); + left = ComparisonExpression.createLessThan(left, right); + break; + case 33: + jj_consume_token(33); + right = addExpression(); + left = ComparisonExpression.createLessThanEqual(left, right); + break; + case LIKE: + u=null; + jj_consume_token(LIKE); + t = stringLitteral(); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case ESCAPE: + jj_consume_token(ESCAPE); + u = stringLitteral(); + break; + default: + ; + } + left = ComparisonExpression.createLike(left, t, u); + break; + default: + if (jj_2_2(2)) { + u=null; + jj_consume_token(NOT); + jj_consume_token(LIKE); + t = stringLitteral(); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case ESCAPE: + jj_consume_token(ESCAPE); + u = stringLitteral(); + break; + default: + ; + } + left = ComparisonExpression.createNotLike(left, t, u); + } else { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case BETWEEN: + jj_consume_token(BETWEEN); + low = addExpression(); + jj_consume_token(AND); + high = addExpression(); + left = ComparisonExpression.createBetween(left, low, high); + break; + default: + if (jj_2_3(2)) { + jj_consume_token(NOT); + jj_consume_token(BETWEEN); + low = addExpression(); + jj_consume_token(AND); + high = addExpression(); + left = ComparisonExpression.createNotBetween(left, low, high); + } else { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IN: + jj_consume_token(IN); + jj_consume_token(34); + t = stringLitteral(); + list = new ArrayList(); + list.add( t ); + label_5: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case 35: + ; + break; + default: + break label_5; + } + jj_consume_token(35); + t = stringLitteral(); + list.add( t ); + } + jj_consume_token(36); + left = ComparisonExpression.createInFilter(left, list); + break; + default: + if (jj_2_4(2)) { + jj_consume_token(NOT); + jj_consume_token(IN); + jj_consume_token(34); + t = stringLitteral(); + list = new ArrayList(); + list.add( t ); + label_6: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case 35: + ; + break; + default: + break label_6; + } + jj_consume_token(35); + t = stringLitteral(); + list.add( t ); + } + jj_consume_token(36); + left = ComparisonExpression.createNotInFilter(left, list); + } else { + jj_consume_token(-1); + throw new ParseException(); + } + } + } + } + } + } + } + {if (true) return left;} + throw new Error("Missing return statement in function"); + } + + final public Expression addExpression() throws ParseException { + Expression left; + Expression right; + left = multExpr(); + label_7: + while (true) { + if (jj_2_5(2147483647)) { + ; + } else { + break label_7; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case 37: + jj_consume_token(37); + right = multExpr(); + left = ArithmeticExpression.createPlus(left, right); + break; + case 38: + jj_consume_token(38); + right = multExpr(); + left = ArithmeticExpression.createMinus(left, right); + break; + default: + jj_consume_token(-1); + throw new ParseException(); + } + } + {if (true) return left;} + throw new Error("Missing return statement in function"); + } + + final public Expression multExpr() throws ParseException { + Expression left; + Expression right; + left = unaryExpr(); + label_8: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case 39: + case 40: + case 41: + ; + break; + default: + break label_8; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case 39: + jj_consume_token(39); + right = unaryExpr(); + left = ArithmeticExpression.createMultiply(left, right); + break; + case 40: + jj_consume_token(40); + right = unaryExpr(); + left = ArithmeticExpression.createDivide(left, right); + break; + case 41: + jj_consume_token(41); + right = unaryExpr(); + left = ArithmeticExpression.createMod(left, right); + break; + default: + jj_consume_token(-1); + throw new ParseException(); + } + } + {if (true) return left;} + throw new Error("Missing return statement in function"); + } + + final public Expression unaryExpr() throws ParseException { + String s=null; + Expression left=null; + if (jj_2_6(2147483647)) { + jj_consume_token(37); + left = unaryExpr(); + } else { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case 38: + jj_consume_token(38); + left = unaryExpr(); + left = UnaryExpression.createNegate(left); + break; + case NOT: + jj_consume_token(NOT); + left = unaryExpr(); + left = UnaryExpression.createNOT( asBooleanExpression(left) ); + break; + case XPATH: + jj_consume_token(XPATH); + s = stringLitteral(); + left = UnaryExpression.createXPath( s ); + break; + case XQUERY: + jj_consume_token(XQUERY); + s = stringLitteral(); + left = UnaryExpression.createXQuery( s ); + break; + case TRUE: + case FALSE: + case NULL: + case DECIMAL_LITERAL: + case HEX_LITERAL: + case OCTAL_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case ID: + case 34: + left = primaryExpr(); + break; + default: + jj_consume_token(-1); + throw new ParseException(); + } + } + {if (true) return left;} + throw new Error("Missing return statement in function"); + } + + final public Expression primaryExpr() throws ParseException { + Expression left=null; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case TRUE: + case FALSE: + case NULL: + case DECIMAL_LITERAL: + case HEX_LITERAL: + case OCTAL_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + left = literal(); + break; + case ID: + left = variable(); + break; + case 34: + jj_consume_token(34); + left = orExpression(); + jj_consume_token(36); + break; + default: + jj_consume_token(-1); + throw new ParseException(); + } + {if (true) return left;} + throw new Error("Missing return statement in function"); + } + + final public ConstantExpression literal() throws ParseException { + Token t; + String s; + ConstantExpression left=null; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case STRING_LITERAL: + s = stringLitteral(); + left = new ConstantExpression(s); + break; + case DECIMAL_LITERAL: + t = jj_consume_token(DECIMAL_LITERAL); + left = ConstantExpression.createFromDecimal(t.image); + break; + case HEX_LITERAL: + t = jj_consume_token(HEX_LITERAL); + left = ConstantExpression.createFromHex(t.image); + break; + case OCTAL_LITERAL: + t = jj_consume_token(OCTAL_LITERAL); + left = ConstantExpression.createFromOctal(t.image); + break; + case FLOATING_POINT_LITERAL: + t = jj_consume_token(FLOATING_POINT_LITERAL); + left = ConstantExpression.createFloat(t.image); + break; + case TRUE: + jj_consume_token(TRUE); + left = ConstantExpression.TRUE; + break; + case FALSE: + jj_consume_token(FALSE); + left = ConstantExpression.FALSE; + break; + case NULL: + jj_consume_token(NULL); + left = ConstantExpression.NULL; + break; + default: + jj_consume_token(-1); + throw new ParseException(); + } + {if (true) return left;} + throw new Error("Missing return statement in function"); + } + + final public String stringLitteral() throws ParseException { + Token t; + StringBuffer rc = new StringBuffer(); + boolean first=true; + t = jj_consume_token(STRING_LITERAL); + // Decode the sting value. + String image = t.image; + for( int i=1; i < image.length()-1; i++ ) { + char c = image.charAt(i); + if( c == '\'' ) + i++; + rc.append(c); + } + {if (true) return rc.toString();} + throw new Error("Missing return statement in function"); + } + + final public PropertyExpression variable() throws ParseException { + Token t; + PropertyExpression left=null; + t = jj_consume_token(ID); + left = new PropertyExpression(t.image); + {if (true) return left;} + throw new Error("Missing return statement in function"); + } + + final private boolean jj_2_1(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + return !jj_3_1(); + } + + final private boolean jj_2_2(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + return !jj_3_2(); + } + + final private boolean jj_2_3(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + return !jj_3_3(); + } + + final private boolean jj_2_4(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + return !jj_3_4(); + } + + final private boolean jj_2_5(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + return !jj_3_5(); + } + + final private boolean jj_2_6(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + return !jj_3_6(); + } + + final private boolean jj_3R_53() { + if (jj_scan_token(32)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_45()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_29() { + if (jj_scan_token(ID)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_63() { + if (jj_scan_token(35)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_23()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_50() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_58()) { + jj_scanpos = xsp; + if (jj_3R_59()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_40() { + if (jj_scan_token(OR)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_39()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_52() { + if (jj_scan_token(31)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_45()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_27() { + if (jj_scan_token(34)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_30()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_scan_token(36)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_26() { + if (jj_3R_29()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_51() { + if (jj_scan_token(30)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_45()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_46() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_51()) { + jj_scanpos = xsp; + if (jj_3R_52()) { + jj_scanpos = xsp; + if (jj_3R_53()) { + jj_scanpos = xsp; + if (jj_3R_54()) { + jj_scanpos = xsp; + if (jj_3R_55()) { + jj_scanpos = xsp; + if (jj_3_2()) { + jj_scanpos = xsp; + if (jj_3R_56()) { + jj_scanpos = xsp; + if (jj_3_3()) { + jj_scanpos = xsp; + if (jj_3R_57()) { + jj_scanpos = xsp; + if (jj_3_4()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_25() { + if (jj_3R_28()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_45() { + if (jj_3R_11()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_50()) { jj_scanpos = xsp; break; } + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } + return false; + } + + final private boolean jj_3R_30() { + if (jj_3R_39()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_40()) { jj_scanpos = xsp; break; } + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } + return false; + } + + final private boolean jj_3R_24() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_25()) { + jj_scanpos = xsp; + if (jj_3R_26()) { + jj_scanpos = xsp; + if (jj_3R_27()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_19() { + if (jj_3R_24()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_23() { + if (jj_scan_token(STRING_LITERAL)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_62() { + if (jj_scan_token(35)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_23()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_18() { + if (jj_scan_token(XQUERY)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_23()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_43() { + if (jj_3R_45()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_46()) { jj_scanpos = xsp; break; } + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } + return false; + } + + final private boolean jj_3R_61() { + if (jj_scan_token(ESCAPE)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_23()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3_4() { + if (jj_scan_token(NOT)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_scan_token(IN)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_scan_token(34)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_23()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_63()) { jj_scanpos = xsp; break; } + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } + if (jj_scan_token(36)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3_6() { + if (jj_scan_token(37)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_12()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_17() { + if (jj_scan_token(XPATH)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_23()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_38() { + if (jj_scan_token(NULL)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_16() { + if (jj_scan_token(NOT)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_12()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_14() { + if (jj_scan_token(37)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_12()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_57() { + if (jj_scan_token(IN)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_scan_token(34)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_23()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_62()) { jj_scanpos = xsp; break; } + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } + if (jj_scan_token(36)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_49() { + if (jj_scan_token(IS)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_scan_token(NOT)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_scan_token(NULL)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_15() { + if (jj_scan_token(38)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_12()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_37() { + if (jj_scan_token(FALSE)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3_1() { + if (jj_scan_token(IS)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_scan_token(NULL)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_12() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_14()) { + jj_scanpos = xsp; + if (jj_3R_15()) { + jj_scanpos = xsp; + if (jj_3R_16()) { + jj_scanpos = xsp; + if (jj_3R_17()) { + jj_scanpos = xsp; + if (jj_3R_18()) { + jj_scanpos = xsp; + if (jj_3R_19()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_36() { + if (jj_scan_token(TRUE)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_48() { + if (jj_scan_token(29)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_43()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3_3() { + if (jj_scan_token(NOT)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_scan_token(BETWEEN)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_45()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_scan_token(AND)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_45()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_47() { + if (jj_scan_token(28)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_43()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_44() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_47()) { + jj_scanpos = xsp; + if (jj_3R_48()) { + jj_scanpos = xsp; + if (jj_3_1()) { + jj_scanpos = xsp; + if (jj_3R_49()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_35() { + if (jj_scan_token(FLOATING_POINT_LITERAL)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_56() { + if (jj_scan_token(BETWEEN)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_45()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_scan_token(AND)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_45()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_60() { + if (jj_scan_token(ESCAPE)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_23()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_34() { + if (jj_scan_token(OCTAL_LITERAL)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_22() { + if (jj_scan_token(41)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_12()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_41() { + if (jj_3R_43()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_44()) { jj_scanpos = xsp; break; } + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } + return false; + } + + final private boolean jj_3_2() { + if (jj_scan_token(NOT)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_scan_token(LIKE)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_23()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_61()) jj_scanpos = xsp; + else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_55() { + if (jj_scan_token(LIKE)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_23()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_60()) jj_scanpos = xsp; + else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_33() { + if (jj_scan_token(HEX_LITERAL)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_21() { + if (jj_scan_token(40)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_12()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_13() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_20()) { + jj_scanpos = xsp; + if (jj_3R_21()) { + jj_scanpos = xsp; + if (jj_3R_22()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_20() { + if (jj_scan_token(39)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_12()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_42() { + if (jj_scan_token(AND)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_41()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_32() { + if (jj_scan_token(DECIMAL_LITERAL)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_11() { + if (jj_3R_12()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_13()) { jj_scanpos = xsp; break; } + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } + return false; + } + + final private boolean jj_3R_10() { + if (jj_scan_token(38)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_31() { + if (jj_3R_23()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_9() { + if (jj_scan_token(37)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_59() { + if (jj_scan_token(38)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_11()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_39() { + if (jj_3R_41()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_42()) { jj_scanpos = xsp; break; } + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } + return false; + } + + final private boolean jj_3_5() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_9()) { + jj_scanpos = xsp; + if (jj_3R_10()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_11()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_28() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_31()) { + jj_scanpos = xsp; + if (jj_3R_32()) { + jj_scanpos = xsp; + if (jj_3R_33()) { + jj_scanpos = xsp; + if (jj_3R_34()) { + jj_scanpos = xsp; + if (jj_3R_35()) { + jj_scanpos = xsp; + if (jj_3R_36()) { + jj_scanpos = xsp; + if (jj_3R_37()) { + jj_scanpos = xsp; + if (jj_3R_38()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + } else if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_54() { + if (jj_scan_token(33)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_45()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + final private boolean jj_3R_58() { + if (jj_scan_token(37)) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + if (jj_3R_11()) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) return false; + return false; + } + + public SelectorParserTokenManager token_source; + SimpleCharStream jj_input_stream; + public Token token, jj_nt; + private int jj_ntk; + private Token jj_scanpos, jj_lastpos; + private int jj_la; + public boolean lookingAhead = false; + private boolean jj_semLA; + + public SelectorParser(java.io.InputStream stream) { + jj_input_stream = new SimpleCharStream(stream, 1, 1); + token_source = new SelectorParserTokenManager(jj_input_stream); + token = new Token(); + jj_ntk = -1; + } + + public void ReInit(java.io.InputStream stream) { + jj_input_stream.ReInit(stream, 1, 1); + token_source.ReInit(jj_input_stream); + token = new Token(); + jj_ntk = -1; + } + + public SelectorParser(java.io.Reader stream) { + jj_input_stream = new SimpleCharStream(stream, 1, 1); + token_source = new SelectorParserTokenManager(jj_input_stream); + token = new Token(); + jj_ntk = -1; + } + + public void ReInit(java.io.Reader stream) { + jj_input_stream.ReInit(stream, 1, 1); + token_source.ReInit(jj_input_stream); + token = new Token(); + jj_ntk = -1; + } + + public SelectorParser(SelectorParserTokenManager tm) { + token_source = tm; + token = new Token(); + jj_ntk = -1; + } + + public void ReInit(SelectorParserTokenManager tm) { + token_source = tm; + token = new Token(); + jj_ntk = -1; + } + + final private Token jj_consume_token(int kind) throws ParseException { + Token oldToken; + if ((oldToken = token).next != null) token = token.next; + else token = token.next = token_source.getNextToken(); + jj_ntk = -1; + if (token.kind == kind) { + return token; + } + token = oldToken; + throw generateParseException(); + } + + final private boolean jj_scan_token(int kind) { + if (jj_scanpos == jj_lastpos) { + jj_la--; + if (jj_scanpos.next == null) { + jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken(); + } else { + jj_lastpos = jj_scanpos = jj_scanpos.next; + } + } else { + jj_scanpos = jj_scanpos.next; + } + return (jj_scanpos.kind != kind); + } + + final public Token getNextToken() { + if (token.next != null) token = token.next; + else token = token.next = token_source.getNextToken(); + jj_ntk = -1; + return token; + } + + final public Token getToken(int index) { + Token t = lookingAhead ? jj_scanpos : token; + for (int i = 0; i < index; i++) { + if (t.next != null) t = t.next; + else t = t.next = token_source.getNextToken(); + } + return t; + } + + final private int jj_ntk() { + if ((jj_nt=token.next) == null) + return (jj_ntk = (token.next=token_source.getNextToken()).kind); + else + return (jj_ntk = jj_nt.kind); + } + + final public ParseException generateParseException() { + Token errortok = token.next; + int line = errortok.beginLine, column = errortok.beginColumn; + String mess = (errortok.kind == 0) ? tokenImage[0] : errortok.image; + return new ParseException("Parse error at line " + line + ", column " + column + ". Encountered: " + mess); + } + + final public void enable_tracing() { + } + + final public void disable_tracing() { + } + +} diff --git a/activemq-core/src/main/java/org/activemq/selector/SelectorParserConstants.java b/activemq-core/src/main/java/org/activemq/selector/SelectorParserConstants.java new file mode 100755 index 0000000000..a1cb94ebde --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/selector/SelectorParserConstants.java @@ -0,0 +1,77 @@ +/* Generated By:JavaCC: Do not edit this line. SelectorParserConstants.java */ +package org.activemq.selector; + +public interface SelectorParserConstants { + + int EOF = 0; + int LINE_COMMENT = 6; + int BLOCK_COMMENT = 7; + int NOT = 8; + int AND = 9; + int OR = 10; + int BETWEEN = 11; + int LIKE = 12; + int ESCAPE = 13; + int IN = 14; + int IS = 15; + int TRUE = 16; + int FALSE = 17; + int NULL = 18; + int XPATH = 19; + int XQUERY = 20; + int DECIMAL_LITERAL = 21; + int HEX_LITERAL = 22; + int OCTAL_LITERAL = 23; + int FLOATING_POINT_LITERAL = 24; + int EXPONENT = 25; + int STRING_LITERAL = 26; + int ID = 27; + + int DEFAULT = 0; + + String[] tokenImage = { + "", + "\" \"", + "\"\\t\"", + "\"\\n\"", + "\"\\r\"", + "\"\\f\"", + "", + "", + "\"NOT\"", + "\"AND\"", + "\"OR\"", + "\"BETWEEN\"", + "\"LIKE\"", + "\"ESCAPE\"", + "\"IN\"", + "\"IS\"", + "\"TRUE\"", + "\"FALSE\"", + "\"NULL\"", + "\"XPATH\"", + "\"XQUERY\"", + "", + "", + "", + "", + "", + "", + "", + "\"=\"", + "\"<>\"", + "\">\"", + "\">=\"", + "\"<\"", + "\"<=\"", + "\"(\"", + "\",\"", + "\")\"", + "\"+\"", + "\"-\"", + "\"*\"", + "\"/\"", + "\"%\"", + }; + +} diff --git a/activemq-core/src/main/java/org/activemq/selector/SelectorParserTokenManager.java b/activemq-core/src/main/java/org/activemq/selector/SelectorParserTokenManager.java new file mode 100755 index 0000000000..3a5e92842d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/selector/SelectorParserTokenManager.java @@ -0,0 +1,1043 @@ +/* Generated By:JavaCC: Do not edit this line. SelectorParserTokenManager.java */ +package org.activemq.selector; +import java.io.*; +import java.util.*; +import javax.jms.InvalidSelectorException; +import org.activemq.filter.*; + +public class SelectorParserTokenManager implements SelectorParserConstants +{ + public java.io.PrintStream debugStream = System.out; + public void setDebugStream(java.io.PrintStream ds) { debugStream = ds; } +private final int jjStopAtPos(int pos, int kind) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + return pos + 1; +} +private final int jjMoveStringLiteralDfa0_0() +{ + switch(curChar) + { + case 9: + jjmatchedKind = 2; + return jjMoveNfa_0(5, 0); + case 10: + jjmatchedKind = 3; + return jjMoveNfa_0(5, 0); + case 12: + jjmatchedKind = 5; + return jjMoveNfa_0(5, 0); + case 13: + jjmatchedKind = 4; + return jjMoveNfa_0(5, 0); + case 32: + jjmatchedKind = 1; + return jjMoveNfa_0(5, 0); + case 37: + jjmatchedKind = 41; + return jjMoveNfa_0(5, 0); + case 40: + jjmatchedKind = 34; + return jjMoveNfa_0(5, 0); + case 41: + jjmatchedKind = 36; + return jjMoveNfa_0(5, 0); + case 42: + jjmatchedKind = 39; + return jjMoveNfa_0(5, 0); + case 43: + jjmatchedKind = 37; + return jjMoveNfa_0(5, 0); + case 44: + jjmatchedKind = 35; + return jjMoveNfa_0(5, 0); + case 45: + jjmatchedKind = 38; + return jjMoveNfa_0(5, 0); + case 47: + jjmatchedKind = 40; + return jjMoveNfa_0(5, 0); + case 60: + jjmatchedKind = 32; + return jjMoveStringLiteralDfa1_0(0x220000000L); + case 61: + jjmatchedKind = 28; + return jjMoveNfa_0(5, 0); + case 62: + jjmatchedKind = 30; + return jjMoveStringLiteralDfa1_0(0x80000000L); + case 65: + return jjMoveStringLiteralDfa1_0(0x200L); + case 66: + return jjMoveStringLiteralDfa1_0(0x800L); + case 69: + return jjMoveStringLiteralDfa1_0(0x2000L); + case 70: + return jjMoveStringLiteralDfa1_0(0x20000L); + case 73: + return jjMoveStringLiteralDfa1_0(0xc000L); + case 76: + return jjMoveStringLiteralDfa1_0(0x1000L); + case 78: + return jjMoveStringLiteralDfa1_0(0x40100L); + case 79: + return jjMoveStringLiteralDfa1_0(0x400L); + case 84: + return jjMoveStringLiteralDfa1_0(0x10000L); + case 88: + return jjMoveStringLiteralDfa1_0(0x180000L); + case 97: + return jjMoveStringLiteralDfa1_0(0x200L); + case 98: + return jjMoveStringLiteralDfa1_0(0x800L); + case 101: + return jjMoveStringLiteralDfa1_0(0x2000L); + case 102: + return jjMoveStringLiteralDfa1_0(0x20000L); + case 105: + return jjMoveStringLiteralDfa1_0(0xc000L); + case 108: + return jjMoveStringLiteralDfa1_0(0x1000L); + case 110: + return jjMoveStringLiteralDfa1_0(0x40100L); + case 111: + return jjMoveStringLiteralDfa1_0(0x400L); + case 116: + return jjMoveStringLiteralDfa1_0(0x10000L); + case 120: + return jjMoveStringLiteralDfa1_0(0x180000L); + default : + return jjMoveNfa_0(5, 0); + } +} +private final int jjMoveStringLiteralDfa1_0(long active0) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + return jjMoveNfa_0(5, 0); + } + switch(curChar) + { + case 61: + if ((active0 & 0x80000000L) != 0L) + { + jjmatchedKind = 31; + jjmatchedPos = 1; + } + else if ((active0 & 0x200000000L) != 0L) + { + jjmatchedKind = 33; + jjmatchedPos = 1; + } + break; + case 62: + if ((active0 & 0x20000000L) != 0L) + { + jjmatchedKind = 29; + jjmatchedPos = 1; + } + break; + case 65: + return jjMoveStringLiteralDfa2_0(active0, 0x20000L); + case 69: + return jjMoveStringLiteralDfa2_0(active0, 0x800L); + case 73: + return jjMoveStringLiteralDfa2_0(active0, 0x1000L); + case 78: + if ((active0 & 0x4000L) != 0L) + { + jjmatchedKind = 14; + jjmatchedPos = 1; + } + return jjMoveStringLiteralDfa2_0(active0, 0x200L); + case 79: + return jjMoveStringLiteralDfa2_0(active0, 0x100L); + case 80: + return jjMoveStringLiteralDfa2_0(active0, 0x80000L); + case 81: + return jjMoveStringLiteralDfa2_0(active0, 0x100000L); + case 82: + if ((active0 & 0x400L) != 0L) + { + jjmatchedKind = 10; + jjmatchedPos = 1; + } + return jjMoveStringLiteralDfa2_0(active0, 0x10000L); + case 83: + if ((active0 & 0x8000L) != 0L) + { + jjmatchedKind = 15; + jjmatchedPos = 1; + } + return jjMoveStringLiteralDfa2_0(active0, 0x2000L); + case 85: + return jjMoveStringLiteralDfa2_0(active0, 0x40000L); + case 97: + return jjMoveStringLiteralDfa2_0(active0, 0x20000L); + case 101: + return jjMoveStringLiteralDfa2_0(active0, 0x800L); + case 105: + return jjMoveStringLiteralDfa2_0(active0, 0x1000L); + case 110: + if ((active0 & 0x4000L) != 0L) + { + jjmatchedKind = 14; + jjmatchedPos = 1; + } + return jjMoveStringLiteralDfa2_0(active0, 0x200L); + case 111: + return jjMoveStringLiteralDfa2_0(active0, 0x100L); + case 112: + return jjMoveStringLiteralDfa2_0(active0, 0x80000L); + case 113: + return jjMoveStringLiteralDfa2_0(active0, 0x100000L); + case 114: + if ((active0 & 0x400L) != 0L) + { + jjmatchedKind = 10; + jjmatchedPos = 1; + } + return jjMoveStringLiteralDfa2_0(active0, 0x10000L); + case 115: + if ((active0 & 0x8000L) != 0L) + { + jjmatchedKind = 15; + jjmatchedPos = 1; + } + return jjMoveStringLiteralDfa2_0(active0, 0x2000L); + case 117: + return jjMoveStringLiteralDfa2_0(active0, 0x40000L); + default : + break; + } + return jjMoveNfa_0(5, 1); +} +private final int jjMoveStringLiteralDfa2_0(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjMoveNfa_0(5, 1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + return jjMoveNfa_0(5, 1); + } + switch(curChar) + { + case 65: + return jjMoveStringLiteralDfa3_0(active0, 0x80000L); + case 67: + return jjMoveStringLiteralDfa3_0(active0, 0x2000L); + case 68: + if ((active0 & 0x200L) != 0L) + { + jjmatchedKind = 9; + jjmatchedPos = 2; + } + break; + case 75: + return jjMoveStringLiteralDfa3_0(active0, 0x1000L); + case 76: + return jjMoveStringLiteralDfa3_0(active0, 0x60000L); + case 84: + if ((active0 & 0x100L) != 0L) + { + jjmatchedKind = 8; + jjmatchedPos = 2; + } + return jjMoveStringLiteralDfa3_0(active0, 0x800L); + case 85: + return jjMoveStringLiteralDfa3_0(active0, 0x110000L); + case 97: + return jjMoveStringLiteralDfa3_0(active0, 0x80000L); + case 99: + return jjMoveStringLiteralDfa3_0(active0, 0x2000L); + case 100: + if ((active0 & 0x200L) != 0L) + { + jjmatchedKind = 9; + jjmatchedPos = 2; + } + break; + case 107: + return jjMoveStringLiteralDfa3_0(active0, 0x1000L); + case 108: + return jjMoveStringLiteralDfa3_0(active0, 0x60000L); + case 116: + if ((active0 & 0x100L) != 0L) + { + jjmatchedKind = 8; + jjmatchedPos = 2; + } + return jjMoveStringLiteralDfa3_0(active0, 0x800L); + case 117: + return jjMoveStringLiteralDfa3_0(active0, 0x110000L); + default : + break; + } + return jjMoveNfa_0(5, 2); +} +private final int jjMoveStringLiteralDfa3_0(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjMoveNfa_0(5, 2); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + return jjMoveNfa_0(5, 2); + } + switch(curChar) + { + case 65: + return jjMoveStringLiteralDfa4_0(active0, 0x2000L); + case 69: + if ((active0 & 0x1000L) != 0L) + { + jjmatchedKind = 12; + jjmatchedPos = 3; + } + else if ((active0 & 0x10000L) != 0L) + { + jjmatchedKind = 16; + jjmatchedPos = 3; + } + return jjMoveStringLiteralDfa4_0(active0, 0x100000L); + case 76: + if ((active0 & 0x40000L) != 0L) + { + jjmatchedKind = 18; + jjmatchedPos = 3; + } + break; + case 83: + return jjMoveStringLiteralDfa4_0(active0, 0x20000L); + case 84: + return jjMoveStringLiteralDfa4_0(active0, 0x80000L); + case 87: + return jjMoveStringLiteralDfa4_0(active0, 0x800L); + case 97: + return jjMoveStringLiteralDfa4_0(active0, 0x2000L); + case 101: + if ((active0 & 0x1000L) != 0L) + { + jjmatchedKind = 12; + jjmatchedPos = 3; + } + else if ((active0 & 0x10000L) != 0L) + { + jjmatchedKind = 16; + jjmatchedPos = 3; + } + return jjMoveStringLiteralDfa4_0(active0, 0x100000L); + case 108: + if ((active0 & 0x40000L) != 0L) + { + jjmatchedKind = 18; + jjmatchedPos = 3; + } + break; + case 115: + return jjMoveStringLiteralDfa4_0(active0, 0x20000L); + case 116: + return jjMoveStringLiteralDfa4_0(active0, 0x80000L); + case 119: + return jjMoveStringLiteralDfa4_0(active0, 0x800L); + default : + break; + } + return jjMoveNfa_0(5, 3); +} +private final int jjMoveStringLiteralDfa4_0(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjMoveNfa_0(5, 3); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + return jjMoveNfa_0(5, 3); + } + switch(curChar) + { + case 69: + if ((active0 & 0x20000L) != 0L) + { + jjmatchedKind = 17; + jjmatchedPos = 4; + } + return jjMoveStringLiteralDfa5_0(active0, 0x800L); + case 72: + if ((active0 & 0x80000L) != 0L) + { + jjmatchedKind = 19; + jjmatchedPos = 4; + } + break; + case 80: + return jjMoveStringLiteralDfa5_0(active0, 0x2000L); + case 82: + return jjMoveStringLiteralDfa5_0(active0, 0x100000L); + case 101: + if ((active0 & 0x20000L) != 0L) + { + jjmatchedKind = 17; + jjmatchedPos = 4; + } + return jjMoveStringLiteralDfa5_0(active0, 0x800L); + case 104: + if ((active0 & 0x80000L) != 0L) + { + jjmatchedKind = 19; + jjmatchedPos = 4; + } + break; + case 112: + return jjMoveStringLiteralDfa5_0(active0, 0x2000L); + case 114: + return jjMoveStringLiteralDfa5_0(active0, 0x100000L); + default : + break; + } + return jjMoveNfa_0(5, 4); +} +private final int jjMoveStringLiteralDfa5_0(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjMoveNfa_0(5, 4); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + return jjMoveNfa_0(5, 4); + } + switch(curChar) + { + case 69: + if ((active0 & 0x2000L) != 0L) + { + jjmatchedKind = 13; + jjmatchedPos = 5; + } + return jjMoveStringLiteralDfa6_0(active0, 0x800L); + case 89: + if ((active0 & 0x100000L) != 0L) + { + jjmatchedKind = 20; + jjmatchedPos = 5; + } + break; + case 101: + if ((active0 & 0x2000L) != 0L) + { + jjmatchedKind = 13; + jjmatchedPos = 5; + } + return jjMoveStringLiteralDfa6_0(active0, 0x800L); + case 121: + if ((active0 & 0x100000L) != 0L) + { + jjmatchedKind = 20; + jjmatchedPos = 5; + } + break; + default : + break; + } + return jjMoveNfa_0(5, 5); +} +private final int jjMoveStringLiteralDfa6_0(long old0, long active0) +{ + if (((active0 &= old0)) == 0L) + return jjMoveNfa_0(5, 5); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + return jjMoveNfa_0(5, 5); + } + switch(curChar) + { + case 78: + if ((active0 & 0x800L) != 0L) + { + jjmatchedKind = 11; + jjmatchedPos = 6; + } + break; + case 110: + if ((active0 & 0x800L) != 0L) + { + jjmatchedKind = 11; + jjmatchedPos = 6; + } + break; + default : + break; + } + return jjMoveNfa_0(5, 6); +} +private final void jjCheckNAdd(int state) +{ + if (jjrounds[state] != jjround) + { + jjstateSet[jjnewStateCnt++] = state; + jjrounds[state] = jjround; + } +} +private final void jjAddStates(int start, int end) +{ + do { + jjstateSet[jjnewStateCnt++] = jjnextStates[start]; + } while (start++ != end); +} +private final void jjCheckNAddTwoStates(int state1, int state2) +{ + jjCheckNAdd(state1); + jjCheckNAdd(state2); +} +private final void jjCheckNAddStates(int start, int end) +{ + do { + jjCheckNAdd(jjnextStates[start]); + } while (start++ != end); +} +private final void jjCheckNAddStates(int start) +{ + jjCheckNAdd(jjnextStates[start]); + jjCheckNAdd(jjnextStates[start + 1]); +} +static final long[] jjbitVec0 = { + 0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +static final long[] jjbitVec2 = { + 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +private final int jjMoveNfa_0(int startState, int curPos) +{ + int strKind = jjmatchedKind; + int strPos = jjmatchedPos; + int seenUpto; + input_stream.backup(seenUpto = curPos + 1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { throw new Error("Internal Error"); } + curPos = 0; + int[] nextStates; + int startsAt = 0; + jjnewStateCnt = 43; + int i = 1; + jjstateSet[0] = startState; + int j, kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 5: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(0, 3); + else if (curChar == 36) + { + if (kind > 27) + kind = 27; + jjCheckNAdd(27); + } + else if (curChar == 39) + jjCheckNAddStates(4, 6); + else if (curChar == 46) + jjCheckNAdd(17); + else if (curChar == 47) + jjstateSet[jjnewStateCnt++] = 6; + else if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 0; + if ((0x3fe000000000000L & l) != 0L) + { + if (kind > 21) + kind = 21; + jjCheckNAddTwoStates(14, 15); + } + else if (curChar == 48) + { + if (kind > 23) + kind = 23; + jjCheckNAddTwoStates(40, 42); + } + break; + case 0: + if (curChar == 45) + jjCheckNAddStates(7, 9); + break; + case 1: + if ((0xffffffffffffdbffL & l) != 0L) + jjCheckNAddStates(7, 9); + break; + case 2: + if ((0x2400L & l) != 0L && kind > 6) + kind = 6; + break; + case 3: + if (curChar == 10 && kind > 6) + kind = 6; + break; + case 4: + if (curChar == 13) + jjstateSet[jjnewStateCnt++] = 3; + break; + case 6: + if (curChar == 42) + jjCheckNAddTwoStates(7, 8); + break; + case 7: + if ((0xfffffbffffffffffL & l) != 0L) + jjCheckNAddTwoStates(7, 8); + break; + case 8: + if (curChar == 42) + jjCheckNAddStates(10, 12); + break; + case 9: + if ((0xffff7bffffffffffL & l) != 0L) + jjCheckNAddTwoStates(10, 8); + break; + case 10: + if ((0xfffffbffffffffffL & l) != 0L) + jjCheckNAddTwoStates(10, 8); + break; + case 11: + if (curChar == 47 && kind > 7) + kind = 7; + break; + case 12: + if (curChar == 47) + jjstateSet[jjnewStateCnt++] = 6; + break; + case 13: + if ((0x3fe000000000000L & l) == 0L) + break; + if (kind > 21) + kind = 21; + jjCheckNAddTwoStates(14, 15); + break; + case 14: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 21) + kind = 21; + jjCheckNAddTwoStates(14, 15); + break; + case 16: + if (curChar == 46) + jjCheckNAdd(17); + break; + case 17: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 24) + kind = 24; + jjCheckNAddTwoStates(17, 18); + break; + case 19: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(20); + break; + case 20: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 24) + kind = 24; + jjCheckNAdd(20); + break; + case 21: + case 22: + if (curChar == 39) + jjCheckNAddStates(4, 6); + break; + case 23: + if (curChar == 39) + jjstateSet[jjnewStateCnt++] = 22; + break; + case 24: + if ((0xffffff7fffffffffL & l) != 0L) + jjCheckNAddStates(4, 6); + break; + case 25: + if (curChar == 39 && kind > 26) + kind = 26; + break; + case 26: + if (curChar != 36) + break; + if (kind > 27) + kind = 27; + jjCheckNAdd(27); + break; + case 27: + if ((0x3ff001000000000L & l) == 0L) + break; + if (kind > 27) + kind = 27; + jjCheckNAdd(27); + break; + case 28: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(0, 3); + break; + case 29: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(29, 30); + break; + case 30: + if (curChar != 46) + break; + if (kind > 24) + kind = 24; + jjCheckNAddTwoStates(31, 32); + break; + case 31: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 24) + kind = 24; + jjCheckNAddTwoStates(31, 32); + break; + case 33: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(34); + break; + case 34: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 24) + kind = 24; + jjCheckNAdd(34); + break; + case 35: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(35, 36); + break; + case 37: + if ((0x280000000000L & l) != 0L) + jjCheckNAdd(38); + break; + case 38: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 24) + kind = 24; + jjCheckNAdd(38); + break; + case 39: + if (curChar != 48) + break; + if (kind > 23) + kind = 23; + jjCheckNAddTwoStates(40, 42); + break; + case 41: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 22) + kind = 22; + jjstateSet[jjnewStateCnt++] = 41; + break; + case 42: + if ((0xff000000000000L & l) == 0L) + break; + if (kind > 23) + kind = 23; + jjCheckNAdd(42); + break; + default : break; + } + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 5: + case 27: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 27) + kind = 27; + jjCheckNAdd(27); + break; + case 1: + jjAddStates(7, 9); + break; + case 7: + jjCheckNAddTwoStates(7, 8); + break; + case 9: + case 10: + jjCheckNAddTwoStates(10, 8); + break; + case 15: + if ((0x100000001000L & l) != 0L && kind > 21) + kind = 21; + break; + case 18: + if ((0x2000000020L & l) != 0L) + jjAddStates(13, 14); + break; + case 24: + jjAddStates(4, 6); + break; + case 32: + if ((0x2000000020L & l) != 0L) + jjAddStates(15, 16); + break; + case 36: + if ((0x2000000020L & l) != 0L) + jjAddStates(17, 18); + break; + case 40: + if ((0x100000001000000L & l) != 0L) + jjCheckNAdd(41); + break; + case 41: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 22) + kind = 22; + jjCheckNAdd(41); + break; + default : break; + } + } while(i != startsAt); + } + else + { + int hiByte = (int)(curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + MatchLoop: do + { + switch(jjstateSet[--i]) + { + case 1: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjAddStates(7, 9); + break; + case 7: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjCheckNAddTwoStates(7, 8); + break; + case 9: + case 10: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjCheckNAddTwoStates(10, 8); + break; + case 24: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) + jjAddStates(4, 6); + break; + default : break; + } + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 43 - (jjnewStateCnt = startsAt))) + break; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { break; } + } + if (jjmatchedPos > strPos) + return curPos; + + int toRet = Math.max(curPos, seenUpto); + + if (curPos < toRet) + for (i = toRet - Math.min(curPos, seenUpto); i-- > 0; ) + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { throw new Error("Internal Error : Please send a bug report."); } + + if (jjmatchedPos < strPos) + { + jjmatchedKind = strKind; + jjmatchedPos = strPos; + } + else if (jjmatchedPos == strPos && jjmatchedKind > strKind) + jjmatchedKind = strKind; + + return toRet; +} +static final int[] jjnextStates = { + 29, 30, 35, 36, 23, 24, 25, 1, 2, 4, 8, 9, 11, 19, 20, 33, + 34, 37, 38, +}; +private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2) +{ + switch(hiByte) + { + case 0: + return ((jjbitVec2[i2] & l2) != 0L); + default : + if ((jjbitVec0[i1] & l1) != 0L) + return true; + return false; + } +} +public static final String[] jjstrLiteralImages = { +"", null, null, null, null, null, null, null, null, null, null, null, null, +null, null, null, null, null, null, null, null, null, null, null, null, null, null, +null, "\75", "\74\76", "\76", "\76\75", "\74", "\74\75", "\50", "\54", "\51", "\53", +"\55", "\52", "\57", "\45", }; +public static final String[] lexStateNames = { + "DEFAULT", +}; +static final long[] jjtoToken = { + 0x3fffdffff01L, +}; +static final long[] jjtoSkip = { + 0xfeL, +}; +static final long[] jjtoSpecial = { + 0x3eL, +}; +private SimpleCharStream input_stream; +private final int[] jjrounds = new int[43]; +private final int[] jjstateSet = new int[86]; +protected char curChar; +public SelectorParserTokenManager(SimpleCharStream stream) +{ + if (SimpleCharStream.staticFlag) + throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer."); + input_stream = stream; +} +public SelectorParserTokenManager(SimpleCharStream stream, int lexState) +{ + this(stream); + SwitchTo(lexState); +} +public void ReInit(SimpleCharStream stream) +{ + jjmatchedPos = jjnewStateCnt = 0; + curLexState = defaultLexState; + input_stream = stream; + ReInitRounds(); +} +private final void ReInitRounds() +{ + int i; + jjround = 0x80000001; + for (i = 43; i-- > 0;) + jjrounds[i] = 0x80000000; +} +public void ReInit(SimpleCharStream stream, int lexState) +{ + ReInit(stream); + SwitchTo(lexState); +} +public void SwitchTo(int lexState) +{ + if (lexState >= 1 || lexState < 0) + throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE); + else + curLexState = lexState; +} + +private final Token jjFillToken() +{ + Token t = Token.newToken(jjmatchedKind); + t.kind = jjmatchedKind; + String im = jjstrLiteralImages[jjmatchedKind]; + t.image = (im == null) ? input_stream.GetImage() : im; + t.beginLine = input_stream.getBeginLine(); + t.beginColumn = input_stream.getBeginColumn(); + t.endLine = input_stream.getEndLine(); + t.endColumn = input_stream.getEndColumn(); + return t; +} + +int curLexState = 0; +int defaultLexState = 0; +int jjnewStateCnt; +int jjround; +int jjmatchedPos; +int jjmatchedKind; + +public final Token getNextToken() +{ + int kind; + Token specialToken = null; + Token matchedToken; + int curPos = 0; + + EOFLoop : + for (;;) + { + try + { + curChar = input_stream.BeginToken(); + } + catch(java.io.IOException e) + { + jjmatchedKind = 0; + matchedToken = jjFillToken(); + matchedToken.specialToken = specialToken; + return matchedToken; + } + + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_0(); + if (jjmatchedKind != 0x7fffffff) + { + if (jjmatchedPos + 1 < curPos) + input_stream.backup(curPos - jjmatchedPos - 1); + if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) + { + matchedToken = jjFillToken(); + matchedToken.specialToken = specialToken; + return matchedToken; + } + else + { + if ((jjtoSpecial[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) + { + matchedToken = jjFillToken(); + if (specialToken == null) + specialToken = matchedToken; + else + { + matchedToken.specialToken = specialToken; + specialToken = (specialToken.next = matchedToken); + } + } + continue EOFLoop; + } + } + int error_line = input_stream.getEndLine(); + int error_column = input_stream.getEndColumn(); + String error_after = null; + boolean EOFSeen = false; + try { input_stream.readChar(); input_stream.backup(1); } + catch (java.io.IOException e1) { + EOFSeen = true; + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + if (curChar == '\n' || curChar == '\r') { + error_line++; + error_column = 0; + } + else + error_column++; + } + if (!EOFSeen) { + input_stream.backup(1); + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + } + throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR); + } +} + +} diff --git a/activemq-core/src/main/java/org/activemq/selector/SimpleCharStream.java b/activemq-core/src/main/java/org/activemq/selector/SimpleCharStream.java new file mode 100755 index 0000000000..77cd03950e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/selector/SimpleCharStream.java @@ -0,0 +1,401 @@ +/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 2.1 */ +package org.activemq.selector; + +/** + * An implementation of interface CharStream, where the stream is assumed to + * contain only ASCII characters (without unicode processing). + */ + +public final class SimpleCharStream +{ + public static final boolean staticFlag = false; + int bufsize; + int available; + int tokenBegin; + public int bufpos = -1; + private int bufline[]; + private int bufcolumn[]; + + private int column = 0; + private int line = 1; + + private boolean prevCharIsCR = false; + private boolean prevCharIsLF = false; + + private java.io.Reader inputStream; + + private char[] buffer; + private int maxNextCharInd = 0; + private int inBuf = 0; + + private final void ExpandBuff(boolean wrapAround) + { + char[] newbuffer = new char[bufsize + 2048]; + int newbufline[] = new int[bufsize + 2048]; + int newbufcolumn[] = new int[bufsize + 2048]; + + try + { + if (wrapAround) + { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + System.arraycopy(buffer, 0, newbuffer, + bufsize - tokenBegin, bufpos); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); + bufcolumn = newbufcolumn; + + maxNextCharInd = (bufpos += (bufsize - tokenBegin)); + } + else + { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + bufcolumn = newbufcolumn; + + maxNextCharInd = (bufpos -= tokenBegin); + } + } + catch (Throwable t) + { + throw new Error(t.getMessage()); + } + + + bufsize += 2048; + available = bufsize; + tokenBegin = 0; + } + + private final void FillBuff() throws java.io.IOException + { + if (maxNextCharInd == available) + { + if (available == bufsize) + { + if (tokenBegin > 2048) + { + bufpos = maxNextCharInd = 0; + available = tokenBegin; + } + else if (tokenBegin < 0) + bufpos = maxNextCharInd = 0; + else + ExpandBuff(false); + } + else if (available > tokenBegin) + available = bufsize; + else if ((tokenBegin - available) < 2048) + ExpandBuff(true); + else + available = tokenBegin; + } + + int i; + try { + if ((i = inputStream.read(buffer, maxNextCharInd, + available - maxNextCharInd)) == -1) + { + inputStream.close(); + throw new java.io.IOException(); + } + else + maxNextCharInd += i; + return; + } + catch(java.io.IOException e) { + --bufpos; + backup(0); + if (tokenBegin == -1) + tokenBegin = bufpos; + throw e; + } + } + + public final char BeginToken() throws java.io.IOException + { + tokenBegin = -1; + char c = readChar(); + tokenBegin = bufpos; + + return c; + } + + private final void UpdateLineColumn(char c) + { + column++; + + if (prevCharIsLF) + { + prevCharIsLF = false; + line += (column = 1); + } + else if (prevCharIsCR) + { + prevCharIsCR = false; + if (c == '\n') + { + prevCharIsLF = true; + } + else + line += (column = 1); + } + + switch (c) + { + case '\r' : + prevCharIsCR = true; + break; + case '\n' : + prevCharIsLF = true; + break; + case '\t' : + column--; + column += (8 - (column & 07)); + break; + default : + break; + } + + bufline[bufpos] = line; + bufcolumn[bufpos] = column; + } + + public final char readChar() throws java.io.IOException + { + if (inBuf > 0) + { + --inBuf; + + if (++bufpos == bufsize) + bufpos = 0; + + return buffer[bufpos]; + } + + if (++bufpos >= maxNextCharInd) + FillBuff(); + + char c = buffer[bufpos]; + + UpdateLineColumn(c); + return (c); + } + + /** + * @deprecated + * @see #getEndColumn + */ + + public final int getColumn() { + return bufcolumn[bufpos]; + } + + /** + * @deprecated + * @see #getEndLine + */ + + public final int getLine() { + return bufline[bufpos]; + } + + public final int getEndColumn() { + return bufcolumn[bufpos]; + } + + public final int getEndLine() { + return bufline[bufpos]; + } + + public final int getBeginColumn() { + return bufcolumn[tokenBegin]; + } + + public final int getBeginLine() { + return bufline[tokenBegin]; + } + + public final void backup(int amount) { + + inBuf += amount; + if ((bufpos -= amount) < 0) + bufpos += bufsize; + } + + public SimpleCharStream(java.io.Reader dstream, int startline, + int startcolumn, int buffersize) + { + inputStream = dstream; + line = startline; + column = startcolumn - 1; + + available = bufsize = buffersize; + buffer = new char[buffersize]; + bufline = new int[buffersize]; + bufcolumn = new int[buffersize]; + } + + public SimpleCharStream(java.io.Reader dstream, int startline, + int startcolumn) + { + this(dstream, startline, startcolumn, 4096); + } + + public SimpleCharStream(java.io.Reader dstream) + { + this(dstream, 1, 1, 4096); + } + public void ReInit(java.io.Reader dstream, int startline, + int startcolumn, int buffersize) + { + inputStream = dstream; + line = startline; + column = startcolumn - 1; + + if (buffer == null || buffersize != buffer.length) + { + available = bufsize = buffersize; + buffer = new char[buffersize]; + bufline = new int[buffersize]; + bufcolumn = new int[buffersize]; + } + prevCharIsLF = prevCharIsCR = false; + tokenBegin = inBuf = maxNextCharInd = 0; + bufpos = -1; + } + + public void ReInit(java.io.Reader dstream, int startline, + int startcolumn) + { + ReInit(dstream, startline, startcolumn, 4096); + } + + public void ReInit(java.io.Reader dstream) + { + ReInit(dstream, 1, 1, 4096); + } + public SimpleCharStream(java.io.InputStream dstream, int startline, + int startcolumn, int buffersize) + { + this(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); + } + + public SimpleCharStream(java.io.InputStream dstream, int startline, + int startcolumn) + { + this(dstream, startline, startcolumn, 4096); + } + + public SimpleCharStream(java.io.InputStream dstream) + { + this(dstream, 1, 1, 4096); + } + + public void ReInit(java.io.InputStream dstream, int startline, + int startcolumn, int buffersize) + { + ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); + } + + public void ReInit(java.io.InputStream dstream) + { + ReInit(dstream, 1, 1, 4096); + } + public void ReInit(java.io.InputStream dstream, int startline, + int startcolumn) + { + ReInit(dstream, startline, startcolumn, 4096); + } + public final String GetImage() + { + if (bufpos >= tokenBegin) + return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); + else + return new String(buffer, tokenBegin, bufsize - tokenBegin) + + new String(buffer, 0, bufpos + 1); + } + + public final char[] GetSuffix(int len) + { + char[] ret = new char[len]; + + if ((bufpos + 1) >= len) + System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); + else + { + System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, + len - bufpos - 1); + System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); + } + + return ret; + } + + public void Done() + { + buffer = null; + bufline = null; + bufcolumn = null; + } + + /** + * Method to adjust line and column numbers for the start of a token.
+ */ + public void adjustBeginLineColumn(int newLine, int newCol) + { + int start = tokenBegin; + int len; + + if (bufpos >= tokenBegin) + { + len = bufpos - tokenBegin + inBuf + 1; + } + else + { + len = bufsize - tokenBegin + bufpos + 1 + inBuf; + } + + int i = 0, j = 0, k = 0; + int nextColDiff = 0, columnDiff = 0; + + while (i < len && + bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) + { + bufline[j] = newLine; + nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; + bufcolumn[j] = newCol + columnDiff; + columnDiff = nextColDiff; + i++; + } + + if (i < len) + { + bufline[j] = newLine++; + bufcolumn[j] = newCol + columnDiff; + + while (i++ < len) + { + if (bufline[j = start % bufsize] != bufline[++start % bufsize]) + bufline[j] = newLine++; + else + bufline[j] = newLine; + } + } + + line = bufline[j]; + column = bufcolumn[j]; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/selector/Token.java b/activemq-core/src/main/java/org/activemq/selector/Token.java new file mode 100755 index 0000000000..2158097424 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/selector/Token.java @@ -0,0 +1,81 @@ +/* Generated By:JavaCC: Do not edit this line. Token.java Version 2.1 */ +package org.activemq.selector; + +/** + * Describes the input token stream. + */ + +public class Token { + + /** + * An integer that describes the kind of this token. This numbering + * system is determined by JavaCCParser, and a table of these numbers is + * stored in the file ...Constants.java. + */ + public int kind; + + /** + * beginLine and beginColumn describe the position of the first character + * of this token; endLine and endColumn describe the position of the + * last character of this token. + */ + public int beginLine, beginColumn, endLine, endColumn; + + /** + * The string image of the token. + */ + public String image; + + /** + * A reference to the next regular (non-special) token from the input + * stream. If this is the last token from the input stream, or if the + * token manager has not read tokens beyond this one, this field is + * set to null. This is true only if this token is also a regular + * token. Otherwise, see below for a description of the contents of + * this field. + */ + public Token next; + + /** + * This field is used to access special tokens that occur prior to this + * token, but after the immediately preceding regular (non-special) token. + * If there are no such special tokens, this field is set to null. + * When there are more than one such special token, this field refers + * to the last of these special tokens, which in turn refers to the next + * previous special token through its specialToken field, and so on + * until the first special token (whose specialToken field is null). + * The next fields of special tokens refer to other special tokens that + * immediately follow it (without an intervening regular token). If there + * is no such token, this field is null. + */ + public Token specialToken; + + /** + * Returns the image. + */ + public final String toString() + { + return image; + } + + /** + * Returns a new Token object, by default. However, if you want, you + * can create and return subclass objects based on the value of ofKind. + * Simply add the cases to the switch for all those special cases. + * For example, if you have a subclass of Token called IDToken that + * you want to create if ofKind is ID, simlpy add something like : + * + * case MyParserConstants.ID : return new IDToken(); + * + * to the following switch statement. Then you can cast matchedToken + * variable to the appropriate type and use it in your lexical actions. + */ + public static final Token newToken(int ofKind) + { + switch(ofKind) + { + default : return new Token(); + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/selector/TokenMgrError.java b/activemq-core/src/main/java/org/activemq/selector/TokenMgrError.java new file mode 100755 index 0000000000..fbbf2cfa1f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/selector/TokenMgrError.java @@ -0,0 +1,133 @@ +/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 2.1 */ +package org.activemq.selector; + +public class TokenMgrError extends Error +{ + /* + * Ordinals for various reasons why an Error of this type can be thrown. + */ + + /** + * Lexical error occured. + */ + static final int LEXICAL_ERROR = 0; + + /** + * An attempt wass made to create a second instance of a static token manager. + */ + static final int STATIC_LEXER_ERROR = 1; + + /** + * Tried to change to an invalid lexical state. + */ + static final int INVALID_LEXICAL_STATE = 2; + + /** + * Detected (and bailed out of) an infinite loop in the token manager. + */ + static final int LOOP_DETECTED = 3; + + /** + * Indicates the reason why the exception is thrown. It will have + * one of the above 4 values. + */ + int errorCode; + + /** + * Replaces unprintable characters by their espaced (or unicode escaped) + * equivalents in the given string + */ + protected static final String addEscapes(String str) { + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) + { + case 0 : + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } + + /** + * Returns a detailed message for the Error when it is thrown by the + * token manager to indicate a lexical error. + * Parameters : + * EOFSeen : indicates if EOF caused the lexicl error + * curLexState : lexical state in which this error occured + * errorLine : line number when the error occured + * errorColumn : column number when the error occured + * errorAfter : prefix that was seen before this error occured + * curchar : the offending character + * Note: You can customize the lexical error message by modifying this method. + */ + private static final String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) { + return("Lexical error at line " + + errorLine + ", column " + + errorColumn + ". Encountered: " + + (EOFSeen ? " " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") + + "after : \"" + addEscapes(errorAfter) + "\""); + } + + /** + * You can also modify the body of this method to customize your error messages. + * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not + * of end-users concern, so you can return something like : + * + * "Internal Error : Please file a bug report .... " + * + * from this method for such cases in the release version of your parser. + */ + public String getMessage() { + return super.getMessage(); + } + + /* + * Constructors of various flavors follow. + */ + + public TokenMgrError() { + } + + public TokenMgrError(String message, int reason) { + super(message); + errorCode = reason; + } + + public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) { + this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); + } +} diff --git a/activemq-core/src/main/java/org/activemq/selector/package.html b/activemq-core/src/main/java/org/activemq/selector/package.html new file mode 100755 index 0000000000..78b22ae1bd --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/selector/package.html @@ -0,0 +1,9 @@ + + + + + +JMS Selector implemetnation + + + diff --git a/activemq-core/src/main/java/org/activemq/state/CommandVisitor.java b/activemq-core/src/main/java/org/activemq/state/CommandVisitor.java new file mode 100755 index 0000000000..86cde102d1 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/state/CommandVisitor.java @@ -0,0 +1,77 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.state; + +import org.activemq.command.BrokerInfo; +import org.activemq.command.ConnectionId; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerId; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.DestinationInfo; +import org.activemq.command.FlushCommand; +import org.activemq.command.KeepAliveInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.ProducerId; +import org.activemq.command.ProducerInfo; +import org.activemq.command.RemoveSubscriptionInfo; +import org.activemq.command.Response; +import org.activemq.command.SessionId; +import org.activemq.command.SessionInfo; +import org.activemq.command.ShutdownInfo; +import org.activemq.command.TransactionInfo; +import org.activemq.command.WireFormatInfo; + +public interface CommandVisitor { + + Response processAddConnection(ConnectionInfo info) throws Throwable; + Response processAddSession(SessionInfo info) throws Throwable; + Response processAddProducer(ProducerInfo info) throws Throwable; + Response processAddConsumer(ConsumerInfo info) throws Throwable; + + Response processRemoveConnection(ConnectionId id) throws Throwable; + Response processRemoveSession(SessionId id) throws Throwable; + Response processRemoveProducer(ProducerId id) throws Throwable; + Response processRemoveConsumer(ConsumerId id) throws Throwable; + + Response processAddDestination(DestinationInfo info) throws Throwable; + Response processRemoveDestination(DestinationInfo info) throws Throwable; + Response processRemoveSubscription(RemoveSubscriptionInfo info) throws Throwable; + + Response processMessage(Message send) throws Throwable; + Response processMessageAck(MessageAck ack) throws Throwable; + + Response processBeginTransaction(TransactionInfo info) throws Throwable; + Response processPrepareTransaction(TransactionInfo info) throws Throwable; + Response processCommitTransactionOnePhase(TransactionInfo info) throws Throwable; + Response processCommitTransactionTwoPhase(TransactionInfo info) throws Throwable; + Response processRollbackTransaction(TransactionInfo info) throws Throwable; + + Response processWireFormat(WireFormatInfo info) throws Throwable; + Response processKeepAlive(KeepAliveInfo info) throws Throwable; + Response processShutdown(ShutdownInfo info) throws Throwable; + Response processFlush(FlushCommand command) throws Throwable; + + Response processBrokerInfo(BrokerInfo info) throws Throwable; + Response processRecoverTransactions(TransactionInfo info) throws Throwable; + Response processForgetTransaction(TransactionInfo info) throws Throwable; + Response processEndTransaction(TransactionInfo info) throws Throwable; + +} + diff --git a/activemq-core/src/main/java/org/activemq/state/ConnectionState.java b/activemq-core/src/main/java/org/activemq/state/ConnectionState.java new file mode 100755 index 0000000000..c13cc9127a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/state/ConnectionState.java @@ -0,0 +1,84 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.state; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.SessionId; +import org.activemq.command.SessionInfo; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +public class ConnectionState { + + final ConnectionInfo info; + private final ConcurrentHashMap sessions = new ConcurrentHashMap(); + private final List tempDestinations = Collections.synchronizedList(new ArrayList()); + + public ConnectionState(ConnectionInfo info) { + this.info = info; + // Add the default session id. + addSession(new SessionInfo(info, -1)); + } + + public String toString() { + return info.toString(); + } + + public void addTempDestination(ActiveMQDestination destination) { + tempDestinations.add(destination); + } + + public void removeTempDestination(ActiveMQDestination destination) { + tempDestinations.remove(destination); + } + + public void addSession(SessionInfo info) { + sessions.put(info.getSessionId(), new SessionState(info)); + } + public SessionState removeSession(SessionId id) { + return (SessionState)sessions.remove(id); + } + public SessionState getSessionState(SessionId id) { + return (SessionState)sessions.get(id); + } + + public ConnectionInfo getInfo() { + return info; + } + + public Set getSessionIds() { + return sessions.keySet(); + } + + public List getTempDesinations() { + return tempDestinations; + } + + public Collection getSessionStates() { + return sessions.values(); + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/state/ConnectionStateTracker.java b/activemq-core/src/main/java/org/activemq/state/ConnectionStateTracker.java new file mode 100755 index 0000000000..54221408ca --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/state/ConnectionStateTracker.java @@ -0,0 +1,301 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.state; + +import java.io.IOException; +import java.util.Iterator; + +import org.activemq.command.BrokerInfo; +import org.activemq.command.Command; +import org.activemq.command.ConnectionId; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerId; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.DestinationInfo; +import org.activemq.command.FlushCommand; +import org.activemq.command.KeepAliveInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.ProducerId; +import org.activemq.command.ProducerInfo; +import org.activemq.command.RemoveSubscriptionInfo; +import org.activemq.command.Response; +import org.activemq.command.SessionId; +import org.activemq.command.SessionInfo; +import org.activemq.command.ShutdownInfo; +import org.activemq.command.TransactionInfo; +import org.activemq.command.WireFormatInfo; +import org.activemq.transport.Transport; +import org.activemq.util.IOExceptionSupport; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * Tracks the state of a connection so a newly established transport can + * be re-initialized to the state that was tracked. + * + * @version $Revision$ + */ +public class ConnectionStateTracker implements CommandVisitor { + + private final static Response TRACKED_RESPONSE_MARKER = new Response(); + + boolean trackTransactions = false; + boolean trackMessages = false; + boolean trackAcks = false; + + private boolean restoreSessions=true; + boolean restoreConsumers=true; + private boolean restoreProducers=true; + + protected final ConcurrentHashMap connectionStates = new ConcurrentHashMap(); + + public boolean track(Command command) throws IOException { + try { + return command.visit(this)!=null; + } catch (IOException e) { + throw e; + } catch (Throwable e) { + throw IOExceptionSupport.create(e); + } + } + + public void restore( Transport transport ) throws IOException { + // Restore the connections. + for (Iterator iter = connectionStates.values().iterator(); iter.hasNext();) { + ConnectionState connectionState = (ConnectionState) iter.next(); + transport.oneway(connectionState.getInfo()); + restoreTempDestinations(transport, connectionState); + + if( restoreSessions ) + restoreSessions(transport, connectionState); + } + } + + /** + * @param transport + * @param connectionState + * @throws IOException + */ + protected void restoreSessions(Transport transport, ConnectionState connectionState) throws IOException { + // Restore the connection's sessions + for (Iterator iter2 = connectionState.getSessionStates().iterator(); iter2.hasNext();) { + SessionState sessionState = (SessionState) iter2.next(); + transport.oneway(sessionState.getInfo()); + + if( restoreProducers ) + restoreProducers(transport, sessionState); + + if( restoreConsumers ) + restoreConsumers(transport, sessionState); + } + } + + /** + * @param transport + * @param sessionState + * @throws IOException + */ + protected void restoreConsumers(Transport transport, SessionState sessionState) throws IOException { + // Restore the session's consumers + for (Iterator iter3 = sessionState.getConsumerStates().iterator(); iter3.hasNext();) { + ConsumerState consumerState = (ConsumerState) iter3.next(); + transport.oneway(consumerState.getInfo()); + } + } + + /** + * @param transport + * @param sessionState + * @throws IOException + */ + protected void restoreProducers(Transport transport, SessionState sessionState) throws IOException { + // Restore the session's producers + for (Iterator iter3 = sessionState.getProducerStates().iterator(); iter3.hasNext();) { + ProducerState producerState = (ProducerState) iter3.next(); + transport.oneway(producerState.getInfo()); + } + } + + /** + * @param transport + * @param connectionState + * @throws IOException + */ + protected void restoreTempDestinations(Transport transport, ConnectionState connectionState) throws IOException { + // Restore the connection's temp destinations. + for (Iterator iter2 = connectionState.getTempDesinations().iterator(); iter2.hasNext();) { + transport.oneway((DestinationInfo) iter2.next()); + } + } + + public Response processAddDestination(DestinationInfo info) throws Throwable { + ConnectionState cs = (ConnectionState) connectionStates.get(info.getConnectionId()); + if( info.getDestination().isTemporary() ) { + cs.addTempDestination(info.getDestination()); + } + return TRACKED_RESPONSE_MARKER; + } + + public Response processRemoveDestination(DestinationInfo info) throws Throwable { + ConnectionState cs = (ConnectionState) connectionStates.get(info.getConnectionId()); + if( info.getDestination().isTemporary() ) { + cs.removeTempDestination(info.getDestination()); + } + return TRACKED_RESPONSE_MARKER; + } + + + public Response processAddProducer(ProducerInfo info) throws Throwable { + SessionId sessionId = info.getProducerId().getParentId(); + ConnectionId connectionId = sessionId.getParentId(); + ConnectionState cs = (ConnectionState) connectionStates.get(connectionId); + SessionState ss = cs.getSessionState(sessionId); + ss.addProducer(info); + return TRACKED_RESPONSE_MARKER; + } + + public Response processRemoveProducer(ProducerId id) throws Throwable { + SessionId sessionId = id.getParentId(); + ConnectionId connectionId = sessionId.getParentId(); + ConnectionState cs = (ConnectionState) connectionStates.get(connectionId); + SessionState ss = cs.getSessionState(sessionId); + ss.removeProducer(id); + return TRACKED_RESPONSE_MARKER; + } + + public Response processAddConsumer(ConsumerInfo info) throws Throwable { + SessionId sessionId = info.getConsumerId().getParentId(); + ConnectionId connectionId = sessionId.getParentId(); + ConnectionState cs = (ConnectionState) connectionStates.get(connectionId); + SessionState ss = cs.getSessionState(sessionId); + ss.addConsumer(info); + return TRACKED_RESPONSE_MARKER; + } + + public Response processRemoveConsumer(ConsumerId id) throws Throwable { + SessionId sessionId = id.getParentId(); + ConnectionId connectionId = sessionId.getParentId(); + ConnectionState cs = (ConnectionState) connectionStates.get(connectionId); + SessionState ss = cs.getSessionState(sessionId); + ss.removeConsumer(id); + return TRACKED_RESPONSE_MARKER; + } + + public Response processAddSession(SessionInfo info) throws Throwable { + ConnectionId connectionId = info.getSessionId().getParentId(); + ConnectionState cs = (ConnectionState) connectionStates.get(connectionId); + cs.addSession(info); + return TRACKED_RESPONSE_MARKER; + } + + public Response processRemoveSession(SessionId id) throws Throwable { + ConnectionId connectionId = id.getParentId(); + ConnectionState cs = (ConnectionState) connectionStates.get(connectionId); + cs.removeSession(id); + return TRACKED_RESPONSE_MARKER; + } + + public Response processAddConnection(ConnectionInfo info) throws Throwable { + connectionStates.put(info.getConnectionId(), new ConnectionState(info)); + return TRACKED_RESPONSE_MARKER; + } + + public Response processRemoveConnection(ConnectionId id) throws Throwable { + connectionStates.remove(id); + return TRACKED_RESPONSE_MARKER; + } + + public Response processRemoveSubscription(RemoveSubscriptionInfo info) throws Throwable { + return null; + } + public Response processMessage(Message send) throws Throwable { + return null; + } + public Response processMessageAck(MessageAck ack) throws Throwable { + return null; + } + public Response processBeginTransaction(TransactionInfo info) throws Throwable { + return null; + } + public Response processPrepareTransaction(TransactionInfo info) throws Throwable { + return null; + } + public Response processCommitTransactionOnePhase(TransactionInfo info) throws Throwable { + return null; + } + public Response processCommitTransactionTwoPhase(TransactionInfo info) throws Throwable { + return null; + } + public Response processRollbackTransaction(TransactionInfo info) throws Throwable { + return null; + } + public Response processWireFormat(WireFormatInfo info) throws Throwable { + return null; + } + public Response processKeepAlive(KeepAliveInfo info) throws Throwable { + return null; + } + public Response processShutdown(ShutdownInfo info) throws Throwable { + return null; + } + public Response processBrokerInfo(BrokerInfo info) throws Throwable { + return null; + } + + public Response processRecoverTransactions(TransactionInfo info) { + return null; + } + + public Response processForgetTransaction(TransactionInfo info) throws Throwable { + return null; + } + + public Response processEndTransaction(TransactionInfo info) throws Throwable { + return null; + } + + public Response processFlush(FlushCommand command) throws Throwable { + return null; + } + + public boolean isRestoreConsumers() { + return restoreConsumers; + } + + public void setRestoreConsumers(boolean restoreConsumers) { + this.restoreConsumers = restoreConsumers; + } + + public boolean isRestoreProducers() { + return restoreProducers; + } + + public void setRestoreProducers(boolean restoreProducers) { + this.restoreProducers = restoreProducers; + } + + public boolean isRestoreSessions() { + return restoreSessions; + } + + public void setRestoreSessions(boolean restoreSessions) { + this.restoreSessions = restoreSessions; + } +} diff --git a/activemq-core/src/main/java/org/activemq/state/ConsumerState.java b/activemq-core/src/main/java/org/activemq/state/ConsumerState.java new file mode 100755 index 0000000000..9fc9f6dd6a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/state/ConsumerState.java @@ -0,0 +1,36 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.state; + +import org.activemq.command.ConsumerInfo; + +public class ConsumerState { + final ConsumerInfo info; + + public ConsumerState(ConsumerInfo info) { + this.info = info; + } + public String toString() { + return info.toString(); + } + public ConsumerInfo getInfo() { + return info; + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/state/ProducerState.java b/activemq-core/src/main/java/org/activemq/state/ProducerState.java new file mode 100755 index 0000000000..998fdd1c42 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/state/ProducerState.java @@ -0,0 +1,36 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.state; + +import org.activemq.command.ProducerInfo; + +public class ProducerState { + final ProducerInfo info; + + public ProducerState(ProducerInfo info) { + this.info = info; + } + public String toString() { + return info.toString(); + } + public ProducerInfo getInfo() { + return info; + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/state/SessionState.java b/activemq-core/src/main/java/org/activemq/state/SessionState.java new file mode 100755 index 0000000000..bd546993f5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/state/SessionState.java @@ -0,0 +1,78 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.state; + +import java.util.Collection; +import java.util.Set; + +import org.activemq.command.ConsumerId; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.ProducerId; +import org.activemq.command.ProducerInfo; +import org.activemq.command.SessionInfo; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +public class SessionState { + final SessionInfo info; + + public final ConcurrentHashMap producers = new ConcurrentHashMap(); + public final ConcurrentHashMap consumers = new ConcurrentHashMap(); + + public SessionState(SessionInfo info) { + this.info = info; + } + public String toString() { + return info.toString(); + } + + public void addProducer(ProducerInfo info) { + producers.put(info.getProducerId(), new ProducerState(info)); + } + public ProducerState removeProducer(ProducerId id) { + return (ProducerState) producers.remove(id); + } + + public void addConsumer(ConsumerInfo info) { + consumers.put(info.getConsumerId(), new ConsumerState(info)); + } + public ConsumerState removeConsumer(ConsumerId id) { + return (ConsumerState) consumers.remove(id); + } + + public SessionInfo getInfo() { + return info; + } + + public Set getConsumerIds() { + return consumers.keySet(); + } + public Set getProducerIds() { + return producers.keySet(); + } + + public Collection getProducerStates() { + return producers.values(); + } + + public Collection getConsumerStates() { + return consumers.values(); + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/DefaultPersistenceAdapterFactory.java b/activemq-core/src/main/java/org/activemq/store/DefaultPersistenceAdapterFactory.java new file mode 100755 index 0000000000..d7e8dc0d23 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/DefaultPersistenceAdapterFactory.java @@ -0,0 +1,199 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store; + +import java.io.File; +import java.io.IOException; + +import org.activeio.journal.Journal; +import org.activeio.journal.active.JournalImpl; +import org.activemq.memory.UsageManager; +import org.activemq.store.jdbc.JDBCPersistenceAdapter; +import org.activemq.store.journal.JournalPersistenceAdapter; +import org.activemq.store.journal.QuickJournalPersistenceAdapter; +import org.activemq.thread.TaskRunnerFactory; +import org.apache.derby.jdbc.EmbeddedDataSource; + +import javax.sql.DataSource; + +/** + * Factory class that can create PersistenceAdapter objects. + * + * @version $Revision: 1.4 $ + */ +public class DefaultPersistenceAdapterFactory implements PersistenceAdapterFactory { + + private int journalLogFileSize = 1024*1024*20; + private int journalLogFiles = 2; + private File dataDirectory; + private UsageManager memManager; + private DataSource dataSource; + private TaskRunnerFactory taskRunnerFactory; + private Journal journal; + private boolean useJournal=true; + private boolean useQuickJournal=false; + private File journalArchiveDirectory; + private JDBCPersistenceAdapter jdbcAdapter = new JDBCPersistenceAdapter(); + + public PersistenceAdapter createPersistenceAdapter() throws IOException { + File dataDirectory = getDataDirectory(); + jdbcAdapter.setDataSource(getDataSource()); + + if( !useJournal ) + return jdbcAdapter; + + // Setup the Journal + if( useQuickJournal ) { + return new QuickJournalPersistenceAdapter(getJournal(), jdbcAdapter, getMemManager(), getTaskRunnerFactory()); + } else { + return new JournalPersistenceAdapter(getJournal(), jdbcAdapter, getMemManager(), getTaskRunnerFactory()); + } + } + + public File getDataDirectory() { + if( dataDirectory==null ) { + dataDirectory = new File("activemq-data"); + } + return dataDirectory; + } + + public void setDataDirectory(File dataDirectory) { + this.dataDirectory = dataDirectory; + } + + public int getJournalLogFiles() { + return journalLogFiles; + } + + public void setJournalLogFiles(int journalLogFiles) { + this.journalLogFiles = journalLogFiles; + } + + public int getJournalLogFileSize() { + return journalLogFileSize; + } + + public void setJournalLogFileSize(int journalLogFileSize) { + this.journalLogFileSize = journalLogFileSize; + } + + public UsageManager getMemManager() { + if( memManager==null ) { + memManager = new UsageManager(); + } + return memManager; + } + + public void setMemManager(UsageManager memManager) { + this.memManager = memManager; + } + + public DataSource getDataSource() throws IOException { + if (dataSource == null) { + dataSource = createDataSource(); + } + return dataSource; + } + + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } + + public JDBCPersistenceAdapter getJdbcAdapter() { + return jdbcAdapter; + } + + public void setJdbcAdapter(JDBCPersistenceAdapter jdbcAdapter) { + this.jdbcAdapter = jdbcAdapter; + } + + public boolean isUseJournal() { + return useJournal; + } + + public void setUseJournal(boolean useJournal) { + this.useJournal = useJournal; + } + + public TaskRunnerFactory getTaskRunnerFactory() { + if( taskRunnerFactory == null ) { + taskRunnerFactory = new TaskRunnerFactory(); + } + return taskRunnerFactory; + } + + public void setTaskRunnerFactory(TaskRunnerFactory taskRunnerFactory) { + this.taskRunnerFactory = taskRunnerFactory; + } + + public Journal getJournal() throws IOException { + if( journal == null ) { + createJournal(); + } + return journal; + } + + public void setJournal(Journal journal) { + this.journal = journal; + } + + public File getJournalArchiveDirectory() { + if( journalArchiveDirectory == null && useQuickJournal ) { + journalArchiveDirectory = new File(getDataDirectory(), "journal"); + } + return journalArchiveDirectory; + } + + public void setJournalArchiveDirectory(File journalArchiveDirectory) { + this.journalArchiveDirectory = journalArchiveDirectory; + } + + + public boolean isUseQuickJournal() { + return useQuickJournal; + } + + public void setUseQuickJournal(boolean useQuickJournal) { + this.useQuickJournal = useQuickJournal; + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected DataSource createDataSource() throws IOException { + + // Setup the Derby datasource. + System.setProperty("derby.system.home", getDataDirectory().getCanonicalPath()); + System.setProperty("derby.storage.fileSyncTransactionLog", "true"); + System.setProperty("derby.storage.pageCacheSize", "100"); + + final EmbeddedDataSource ds = new EmbeddedDataSource(); + ds.setDatabaseName("derbydb"); + ds.setCreateDatabase("create"); + return ds; + } + + /** + * @throws IOException + */ + protected void createJournal() throws IOException { + File journalDir = new File(getDataDirectory(), "journal"); + journal = new JournalImpl(journalDir, journalLogFiles, journalLogFileSize, getJournalArchiveDirectory()); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/store/MessageRecoveryListener.java b/activemq-core/src/main/java/org/activemq/store/MessageRecoveryListener.java new file mode 100755 index 0000000000..209f234354 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/MessageRecoveryListener.java @@ -0,0 +1,29 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store; + +import org.activemq.command.Message; + +/** + * @version $Revision: 1.4 $ + */ +public interface MessageRecoveryListener { + void recoverMessage(Message message) throws Throwable; + void recoverMessageReference(String messageReference) throws Throwable; +} diff --git a/activemq-core/src/main/java/org/activemq/store/MessageStore.java b/activemq-core/src/main/java/org/activemq/store/MessageStore.java new file mode 100755 index 0000000000..8402d7d068 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/MessageStore.java @@ -0,0 +1,98 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store; + +import java.io.IOException; + +import org.activeio.Service; +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageId; + +/** + * Represents a message store which is used by the persistent {@link org.activemq.service.MessageContainer} + * implementations + * + * @version $Revision: 1.5 $ + */ +public interface MessageStore extends Service { + + /** + * Adds a message to the message store + * @param context TODO + */ + public void addMessage(ConnectionContext context, Message message) throws IOException; + + /** + * Adds a message reference to the message store + * @param context TODO + * @param messageId TODO + * @param expirationTime TODO + */ + public void addMessageReference(ConnectionContext context, MessageId messageId, long expirationTime, String messageRef) throws IOException; + + /** + * Looks up a message using either the String messageID or + * the messageNumber. Implementations are encouraged to fill in the missing + * key if its easy to do so. + * @param identity which contains either the messageID or the messageNumber + * @return the message or null if it does not exist + */ + public Message getMessage(MessageId identity) throws IOException; + + /** + * Looks up a message using either the String messageID or + * the messageNumber. Implementations are encouraged to fill in the missing + * key if its easy to do so. + * @param identity which contains either the messageID or the messageNumber + * @return the message or null if it does not exist + */ + public String getMessageReference(MessageId identity) throws IOException; + + /** + * Removes a message from the message store. + * @param context TODO + * @param ack the ack request that cause the message to be removed. It conatins + * the identity which contains the messageID of the message that needs to be removed. + */ + public void removeMessage(ConnectionContext context, MessageAck ack) throws IOException; + + /** + * Removes all the messages from the message store. + * @param context TODO + */ + public void removeAllMessages(ConnectionContext context) throws IOException; + + /** + * Recover any messages to be delivered. + * + * @param container + * @throws Throwable + */ + public void recover(MessageRecoveryListener container) throws Throwable; + + /** + * The destination that the message store is holding messages for. + * @return + */ + public ActiveMQDestination getDestination(); + +} diff --git a/activemq-core/src/main/java/org/activemq/store/PersistenceAdapter.java b/activemq-core/src/main/java/org/activemq/store/PersistenceAdapter.java new file mode 100755 index 0000000000..c330a6d26c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/PersistenceAdapter.java @@ -0,0 +1,103 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store; + +import org.activemq.Service; +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; + +import java.io.IOException; +import java.util.Set; + +/** + * Adapter to the actual persistence mechanism used with ActiveMQ + * + * @version $Revision: 1.3 $ + */ +public interface PersistenceAdapter extends Service { + + /** + * Returns a set of all the {@link org.activemq.command.ActiveMQDestination} + * objects that the persistence store is aware exist. + * + * @return + */ + public Set getDestinations(); + + /** + * Factory method to create a new queue message store with the given destination name + */ + public MessageStore createQueueMessageStore(ActiveMQQueue destination) throws IOException; + + /** + * Factory method to create a new topic message store with the given destination name + */ + public TopicMessageStore createTopicMessageStore(ActiveMQTopic destination) throws IOException; + + /** + * Factory method to create a new persistent prepared transaction store for XA recovery + */ + public TransactionStore createTransactionStore() throws IOException; + + /** + * This method starts a transaction on the persistent storage - which is nothing to + * do with JMS or XA transactions - its purely a mechanism to perform multiple writes + * to a persistent store in 1 transaction as a performance optimization. + *

+ * Typically one transaction will require one disk synchronization point and so for + * real high performance its usually faster to perform many writes within the same + * transaction to minimize latency caused by disk synchronization. This is especially + * true when using tools like Berkeley Db or embedded JDBC servers. + */ + public void beginTransaction(ConnectionContext context) throws IOException; + + + /** + * Commit a persistence transaction + * + * @see PersistenceAdapter#beginTransaction() + */ + public void commitTransaction(ConnectionContext context) throws IOException; + + /** + * Rollback a persistence transaction + * + * @see PersistenceAdapter#beginTransaction() + */ + public void rollbackTransaction(ConnectionContext context) throws IOException; + + /** + * + * @return + * @throws IOException + */ + public long getLastMessageBrokerSequenceId() throws IOException; + + /** + * Delete's all the messages in the persistent store. + * + * @throws IOException + */ + public void deleteAllMessages() throws IOException; + + public boolean isUseExternalMessageReferences(); + public void setUseExternalMessageReferences(boolean enable); + +} diff --git a/activemq-core/src/main/java/org/activemq/store/PersistenceAdapterFactory.java b/activemq-core/src/main/java/org/activemq/store/PersistenceAdapterFactory.java new file mode 100755 index 0000000000..3baed46c12 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/PersistenceAdapterFactory.java @@ -0,0 +1,36 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store; + +import java.io.IOException; + +/** + * Factory class that can create PersistenceAdapter objects. + * + * @version $Revision: 1.3 $ + */ +public interface PersistenceAdapterFactory { + + /** + * Creates a persistence Adapter that can use a given directory to store it's data. + * @throws IOException + */ + public PersistenceAdapter createPersistenceAdapter() throws IOException; + +} diff --git a/activemq-core/src/main/java/org/activemq/store/PersistenceAdapterFactoryBean.java b/activemq-core/src/main/java/org/activemq/store/PersistenceAdapterFactoryBean.java new file mode 100644 index 0000000000..657e356830 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/PersistenceAdapterFactoryBean.java @@ -0,0 +1,48 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.store; + +import org.springframework.beans.factory.FactoryBean; + +/** + * Creates a default persistence model using the Journal and JDBC + * + * @org.xbean.XBean element="journaledJDBC" + * + * @version $Revision: 1.1 $ + */ +public class PersistenceAdapterFactoryBean extends DefaultPersistenceAdapterFactory implements FactoryBean { + + private PersistenceAdapter persistenceAdaptor; + + public Object getObject() throws Exception { + if (persistenceAdaptor == null) { + persistenceAdaptor = createPersistenceAdapter(); + } + return persistenceAdaptor; + } + + public Class getObjectType() { + return PersistenceAdapter.class; + } + + public boolean isSingleton() { + return false; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/store/ProxyMessageStore.java b/activemq-core/src/main/java/org/activemq/store/ProxyMessageStore.java new file mode 100755 index 0000000000..11ce0aae63 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/ProxyMessageStore.java @@ -0,0 +1,76 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store; + +import java.io.IOException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageId; + +/** + * A simple proxy that delegates to another MessageStore. + */ +public class ProxyMessageStore implements MessageStore { + + final MessageStore delegate; + + public ProxyMessageStore(MessageStore delegate) { + this.delegate = delegate; + } + + public MessageStore getDelegate() { + return delegate; + } + + public void addMessage(ConnectionContext context, Message message) throws IOException { + delegate.addMessage(context, message); + } + public Message getMessage(MessageId identity) throws IOException { + return delegate.getMessage(identity); + } + public void recover(MessageRecoveryListener listener) throws Throwable { + delegate.recover(listener); + } + public void removeAllMessages(ConnectionContext context) throws IOException { + delegate.removeAllMessages(context); + } + public void removeMessage(ConnectionContext context, MessageAck ack) throws IOException { + delegate.removeMessage(context, ack); + } + public void start() throws IOException { + delegate.start(); + } + public void stop(long timeout) throws IOException { + delegate.stop(timeout); + } + public ActiveMQDestination getDestination() { + return delegate.getDestination(); + } + + public void addMessageReference(ConnectionContext context, MessageId messageId, long expirationTime, String messageRef) throws IOException { + delegate.addMessageReference(context, messageId, expirationTime, messageRef); + } + + public String getMessageReference(MessageId identity) throws IOException { + return delegate.getMessageReference(identity); + } +} diff --git a/activemq-core/src/main/java/org/activemq/store/ProxyTopicMessageStore.java b/activemq-core/src/main/java/org/activemq/store/ProxyTopicMessageStore.java new file mode 100755 index 0000000000..45b15e8c6f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/ProxyTopicMessageStore.java @@ -0,0 +1,95 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store; + +import java.io.IOException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageId; +import org.activemq.command.SubscriptionInfo; + +/** + * A simple proxy that delegates to another MessageStore. + */ +public class ProxyTopicMessageStore implements TopicMessageStore { + + final TopicMessageStore delegate; + + public ProxyTopicMessageStore(TopicMessageStore delegate) { + this.delegate = delegate; + } + + public MessageStore getDelegate() { + return delegate; + } + + public void addMessage(ConnectionContext context, Message message) throws IOException { + delegate.addMessage(context, message); + } + public Message getMessage(MessageId identity) throws IOException { + return delegate.getMessage(identity); + } + public void recover(MessageRecoveryListener listener) throws Throwable { + delegate.recover(listener); + } + public void removeAllMessages(ConnectionContext context) throws IOException { + delegate.removeAllMessages(context); + } + public void removeMessage(ConnectionContext context, MessageAck ack) throws IOException { + delegate.removeMessage(context, ack); + } + public void start() throws IOException { + delegate.start(); + } + public void stop(long timeout) throws IOException { + delegate.stop(timeout); + } + + public SubscriptionInfo lookupSubscription(String clientId, String subscriptionName) throws IOException { + return delegate.lookupSubscription(clientId, subscriptionName); + } + public void acknowledge(ConnectionContext context, String clientId, String subscriptionName, MessageId messageId) + throws IOException { + delegate.acknowledge(context, clientId, subscriptionName, messageId); + } + public void addSubsciption(String clientId, String subscriptionName, String selector, boolean retroactive) throws IOException { + delegate.addSubsciption(clientId, subscriptionName, selector, retroactive); + } + public void deleteSubscription(String clientId, String subscriptionName) throws IOException { + delegate.deleteSubscription(clientId, subscriptionName); + } + + public void recoverSubscription(String clientId, String subscriptionName, MessageRecoveryListener listener) throws Throwable { + delegate.recoverSubscription(clientId, subscriptionName, listener); + } + + public ActiveMQDestination getDestination() { + return delegate.getDestination(); + } + + public void addMessageReference(ConnectionContext context, MessageId messageId, long expirationTime, String messageRef) throws IOException { + delegate.addMessageReference(context, messageId, expirationTime, messageRef); + } + public String getMessageReference(MessageId identity) throws IOException { + return delegate.getMessageReference(identity); + } +} diff --git a/activemq-core/src/main/java/org/activemq/store/TopicMessageStore.java b/activemq-core/src/main/java/org/activemq/store/TopicMessageStore.java new file mode 100755 index 0000000000..07f80fb397 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/TopicMessageStore.java @@ -0,0 +1,87 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store; + +import java.io.IOException; + +import javax.jms.JMSException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.MessageId; +import org.activemq.command.SubscriptionInfo; + +/** + * A MessageStore for durable topic subscriptions + * + * @version $Revision: 1.4 $ + */ +public interface TopicMessageStore extends MessageStore { + + /** + * Stores the last acknowledged messgeID for the given subscription + * so that we can recover and commence dispatching messages from the last + * checkpoint + * @param context TODO + * @param messageId + * @param subscriptionPersistentId + */ + public void acknowledge(ConnectionContext context, String clientId, String subscriptionName, MessageId messageId) throws IOException; + + /** + * @param sub + * @throws JMSException + */ + public void deleteSubscription(String clientId, String subscriptionName) throws IOException; + + /** + * For the new subscription find the last acknowledged message ID + * and then find any new messages since then and dispatch them + * to the subscription. + *

+ * e.g. if we dispatched some messages to a new durable topic subscriber, then went down before + * acknowledging any messages, we need to know the correct point from which to recover from. + * @param subscription + * + * @throws Throwable + */ + public void recoverSubscription(String clientId, String subscriptionName, MessageRecoveryListener listener) throws Throwable; + + /** + * Finds the subscriber entry for the given consumer info + * + * @param clientId TODO + * @param subscriptionName TODO + * @return + */ + public SubscriptionInfo lookupSubscription(String clientId, String subscriptionName) throws IOException; + + /** + * Inserts the subscriber info due to a subscription change + *

+ * If this is a new subscription and the retroactive is false, then the last + * message sent to the topic should be set as the last message acknowledged by they new + * subscription. Otherwise, if retroactive is true, then create the subscription without + * it having an acknowledged message so that on recovery, all message recorded for the + * topic get replayed. + * @param retroactive TODO + * + */ + public void addSubsciption(String clientId, String subscriptionName, String selector, boolean retroactive) throws IOException; + +} diff --git a/activemq-core/src/main/java/org/activemq/store/TransactionRecoveryListener.java b/activemq-core/src/main/java/org/activemq/store/TransactionRecoveryListener.java new file mode 100755 index 0000000000..d99e14d920 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/TransactionRecoveryListener.java @@ -0,0 +1,27 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store; + +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.XATransactionId; + +public interface TransactionRecoveryListener { + public void recover(XATransactionId xid, Message[] addedMessages, MessageAck aks[]); +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/TransactionStore.java b/activemq-core/src/main/java/org/activemq/store/TransactionStore.java new file mode 100755 index 0000000000..389b41ad81 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/TransactionStore.java @@ -0,0 +1,40 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store; + +import java.io.IOException; + +import org.activemq.Service; +import org.activemq.command.TransactionId; + + +/** + * Represents the durable store of the commit/rollback operations taken against the + * broker. + * + * @version $Revision: 1.2 $ + */ +public interface TransactionStore extends Service { + + public void prepare(TransactionId txid) throws IOException; + public void commit(TransactionId txid, boolean wasPrepared) throws IOException; + public void rollback(TransactionId txid) throws IOException; + public void recover(TransactionRecoveryListener listener) throws IOException; + +} diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCAdapter.java b/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCAdapter.java new file mode 100755 index 0000000000..bebac0b82e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCAdapter.java @@ -0,0 +1,80 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.jdbc; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.Set; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.MessageId; +import org.activemq.command.SubscriptionInfo; + +/** + * @version $Revision: 1.5 $ + */ +public interface JDBCAdapter { + + public abstract void doCreateTables(TransactionContext c) throws SQLException, IOException; + + public abstract void doDropTables(TransactionContext c) throws SQLException, IOException; + + public abstract void doAddMessage(TransactionContext c, MessageId messageID, ActiveMQDestination destination, byte[] data, + long expiration) throws SQLException, IOException; + public abstract void doAddMessageReference(TransactionContext c, MessageId messageId, ActiveMQDestination destination, long expirationTime, String messageRef) throws SQLException, IOException; + + public abstract byte[] doGetMessage(TransactionContext c, long seq) throws SQLException, IOException; + public abstract String doGetMessageReference(TransactionContext c, long id) throws SQLException, IOException; + + public abstract void doRemoveMessage(TransactionContext c, long seq) throws SQLException, IOException; + + public abstract void doRecover(TransactionContext c, ActiveMQDestination destination, JDBCMessageRecoveryListener listener) + throws Throwable; + + public abstract void doSetLastAck(TransactionContext c, ActiveMQDestination destination, String clientId, String subscriptionName, long seq) throws SQLException, + IOException; + + public abstract void doRecoverSubscription(TransactionContext c, ActiveMQDestination destination, String clientId, + String subscriptionName, JDBCMessageRecoveryListener listener) throws Throwable; + + public abstract void doSetSubscriberEntry(TransactionContext c, ActiveMQDestination destination, String clientId, + String subscriptionName, String selector, boolean retroactive) throws SQLException, IOException; + + public abstract SubscriptionInfo doGetSubscriberEntry(TransactionContext c, ActiveMQDestination destination, + String clientId, String subscriptionName) + throws SQLException, IOException; + + public abstract long getBrokerSequenceId(TransactionContext c, MessageId messageID) throws SQLException, IOException; + + public abstract void doRemoveAllMessages(TransactionContext c, ActiveMQDestination destinationName) throws SQLException, IOException; + + public abstract void doDeleteSubscription(TransactionContext c, ActiveMQDestination destinationName, String clientId, String subscriptionName) + throws SQLException, IOException; + + public abstract void doDeleteOldMessages(TransactionContext c) + throws SQLException, IOException; + + public abstract long doGetLastMessageBrokerSequenceId(TransactionContext c) throws SQLException, IOException; + + public abstract Set doGetDestinations(TransactionContext c) throws SQLException, IOException; + + public abstract void setUseExternalMessageReferences(boolean useExternalMessageReferences); + + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCMessageRecoveryListener.java b/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCMessageRecoveryListener.java new file mode 100755 index 0000000000..a8b2009182 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCMessageRecoveryListener.java @@ -0,0 +1,30 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.jdbc; + +import java.io.IOException; + + +/** + * @version $Revision: 1.3 $ + */ +public interface JDBCMessageRecoveryListener { + void recoverMessage(long sequenceId, byte[] message) throws IOException, Throwable; + void recoverMessageReference(String reference) throws IOException, Throwable; +} diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCMessageStore.java b/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCMessageStore.java new file mode 100755 index 0000000000..d894053457 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCMessageStore.java @@ -0,0 +1,191 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.jdbc; + +import java.io.IOException; +import java.sql.SQLException; + +import org.activeio.Packet; +import org.activeio.command.WireFormat; +import org.activeio.packet.ByteArrayPacket; +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageId; +import org.activemq.store.MessageRecoveryListener; +import org.activemq.store.MessageStore; +import org.activemq.util.IOExceptionSupport; + +/** + * @version $Revision: 1.10 $ + */ +public class JDBCMessageStore implements MessageStore { + + protected final WireFormat wireFormat; + protected final ActiveMQDestination destination; + protected final JDBCAdapter adapter; + protected final JDBCPersistenceAdapter persistenceAdapter; + + public JDBCMessageStore(JDBCPersistenceAdapter persistenceAdapter, JDBCAdapter adapter, WireFormat wireFormat, + ActiveMQDestination destination) { + this.persistenceAdapter = persistenceAdapter; + this.adapter = adapter; + this.wireFormat = wireFormat; + this.destination = destination; + } + + public void addMessage(ConnectionContext context, Message message) throws IOException { + + // Serialize the Message.. + byte data[]; + try { + Packet packet = wireFormat.marshal(message); + data = packet.sliceAsBytes(); + } catch (IOException e) { + throw IOExceptionSupport.create("Failed to broker message: " + message.getMessageId() + " in container: " + + e, e); + } + + // Get a connection and insert the message into the DB. + TransactionContext c = persistenceAdapter.getTransactionContext(context); + try { + adapter.doAddMessage(c, message.getMessageId(), destination, data, message.getExpiration()); + } catch (SQLException e) { + throw IOExceptionSupport.create("Failed to broker message: " + message.getMessageId() + " in container: " + + e, e); + } finally { + c.close(); + } + } + + public void addMessageReference(ConnectionContext context, MessageId messageId, long expirationTime, String messageRef) throws IOException { + // Get a connection and insert the message into the DB. + TransactionContext c = persistenceAdapter.getTransactionContext(context); + try { + adapter.doAddMessageReference(c, messageId, destination, expirationTime, messageRef); + } catch (SQLException e) { + throw IOExceptionSupport.create("Failed to broker message: " + messageId + " in container: " + + e, e); + } finally { + c.close(); + } + } + + public Message getMessage(MessageId messageId) throws IOException { + + long id = messageId.getBrokerSequenceId(); + + // Get a connection and pull the message out of the DB + TransactionContext c = persistenceAdapter.getTransactionContext(); + try { + byte data[] = adapter.doGetMessage(c, id); + if (data == null) + return null; + + Message answer = (Message) wireFormat.unmarshal(new ByteArrayPacket(data)); + return answer; + } catch (IOException e) { + throw IOExceptionSupport.create("Failed to broker message: " + messageId + " in container: " + e, e); + } catch (SQLException e) { + throw IOExceptionSupport.create("Failed to broker message: " + messageId + " in container: " + e, e); + } finally { + c.close(); + } + } + + public String getMessageReference(MessageId messageId) throws IOException { + long id = messageId.getBrokerSequenceId(); + + // Get a connection and pull the message out of the DB + TransactionContext c = persistenceAdapter.getTransactionContext(); + try { + return adapter.doGetMessageReference(c, id); + } catch (IOException e) { + throw IOExceptionSupport.create("Failed to broker message: " + messageId + " in container: " + e, e); + } catch (SQLException e) { + throw IOExceptionSupport.create("Failed to broker message: " + messageId + " in container: " + e, e); + } finally { + c.close(); + } + } + + public void removeMessage(ConnectionContext context, MessageAck ack) throws IOException { + long seq = ack.getLastMessageId().getBrokerSequenceId(); + + // Get a connection and remove the message from the DB + TransactionContext c = persistenceAdapter.getTransactionContext(context); + try { + adapter.doRemoveMessage(c, seq); + } catch (SQLException e) { + throw IOExceptionSupport.create("Failed to broker message: " + ack.getLastMessageId() + " in container: " + e, e); + } finally { + c.close(); + } + } + + public void recover(final MessageRecoveryListener listener) throws Throwable { + + // Get all the Message ids out of the database. + TransactionContext c = persistenceAdapter.getTransactionContext(); + try { + c = persistenceAdapter.getTransactionContext(); + adapter.doRecover(c, destination, new JDBCMessageRecoveryListener() { + public void recoverMessage(long sequenceId, byte[] data) throws Throwable { + Message msg = (Message) wireFormat.unmarshal(new ByteArrayPacket(data)); + msg.getMessageId().setBrokerSequenceId(sequenceId); + listener.recoverMessage(msg); + } + public void recoverMessageReference(String reference) throws IOException, Throwable { + listener.recoverMessageReference(reference); + } + }); + } catch (SQLException e) { + throw IOExceptionSupport.create("Failed to recover container. Reason: " + e, e); + } finally { + c.close(); + } + } + + public void start() throws IOException { + } + + public void stop(long timeout) throws IOException { + } + + /** + * @see org.activemq.store.MessageStore#removeAllMessages(ConnectionContext) + */ + public void removeAllMessages(ConnectionContext context) throws IOException { + // Get a connection and remove the message from the DB + TransactionContext c = persistenceAdapter.getTransactionContext(context); + try { + adapter.doRemoveAllMessages(c, destination); + } catch (SQLException e) { + throw IOExceptionSupport.create("Failed to broker remove all messages: " + e, e); + } finally { + c.close(); + } + } + + public ActiveMQDestination getDestination() { + return destination; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCPersistenceAdapter.java b/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCPersistenceAdapter.java new file mode 100755 index 0000000000..0634a34376 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCPersistenceAdapter.java @@ -0,0 +1,367 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.store.jdbc; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.Collections; +import java.util.Set; + +import javax.sql.DataSource; + +import org.activeio.FactoryFinder; +import org.activeio.command.WireFormat; +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.activemq.openwire.OpenWireFormat; +import org.activemq.store.MessageStore; +import org.activemq.store.PersistenceAdapter; +import org.activemq.store.TopicMessageStore; +import org.activemq.store.TransactionStore; +import org.activemq.store.jdbc.adapter.DefaultJDBCAdapter; +import org.activemq.store.memory.MemoryTransactionStore; +import org.activemq.util.IOExceptionSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.ScheduledFuture; +import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +/** + * A {@link PersistenceAdapter} implementation using JDBC for persistence + * storage. + * + * This persistence adapter will correctly remember prepared XA transactions, + * but it will not keep track of local transaction commits so that operations + * performed against the Message store are done as a single uow. + * + * @org.xbean.XBean element="jdbcPersistenceAdapter" + * + * @version $Revision: 1.9 $ + */ +public class JDBCPersistenceAdapter implements PersistenceAdapter { + + private static final Log log = LogFactory.getLog(JDBCPersistenceAdapter.class); + private static FactoryFinder factoryFinder = new FactoryFinder("META-INF/services/org/activemq/store/jdbc/"); + + private WireFormat wireFormat = new OpenWireFormat(false); + private DataSource dataSource; + private JDBCAdapter adapter; + private String adapterClass; + private MemoryTransactionStore transactionStore; + private ScheduledThreadPoolExecutor clockDaemon; + private ScheduledFuture clockTicket; + int cleanupPeriod = 1000 * 60 * 5; + private boolean useExternalMessageReferences; + + public JDBCPersistenceAdapter() { + } + + public JDBCPersistenceAdapter(DataSource ds, WireFormat wireFormat) { + this.dataSource = ds; + this.wireFormat = wireFormat; + } + + public Set getDestinations() { + // Get a connection and insert the message into the DB. + TransactionContext c = getTransactionContext(); + try { + return getAdapter().doGetDestinations(c); + } catch (IOException e) { + return Collections.EMPTY_SET; + } catch (SQLException e) { + return Collections.EMPTY_SET; + } finally { + try { + c.close(); + } catch (Throwable e) { + } + } + } + + public MessageStore createQueueMessageStore(ActiveMQQueue destination) throws IOException { + MessageStore rc = new JDBCMessageStore(this, getAdapter(), wireFormat, destination); + if (transactionStore != null) { + rc = transactionStore.proxy(rc); + } + return rc; + } + + public TopicMessageStore createTopicMessageStore(ActiveMQTopic destination) throws IOException { + TopicMessageStore rc = new JDBCTopicMessageStore(this, getAdapter(), wireFormat, destination); + if (transactionStore != null) { + rc = transactionStore.proxy(rc); + } + return rc; + } + + public TransactionStore createTransactionStore() throws IOException { + if (transactionStore == null) { + transactionStore = new MemoryTransactionStore(); + } + return this.transactionStore; + } + + public long getLastMessageBrokerSequenceId() throws IOException { + // Get a connection and insert the message into the DB. + TransactionContext c = getTransactionContext(); + try { + return getAdapter().doGetLastMessageBrokerSequenceId(c); + } catch (SQLException e) { + throw IOExceptionSupport.create("Failed to get last broker message id: " + e, e); + } finally { + c.close(); + } + } + + public void start() throws Exception { + + getAdapter().setUseExternalMessageReferences(isUseExternalMessageReferences()); + + TransactionContext transactionContext = getTransactionContext(); + transactionContext.begin(); + try { + try { + getAdapter().doCreateTables(transactionContext); + } catch (SQLException e) { + log.warn("Cannot create tables due to: " + e, e); + } + } finally { + transactionContext.commit(); + } + + cleanup(); + + // Cleanup the db periodically. + if (cleanupPeriod > 0) { + clockTicket = getScheduledThreadPoolExecutor().scheduleAtFixedRate(new Runnable() { + public void run() { + cleanup(); + } + }, cleanupPeriod, cleanupPeriod, TimeUnit.MILLISECONDS); + } + } + + public synchronized void stop() throws Exception { + if (clockTicket != null) { + clockTicket.cancel(true); + clockTicket = null; + clockDaemon.shutdown(); + } + } + + public void cleanup() { + TransactionContext c = getTransactionContext(); + try { + log.debug("Cleaning up old messages."); + c = getTransactionContext(); + getAdapter().doDeleteOldMessages(c); + } catch (IOException e) { + log.warn("Old message cleanup failed due to: " + e, e); + } catch (SQLException e) { + log.warn("Old message cleanup failed due to: " + e, e); + } finally { + try { + c.close(); + } catch (Throwable e) { + } + log.debug("Cleanup done."); + } + } + + public void setScheduledThreadPoolExecutor(ScheduledThreadPoolExecutor clockDaemon) { + this.clockDaemon = clockDaemon; + } + + public ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor() { + if (clockDaemon == null) { + clockDaemon = new ScheduledThreadPoolExecutor(5, new ThreadFactory() { + public Thread newThread(Runnable runnable) { + Thread thread = new Thread(runnable, "Cleanup Timmer"); + thread.setDaemon(true); + return thread; + } + }); + } + return clockDaemon; + } + + public JDBCAdapter getAdapter() throws IOException { + if (adapter == null) { + adapter = createAdapter(); + } + return adapter; + } + + /** + * @throws IOException + */ + protected JDBCAdapter createAdapter() throws IOException { + JDBCAdapter adapter=null; + TransactionContext c = getTransactionContext(); + try { + + // If the adapter class is not specified.. try to detect they + // right + // type by getting info from the database. + if (adapterClass == null) { + + try { + + // Make the filename file system safe. + String dirverName = c.getConnection().getMetaData().getDriverName(); + dirverName = dirverName.replaceAll("[^a-zA-Z0-9\\-]", "_").toLowerCase(); + + try { + adapter = (DefaultJDBCAdapter) factoryFinder.newInstance(dirverName); + log.info("Database driver recognized: [" + dirverName + "]"); + } catch (Throwable e) { + log.warn("Database driver NOT recognized: [" + dirverName + + "]. Will use default JDBC implementation."); + } + + } catch (SQLException e) { + log + .warn("JDBC error occured while trying to detect database type. Will use default JDBC implementation: " + + e.getMessage()); + log.debug("Reason: " + e, e); + } + + } else { + try { + Class clazz = JDBCPersistenceAdapter.class.getClassLoader().loadClass(adapterClass); + adapter = (DefaultJDBCAdapter) clazz.newInstance(); + } catch (Throwable e) { + log.warn("Invalid JDBC adapter class class (" + adapterClass + + "). Will use default JDBC implementation."); + log.debug("Reason: " + e, e); + } + } + + // Use the default JDBC adapter if the + // Database type is not recognized. + if (adapter == null) { + adapter = new DefaultJDBCAdapter(); + } + + } finally { + c.close(); + } + return adapter; + } + + public void setAdapter(JDBCAdapter adapter) { + this.adapter = adapter; + } + + public DataSource getDataSource() { + return dataSource; + } + + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } + + public WireFormat getWireFormat() { + return wireFormat; + } + + public void setWireFormat(WireFormat wireFormat) { + this.wireFormat = wireFormat; + } + + public TransactionContext getTransactionContext(ConnectionContext context) { + if (context == null) { + return getTransactionContext(); + } else { + TransactionContext answer = (TransactionContext) context.getLongTermStoreContext(); + if (answer == null) { + answer = new TransactionContext(dataSource); + context.setLongTermStoreContext(answer); + } + return answer; + } + } + + public TransactionContext getTransactionContext() { + return new TransactionContext(dataSource); + } + + public void beginTransaction(ConnectionContext context) throws IOException { + TransactionContext transactionContext = getTransactionContext(context); + transactionContext.begin(); + } + + public void commitTransaction(ConnectionContext context) throws IOException { + TransactionContext transactionContext = getTransactionContext(context); + transactionContext.commit(); + } + + public void rollbackTransaction(ConnectionContext context) throws IOException { + TransactionContext transactionContext = getTransactionContext(context); + transactionContext.rollback(); + } + + /** + * @return Returns the adapterClass. + */ + public String getAdapterClass() { + return adapterClass; + } + + /** + * @param adapterClass + * The adapterClass to set. + */ + public void setAdapterClass(String adapterClass) { + this.adapterClass = adapterClass; + } + + public int getCleanupPeriod() { + return cleanupPeriod; + } + + public void setCleanupPeriod(int cleanupPeriod) { + this.cleanupPeriod = cleanupPeriod; + } + + public void deleteAllMessages() throws IOException { + TransactionContext c = getTransactionContext(); + try { + getAdapter().doDropTables(c); + getAdapter().setUseExternalMessageReferences(isUseExternalMessageReferences()); + getAdapter().doCreateTables(c); + } catch (SQLException e) { + throw IOExceptionSupport.create(e); + } finally { + c.close(); + } + } + + public boolean isUseExternalMessageReferences() { + return useExternalMessageReferences; + } + + public void setUseExternalMessageReferences(boolean useExternalMessageReferences) { + this.useExternalMessageReferences = useExternalMessageReferences; + } +} diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCTopicMessageStore.java b/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCTopicMessageStore.java new file mode 100755 index 0000000000..0a7de2b746 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/JDBCTopicMessageStore.java @@ -0,0 +1,131 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.store.jdbc; + +import java.io.IOException; +import java.sql.SQLException; + +import org.activeio.command.WireFormat; +import org.activeio.packet.ByteArrayPacket; +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.Message; +import org.activemq.command.MessageId; +import org.activemq.command.SubscriptionInfo; +import org.activemq.store.MessageRecoveryListener; +import org.activemq.store.TopicMessageStore; +import org.activemq.util.IOExceptionSupport; + +/** + * @version $Revision: 1.6 $ + */ +public class JDBCTopicMessageStore extends JDBCMessageStore implements TopicMessageStore { + + public JDBCTopicMessageStore(JDBCPersistenceAdapter persistenceAdapter, JDBCAdapter adapter, WireFormat wireFormat, + ActiveMQTopic topic) { + super(persistenceAdapter, adapter, wireFormat, topic); + } + + public void acknowledge(ConnectionContext context, String clientId, String subscriptionName, MessageId messageId) + throws IOException { + long seq = messageId.getBrokerSequenceId(); + // Get a connection and insert the message into the DB. + TransactionContext c = persistenceAdapter.getTransactionContext(context); + try { + adapter.doSetLastAck(c, destination, clientId, subscriptionName, seq); + } catch (SQLException e) { + throw IOExceptionSupport.create("Failed to store acknowledgment for: " + clientId + " on message " + + messageId + " in container: " + e, e); + } finally { + c.close(); + } + } + + /** + * @throws Throwable + * + */ + public void recoverSubscription(String clientId, String subscriptionName, final MessageRecoveryListener listener) + throws Throwable { + + TransactionContext c = persistenceAdapter.getTransactionContext(); + try { + adapter.doRecoverSubscription(c, destination, clientId, subscriptionName, + new JDBCMessageRecoveryListener() { + public void recoverMessage(long sequenceId, byte[] data) throws Throwable { + Message msg = (Message) wireFormat.unmarshal(new ByteArrayPacket(data)); + msg.getMessageId().setBrokerSequenceId(sequenceId); + listener.recoverMessage(msg); + } + public void recoverMessageReference(String reference) throws IOException, Throwable { + listener.recoverMessageReference(reference); + } + }); + } catch (SQLException e) { + throw IOExceptionSupport.create("Failed to recover subscription: " + clientId + ". Reason: " + e, e); + } finally { + c.close(); + } + } + + /** + * @see org.activemq.store.TopicMessageStore#storeSubsciption(org.activemq.service.SubscriptionInfo, + * boolean) + */ + public void addSubsciption(String clientId, String subscriptionName, String selector, boolean retroactive) + throws IOException { + TransactionContext c = persistenceAdapter.getTransactionContext(); + try { + c = persistenceAdapter.getTransactionContext(); + adapter.doSetSubscriberEntry(c, destination, clientId, subscriptionName, selector, retroactive); + } catch (SQLException e) { + throw IOExceptionSupport + .create("Failed to lookup subscription for info: " + clientId + ". Reason: " + e, e); + } finally { + c.close(); + } + } + + /** + * @see org.activemq.store.TopicMessageStore#lookupSubscription(String, + * String) + */ + public SubscriptionInfo lookupSubscription(String clientId, String subscriptionName) throws IOException { + TransactionContext c = persistenceAdapter.getTransactionContext(); + try { + return adapter.doGetSubscriberEntry(c, destination, clientId, subscriptionName); + } catch (SQLException e) { + throw IOExceptionSupport.create("Failed to lookup subscription for: " + clientId + ". Reason: " + e, e); + } finally { + c.close(); + } + } + + public void deleteSubscription(String clientId, String subscriptionName) throws IOException { + TransactionContext c = persistenceAdapter.getTransactionContext(); + try { + adapter.doDeleteSubscription(c, destination, clientId, subscriptionName); + } catch (SQLException e) { + throw IOExceptionSupport.create("Failed to remove subscription for: " + clientId + ". Reason: " + e, e); + } finally { + c.close(); + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/StatementProvider.java b/activemq-core/src/main/java/org/activemq/store/jdbc/StatementProvider.java new file mode 100755 index 0000000000..75295830ce --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/StatementProvider.java @@ -0,0 +1,51 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.jdbc; + +/** + * Generates the SQL statements that are used by the JDBCAdapter. + * + * @version $Revision: 1.4 $ + */ +public interface StatementProvider { + + public String[] getCreateSchemaStatments(); + public String[] getDropSchemaStatments(); + public String getAddMessageStatment(); + public String getUpdateMessageStatment(); + public String getRemoveMessageStatment(); + public String getFindMessageSequenceIdStatment(); + public String getFindMessageStatment(); + public String getFindAllMessagesStatment(); + public String getFindLastSequenceIdInMsgs(); + public String getFindLastSequenceIdInAcks(); + public String getCreateDurableSubStatment(); + public String getFindDurableSubStatment(); + public String getUpdateLastAckOfDurableSub(); + public String getFindAllDurableSubMessagesStatment(); + public String getRemoveAllMessagesStatment(); + public String getRemoveAllSubscriptionsStatment(); + public String getDeleteSubscriptionStatment(); + public String getDeleteOldMessagesStatment(); + public String getFindAllDestinationsStatment(); + + public void setUseExternalMessageReferences(boolean useExternalMessageReferences); + public boolean isUseExternalMessageReferences(); + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/TransactionContext.java b/activemq-core/src/main/java/org/activemq/store/jdbc/TransactionContext.java new file mode 100755 index 0000000000..3848f4b10b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/TransactionContext.java @@ -0,0 +1,201 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.store.jdbc; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import javax.sql.DataSource; + +import org.activemq.util.IOExceptionSupport; + +/** + * Helps keep track of the current transaction/JDBC connection. + * + * @version $Revision: 1.2 $ + */ +public class TransactionContext { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(TransactionContext.class); + + private final DataSource dataSource; + private Connection connection; + private boolean inTx; + private PreparedStatement addMessageStatement; + private PreparedStatement removedMessageStatement; + private PreparedStatement updateLastAckStatement; + + public TransactionContext(DataSource dataSource) { + this.dataSource = dataSource; + } + + public Connection getConnection() throws IOException { + if( connection == null ) { + try { + connection = dataSource.getConnection(); + connection.setAutoCommit(!inTx); + } catch (SQLException e) { + throw IOExceptionSupport.create(e); + } + + try { + connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); + } catch (Throwable e) { + } + } + return connection; + } + + public void executeBatch() throws SQLException { + try { + executeBatch(addMessageStatement, "Failed add a message"); + } finally { + addMessageStatement = null; + try { + executeBatch(removedMessageStatement, "Failed to remove a message"); + } finally { + removedMessageStatement=null; + try { + executeBatch(updateLastAckStatement, "Failed to ack a message"); + } finally { + updateLastAckStatement=null; + } + } + } + } + + private void executeBatch(PreparedStatement p, String message) throws SQLException { + if( p == null ) + return; + + try { + int[] rc = p.executeBatch(); + for (int i = 0; i < rc.length; i++) { + if ( rc[i]!= 1 ) { + throw new SQLException(message); + } + } + } finally { + try { p.close(); } catch (Throwable e) { } + } + } + + public void close() throws IOException { + if( !inTx ) { + try { + try{ + executeBatch(); + } finally { + if (connection != null) { + connection.commit(); + } + } + } catch (SQLException e) { + throw IOExceptionSupport.create(e); + } finally { + try { + if (connection != null) { + connection.close(); + } + } catch (Throwable e) { + log.warn("Close failed: "+e.getMessage(), e); + } finally { + connection=null; + } + } + } + } + + public void begin() throws IOException { + if( inTx ) + throw new IOException("Already started."); + inTx = true; + connection = getConnection(); + } + + public void commit() throws IOException { + if( !inTx ) + throw new IOException("Not started."); + try { + executeBatch(); + connection.commit(); + } catch (SQLException e) { + log.info("commit failed: "+e.getMessage(), e); + while( e.getNextException() !=null ) { + e = e.getNextException(); + log.info("Nested exception: "+e); + } + throw IOExceptionSupport.create(e); + } finally { + inTx=false; + close(); + } + } + + public void rollback() throws IOException { + if( !inTx ) + throw new IOException("Not started."); + try { + if( addMessageStatement != null ) { + addMessageStatement.close(); + addMessageStatement=null; + } + if( removedMessageStatement != null ) { + removedMessageStatement.close(); + removedMessageStatement=null; + } + if( updateLastAckStatement != null ) { + updateLastAckStatement.close(); + updateLastAckStatement=null; + } + connection.rollback(); + + } catch (SQLException e) { + throw IOExceptionSupport.create(e); + } finally { + inTx=false; + close(); + } + } + + public PreparedStatement getAddMessageStatement() { + return addMessageStatement; + } + public void setAddMessageStatement(PreparedStatement addMessageStatement) { + this.addMessageStatement = addMessageStatement; + } + + public PreparedStatement getUpdateLastAckStatement() { + return updateLastAckStatement; + } + public void setUpdateLastAckStatement(PreparedStatement ackMessageStatement) { + this.updateLastAckStatement = ackMessageStatement; + } + + public PreparedStatement getRemovedMessageStatement() { + return removedMessageStatement; + } + public void setRemovedMessageStatement(PreparedStatement removedMessageStatement) { + this.removedMessageStatement = removedMessageStatement; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/AxionJDBCAdapter.java b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/AxionJDBCAdapter.java new file mode 100755 index 0000000000..c105c6bb14 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/AxionJDBCAdapter.java @@ -0,0 +1,76 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.jdbc.adapter; + +import org.activemq.store.jdbc.StatementProvider; + +/** + * Axion specific Adapter. + * + * Axion does not seem to support ALTER statements or sub-selects. This means: + * - We cannot auto upgrade the schema was we roll out new versions of ActiveMQ + * - We cannot delete durable sub messages that have be acknowledged by all consumers. + * + * @version $Revision: 1.4 $ + */ +public class AxionJDBCAdapter extends StreamJDBCAdapter { + + public static StatementProvider createStatementProvider() { + DefaultStatementProvider answer = new DefaultStatementProvider() { + public String [] getCreateSchemaStatments() { + return new String[]{ + "CREATE TABLE "+tablePrefix+messageTableName+"(" + +"ID "+sequenceDataType+" NOT NULL" + +", CONTAINER "+containerNameDataType + +", MSGID_PROD "+msgIdDataType + +", MSGID_SEQ "+sequenceDataType + +", EXPIRATION "+longDataType + +", MSG "+(useExternalMessageReferences ? stringIdDataType : binaryDataType) + +", PRIMARY KEY ( ID ) )", + "CREATE INDEX "+tablePrefix+messageTableName+"_MIDX ON "+tablePrefix+messageTableName+" (MSGID_PROD,MSGID_SEQ)", + "CREATE INDEX "+tablePrefix+messageTableName+"_CIDX ON "+tablePrefix+messageTableName+" (CONTAINER)", + "CREATE TABLE "+tablePrefix+durableSubAcksTableName+"(" + +"CONTAINER "+containerNameDataType+" NOT NULL" + +", CLIENT_ID "+stringIdDataType+" NOT NULL" + +", SUB_NAME "+stringIdDataType+" NOT NULL" + +", SELECTOR "+stringIdDataType + +", LAST_ACKED_ID "+sequenceDataType + +", PRIMARY KEY ( CONTAINER, CLIENT_ID, SUB_NAME))", + + }; + } + + public String getDeleteOldMessagesStatment() { + return "DELETE FROM "+tablePrefix+messageTableName+ + " WHERE ( EXPIRATION<>0 AND EXPIRATIONActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.jdbc.adapter; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.sql.Blob; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import javax.jms.JMSException; + +import org.activeio.ByteArrayOutputStream; +import org.activemq.store.jdbc.StatementProvider; +import org.activemq.store.jdbc.TransactionContext; + + +/** + * This JDBCAdapter inserts and extracts BLOB data using the + * getBlob()/setBlob() operations. This is a little more involved + * since to insert a blob you have to: + * + * 1: insert empty blob. + * 2: select the blob + * 3: finally update the blob with data value. + * + * The databases/JDBC drivers that use this adapter are: + *

    + *
  • + *
+ * + * @version $Revision: 1.2 $ + */ +public class BlobJDBCAdapter extends DefaultJDBCAdapter { + + public BlobJDBCAdapter() { + super(); + } + + public BlobJDBCAdapter(StatementProvider provider) { + super(provider); + } + + public void doAddMessage(Connection c, long seq, String messageID, String destinationName, byte[] data) throws SQLException, + JMSException { + PreparedStatement s = null; + ResultSet rs = null; + try { + + // Add the Blob record. + s = c.prepareStatement(statementProvider.getAddMessageStatment()); + s.setLong(1, seq); + s.setString(2, destinationName); + s.setString(3, messageID); + s.setString(4, " "); + + if (s.executeUpdate() != 1) + throw new JMSException("Failed to broker message: " + messageID + + " in container."); + s.close(); + + // Select the blob record so that we can update it. + s = c.prepareStatement(statementProvider.getFindMessageStatment()); + s.setLong(1, seq); + rs = s.executeQuery(); + if (!rs.next()) + throw new JMSException("Failed to broker message: " + messageID + + " in container."); + + // Update the blob + Blob blob = rs.getBlob(1); + OutputStream stream = blob.setBinaryStream(data.length); + stream.write(data); + stream.close(); + s.close(); + + // Update the row with the updated blob + s = c.prepareStatement(statementProvider.getUpdateMessageStatment()); + s.setBlob(1, blob); + s.setLong(2, seq); + + } catch (IOException e) { + throw (SQLException) new SQLException("BLOB could not be updated: " + + e).initCause(e); + } finally { + try { + rs.close(); + } catch (Throwable e) { + } + try { + s.close(); + } catch (Throwable e) { + } + } + } + + public byte[] doGetMessage(TransactionContext c, long seq) throws SQLException { + PreparedStatement s=null; ResultSet rs=null; + try { + + s = c.getConnection().prepareStatement(statementProvider.getFindMessageStatment()); + s.setLong(1, seq); + rs = s.executeQuery(); + + if( !rs.next() ) + return null; + Blob blob = rs.getBlob(1); + InputStream is = blob.getBinaryStream(); + + ByteArrayOutputStream os = new ByteArrayOutputStream((int)blob.length()); + int ch; + while( (ch=is.read())>= 0 ) { + os.write(ch); + } + is.close(); + os.close(); + + return os.toByteArray(); + + } catch (IOException e) { + throw (SQLException) new SQLException("BLOB could not be updated: " + + e).initCause(e); + } finally { + try { rs.close(); } catch (Throwable e) {} + try { s.close(); } catch (Throwable e) {} + } + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/BytesJDBCAdapter.java b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/BytesJDBCAdapter.java new file mode 100755 index 0000000000..45b34603a7 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/BytesJDBCAdapter.java @@ -0,0 +1,63 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.jdbc.adapter; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.activemq.store.jdbc.StatementProvider; + +/** + * This JDBCAdapter inserts and extracts BLOB data using the + * setBytes()/getBytes() operations. + * + * The databases/JDBC drivers that use this adapter are: + *
    + *
  • + *
+ * + * @version $Revision: 1.2 $ + */ +public class BytesJDBCAdapter extends DefaultJDBCAdapter { + + + public BytesJDBCAdapter() { + super(); + } + + public BytesJDBCAdapter(StatementProvider provider) { + super(provider); + } + + /** + * @see org.activemq.store.jdbc.adapter.DefaultJDBCAdapter#getBinaryData(java.sql.ResultSet, int) + */ + protected byte[] getBinaryData(ResultSet rs, int index) throws SQLException { + return rs.getBytes(index); + } + + /** + * @see org.activemq.store.jdbc.adapter.DefaultJDBCAdapter#setBinaryData(java.sql.PreparedStatement, int, byte[]) + */ + protected void setBinaryData(PreparedStatement s, int index, byte[] data) throws SQLException { + s.setBytes(index, data); + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/CachingStatementProvider.java b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/CachingStatementProvider.java new file mode 100755 index 0000000000..6fd076a14d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/CachingStatementProvider.java @@ -0,0 +1,223 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.jdbc.adapter; + +import org.activemq.store.jdbc.StatementProvider; + +/** + * A StatementProvider filter that caches the responses + * of the filtered StatementProvider. + * + * @version $Revision: 1.4 $ + */ +public class CachingStatementProvider implements StatementProvider { + + final private StatementProvider statementProvider; + + private String addMessageStatment; + private String[] createSchemaStatments; + private String[] dropSchemaStatments; + private String findAllMessagesStatment; + private String findLastSequenceIdInMsgs; + private String findMessageStatment; + private String removeMessageStatment; + private String updateMessageStatment; + private String createDurableSubStatment; + private String findDurableSubStatment; + private String findAllDurableSubMessagesStatment; + private String updateLastAckOfDurableSub; + private String findMessageSequenceIdStatment; + private String removeAllMessagesStatment; + private String removeAllSubscriptionsStatment; + private String deleteSubscriptionStatment; + private String deleteOldMessagesStatment; + private String findLastSequenceIdInAcks; + private String findAllDestinationsStatment; + + public CachingStatementProvider(StatementProvider statementProvider) { + this.statementProvider = statementProvider; + } + + public StatementProvider getNext() { + return statementProvider; + } + + public String getAddMessageStatment() { + if (addMessageStatment == null) { + addMessageStatment = statementProvider.getAddMessageStatment(); + } + return addMessageStatment; + } + + public String[] getCreateSchemaStatments() { + if( createSchemaStatments==null ) { + createSchemaStatments = statementProvider.getCreateSchemaStatments(); + } + return createSchemaStatments; + } + + public String[] getDropSchemaStatments() { + if( dropSchemaStatments==null ) { + dropSchemaStatments = statementProvider.getDropSchemaStatments(); + } + return dropSchemaStatments; + } + + public String getFindAllMessagesStatment() { + if( findAllMessagesStatment==null ) { + findAllMessagesStatment = statementProvider.getFindAllMessagesStatment(); + } + return findAllMessagesStatment; + } + + public String getFindLastSequenceIdInMsgs() { + if( findLastSequenceIdInMsgs==null ) { + findLastSequenceIdInMsgs = statementProvider.getFindLastSequenceIdInMsgs(); + } + return findLastSequenceIdInMsgs; + } + + public String getFindLastSequenceIdInAcks() { + if( findLastSequenceIdInAcks==null ) { + findLastSequenceIdInAcks = statementProvider.getFindLastSequenceIdInAcks(); + } + return findLastSequenceIdInAcks; + } + + public String getFindMessageStatment() { + if( findMessageStatment==null ) { + findMessageStatment = statementProvider.getFindMessageStatment(); + } + return findMessageStatment; + } + + /** + * @return + */ + public String getRemoveMessageStatment() { + if( removeMessageStatment==null ) { + removeMessageStatment = statementProvider.getRemoveMessageStatment(); + } + return removeMessageStatment; + } + + public String getUpdateMessageStatment() { + if( updateMessageStatment==null ) { + updateMessageStatment = statementProvider.getUpdateMessageStatment(); + } + return updateMessageStatment; + } + + public String getCreateDurableSubStatment() { + if(createDurableSubStatment==null) { + createDurableSubStatment = statementProvider.getCreateDurableSubStatment(); + } + return createDurableSubStatment; + } + + public String getFindDurableSubStatment() { + if(findDurableSubStatment==null) { + findDurableSubStatment = statementProvider.getFindDurableSubStatment(); + } + return findDurableSubStatment; + } + + public String getFindAllDurableSubMessagesStatment() { + if(findAllDurableSubMessagesStatment==null) { + findAllDurableSubMessagesStatment = statementProvider.getFindAllDurableSubMessagesStatment(); + } + return findAllDurableSubMessagesStatment; + } + + public String getUpdateLastAckOfDurableSub() { + if(updateLastAckOfDurableSub==null) { + updateLastAckOfDurableSub = statementProvider.getUpdateLastAckOfDurableSub(); + } + return updateLastAckOfDurableSub; + } + + public String getFindMessageSequenceIdStatment() { + if ( findMessageSequenceIdStatment==null ) { + findMessageSequenceIdStatment = statementProvider.getFindMessageSequenceIdStatment(); + } + return findMessageSequenceIdStatment; + } + + public String getRemoveAllMessagesStatment() { + if ( removeAllMessagesStatment==null ) { + removeAllMessagesStatment = statementProvider.getRemoveAllMessagesStatment(); + } + return removeAllMessagesStatment; + } + + public String getRemoveAllSubscriptionsStatment() { + if ( removeAllSubscriptionsStatment==null ) { + removeAllSubscriptionsStatment = statementProvider.getRemoveAllSubscriptionsStatment(); + } + return removeAllSubscriptionsStatment; + } + + public String getDeleteSubscriptionStatment() { + if ( deleteSubscriptionStatment==null ) { + deleteSubscriptionStatment = statementProvider.getDeleteSubscriptionStatment(); + } + return deleteSubscriptionStatment; + } + + public String getDeleteOldMessagesStatment() { + if ( deleteOldMessagesStatment==null ) { + deleteOldMessagesStatment = statementProvider.getDeleteOldMessagesStatment(); + } + return deleteOldMessagesStatment; + } + + public String getFindAllDestinationsStatment() { + if ( findAllDestinationsStatment==null ) { + findAllDestinationsStatment = statementProvider.getFindAllDestinationsStatment(); + } + return findAllDestinationsStatment; + } + + public void setUseExternalMessageReferences(boolean useExternalMessageReferences) { + addMessageStatment=null; + createSchemaStatments=null; + dropSchemaStatments=null; + findAllMessagesStatment=null; + findLastSequenceIdInMsgs=null; + findMessageStatment=null; + removeMessageStatment=null; + updateMessageStatment=null; + createDurableSubStatment=null; + findDurableSubStatment=null; + findAllDurableSubMessagesStatment=null; + updateLastAckOfDurableSub=null; + findMessageSequenceIdStatment=null; + removeAllMessagesStatment=null; + removeAllSubscriptionsStatment=null; + deleteSubscriptionStatment=null; + deleteOldMessagesStatment=null; + findLastSequenceIdInAcks=null; + findAllDestinationsStatment=null; + statementProvider.setUseExternalMessageReferences(useExternalMessageReferences); + } + + public boolean isUseExternalMessageReferences() { + return statementProvider.isUseExternalMessageReferences(); + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/DefaultJDBCAdapter.java b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/DefaultJDBCAdapter.java new file mode 100755 index 0000000000..f36398aafa --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/DefaultJDBCAdapter.java @@ -0,0 +1,608 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.jdbc.adapter; + +import java.io.IOException; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashSet; +import java.util.Set; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.MessageId; +import org.activemq.command.SubscriptionInfo; +import org.activemq.store.jdbc.JDBCAdapter; +import org.activemq.store.jdbc.JDBCMessageRecoveryListener; +import org.activemq.store.jdbc.StatementProvider; +import org.activemq.store.jdbc.TransactionContext; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Implements all the default JDBC operations that are used + * by the JDBCPersistenceAdapter. + *

+ * sub-classing is encouraged to override the default + * implementation of methods to account for differences + * in JDBC Driver implementations. + *

+ * The JDBCAdapter inserts and extracts BLOB data using the + * getBytes()/setBytes() operations. + *

+ * The databases/JDBC drivers that use this adapter are: + *

    + *
  • + *
+ * + * @version $Revision: 1.10 $ + */ +public class DefaultJDBCAdapter implements JDBCAdapter { + + private static final Log log = LogFactory.getLog(DefaultJDBCAdapter.class); + + final protected StatementProvider statementProvider; + protected boolean batchStatments=true; + + protected void setBinaryData(PreparedStatement s, int index, byte data[]) throws SQLException { + s.setBytes(index, data); + } + + protected byte[] getBinaryData(ResultSet rs, int index) throws SQLException { + return rs.getBytes(index); + } + + /** + * @param provider + */ + public DefaultJDBCAdapter(StatementProvider provider) { + this.statementProvider = new CachingStatementProvider(provider); + } + + public DefaultJDBCAdapter() { + this(new DefaultStatementProvider()); + } + + public void doCreateTables(TransactionContext c) throws SQLException, IOException { + Statement s = null; + try { + log.info("creating tables"); + s = c.getConnection().createStatement(); + String[] createStatments = statementProvider.getCreateSchemaStatments(); + for (int i = 0; i < createStatments.length; i++) { + // This will fail usually since the tables will be + // created already. + try { + boolean rc = s.execute(createStatments[i]); + } + catch (SQLException e) { + log.warn("Could not create JDBC tables; they could already exist." + + " Failure was: " + createStatments[i] + " Message: " + e.getMessage() + + " SQLState: " + e.getSQLState() + " Vendor code: " + e.getErrorCode() ); + } + } + c.getConnection().commit(); + log.info("done creating tables"); + } + finally { + try { + s.close(); + } + catch (Throwable e) { + } + } + } + + public void doDropTables(TransactionContext c) throws SQLException, IOException { + Statement s = null; + try { + s = c.getConnection().createStatement(); + String[] dropStatments = statementProvider.getDropSchemaStatments(); + for (int i = 0; i < dropStatments.length; i++) { + // This will fail usually since the tables will be + // created already. + try { + boolean rc = s.execute(dropStatments[i]); + } + catch (SQLException e) { + log.warn("Could not drop JDBC tables; they may not exist." + + " Failure was: " + dropStatments[i] + " Message: " + e.getMessage() + + " SQLState: " + e.getSQLState() + " Vendor code: " + e.getErrorCode() ); + } + } + c.getConnection().commit(); + } + finally { + try { + s.close(); + } + catch (Throwable e) { + } + } + } + public long doGetLastMessageBrokerSequenceId(TransactionContext c) throws SQLException, IOException { + PreparedStatement s = null; + ResultSet rs = null; + try { + s = c.getConnection().prepareStatement(statementProvider.getFindLastSequenceIdInMsgs()); + rs = s.executeQuery(); + long seq1 = 0; + if (rs.next()) { + seq1 = rs.getLong(1); + } + rs.close(); + s.close(); + s = c.getConnection().prepareStatement(statementProvider.getFindLastSequenceIdInAcks()); + rs = s.executeQuery(); + long seq2 = 0; + if (rs.next()) { + seq2 = rs.getLong(1); + } + + return Math.max(seq1, seq2); + } + finally { + close(rs); + close(s); + } + } + + public void doAddMessage(TransactionContext c, MessageId messageID, ActiveMQDestination destination, byte[] data, long expiration) throws SQLException, IOException { + PreparedStatement s = c.getAddMessageStatement(); + try { + if( s == null ) { + s = c.getConnection().prepareStatement(statementProvider.getAddMessageStatment()); + if( batchStatments ) { + c.setAddMessageStatement(s); + } + } + s.setLong(1, messageID.getBrokerSequenceId()); + s.setString(2, messageID.getProducerId().toString()); + s.setLong(3, messageID.getProducerSequenceId()); + s.setString(4, destination.getQualifiedName()); + s.setLong(5, expiration); + setBinaryData(s, 6, data); + if( batchStatments ) { + s.addBatch(); + } else if ( s.executeUpdate() != 1 ) { + throw new SQLException("Failed add a message"); + } + } finally { + if( !batchStatments ) { + s.close(); + } + } + } + + public void doAddMessageReference(TransactionContext c, MessageId messageID, ActiveMQDestination destination, long expirationTime, String messageRef) throws SQLException, IOException { + PreparedStatement s = c.getAddMessageStatement(); + try { + if( s == null ) { + s = c.getConnection().prepareStatement(statementProvider.getAddMessageStatment()); + if( batchStatments ) { + c.setAddMessageStatement(s); + } + } + s.setLong(1, messageID.getBrokerSequenceId()); + s.setString(2, messageID.getProducerId().toString()); + s.setLong(3, messageID.getProducerSequenceId()); + s.setString(4, destination.getQualifiedName()); + s.setLong(5, expirationTime); + s.setString(6, messageRef); + if( batchStatments ) { + s.addBatch(); + } else if ( s.executeUpdate() != 1 ) { + throw new SQLException("Failed add a message"); + } + } finally { + if( !batchStatments ) { + s.close(); + } + } + } + + public long getBrokerSequenceId(TransactionContext c, MessageId messageID) throws SQLException, IOException { + PreparedStatement s = null; + ResultSet rs = null; + try { + + s = c.getConnection().prepareStatement(statementProvider.getFindMessageSequenceIdStatment()); + s.setString(1, messageID.getProducerId().toString()); + s.setLong(2, messageID.getProducerSequenceId()); + rs = s.executeQuery(); + + if (!rs.next()) { + return 0; + } + return rs.getLong(1); + + } + finally { + close(rs); + close(s); + } + } + + public byte[] doGetMessage(TransactionContext c, long seq) throws SQLException, IOException { + PreparedStatement s = null; + ResultSet rs = null; + try { + + s = c.getConnection().prepareStatement(statementProvider.getFindMessageStatment()); + s.setLong(1, seq); + rs = s.executeQuery(); + + if (!rs.next()) { + return null; + } + return getBinaryData(rs, 1); + + } + finally { + close(rs); + close(s); + } + } + + public String doGetMessageReference(TransactionContext c, long seq) throws SQLException, IOException { + PreparedStatement s = null; + ResultSet rs = null; + try { + + s = c.getConnection().prepareStatement(statementProvider.getFindMessageStatment()); + s.setLong(1, seq); + rs = s.executeQuery(); + + if (!rs.next()) { + return null; + } + return rs.getString(1); + + } + finally { + close(rs); + close(s); + } + } + + public void doRemoveMessage(TransactionContext c, long seq) throws SQLException, IOException { + + PreparedStatement s = c.getAddMessageStatement(); + try { + if( s == null ) { + s = c.getConnection().prepareStatement(statementProvider.getRemoveMessageStatment()); + if( batchStatments ) { + c.setRemovedMessageStatement(s); + } + } + s.setLong(1, seq); + + if( batchStatments ) { + s.addBatch(); + } else if ( s.executeUpdate() != 1 ) { + throw new SQLException("Failed to remove message"); + } + } finally { + if( !batchStatments ) { + s.close(); + } + } + } + + public void doRecover(TransactionContext c, ActiveMQDestination destination, JDBCMessageRecoveryListener listener) throws Throwable { +// printQuery(c, "Select * from ACTIVEMQ_MSGS", System.out); + + PreparedStatement s = null; + ResultSet rs = null; + try { + + s = c.getConnection().prepareStatement(statementProvider.getFindAllMessagesStatment()); + s.setString(1, destination.getQualifiedName()); + rs = s.executeQuery(); + + if( statementProvider.isUseExternalMessageReferences() ) { + while (rs.next()) { + listener.recoverMessageReference(rs.getString(2)); + } + } else { + while (rs.next()) { + listener.recoverMessage(rs.getLong(1), getBinaryData(rs, 2)); + } + } + + } + finally { + close(rs); + close(s); + } + } + + + public void doSetLastAck(TransactionContext c, ActiveMQDestination destination, String clientId, String subscriptionName, long seq) throws SQLException, IOException { + + PreparedStatement s = c.getAddMessageStatement(); + try { + if( s == null ) { + s = c.getConnection().prepareStatement(statementProvider.getUpdateLastAckOfDurableSub()); + if( batchStatments ) { + c.setUpdateLastAckStatement(s); + } + } + + s.setLong(1, seq); + s.setString(2, destination.getQualifiedName()); + s.setString(3, clientId); + s.setString(4, subscriptionName); + + if( batchStatments ) { + s.addBatch(); + } else if ( s.executeUpdate() != 1 ) { + throw new SQLException("Failed add a message"); + } + } finally { + if( !batchStatments ) { + s.close(); + } + } + + } + + public void doRecoverSubscription(TransactionContext c, ActiveMQDestination destination, String clientId, String subscriptionName, JDBCMessageRecoveryListener listener) throws Throwable { +// dumpTables(c, destination.getQualifiedName(),clientId,subscriptionName); + + PreparedStatement s = null; + ResultSet rs = null; + try { + + s = c.getConnection().prepareStatement(statementProvider.getFindAllDurableSubMessagesStatment()); + s.setString(1, destination.getQualifiedName()); + s.setString(2, clientId); + s.setString(3, subscriptionName); + rs = s.executeQuery(); + + if( statementProvider.isUseExternalMessageReferences() ) { + while (rs.next()) { + listener.recoverMessageReference(rs.getString(2)); + } + } else { + while (rs.next()) { + listener.recoverMessage(rs.getLong(1), getBinaryData(rs, 2)); + } + } + + } + finally { + close(rs); + close(s); + } + } + + /** + * @see org.activemq.store.jdbc.JDBCAdapter#doSetSubscriberEntry(java.sql.Connection, java.lang.Object, org.activemq.service.SubscriptionInfo) + */ + + public void doSetSubscriberEntry(TransactionContext c, ActiveMQDestination destination, String clientId, String subscriptionName, String selector, boolean retroactive) throws SQLException, IOException { + +// dumpTables(c, destination.getQualifiedName(), clientId, subscriptionName); + + PreparedStatement s = null; + try { + + long lastMessageId = -1; + if(!retroactive) { + s = c.getConnection().prepareStatement(statementProvider.getFindLastSequenceIdInMsgs()); + ResultSet rs=null; + try { + rs = s.executeQuery(); + if (rs.next()) { + lastMessageId = rs.getLong(1); + } + } finally { + close(rs); + close(s); + } + } + + s = c.getConnection().prepareStatement(statementProvider.getCreateDurableSubStatment()); + s.setString(1, destination.getQualifiedName()); + s.setString(2, clientId); + s.setString(3, subscriptionName); + s.setString(4, selector); + s.setLong(5, lastMessageId); + + if (s.executeUpdate() != 1) { + throw new IOException("Could not create durable subscription for: "+clientId); + } + } + finally { + close(s); + } + } + + public SubscriptionInfo doGetSubscriberEntry(TransactionContext c, ActiveMQDestination destination, String clientId, String subscriptionName) throws SQLException, IOException { + + PreparedStatement s = null; + ResultSet rs = null; + try { + + s = c.getConnection().prepareStatement(statementProvider.getFindDurableSubStatment()); + s.setString(1, destination.getQualifiedName()); + s.setString(2, clientId); + s.setString(3, subscriptionName); + rs = s.executeQuery(); + + if (!rs.next()) { + return null; + } + + SubscriptionInfo subscription = new SubscriptionInfo(); + subscription.setDestination(destination); + subscription.setClientId(clientId); + subscription.setSubcriptionName(subscriptionName); + subscription.setSelector(rs.getString(1)); + return subscription; + + } + finally { + close(rs); + close(s); + } + } + + public void doRemoveAllMessages(TransactionContext c, ActiveMQDestination destinationName) throws SQLException, IOException { + PreparedStatement s = null; + try { + s = c.getConnection().prepareStatement(statementProvider.getRemoveAllMessagesStatment()); + s.setString(1, destinationName.getQualifiedName()); + s.executeUpdate(); + s.close(); + + s = c.getConnection().prepareStatement(statementProvider.getRemoveAllSubscriptionsStatment()); + s.setString(1, destinationName.getQualifiedName()); + s.executeUpdate(); + + } + finally { + close(s); + } + } + + public void doDeleteSubscription(TransactionContext c, ActiveMQDestination destination, String clientId, String subscriptionName) throws SQLException, IOException { + PreparedStatement s = null; + try { + s = c.getConnection().prepareStatement(statementProvider.getDeleteSubscriptionStatment()); + s.setString(1, destination.getQualifiedName()); + s.setString(2, clientId); + s.setString(3, subscriptionName); + s.executeUpdate(); + } + finally { + close(s); + } + } + + public void doDeleteOldMessages(TransactionContext c) throws SQLException, IOException { + PreparedStatement s = null; + try { + s = c.getConnection().prepareStatement(statementProvider.getDeleteOldMessagesStatment()); + s.setLong(1, System.currentTimeMillis()); + int i = s.executeUpdate(); + log.debug("Deleted "+i+" old message(s)."); + } + finally { + close(s); + } + } + + static private void close(PreparedStatement s) { + try { + s.close(); + } + catch (Throwable e) { + } + } + + static private void close(ResultSet rs) { + try { + rs.close(); + } + catch (Throwable e) { + } + } + + public Set doGetDestinations(TransactionContext c) throws SQLException, IOException { + HashSet rc = new HashSet(); + PreparedStatement s = null; + ResultSet rs = null; + try { + s = c.getConnection().prepareStatement(statementProvider.getFindAllDestinationsStatment()); + rs = s.executeQuery(); + + while (rs.next()) { + rc.add( ActiveMQDestination.createDestination(rs.getString(1), ActiveMQDestination.QUEUE_TYPE)); + } + } + finally { + close(rs); + close(s); + } + return rc; + } + + public boolean isBatchStatments() { + return batchStatments; + } + + public void setBatchStatments(boolean batchStatments) { + this.batchStatments = batchStatments; + } + + public void setUseExternalMessageReferences(boolean useExternalMessageReferences) { + statementProvider.setUseExternalMessageReferences(useExternalMessageReferences); + } + + /* + * Useful for debugging. + public void dumpTables(Connection c, String destinationName, String clientId, String subscriptionName) throws SQLException { + printQuery(c, "Select * from ACTIVEMQ_MSGS", System.out); + printQuery(c, "Select * from ACTIVEMQ_ACKS", System.out); + PreparedStatement s = c.prepareStatement("SELECT M.ID, D.LAST_ACKED_ID FROM " + +"ACTIVEMQ_MSGS M, " + +"ACTIVEMQ_ACKS D " + +"WHERE D.CONTAINER=? AND D.CLIENT_ID=? AND D.SUB_NAME=?" + +" AND M.CONTAINER=D.CONTAINER AND M.ID > D.LAST_ACKED_ID" + +" ORDER BY M.ID"); + s.setString(1,destinationName); + s.setString(2,clientId); + s.setString(3,subscriptionName); + printQuery(s,System.out); + } + + private void printQuery(Connection c, String query, PrintStream out) throws SQLException { + printQuery(c.prepareStatement(query), out); + } + + private void printQuery(PreparedStatement s, PrintStream out) throws SQLException { + + ResultSet set=null; + try { + set = s.executeQuery(); + ResultSetMetaData metaData = set.getMetaData(); + for( int i=1; i<= metaData.getColumnCount(); i++ ) { + if(i==1) + out.print("||"); + out.print(metaData.getColumnName(i)+"||"); + } + out.println(); + while(set.next()) { + for( int i=1; i<= metaData.getColumnCount(); i++ ) { + if(i==1) + out.print("|"); + out.print(set.getString(i)+"|"); + } + out.println(); + } + } finally { + try { set.close(); } catch (Throwable ignore) {} + try { s.close(); } catch (Throwable ignore) {} + } + } + */ +} diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/DefaultStatementProvider.java b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/DefaultStatementProvider.java new file mode 100755 index 0000000000..04848b65c1 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/DefaultStatementProvider.java @@ -0,0 +1,273 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.jdbc.adapter; + +import org.activemq.store.jdbc.StatementProvider; + + +/** + * @version $Revision: 1.4 $ + */ +public class DefaultStatementProvider implements StatementProvider { + + protected String tablePrefix = ""; + protected String messageTableName = "ACTIVEMQ_MSGS"; + protected String durableSubAcksTableName = "ACTIVEMQ_ACKS"; + + protected String binaryDataType = "BLOB"; + protected String containerNameDataType = "VARCHAR(250)"; + protected String xidDataType = "VARCHAR(250)"; + protected String msgIdDataType = "VARCHAR(250)"; + protected String sequenceDataType = "INTEGER"; + protected String longDataType = "BIGINT"; + protected String stringIdDataType = "VARCHAR(250)"; + + protected boolean useExternalMessageReferences=false; + + + public String [] getCreateSchemaStatments() { + return new String[]{ + "CREATE TABLE "+tablePrefix+messageTableName+"(" + +"ID "+sequenceDataType+" NOT NULL" + +", CONTAINER "+containerNameDataType + +", MSGID_PROD "+msgIdDataType + +", MSGID_SEQ "+sequenceDataType + +", EXPIRATION "+longDataType + +", MSG "+(useExternalMessageReferences ? stringIdDataType : binaryDataType) + +", PRIMARY KEY ( ID ) )", + "CREATE INDEX "+tablePrefix+messageTableName+"_MIDX ON "+tablePrefix+messageTableName+" (MSGID_PROD,MSGID_SEQ)", + "CREATE INDEX "+tablePrefix+messageTableName+"_CIDX ON "+tablePrefix+messageTableName+" (CONTAINER)", + "CREATE TABLE "+tablePrefix+durableSubAcksTableName+"(" + +"CONTAINER "+containerNameDataType+" NOT NULL" + +", CLIENT_ID "+stringIdDataType+" NOT NULL" + +", SUB_NAME "+stringIdDataType+" NOT NULL" + +", SELECTOR "+stringIdDataType + +", LAST_ACKED_ID "+sequenceDataType + +", PRIMARY KEY ( CONTAINER, CLIENT_ID, SUB_NAME))", + }; + } + + public String [] getDropSchemaStatments() { + return new String[]{ + "DROP TABLE "+tablePrefix+durableSubAcksTableName+"", + "DROP TABLE "+tablePrefix+messageTableName+"", + }; + } + + public String getAddMessageStatment() { + return "INSERT INTO "+tablePrefix+messageTableName+"(ID, MSGID_PROD, MSGID_SEQ, CONTAINER, EXPIRATION, MSG) VALUES (?, ?, ?, ?, ?, ?)"; + } + public String getUpdateMessageStatment() { + return "UPDATE "+tablePrefix+messageTableName+" SET MSG=? WHERE ID=?"; + } + public String getRemoveMessageStatment() { + return "DELETE FROM "+tablePrefix+messageTableName+" WHERE ID=?"; + } + public String getFindMessageSequenceIdStatment() { + return "SELECT ID FROM "+tablePrefix+messageTableName+" WHERE MSGID_PROD=? AND MSGID_SEQ=?"; + } + public String getFindMessageStatment() { + return "SELECT MSG FROM "+tablePrefix+messageTableName+" WHERE ID=?"; + } + public String getFindAllMessagesStatment() { + return "SELECT ID, MSG FROM "+tablePrefix+messageTableName+" WHERE CONTAINER=? ORDER BY ID"; + } + public String getFindLastSequenceIdInMsgs() { + return "SELECT MAX(ID) FROM "+tablePrefix+messageTableName; + } + public String getFindLastSequenceIdInAcks() { + return "SELECT MAX(LAST_ACKED_ID) FROM "+tablePrefix+durableSubAcksTableName; + } + + public String getCreateDurableSubStatment() { + return "INSERT INTO "+tablePrefix+durableSubAcksTableName + +"(CONTAINER, CLIENT_ID, SUB_NAME, SELECTOR, LAST_ACKED_ID) " + +"VALUES (?, ?, ?, ?, ?)"; + } + + public String getFindDurableSubStatment() { + return "SELECT SELECTOR, SUB_NAME " + + "FROM "+tablePrefix+durableSubAcksTableName+ + " WHERE CONTAINER=? AND CLIENT_ID=? AND SUB_NAME=?"; + } + + public String getUpdateLastAckOfDurableSub() { + return "UPDATE "+tablePrefix+durableSubAcksTableName+ + " SET LAST_ACKED_ID=?" + + " WHERE CONTAINER=? AND CLIENT_ID=? AND SUB_NAME=?"; + } + + public String getDeleteSubscriptionStatment() { + return "DELETE FROM "+tablePrefix+durableSubAcksTableName+ + " WHERE CONTAINER=? AND CLIENT_ID=? AND SUB_NAME=?"; + } + + public String getFindAllDurableSubMessagesStatment() { + return "SELECT M.ID, M.MSG FROM " + +tablePrefix+messageTableName+" M, " + +tablePrefix+durableSubAcksTableName +" D " + +" WHERE D.CONTAINER=? AND D.CLIENT_ID=? AND D.SUB_NAME=?" + +" AND M.CONTAINER=D.CONTAINER AND M.ID > D.LAST_ACKED_ID" + +" ORDER BY M.ID"; + } + + public String getFindAllDestinationsStatment() { + return "SELECT DISTINCT CONTAINER FROM "+tablePrefix+messageTableName; + } + + public String getRemoveAllMessagesStatment() { + return "DELETE FROM "+tablePrefix+messageTableName+" WHERE CONTAINER=?"; + } + + public String getRemoveAllSubscriptionsStatment() { + return "DELETE FROM "+tablePrefix+durableSubAcksTableName+" WHERE CONTAINER=?"; + } + + public String getDeleteOldMessagesStatment() { + return "DELETE FROM "+tablePrefix+messageTableName+ + " WHERE ( EXPIRATION<>0 AND EXPIRATIONActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.jdbc.adapter; + +import org.activemq.store.jdbc.StatementProvider; + +/** + * + * @version $Revision: 1.2 $ + */ +public class HsqldbJDBCAdapter extends BytesJDBCAdapter { + + public static class HSQLStatementProvider extends DefaultStatementProvider { + public HSQLStatementProvider() { + setBinaryDataType("OTHER"); + } + } + + public HsqldbJDBCAdapter() { + super(new HSQLStatementProvider()); + } + + public HsqldbJDBCAdapter(StatementProvider provider) { + super(provider); + } + + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/ImageBasedJDBCAdaptor.java b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/ImageBasedJDBCAdaptor.java new file mode 100755 index 0000000000..3a1409bb6c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/ImageBasedJDBCAdaptor.java @@ -0,0 +1,51 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.jdbc.adapter; + +import org.activemq.store.jdbc.StatementProvider; + +/** + * Provides JDBCAdapter since that uses + * IMAGE datatype to hold binary data. + * + * The databases/JDBC drivers that use this adapter are: + *
    + *
  • Sybase
  • + *
  • MS SQL
  • + *
+ * + */ +public class ImageBasedJDBCAdaptor extends DefaultJDBCAdapter { + + public static StatementProvider createStatementProvider() { + DefaultStatementProvider answer = new DefaultStatementProvider(); + answer.setBinaryDataType("IMAGE"); + return answer; + } + + public ImageBasedJDBCAdaptor() { + super(createStatementProvider()); + } + + public ImageBasedJDBCAdaptor(StatementProvider provider) { + super(provider); + + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/InformixJDBCAdapter.java b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/InformixJDBCAdapter.java new file mode 100755 index 0000000000..a8aaa23c24 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/InformixJDBCAdapter.java @@ -0,0 +1,46 @@ +/** + * + * Copyright 2005 Pawel Tucholski + * + * Licensed 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.activemq.store.jdbc.adapter; + +import org.activemq.store.jdbc.StatementProvider; + +/** + * JDBC Adapter for Informix database. + * Because Informix database restricts length of composite primary keys, length of + * container name field and subscription id field must be reducted to 150 characters. + * Therefore be sure not to use longer names for container name and subscription id than 150 characters. + */ +public class InformixJDBCAdapter extends BlobJDBCAdapter { + + public static StatementProvider createStatementProvider() { + DefaultStatementProvider answer = new DefaultStatementProvider(); + answer.setContainerNameDataType("VARCHAR(150)"); + answer.setStringIdDataType("VARCHAR(150)"); + answer.setLongDataType("INT8"); + answer.setBinaryDataType("BYTE"); + return answer; + } + + public InformixJDBCAdapter() { + this(createStatementProvider()); + } + + public InformixJDBCAdapter(StatementProvider provider) { + super(provider); + } +} diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/OracleJDBCAdapter.java b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/OracleJDBCAdapter.java new file mode 100755 index 0000000000..54742c2064 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/OracleJDBCAdapter.java @@ -0,0 +1,67 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.jdbc.adapter; + +import java.sql.Blob; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.activemq.store.jdbc.StatementProvider; + +/** + * Implements all the default JDBC operations that are used + * by the JDBCPersistenceAdapter. + *

+ * Subclassing is encouraged to override the default + * implementation of methods to account for differences + * in JDBC Driver implementations. + *

+ * The JDBCAdapter inserts and extracts BLOB data using the + * getBytes()/setBytes() operations. + *

+ * The databases/JDBC drivers that use this adapter are: + *

    + *
  • + *
+ * + * @version $Revision: 1.2 $ + */ +public class OracleJDBCAdapter extends DefaultJDBCAdapter { + + public static StatementProvider createStatementProvider() { + DefaultStatementProvider answer = new DefaultStatementProvider(); + answer.setLongDataType("NUMBER"); + return answer; + } + + public OracleJDBCAdapter() { + this(createStatementProvider()); + } + + public OracleJDBCAdapter(StatementProvider provider) { + super(provider); + } + + protected byte[] getBinaryData(ResultSet rs, int index) throws SQLException { + + // Get as a BLOB + Blob aBlob = rs.getBlob(1); + return aBlob.getBytes(1, (int) aBlob.length()); + } +} diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/PostgresqlJDBCAdapter.java b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/PostgresqlJDBCAdapter.java new file mode 100644 index 0000000000..14c9f4e550 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/PostgresqlJDBCAdapter.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.store.jdbc.adapter; + +import org.activemq.store.jdbc.StatementProvider; + +/** + * Implements all the default JDBC operations that are used + * by the JDBCPersistenceAdapter. + *

+ * Subclassing is encouraged to override the default + * implementation of methods to account for differences + * in JDBC Driver implementations. + *

+ * The JDBCAdapter inserts and extracts BLOB data using the + * getBytes()/setBytes() operations. + *

+ * The databases/JDBC drivers that use this adapter are: + *

    + *
  • + *
+ * + * @version $Revision: 1.1 $ + */ +public class PostgresqlJDBCAdapter extends BytesJDBCAdapter { + + public static StatementProvider createStatementProvider() { + DefaultStatementProvider answer = new DefaultStatementProvider(); + answer.setBinaryDataType("BYTEA"); + return answer; + } + + public PostgresqlJDBCAdapter() { + this(createStatementProvider()); + } + + public PostgresqlJDBCAdapter(StatementProvider provider) { + super(provider); + } +} diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/StreamJDBCAdapter.java b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/StreamJDBCAdapter.java new file mode 100755 index 0000000000..70f202a30a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/StreamJDBCAdapter.java @@ -0,0 +1,81 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.jdbc.adapter; + +import java.io.IOException; +import java.io.InputStream; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.activeio.ByteArrayInputStream; +import org.activeio.ByteArrayOutputStream; +import org.activemq.store.jdbc.StatementProvider; + +/** + * This JDBCAdapter inserts and extracts BLOB data using the + * setBinaryStream()/getBinaryStream() operations. + * + * The databases/JDBC drivers that use this adapter are: + *
    + *
  • Axion
  • + *
+ * + * @version $Revision: 1.2 $ + */ +public class StreamJDBCAdapter extends DefaultJDBCAdapter { + + public StreamJDBCAdapter() { + super(); + } + + public StreamJDBCAdapter(StatementProvider provider) { + super(provider); + } + + /** + * @see org.activemq.store.jdbc.adapter.DefaultJDBCAdapter#getBinaryData(java.sql.ResultSet, int) + */ + protected byte[] getBinaryData(ResultSet rs, int index) throws SQLException { + + try { + InputStream is = rs.getBinaryStream(index); + ByteArrayOutputStream os = new ByteArrayOutputStream(1024 * 4); + + int ch; + while ((ch = is.read()) >= 0) { + os.write(ch); + } + is.close(); + os.close(); + + return os.toByteArray(); + } catch (IOException e) { + throw (SQLException)new SQLException("Error reading binary parameter: "+index).initCause(e); + } + } + + /** + * @see org.activemq.store.jdbc.adapter.DefaultJDBCAdapter#setBinaryData(java.sql.PreparedStatement, int, byte[]) + */ + protected void setBinaryData(PreparedStatement s, int index, byte[] data) throws SQLException { + s.setBinaryStream(index, new ByteArrayInputStream(data), data.length); + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/package.html b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/package.html new file mode 100755 index 0000000000..62b80d753b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/adapter/package.html @@ -0,0 +1,12 @@ + + + + + +

+ Implements database/driver apapters to compensate for the + wide differences in the BLOB handing of JDBC drivers. +

+ + + diff --git a/activemq-core/src/main/java/org/activemq/store/jdbc/package.html b/activemq-core/src/main/java/org/activemq/store/jdbc/package.html new file mode 100755 index 0000000000..0295c3f577 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/jdbc/package.html @@ -0,0 +1,11 @@ + + + + + +

+ Message persistence implemented using JDBC +

+ + + diff --git a/activemq-core/src/main/java/org/activemq/store/journal/JournalMessageStore.java b/activemq-core/src/main/java/org/activemq/store/journal/JournalMessageStore.java new file mode 100755 index 0000000000..16f2a31836 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/journal/JournalMessageStore.java @@ -0,0 +1,373 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.journal; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; + +import org.activeio.journal.RecordLocation; +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.JournalQueueAck; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageId; +import org.activemq.store.MessageRecoveryListener; +import org.activemq.store.MessageStore; +import org.activemq.store.PersistenceAdapter; +import org.activemq.transaction.Synchronization; +import org.activemq.util.Callback; +import org.activemq.util.TransactionTemplate; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A MessageStore that uses a Journal to store it's messages. + * + * @version $Revision: 1.14 $ + */ +public class JournalMessageStore implements MessageStore { + + private static final Log log = LogFactory.getLog(JournalMessageStore.class); + + protected final JournalPersistenceAdapter peristenceAdapter; + protected final JournalTransactionStore transactionStore; + protected final MessageStore longTermStore; + protected final ActiveMQDestination destination; + protected final TransactionTemplate transactionTemplate; + + private LinkedHashMap messages = new LinkedHashMap(); + private ArrayList messageAcks = new ArrayList(); + + /** A MessageStore that we can use to retrieve messages quickly. */ + private LinkedHashMap cpAddedMessageIds; + + protected RecordLocation lastLocation; + protected HashSet inFlightTxLocations = new HashSet(); + + public JournalMessageStore(JournalPersistenceAdapter adapter, MessageStore checkpointStore, ActiveMQDestination destination) { + this.peristenceAdapter = adapter; + this.transactionStore = adapter.getTransactionStore(); + this.longTermStore = checkpointStore; + this.destination = destination; + this.transactionTemplate = new TransactionTemplate(adapter, new ConnectionContext()); + } + + /** + * Not synchronized since the Journal has better throughput if you increase + * the number of concurrent writes that it is doing. + */ + public void addMessage(ConnectionContext context, final Message message) throws IOException { + + final MessageId id = message.getMessageId(); + + final boolean debug = log.isDebugEnabled(); + message.incrementReferenceCount(); + + final RecordLocation location = peristenceAdapter.writeCommand(message, message.isResponseRequired()); + if( !context.isInTransaction() ) { + if( debug ) + log.debug("Journalled message add for: "+id+", at: "+location); + addMessage(message, location); + } else { + if( debug ) + log.debug("Journalled transacted message add for: "+id+", at: "+location); + synchronized( this ) { + inFlightTxLocations.add(location); + } + transactionStore.addMessage(this, message, location); + context.getTransaction().addSynchronization(new Synchronization(){ + public void afterCommit() { + if( debug ) + log.debug("Transacted message add commit for: "+id+", at: "+location); + synchronized( JournalMessageStore.this ) { + inFlightTxLocations.remove(location); + addMessage(message, location); + } + } + public void afterRollback() { + if( debug ) + log.debug("Transacted message add rollback for: "+id+", at: "+location); + synchronized( JournalMessageStore.this ) { + inFlightTxLocations.remove(location); + } + message.decrementReferenceCount(); + } + }); + } + } + + private void addMessage(final Message message, final RecordLocation location) { + synchronized (this) { + lastLocation = location; + MessageId id = message.getMessageId(); + messages.put(id, message); + } + } + + public void replayAddMessage(ConnectionContext context, Message message) { + try { + // Only add the message if it has not already been added. + Message t = longTermStore.getMessage(message.getMessageId()); + if( t==null ) { + longTermStore.addMessage(context, message); + } + } + catch (Throwable e) { + log.warn("Could not replay add for message '" + message.getMessageId() + "'. Message may have already been added. reason: " + e); + } + } + + /** + */ + public void removeMessage(ConnectionContext context, final MessageAck ack) throws IOException { + final boolean debug = log.isDebugEnabled(); + JournalQueueAck remove = new JournalQueueAck(); + remove.setDestination(destination); + remove.setMessageAck(ack); + + final RecordLocation location = peristenceAdapter.writeCommand(remove, ack.isResponseRequired()); + if( !context.isInTransaction() ) { + if( debug ) + log.debug("Journalled message remove for: "+ack.getLastMessageId()+", at: "+location); + removeMessage(ack, location); + } else { + if( debug ) + log.debug("Journalled transacted message remove for: "+ack.getLastMessageId()+", at: "+location); + synchronized( this ) { + inFlightTxLocations.add(location); + } + transactionStore.removeMessage(this, ack, location); + context.getTransaction().addSynchronization(new Synchronization(){ + public void afterCommit() { + if( debug ) + log.debug("Transacted message remove commit for: "+ack.getLastMessageId()+", at: "+location); + synchronized( JournalMessageStore.this ) { + inFlightTxLocations.remove(location); + removeMessage(ack, location); + } + } + public void afterRollback() { + if( debug ) + log.debug("Transacted message remove rollback for: "+ack.getLastMessageId()+", at: "+location); + synchronized( JournalMessageStore.this ) { + inFlightTxLocations.remove(location); + } + } + }); + + } + } + + private void removeMessage(final MessageAck ack, final RecordLocation location) { + synchronized (this) { + lastLocation = location; + MessageId id = ack.getLastMessageId(); + Message message = (Message) messages.remove(id); + if (message == null) { + messageAcks.add(ack); + } else { + message.decrementReferenceCount(); + } + } + } + + public void replayRemoveMessage(ConnectionContext context, MessageAck messageAck) { + try { + // Only remove the message if it has not already been removed. + Message t = longTermStore.getMessage(messageAck.getLastMessageId()); + if( t!=null ) { + longTermStore.removeMessage(context, messageAck); + } + } + catch (Throwable e) { + log.warn("Could not replay acknowledge for message '" + messageAck.getLastMessageId() + "'. Message may have already been acknowledged. reason: " + e); + } + } + + /** + * @return + * @throws IOException + */ + public RecordLocation checkpoint() throws IOException { + return checkpoint(null); + } + + /** + * @return + * @throws IOException + */ + public RecordLocation checkpoint(final Callback postCheckpointTest) throws IOException { + + + RecordLocation rc; + final ArrayList cpRemovedMessageLocations; + final ArrayList cpActiveJournalLocations; + final int maxCheckpointMessageAddSize = peristenceAdapter.getMaxCheckpointMessageAddSize(); + + // swap out the message hash maps.. + synchronized (this) { + cpAddedMessageIds = this.messages; + cpRemovedMessageLocations = this.messageAcks; + + cpActiveJournalLocations=new ArrayList(inFlightTxLocations); + + this.messages = new LinkedHashMap(); + this.messageAcks = new ArrayList(); + } + + transactionTemplate.run(new Callback() { + public void execute() throws Throwable { + + int size = 0; + + PersistenceAdapter persitanceAdapter = transactionTemplate.getPersistenceAdapter(); + ConnectionContext context = transactionTemplate.getContext(); + + // Checkpoint the added messages. + Iterator iterator = cpAddedMessageIds.values().iterator(); + while (iterator.hasNext()) { + Message message = (Message) iterator.next(); + try { + longTermStore.addMessage(context, message); + } catch (Throwable e) { + log.warn("Message could not be added to long term store: " + e.getMessage(), e); + } + + size += message.getSize(); + + iterator.remove(); + message.decrementReferenceCount(); + + // Commit the batch if it's getting too big + if( size >= maxCheckpointMessageAddSize ) { + persitanceAdapter.commitTransaction(context); + persitanceAdapter.beginTransaction(context); + size=0; + } + + } + + persitanceAdapter.commitTransaction(context); + persitanceAdapter.beginTransaction(context); + + // Checkpoint the removed messages. + iterator = cpRemovedMessageLocations.iterator(); + while (iterator.hasNext()) { + try { + MessageAck ack = (MessageAck) iterator.next(); + longTermStore.removeMessage(transactionTemplate.getContext(), ack); + } catch (Throwable e) { + log.debug("Message could not be removed from long term store: " + e.getMessage(), e); + } + } + + if( postCheckpointTest!= null ) { + postCheckpointTest.execute(); + } + } + + }); + + synchronized (this) { + cpAddedMessageIds = null; + } + + if( cpActiveJournalLocations.size() > 0 ) { + Collections.sort(cpActiveJournalLocations); + return (RecordLocation) cpActiveJournalLocations.get(0); + } else { + return lastLocation; + } + } + + /** + * + */ + public Message getMessage(MessageId identity) throws IOException { + Message answer = null; + + synchronized (this) { + // Do we have a still have it in the journal? + answer = (Message) messages.get(identity); + if( answer==null && cpAddedMessageIds!=null ) + answer = (Message) cpAddedMessageIds.get(identity); + } + + if (answer != null ) { + return answer; + } + + // If all else fails try the long term message store. + return longTermStore.getMessage(identity); + } + + /** + * Replays the checkpointStore first as those messages are the oldest ones, + * then messages are replayed from the transaction log and then the cache is + * updated. + * + * @param listener + * @throws Throwable + */ + public void recover(final MessageRecoveryListener listener) throws Throwable { + peristenceAdapter.checkpoint(true, true); + longTermStore.recover(listener); + } + + public void start() throws IOException { + longTermStore.start(); + } + + public void stop(long timeout) throws IOException { + longTermStore.stop(timeout); + } + + /** + * @return Returns the longTermStore. + */ + public MessageStore getLongTermMessageStore() { + return longTermStore; + } + + /** + * @see org.activemq.store.MessageStore#removeAllMessages(ConnectionContext) + */ + public void removeAllMessages(ConnectionContext context) throws IOException { + peristenceAdapter.checkpoint(true, true); + longTermStore.removeAllMessages(context); + } + + public ActiveMQDestination getDestination() { + return destination; + } + + public void addMessageReference(ConnectionContext context, MessageId messageId, long expirationTime, String messageRef) throws IOException { + throw new IOException("The journal does not support message references."); + } + + public String getMessageReference(MessageId identity) throws IOException { + throw new IOException("The journal does not support message references."); + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/journal/JournalPersistenceAdapter.java b/activemq-core/src/main/java/org/activemq/store/journal/JournalPersistenceAdapter.java new file mode 100755 index 0000000000..6c93674310 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/journal/JournalPersistenceAdapter.java @@ -0,0 +1,645 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.store.journal; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; + +import org.activeio.command.WireFormat; +import org.activeio.journal.InvalidRecordLocationException; +import org.activeio.journal.Journal; +import org.activeio.journal.JournalEventListener; +import org.activeio.journal.RecordLocation; +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.DataStructure; +import org.activemq.command.JournalQueueAck; +import org.activemq.command.JournalTopicAck; +import org.activemq.command.JournalTrace; +import org.activemq.command.JournalTransaction; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.memory.UsageListener; +import org.activemq.memory.UsageManager; +import org.activemq.openwire.OpenWireFormat; +import org.activemq.store.MessageStore; +import org.activemq.store.PersistenceAdapter; +import org.activemq.store.TopicMessageStore; +import org.activemq.store.TransactionStore; +import org.activemq.store.jdbc.JDBCPersistenceAdapter; +import org.activemq.store.journal.JournalTransactionStore.Tx; +import org.activemq.store.journal.JournalTransactionStore.TxOperation; +import org.activemq.thread.Scheduler; +import org.activemq.thread.Task; +import org.activemq.thread.TaskRunner; +import org.activemq.thread.TaskRunnerFactory; +import org.activemq.util.IOExceptionSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.Callable; +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.FutureTask; +import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +/** + * An implementation of {@link PersistenceAdapter} designed for use with a + * {@link Journal} and then check pointing asynchronously on a timeout with some + * other long term persistent storage. + * + * @org.xbean.XBean + * + * @version $Revision: 1.17 $ + */ +public class JournalPersistenceAdapter implements PersistenceAdapter, JournalEventListener, UsageListener { + + private static final Log log = LogFactory.getLog(JournalPersistenceAdapter.class); + + private final Journal journal; + private final PersistenceAdapter longTermPersistence; + final UsageManager usageManager; + + private final WireFormat wireFormat = new OpenWireFormat(false); + + private final ConcurrentHashMap queues = new ConcurrentHashMap(); + private final ConcurrentHashMap topics = new ConcurrentHashMap(); + + private long checkpointInterval = 1000 * 60 * 5; + private long lastCheckpointRequest = System.currentTimeMillis(); + private long lastCleanup = System.currentTimeMillis(); + private int maxCheckpointWorkers = 10; + private int maxCheckpointMessageAddSize = 1024*1024; + + private JournalTransactionStore transactionStore = new JournalTransactionStore(this); + private ThreadPoolExecutor checkpointExecutor; + + private TaskRunner checkpointTask; + private CountDownLatch nextCheckpointCountDownLatch = new CountDownLatch(1); + private boolean fullCheckPoint; + + private AtomicBoolean started = new AtomicBoolean(false); + + private final Runnable periodicCheckpointTask = new Runnable() { + public void run() { + if( System.currentTimeMillis()>lastCheckpointRequest+checkpointInterval ) { + checkpoint(false, true); + } + } + }; + + public JournalPersistenceAdapter(Journal journal, PersistenceAdapter longTermPersistence, UsageManager memManager, TaskRunnerFactory taskRunnerFactory) throws IOException { + + this.journal = journal; + journal.setJournalEventListener(this); + + checkpointTask = taskRunnerFactory.createTaskRunner(new Task(){ + public boolean iterate() { + return doCheckpoint(); + } + }); + + this.longTermPersistence = longTermPersistence; + this.usageManager = memManager; + } + + public Set getDestinations() { + Set destinations = longTermPersistence.getDestinations(); + destinations.addAll(queues.keySet()); + destinations.addAll(topics.keySet()); + return destinations; + } + + private MessageStore createMessageStore(ActiveMQDestination destination) throws IOException { + if (destination.isQueue()) { + return createQueueMessageStore((ActiveMQQueue) destination); + } + else { + return createTopicMessageStore((ActiveMQTopic) destination); + } + } + + public MessageStore createQueueMessageStore(ActiveMQQueue destination) throws IOException { + JournalMessageStore store = (JournalMessageStore) queues.get(destination); + if (store == null) { + MessageStore checkpointStore = longTermPersistence.createQueueMessageStore(destination); + store = new JournalMessageStore(this, checkpointStore, destination); + queues.put(destination, store); + } + return store; + } + + public TopicMessageStore createTopicMessageStore(ActiveMQTopic destinationName) throws IOException { + JournalTopicMessageStore store = (JournalTopicMessageStore) topics.get(destinationName); + if (store == null) { + TopicMessageStore checkpointStore = longTermPersistence.createTopicMessageStore(destinationName); + store = new JournalTopicMessageStore(this, checkpointStore, destinationName); + topics.put(destinationName, store); + } + return store; + } + + public TransactionStore createTransactionStore() throws IOException { + return transactionStore; + } + + public long getLastMessageBrokerSequenceId() throws IOException { + return longTermPersistence.getLastMessageBrokerSequenceId(); + } + + public void beginTransaction(ConnectionContext context) throws IOException { + longTermPersistence.beginTransaction(context); + } + + public void commitTransaction(ConnectionContext context) throws IOException { + longTermPersistence.commitTransaction(context); + } + + public void rollbackTransaction(ConnectionContext context) throws IOException { + longTermPersistence.rollbackTransaction(context); + } + + public synchronized void start() throws Exception { + if( !started.compareAndSet(false, true) ) + return; + + longTermPersistence.setUseExternalMessageReferences(false); + + checkpointExecutor = new ThreadPoolExecutor(maxCheckpointWorkers, maxCheckpointWorkers, 30, TimeUnit.SECONDS, new LinkedBlockingQueue(), new ThreadFactory() { + public Thread newThread(Runnable runable) { + Thread t = new Thread(runable, "Journal checkpoint worker"); + t.setPriority(7); + return t; + } + }); + checkpointExecutor.allowCoreThreadTimeOut(true); + + this.usageManager.addUsageListener(this); + + if (longTermPersistence instanceof JDBCPersistenceAdapter) { + // Disabled periodic clean up as it deadlocks with the checkpoint + // operations. + ((JDBCPersistenceAdapter) longTermPersistence).setCleanupPeriod(0); + } + + longTermPersistence.start(); + createTransactionStore(); + recover(); + + // Do a checkpoint periodically. + Scheduler.executePeriodically(periodicCheckpointTask, checkpointInterval/10); + + } + + public void stop() throws Exception { + + if( !started.compareAndSet(true, false) ) + return; + + Scheduler.cancel(periodicCheckpointTask); + + // Take one final checkpoint and stop checkpoint processing. + checkpoint(false, true); + checkpointTask.shutdown(); + checkpointExecutor.shutdown(); + + queues.clear(); + topics.clear(); + + IOException firstException = null; + try { + journal.close(); + } catch (Exception e) { + firstException = IOExceptionSupport.create("Failed to close journals: " + e, e); + } + longTermPersistence.stop(); + + if (firstException != null) { + throw firstException; + } + } + + // Properties + // ------------------------------------------------------------------------- + public PersistenceAdapter getLongTermPersistence() { + return longTermPersistence; + } + + /** + * @return Returns the wireFormat. + */ + public WireFormat getWireFormat() { + return wireFormat; + } + + // Implementation methods + // ------------------------------------------------------------------------- + + /** + * The Journal give us a call back so that we can move old data out of the + * journal. Taking a checkpoint does this for us. + * + * @see org.activemq.journal.JournalEventListener#overflowNotification(org.activemq.journal.RecordLocation) + */ + public void overflowNotification(RecordLocation safeLocation) { + checkpoint(false, true); + } + + /** + * When we checkpoint we move all the journalled data to long term storage. + * @param stopping + * + * @param b + */ + public void checkpoint(boolean sync, boolean fullCheckpoint) { + try { + if (journal == null ) + throw new IllegalStateException("Journal is closed."); + + long now = System.currentTimeMillis(); + CountDownLatch latch = null; + synchronized(this) { + latch = nextCheckpointCountDownLatch; + lastCheckpointRequest = now; + if( fullCheckpoint ) { + this.fullCheckPoint = true; + } + } + + checkpointTask.wakeup(); + + if (sync) { + log.debug("Waking for checkpoint to complete."); + latch.await(); + } + } + catch (InterruptedException e) { + log.warn("Request to start checkpoint failed: " + e, e); + } + } + + /** + * This does the actual checkpoint. + * @return + */ + public boolean doCheckpoint() { + CountDownLatch latch = null; + boolean fullCheckpoint; + synchronized(this) { + latch = nextCheckpointCountDownLatch; + nextCheckpointCountDownLatch = new CountDownLatch(1); + fullCheckpoint = this.fullCheckPoint; + this.fullCheckPoint=false; + } + try { + + log.debug("Checkpoint started."); + RecordLocation newMark = null; + + ArrayList futureTasks = new ArrayList(queues.size()+topics.size()); + + // + // We do many partial checkpoints (fullCheckpoint==false) to move topic messages + // to long term store as soon as possible. + // + // We want to avoid doing that for queue messages since removes the come in the same + // checkpoint cycle will nullify the previous message add. Therefore, we only + // checkpoint queues on the fullCheckpoint cycles. + // + if( fullCheckpoint ) { + Iterator iterator = queues.values().iterator(); + while (iterator.hasNext()) { + try { + final JournalMessageStore ms = (JournalMessageStore) iterator.next(); + FutureTask task = new FutureTask(new Callable() { + public Object call() throws Exception { + return ms.checkpoint(); + }}); + futureTasks.add(task); + checkpointExecutor.execute(task); + } + catch (Exception e) { + log.error("Failed to checkpoint a message store: " + e, e); + } + } + } + + Iterator iterator = topics.values().iterator(); + while (iterator.hasNext()) { + try { + final JournalTopicMessageStore ms = (JournalTopicMessageStore) iterator.next(); + FutureTask task = new FutureTask(new Callable() { + public Object call() throws Exception { + return ms.checkpoint(); + }}); + futureTasks.add(task); + checkpointExecutor.execute(task); + } + catch (Exception e) { + log.error("Failed to checkpoint a message store: " + e, e); + } + } + + try { + for (Iterator iter = futureTasks.iterator(); iter.hasNext();) { + FutureTask ft = (FutureTask) iter.next(); + RecordLocation mark = (RecordLocation) ft.get(); + // We only set a newMark on full checkpoints. + if( fullCheckpoint ) { + if (mark != null && (newMark == null || newMark.compareTo(mark) < 0)) { + newMark = mark; + } + } + } + } catch (Throwable e) { + log.error("Failed to checkpoint a message store: " + e, e); + } + + + if( fullCheckpoint ) { + try { + if (newMark != null) { + log.debug("Marking journal at: " + newMark); + journal.setMark(newMark, true); + } + } + catch (Exception e) { + log.error("Failed to mark the Journal: " + e, e); + } + + if (longTermPersistence instanceof JDBCPersistenceAdapter) { + // We may be check pointing more often than the checkpointInterval if under high use + // But we don't want to clean up the db that often. + long now = System.currentTimeMillis(); + if( now > lastCleanup+checkpointInterval ) { + lastCleanup = now; + ((JDBCPersistenceAdapter) longTermPersistence).cleanup(); + } + } + } + + log.debug("Checkpoint done."); + } + finally { + latch.countDown(); + } + synchronized(this) { + return this.fullCheckPoint; + } + + } + + /** + * @param location + * @return + * @throws IOException + */ + public DataStructure readCommand(RecordLocation location) throws IOException { + try { + org.activeio.Packet data = journal.read(location); + return (DataStructure) wireFormat.unmarshal(data); + } + catch (InvalidRecordLocationException e) { + throw createReadException(location, e); + } + catch (IOException e) { + throw createReadException(location, e); + } + } + + /** + * Move all the messages that were in the journal into long term storage. We + * just replay and do a checkpoint. + * + * @throws IOException + * @throws IOException + * @throws InvalidRecordLocationException + * @throws IllegalStateException + */ + private void recover() throws IllegalStateException, InvalidRecordLocationException, IOException, IOException { + + RecordLocation pos = null; + int transactionCounter = 0; + + log.info("Journal Recovery Started."); + ConnectionContext context = new ConnectionContext(); + + // While we have records in the journal. + while ((pos = journal.getNextRecordLocation(pos)) != null) { + org.activeio.Packet data = journal.read(pos); + DataStructure c = (DataStructure) wireFormat.unmarshal(data); + + if (c instanceof Message ) { + Message message = (Message) c; + JournalMessageStore store = (JournalMessageStore) createMessageStore(message.getDestination()); + if ( message.isInTransaction()) { + transactionStore.addMessage(store, message, pos); + } + else { + store.replayAddMessage(context, message); + transactionCounter++; + } + } else { + switch (c.getDataStructureType()) { + case JournalQueueAck.DATA_STRUCTURE_TYPE: + { + JournalQueueAck command = (JournalQueueAck) c; + JournalMessageStore store = (JournalMessageStore) createMessageStore(command.getDestination()); + if (command.getMessageAck().isInTransaction()) { + transactionStore.removeMessage(store, command.getMessageAck(), pos); + } + else { + store.replayRemoveMessage(context, command.getMessageAck()); + transactionCounter++; + } + } + break; + case JournalTopicAck.DATA_STRUCTURE_TYPE: + { + JournalTopicAck command = (JournalTopicAck) c; + JournalTopicMessageStore store = (JournalTopicMessageStore) createMessageStore(command.getDestination()); + if (command.getTransactionId() != null) { + transactionStore.acknowledge(store, command, pos); + } + else { + store.replayAcknowledge(context, command.getClientId(), command.getSubscritionName(), command.getMessageId()); + transactionCounter++; + } + } + break; + case JournalTransaction.DATA_STRUCTURE_TYPE: + { + JournalTransaction command = (JournalTransaction) c; + try { + // Try to replay the packet. + switch (command.getType()) { + case JournalTransaction.XA_PREPARE: + transactionStore.replayPrepare(command.getTransactionId()); + break; + case JournalTransaction.XA_COMMIT: + case JournalTransaction.LOCAL_COMMIT: + Tx tx = transactionStore.replayCommit(command.getTransactionId(), command.getWasPrepared()); + if (tx == null) + break; // We may be trying to replay a commit that + // was already committed. + + // Replay the committed operations. + tx.getOperations(); + for (Iterator iter = tx.getOperations().iterator(); iter.hasNext();) { + TxOperation op = (TxOperation) iter.next(); + if (op.operationType == TxOperation.ADD_OPERATION_TYPE) { + op.store.replayAddMessage(context, (Message) op.data); + } + if (op.operationType == TxOperation.REMOVE_OPERATION_TYPE) { + op.store.replayRemoveMessage(context, (MessageAck) op.data); + } + if (op.operationType == TxOperation.ACK_OPERATION_TYPE) { + JournalTopicAck ack = (JournalTopicAck) op.data; + ((JournalTopicMessageStore) op.store).replayAcknowledge(context, ack.getClientId(), ack.getSubscritionName(), ack + .getMessageId()); + } + } + transactionCounter++; + break; + case JournalTransaction.LOCAL_ROLLBACK: + case JournalTransaction.XA_ROLLBACK: + transactionStore.replayRollback(command.getTransactionId()); + break; + } + } + catch (IOException e) { + log.error("Recovery Failure: Could not replay: " + c + ", reason: " + e, e); + } + } + break; + case JournalTrace.DATA_STRUCTURE_TYPE: + JournalTrace trace = (JournalTrace) c; + log.debug("TRACE Entry: " + trace.getMessage()); + break; + default: + log.error("Unknown type of record in transaction log which will be discarded: " + c); + } + } + } + + RecordLocation location = writeTraceMessage("RECOVERED", true); + journal.setMark(location, true); + + log.info("Journal Recovered: " + transactionCounter + " message(s) in transactions recovered."); + } + + private IOException createReadException(RecordLocation location, Exception e) { + return IOExceptionSupport.create("Failed to read to journal for: " + location + ". Reason: " + e, e); + } + + protected IOException createWriteException(DataStructure packet, Exception e) { + return IOExceptionSupport.create("Failed to write to journal for: " + packet + ". Reason: " + e, e); + } + + protected IOException createWriteException(String command, Exception e) { + return IOExceptionSupport.create("Failed to write to journal for command: " + command + ". Reason: " + e, e); + } + + protected IOException createRecoveryFailedException(Exception e) { + return IOExceptionSupport.create("Failed to recover from journal. Reason: " + e, e); + } + + /** + * + * @param command + * @param sync + * @return + * @throws IOException + */ + public RecordLocation writeCommand(DataStructure command, boolean sync) throws IOException { + if( started.get() ) + return journal.write(wireFormat.marshal(command), sync); + throw new IOException("closed"); + } + + private RecordLocation writeTraceMessage(String message, boolean sync) throws IOException { + JournalTrace trace = new JournalTrace(); + trace.setMessage(message); + return writeCommand(trace, sync); + } + + public void onMemoryUseChanged(UsageManager memoryManager, int oldPercentUsage, int newPercentUsage) { + if (newPercentUsage > 80 && oldPercentUsage < newPercentUsage) { + checkpoint(false, true); + } + } + + public JournalTransactionStore getTransactionStore() { + return transactionStore; + } + + public void deleteAllMessages() throws IOException { + try { + JournalTrace trace = new JournalTrace(); + trace.setMessage("DELETED"); + RecordLocation location = journal.write(wireFormat.marshal(trace), false); + journal.setMark(location, true); + log.info("Journal deleted: "); + } catch (IOException e) { + throw e; + } catch (Throwable e) { + throw IOExceptionSupport.create(e); + } + longTermPersistence.setUseExternalMessageReferences(false); + longTermPersistence.deleteAllMessages(); + } + + public UsageManager getUsageManager() { + return usageManager; + } + + public int getMaxCheckpointMessageAddSize() { + return maxCheckpointMessageAddSize; + } + + public void setMaxCheckpointMessageAddSize(int maxCheckpointMessageAddSize) { + this.maxCheckpointMessageAddSize = maxCheckpointMessageAddSize; + } + + public int getMaxCheckpointWorkers() { + return maxCheckpointWorkers; + } + + public void setMaxCheckpointWorkers(int maxCheckpointWorkers) { + this.maxCheckpointWorkers = maxCheckpointWorkers; + } + + public boolean isUseExternalMessageReferences() { + return false; + } + + public void setUseExternalMessageReferences(boolean enable) { + if( enable ) + throw new IllegalArgumentException("The journal does not support message references."); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/store/journal/JournalTopicMessageStore.java b/activemq-core/src/main/java/org/activemq/store/journal/JournalTopicMessageStore.java new file mode 100755 index 0000000000..3914f589ea --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/journal/JournalTopicMessageStore.java @@ -0,0 +1,185 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.journal; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; + +import org.activeio.journal.RecordLocation; +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.JournalTopicAck; +import org.activemq.command.Message; +import org.activemq.command.MessageId; +import org.activemq.command.SubscriptionInfo; +import org.activemq.store.MessageRecoveryListener; +import org.activemq.store.TopicMessageStore; +import org.activemq.transaction.Synchronization; +import org.activemq.util.Callback; +import org.activemq.util.SubscriptionKey; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A MessageStore that uses a Journal to store it's messages. + * + * @version $Revision: 1.13 $ + */ +public class JournalTopicMessageStore extends JournalMessageStore implements TopicMessageStore { + + private static final Log log = LogFactory.getLog(JournalTopicMessageStore.class); + + private TopicMessageStore longTermStore; + private HashMap ackedLastAckLocations = new HashMap(); + + public JournalTopicMessageStore(JournalPersistenceAdapter adapter, TopicMessageStore checkpointStore, ActiveMQTopic destinationName) { + super(adapter, checkpointStore, destinationName); + this.longTermStore = checkpointStore; + } + + public void recoverSubscription(String clientId, String subscriptionName, MessageRecoveryListener listener) throws Throwable { + this.peristenceAdapter.checkpoint(true, true); + longTermStore.recoverSubscription(clientId, subscriptionName, listener); + } + + public SubscriptionInfo lookupSubscription(String clientId, String subscriptionName) throws IOException { + return longTermStore.lookupSubscription(clientId, subscriptionName); + } + + public void addSubsciption(String clientId, String subscriptionName, String selector, boolean retroactive) throws IOException { + this.peristenceAdapter.checkpoint(true, true); + longTermStore.addSubsciption(clientId, subscriptionName, selector, retroactive); + } + + public void addMessage(ConnectionContext context, Message message) throws IOException { + super.addMessage(context, message); + this.peristenceAdapter.checkpoint(false, false); + } + + /** + */ + public void acknowledge(ConnectionContext context, String clientId, String subscriptionName, final MessageId messageId) throws IOException { + final boolean debug = log.isDebugEnabled(); + + JournalTopicAck ack = new JournalTopicAck(); + ack.setDestination(destination); + ack.setMessageId(messageId); + ack.setMessageSequenceId(messageId.getBrokerSequenceId()); + ack.setSubscritionName(subscriptionName); + ack.setClientId(clientId); + ack.setTransactionId( context.getTransaction()!=null ? context.getTransaction().getTransactionId():null); + final RecordLocation location = peristenceAdapter.writeCommand(ack, false); + + final SubscriptionKey key = new SubscriptionKey(clientId, subscriptionName); + if( !context.isInTransaction() ) { + if( debug ) + log.debug("Journalled acknowledge for: "+messageId+", at: "+location); + acknowledge(messageId, location, key); + } else { + if( debug ) + log.debug("Journalled transacted acknowledge for: "+messageId+", at: "+location); + synchronized (this) { + inFlightTxLocations.add(location); + } + transactionStore.acknowledge(this, ack, location); + context.getTransaction().addSynchronization(new Synchronization(){ + public void afterCommit() { + if( debug ) + log.debug("Transacted acknowledge commit for: "+messageId+", at: "+location); + synchronized (JournalTopicMessageStore.this) { + inFlightTxLocations.remove(location); + acknowledge(messageId, location, key); + } + } + public void afterRollback() { + if( debug ) + log.debug("Transacted acknowledge rollback for: "+messageId+", at: "+location); + synchronized (JournalTopicMessageStore.this) { + inFlightTxLocations.remove(location); + } + } + }); + } + + } + + public void replayAcknowledge(ConnectionContext context, String clientId, String subscritionName, MessageId messageId) { + try { + SubscriptionInfo sub = longTermStore.lookupSubscription(clientId, subscritionName); + if( sub != null ) { + longTermStore.acknowledge(context, clientId, subscritionName, messageId); + } + } + catch (Throwable e) { + log.debug("Could not replay acknowledge for message '" + messageId + "'. Message may have already been acknowledged. reason: " + e); + } + } + + + /** + * @param messageId + * @param location + * @param key + */ + private void acknowledge(MessageId messageId, RecordLocation location, SubscriptionKey key) { + synchronized(this) { + lastLocation = location; + ackedLastAckLocations.put(key, messageId); + } + } + + public RecordLocation checkpoint() throws IOException { + + final HashMap cpAckedLastAckLocations; + + // swap out the hash maps.. + synchronized (this) { + cpAckedLastAckLocations = this.ackedLastAckLocations; + this.ackedLastAckLocations = new HashMap(); + } + + return super.checkpoint( new Callback() { + public void execute() throws Throwable { + + // Checkpoint the acknowledged messages. + Iterator iterator = cpAckedLastAckLocations.keySet().iterator(); + while (iterator.hasNext()) { + SubscriptionKey subscriptionKey = (SubscriptionKey) iterator.next(); + MessageId identity = (MessageId) cpAckedLastAckLocations.get(subscriptionKey); + longTermStore.acknowledge(transactionTemplate.getContext(), subscriptionKey.clientId, subscriptionKey.subscriptionName, identity); + } + + } + }); + + } + + /** + * @return Returns the longTermStore. + */ + public TopicMessageStore getLongTermTopicMessageStore() { + return longTermStore; + } + + public void deleteSubscription(String clientId, String subscriptionName) throws IOException { + longTermStore.deleteSubscription(clientId, subscriptionName); + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/journal/JournalTransactionStore.java b/activemq-core/src/main/java/org/activemq/store/journal/JournalTransactionStore.java new file mode 100755 index 0000000000..5f7fee01d1 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/journal/JournalTransactionStore.java @@ -0,0 +1,304 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.journal; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +import javax.transaction.xa.XAException; + +import org.activeio.journal.RecordLocation; +import org.activemq.command.JournalTopicAck; +import org.activemq.command.JournalTransaction; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.TransactionId; +import org.activemq.command.XATransactionId; +import org.activemq.store.TransactionRecoveryListener; +import org.activemq.store.TransactionStore; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + */ +public class JournalTransactionStore implements TransactionStore { + + private final JournalPersistenceAdapter peristenceAdapter; + ConcurrentHashMap inflightTransactions = new ConcurrentHashMap(); + ConcurrentHashMap preparedTransactions = new ConcurrentHashMap(); + private boolean doingRecover; + + + public static class TxOperation { + + static final byte ADD_OPERATION_TYPE = 0; + static final byte REMOVE_OPERATION_TYPE = 1; + static final byte ACK_OPERATION_TYPE = 3; + + public byte operationType; + public JournalMessageStore store; + public Object data; + + public TxOperation(byte operationType, JournalMessageStore store, Object data) { + this.operationType=operationType; + this.store=store; + this.data=data; + } + + } + /** + * Operations + * @version $Revision: 1.6 $ + */ + public static class Tx { + + private final RecordLocation location; + private ArrayList operations = new ArrayList(); + + public Tx(RecordLocation location) { + this.location=location; + } + + public void add(JournalMessageStore store, Message msg) { + operations.add(new TxOperation(TxOperation.ADD_OPERATION_TYPE, store, msg)); + } + + public void add(JournalMessageStore store, MessageAck ack) { + operations.add(new TxOperation(TxOperation.REMOVE_OPERATION_TYPE, store, ack)); + } + + public void add(JournalTopicMessageStore store, JournalTopicAck ack) { + operations.add(new TxOperation(TxOperation.ACK_OPERATION_TYPE, store, ack)); + } + + public Message[] getMessages() { + ArrayList list = new ArrayList(); + for (Iterator iter = operations.iterator(); iter.hasNext();) { + TxOperation op = (TxOperation) iter.next(); + if( op.operationType==TxOperation.ADD_OPERATION_TYPE ) { + list.add(op.data); + } + } + Message rc[] = new Message[list.size()]; + list.toArray(rc); + return rc; + } + + public MessageAck[] getAcks() { + ArrayList list = new ArrayList(); + for (Iterator iter = operations.iterator(); iter.hasNext();) { + TxOperation op = (TxOperation) iter.next(); + if( op.operationType==TxOperation.REMOVE_OPERATION_TYPE ) { + list.add(op.data); + } + } + MessageAck rc[] = new MessageAck[list.size()]; + list.toArray(rc); + return rc; + } + + public ArrayList getOperations() { + return operations; + } + + } + + public JournalTransactionStore(JournalPersistenceAdapter adapter) { + this.peristenceAdapter = adapter; + } + + /** + * @throws IOException + * @see org.activemq.store.TransactionStore#prepare(TransactionId) + */ + public void prepare(TransactionId txid) throws IOException { + Tx tx = (Tx) inflightTransactions.remove(txid); + if (tx == null) + return; + peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.XA_PREPARE, txid, false), true); + preparedTransactions.put(txid, tx); + } + + /** + * @throws IOException + * @see org.activemq.store.TransactionStore#prepare(TransactionId) + */ + public void replayPrepare(TransactionId txid) throws IOException { + Tx tx = (Tx) inflightTransactions.remove(txid); + if (tx == null) + return; + preparedTransactions.put(txid, tx); + } + + public Tx getTx(Object txid, RecordLocation location) { + Tx tx = (Tx) inflightTransactions.get(txid); + if (tx == null) { + tx = new Tx(location); + inflightTransactions.put(txid, tx); + } + return tx; + } + + /** + * @throws XAException + * @see org.activemq.store.TransactionStore#commit(org.activemq.service.Transaction) + */ + public void commit(TransactionId txid, boolean wasPrepared) throws IOException { + Tx tx; + if (wasPrepared) { + tx = (Tx) preparedTransactions.remove(txid); + } else { + tx = (Tx) inflightTransactions.remove(txid); + } + + if (tx == null) + return; + + if (txid.isXATransaction()) { + peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.XA_COMMIT, txid, wasPrepared), + true); + } else { + peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.LOCAL_COMMIT, txid, wasPrepared), + true); + } + } + + /** + * @throws XAException + * @see org.activemq.store.TransactionStore#commit(org.activemq.service.Transaction) + */ + public Tx replayCommit(TransactionId txid, boolean wasPrepared) throws IOException { + if (wasPrepared) { + return (Tx) preparedTransactions.remove(txid); + } else { + return (Tx) inflightTransactions.remove(txid); + } + } + + /** + * @throws IOException + * @see org.activemq.store.TransactionStore#rollback(TransactionId) + */ + public void rollback(TransactionId txid) throws IOException { + + Tx tx = (Tx) inflightTransactions.remove(txid); + if (tx != null) + tx = (Tx) preparedTransactions.remove(txid); + + if (tx != null) { + if (txid.isXATransaction()) { + peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.XA_ROLLBACK, txid, false), + true); + } else { + peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.LOCAL_ROLLBACK, txid, false), + true); + } + } + + } + + /** + * @throws IOException + * @see org.activemq.store.TransactionStore#rollback(TransactionId) + */ + public void replayRollback(TransactionId txid) throws IOException { + if (inflightTransactions.remove(txid) != null) + preparedTransactions.remove(txid); + } + + public void start() throws Exception { + } + + public void stop() throws Exception { + } + + synchronized public void recover(TransactionRecoveryListener listener) throws IOException { + // All the in-flight transactions get rolled back.. + inflightTransactions.clear(); + this.doingRecover = true; + try { + for (Iterator iter = preparedTransactions.keySet().iterator(); iter.hasNext();) { + Object txid = (Object) iter.next(); + Tx tx = (Tx) preparedTransactions.get(txid); + listener.recover((XATransactionId) txid, tx.getMessages(), tx.getAcks()); + } + } finally { + this.doingRecover = false; + } + } + + /** + * @param message + * @throws IOException + */ + void addMessage(JournalMessageStore store, Message message, RecordLocation location) throws IOException { + Tx tx = getTx(message.getTransactionId(), location); + tx.add(store, message); + } + + /** + * @param ack + * @throws IOException + */ + public void removeMessage(JournalMessageStore store, MessageAck ack, RecordLocation location) throws IOException { + Tx tx = getTx(ack.getTransactionId(), location); + tx.add(store, ack); + } + + + public void acknowledge(JournalTopicMessageStore store, JournalTopicAck ack, RecordLocation location) { + Tx tx = getTx(ack.getTransactionId(), location); + tx.add(store, ack); + } + + + public RecordLocation checkpoint() throws IOException { + + // Nothing really to checkpoint.. since, we don't + // checkpoint tx operations in to long term store until they are committed. + + // But we keep track of the first location of an operation + // that was associated with an active tx. The journal can not + // roll over active tx records. + RecordLocation rc = null; + for (Iterator iter = inflightTransactions.values().iterator(); iter.hasNext();) { + Tx tx = (Tx) iter.next(); + RecordLocation location = tx.location; + if (rc == null || rc.compareTo(location) < 0) { + rc = location; + } + } + for (Iterator iter = preparedTransactions.values().iterator(); iter.hasNext();) { + Tx tx = (Tx) iter.next(); + RecordLocation location = tx.location; + if (rc == null || rc.compareTo(location) < 0) { + rc = location; + } + } + return rc; + } + + public boolean isDoingRecover() { + return doingRecover; + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalMessageData.java b/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalMessageData.java new file mode 100644 index 0000000000..db95452b42 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalMessageData.java @@ -0,0 +1,48 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Hiram Chirino + * + * Licensed 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.activemq.store.journal; + +import org.activeio.journal.RecordLocation; +import org.activemq.command.Message; +import org.activemq.command.MessageId; + +public class QuickJournalMessageData { + public final MessageId messageId; + public final long expiration; + public final RecordLocation location; + + public QuickJournalMessageData(Message message, RecordLocation location) { + this.messageId = message.getMessageId(); + this.expiration = message.getExpiration(); + this.location=location; + } + + public long getExpiration() { + return expiration; + } + + public MessageId getMessageId() { + return messageId; + } + + public RecordLocation getLocation() { + return location; + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalMessageStore.java b/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalMessageStore.java new file mode 100755 index 0000000000..459e8f03ac --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalMessageStore.java @@ -0,0 +1,403 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.journal; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; + +import org.activeio.journal.RecordLocation; +import org.activeio.journal.active.Location; +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.JournalQueueAck; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageId; +import org.activemq.store.MessageRecoveryListener; +import org.activemq.store.MessageStore; +import org.activemq.store.PersistenceAdapter; +import org.activemq.transaction.Synchronization; +import org.activemq.util.Callback; +import org.activemq.util.TransactionTemplate; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A MessageStore that uses a Journal to store it's messages. + * + * @version $Revision: 1.14 $ + */ +public class QuickJournalMessageStore implements MessageStore { + + private static final Log log = LogFactory.getLog(QuickJournalMessageStore.class); + + protected final QuickJournalPersistenceAdapter peristenceAdapter; + protected final QuickJournalTransactionStore transactionStore; + protected final MessageStore longTermStore; + protected final ActiveMQDestination destination; + protected final TransactionTemplate transactionTemplate; + + private LinkedHashMap messages = new LinkedHashMap(); + private ArrayList messageAcks = new ArrayList(); + + /** A MessageStore that we can use to retrieve messages quickly. */ + private LinkedHashMap cpAddedMessageIds; + + protected RecordLocation lastLocation; + protected HashSet inFlightTxLocations = new HashSet(); + + public QuickJournalMessageStore(QuickJournalPersistenceAdapter adapter, MessageStore checkpointStore, ActiveMQDestination destination) { + this.peristenceAdapter = adapter; + this.transactionStore = adapter.getTransactionStore(); + this.longTermStore = checkpointStore; + this.destination = destination; + this.transactionTemplate = new TransactionTemplate(adapter, new ConnectionContext()); + } + + /** + * Not synchronized since the Journal has better throughput if you increase + * the number of concurrent writes that it is doing. + */ + public void addMessage(ConnectionContext context, final Message message) throws IOException { + + final MessageId id = message.getMessageId(); + + final boolean debug = log.isDebugEnabled(); + final RecordLocation location = peristenceAdapter.writeCommand(message, message.isResponseRequired()); + final QuickJournalMessageData md = new QuickJournalMessageData(message, location); + + if( !context.isInTransaction() ) { + if( debug ) + log.debug("Journalled message add for: "+id+", at: "+location); + addMessage(md, location); + } else { + message.incrementReferenceCount(); + if( debug ) + log.debug("Journalled transacted message add for: "+id+", at: "+location); + synchronized( this ) { + inFlightTxLocations.add(location); + } + transactionStore.addMessage(this, message, location); + context.getTransaction().addSynchronization(new Synchronization(){ + public void afterCommit() { + if( debug ) + log.debug("Transacted message add commit for: "+id+", at: "+location); + message.decrementReferenceCount(); + synchronized( QuickJournalMessageStore.this ) { + inFlightTxLocations.remove(location); + addMessage(md, location); + } + } + public void afterRollback() { + if( debug ) + log.debug("Transacted message add rollback for: "+id+", at: "+location); + message.decrementReferenceCount(); + synchronized( QuickJournalMessageStore.this ) { + inFlightTxLocations.remove(location); + } + } + }); + } + } + + private void addMessage(final QuickJournalMessageData message, final RecordLocation location) { + synchronized (this) { + lastLocation = location; + MessageId id = message.getMessageId(); + messages.put(id, message); + } + } + + static protected String toString(RecordLocation location) { + Location l = (Location) location; + return l.getLogFileId()+":"+l.getLogFileOffset(); + } + + static protected RecordLocation toRecordLocation(String t) { + String[] strings = t.split(":"); + if( strings.length!=2 ) + throw new IllegalArgumentException("Invalid location: "+t); + return new Location(Integer.parseInt(strings[0]),Integer.parseInt(strings[1])); + } + + public void replayAddMessage(ConnectionContext context, Message message, RecordLocation location) { + try { + // Only add the message if it has not already been added. + String t = longTermStore.getMessageReference(message.getMessageId()); + if( t==null ) { + longTermStore.addMessageReference(context, message.getMessageId(), message.getExpiration(), toString(location)); + } + } + catch (Throwable e) { + log.warn("Could not replay add for message '" + message.getMessageId() + "'. Message may have already been added. reason: " + e); + } + } + + + /** + */ + public void removeMessage(ConnectionContext context, final MessageAck ack) throws IOException { + final boolean debug = log.isDebugEnabled(); + JournalQueueAck remove = new JournalQueueAck(); + remove.setDestination(destination); + remove.setMessageAck(ack); + + final RecordLocation location = peristenceAdapter.writeCommand(remove, ack.isResponseRequired()); + if( !context.isInTransaction() ) { + if( debug ) + log.debug("Journalled message remove for: "+ack.getLastMessageId()+", at: "+location); + removeMessage(ack, location); + } else { + if( debug ) + log.debug("Journalled transacted message remove for: "+ack.getLastMessageId()+", at: "+location); + synchronized( this ) { + inFlightTxLocations.add(location); + } + transactionStore.removeMessage(this, ack, location); + context.getTransaction().addSynchronization(new Synchronization(){ + public void afterCommit() { + if( debug ) + log.debug("Transacted message remove commit for: "+ack.getLastMessageId()+", at: "+location); + synchronized( QuickJournalMessageStore.this ) { + inFlightTxLocations.remove(location); + removeMessage(ack, location); + } + } + public void afterRollback() { + if( debug ) + log.debug("Transacted message remove rollback for: "+ack.getLastMessageId()+", at: "+location); + synchronized( QuickJournalMessageStore.this ) { + inFlightTxLocations.remove(location); + } + } + }); + + } + } + + private void removeMessage(final MessageAck ack, final RecordLocation location) { + synchronized (this) { + lastLocation = location; + MessageId id = ack.getLastMessageId(); + if (messages.remove(id) == null) { + messageAcks.add(ack); + } + } + } + + public void replayRemoveMessage(ConnectionContext context, MessageAck messageAck) { + try { + // Only remove the message if it has not already been removed. + String t = longTermStore.getMessageReference(messageAck.getLastMessageId()); + if( t!=null ) { + longTermStore.removeMessage(context, messageAck); + } + } + catch (Throwable e) { + log.warn("Could not replay acknowledge for message '" + messageAck.getLastMessageId() + "'. Message may have already been acknowledged. reason: " + e); + } + } + + /** + * @return + * @throws IOException + */ + public RecordLocation checkpoint() throws IOException { + return checkpoint(null); + } + + /** + * @return + * @throws IOException + */ + public RecordLocation checkpoint(final Callback postCheckpointTest) throws IOException { + + + RecordLocation rc; + final ArrayList cpRemovedMessageLocations; + final ArrayList cpActiveJournalLocations; + final int maxCheckpointMessageAddSize = peristenceAdapter.getMaxCheckpointMessageAddSize(); + + // swap out the message hash maps.. + synchronized (this) { + cpAddedMessageIds = this.messages; + cpRemovedMessageLocations = this.messageAcks; + + cpActiveJournalLocations=new ArrayList(inFlightTxLocations); + + this.messages = new LinkedHashMap(); + this.messageAcks = new ArrayList(); + } + + transactionTemplate.run(new Callback() { + public void execute() throws Throwable { + + int size = 0; + + PersistenceAdapter persitanceAdapter = transactionTemplate.getPersistenceAdapter(); + ConnectionContext context = transactionTemplate.getContext(); + + // Checkpoint the added messages. + Iterator iterator = cpAddedMessageIds.values().iterator(); + while (iterator.hasNext()) { + QuickJournalMessageData message = (QuickJournalMessageData) iterator.next(); + try { + String l = QuickJournalMessageStore.toString(message.getLocation()); + longTermStore.addMessageReference(context, message.getMessageId(), message.getExpiration(), l); + } catch (Throwable e) { + log.warn("Message could not be added to long term store: " + e.getMessage(), e); + } + + size ++; + + iterator.remove(); + + // Commit the batch if it's getting too big + if( size >= maxCheckpointMessageAddSize ) { + persitanceAdapter.commitTransaction(context); + persitanceAdapter.beginTransaction(context); + size=0; + } + + } + + persitanceAdapter.commitTransaction(context); + persitanceAdapter.beginTransaction(context); + + // Checkpoint the removed messages. + iterator = cpRemovedMessageLocations.iterator(); + while (iterator.hasNext()) { + try { + MessageAck ack = (MessageAck) iterator.next(); + longTermStore.removeMessage(transactionTemplate.getContext(), ack); + } catch (Throwable e) { + log.debug("Message could not be removed from long term store: " + e.getMessage(), e); + } + } + + if( postCheckpointTest!= null ) { + postCheckpointTest.execute(); + } + } + + }); + + synchronized (this) { + cpAddedMessageIds = null; + } + + if( cpActiveJournalLocations.size() > 0 ) { + Collections.sort(cpActiveJournalLocations); + return (RecordLocation) cpActiveJournalLocations.get(0); + } else { + return lastLocation; + } + } + + /** + * + */ + public Message getMessage(MessageId identity) throws IOException { + RecordLocation loc=null; + + synchronized (this) { + QuickJournalMessageData answer = null; + // Do we have a still have it in the journal? + answer = (QuickJournalMessageData) messages.get(identity); + if( answer==null && cpAddedMessageIds!=null ) + answer = (QuickJournalMessageData) cpAddedMessageIds.get(identity); + + if( answer!=null ) { + loc = answer.getLocation(); + } else { + String t = longTermStore.getMessageReference(identity); + if( t!=null ) { + loc = toRecordLocation(t); + } + } + } + + if (loc == null ) + return null; + + return (Message) peristenceAdapter.readCommand(loc); + } + + /** + * Replays the checkpointStore first as those messages are the oldest ones, + * then messages are replayed from the transaction log and then the cache is + * updated. + * + * @param listener + * @throws Throwable + */ + public void recover(final MessageRecoveryListener listener) throws Throwable { + peristenceAdapter.checkpoint(true, true); + longTermStore.recover(new MessageRecoveryListener() { + public void recoverMessage(Message message) throws Throwable { + throw new IOException("Should not get called."); + } + public void recoverMessageReference(String messageReference) throws Throwable { + RecordLocation loc = toRecordLocation(messageReference); + Message message = (Message) peristenceAdapter.readCommand(loc); + listener.recoverMessage(message); + } + }); + } + + public void start() throws IOException { + longTermStore.start(); + } + + public void stop(long timeout) throws IOException { + longTermStore.stop(timeout); + } + + /** + * @return Returns the longTermStore. + */ + public MessageStore getLongTermMessageStore() { + return longTermStore; + } + + /** + * @see org.activemq.store.MessageStore#removeAllMessages(ConnectionContext) + */ + public void removeAllMessages(ConnectionContext context) throws IOException { + peristenceAdapter.checkpoint(true, true); + longTermStore.removeAllMessages(context); + } + + public ActiveMQDestination getDestination() { + return destination; + } + + public void addMessageReference(ConnectionContext context, MessageId messageId, long expirationTime, String messageRef) throws IOException { + throw new IOException("The journal does not support message references."); + } + + public String getMessageReference(MessageId identity) throws IOException { + throw new IOException("The journal does not support message references."); + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalPersistenceAdapter.java b/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalPersistenceAdapter.java new file mode 100755 index 0000000000..82493f976c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalPersistenceAdapter.java @@ -0,0 +1,644 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.store.journal; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; + +import org.activeio.command.WireFormat; +import org.activeio.journal.InvalidRecordLocationException; +import org.activeio.journal.Journal; +import org.activeio.journal.JournalEventListener; +import org.activeio.journal.RecordLocation; +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.DataStructure; +import org.activemq.command.JournalQueueAck; +import org.activemq.command.JournalTopicAck; +import org.activemq.command.JournalTrace; +import org.activemq.command.JournalTransaction; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.memory.UsageListener; +import org.activemq.memory.UsageManager; +import org.activemq.openwire.OpenWireFormat; +import org.activemq.store.MessageStore; +import org.activemq.store.PersistenceAdapter; +import org.activemq.store.TopicMessageStore; +import org.activemq.store.TransactionStore; +import org.activemq.store.jdbc.JDBCPersistenceAdapter; +import org.activemq.store.journal.QuickJournalTransactionStore.Tx; +import org.activemq.store.journal.QuickJournalTransactionStore.TxOperation; +import org.activemq.thread.Scheduler; +import org.activemq.thread.Task; +import org.activemq.thread.TaskRunner; +import org.activemq.thread.TaskRunnerFactory; +import org.activemq.util.IOExceptionSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.Callable; +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.FutureTask; +import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +/** + * An implementation of {@link PersistenceAdapter} designed for use with a + * {@link Journal} and then check pointing asynchronously on a timeout with some + * other long term persistent storage. + * + * @org.xbean.XBean + * + * @version $Revision: 1.17 $ + */ +public class QuickJournalPersistenceAdapter implements PersistenceAdapter, JournalEventListener, UsageListener { + + private static final Log log = LogFactory.getLog(QuickJournalPersistenceAdapter.class); + + private final Journal journal; + private final PersistenceAdapter longTermPersistence; + final UsageManager usageManager; + + private final WireFormat wireFormat = new OpenWireFormat(false); + + private final ConcurrentHashMap queues = new ConcurrentHashMap(); + private final ConcurrentHashMap topics = new ConcurrentHashMap(); + + private long checkpointInterval = 1000 * 60 * 5; + private long lastCheckpointRequest = System.currentTimeMillis(); + private long lastCleanup = System.currentTimeMillis(); + private int maxCheckpointWorkers = 10; + private int maxCheckpointMessageAddSize = 5000; + + private QuickJournalTransactionStore transactionStore = new QuickJournalTransactionStore(this); + private ThreadPoolExecutor checkpointExecutor; + + private TaskRunner checkpointTask; + private CountDownLatch nextCheckpointCountDownLatch = new CountDownLatch(1); + private boolean fullCheckPoint; + + private AtomicBoolean started = new AtomicBoolean(false); + + private final Runnable periodicCheckpointTask = new Runnable() { + public void run() { + if( System.currentTimeMillis()>lastCheckpointRequest+checkpointInterval ) { + checkpoint(false, true); + } + } + }; + + public QuickJournalPersistenceAdapter(Journal journal, PersistenceAdapter longTermPersistence, UsageManager memManager, TaskRunnerFactory taskRunnerFactory) throws IOException { + + this.journal = journal; + journal.setJournalEventListener(this); + + checkpointTask = taskRunnerFactory.createTaskRunner(new Task(){ + public boolean iterate() { + return doCheckpoint(); + } + }); + + this.longTermPersistence = longTermPersistence; + this.usageManager = memManager; + } + + public Set getDestinations() { + Set destinations = longTermPersistence.getDestinations(); + destinations.addAll(queues.keySet()); + destinations.addAll(topics.keySet()); + return destinations; + } + + private MessageStore createMessageStore(ActiveMQDestination destination) throws IOException { + if (destination.isQueue()) { + return createQueueMessageStore((ActiveMQQueue) destination); + } + else { + return createTopicMessageStore((ActiveMQTopic) destination); + } + } + + public MessageStore createQueueMessageStore(ActiveMQQueue destination) throws IOException { + QuickJournalMessageStore store = (QuickJournalMessageStore) queues.get(destination); + if (store == null) { + MessageStore checkpointStore = longTermPersistence.createQueueMessageStore(destination); + store = new QuickJournalMessageStore(this, checkpointStore, destination); + queues.put(destination, store); + } + return store; + } + + public TopicMessageStore createTopicMessageStore(ActiveMQTopic destinationName) throws IOException { + QuickJournalTopicMessageStore store = (QuickJournalTopicMessageStore) topics.get(destinationName); + if (store == null) { + TopicMessageStore checkpointStore = longTermPersistence.createTopicMessageStore(destinationName); + store = new QuickJournalTopicMessageStore(this, checkpointStore, destinationName); + topics.put(destinationName, store); + } + return store; + } + + public TransactionStore createTransactionStore() throws IOException { + return transactionStore; + } + + public long getLastMessageBrokerSequenceId() throws IOException { + return longTermPersistence.getLastMessageBrokerSequenceId(); + } + + public void beginTransaction(ConnectionContext context) throws IOException { + longTermPersistence.beginTransaction(context); + } + + public void commitTransaction(ConnectionContext context) throws IOException { + longTermPersistence.commitTransaction(context); + } + + public void rollbackTransaction(ConnectionContext context) throws IOException { + longTermPersistence.rollbackTransaction(context); + } + + public synchronized void start() throws Exception { + if( !started.compareAndSet(false, true) ) + return; + + longTermPersistence.setUseExternalMessageReferences(true); + + checkpointExecutor = new ThreadPoolExecutor(maxCheckpointWorkers, maxCheckpointWorkers, 30, TimeUnit.SECONDS, new LinkedBlockingQueue(), new ThreadFactory() { + public Thread newThread(Runnable runable) { + Thread t = new Thread(runable, "Journal checkpoint worker"); + t.setPriority(7); + return t; + } + }); + checkpointExecutor.allowCoreThreadTimeOut(true); + + this.usageManager.addUsageListener(this); + + if (longTermPersistence instanceof JDBCPersistenceAdapter) { + // Disabled periodic clean up as it deadlocks with the checkpoint + // operations. + ((JDBCPersistenceAdapter) longTermPersistence).setCleanupPeriod(0); + } + + longTermPersistence.start(); + createTransactionStore(); + recover(); + + // Do a checkpoint periodically. + Scheduler.executePeriodically(periodicCheckpointTask, checkpointInterval/10); + + } + + public void stop() throws Exception { + + if( !started.compareAndSet(true, false) ) + return; + + Scheduler.cancel(periodicCheckpointTask); + + // Take one final checkpoint and stop checkpoint processing. + checkpoint(false, true); + checkpointTask.shutdown(); + checkpointExecutor.shutdown(); + + queues.clear(); + topics.clear(); + + IOException firstException = null; + try { + journal.close(); + } catch (Exception e) { + firstException = IOExceptionSupport.create("Failed to close journals: " + e, e); + } + longTermPersistence.stop(); + + if (firstException != null) { + throw firstException; + } + } + + // Properties + // ------------------------------------------------------------------------- + public PersistenceAdapter getLongTermPersistence() { + return longTermPersistence; + } + + /** + * @return Returns the wireFormat. + */ + public WireFormat getWireFormat() { + return wireFormat; + } + + // Implementation methods + // ------------------------------------------------------------------------- + + /** + * The Journal give us a call back so that we can move old data out of the + * journal. Taking a checkpoint does this for us. + * + * @see org.activemq.journal.JournalEventListener#overflowNotification(org.activemq.journal.RecordLocation) + */ + public void overflowNotification(RecordLocation safeLocation) { + checkpoint(false, true); + } + + /** + * When we checkpoint we move all the journalled data to long term storage. + * @param stopping + * + * @param b + */ + public void checkpoint(boolean sync, boolean fullCheckpoint) { + try { + if (journal == null ) + throw new IllegalStateException("Journal is closed."); + + long now = System.currentTimeMillis(); + CountDownLatch latch = null; + synchronized(this) { + latch = nextCheckpointCountDownLatch; + lastCheckpointRequest = now; + if( fullCheckpoint ) { + this.fullCheckPoint = true; + } + } + + checkpointTask.wakeup(); + + if (sync) { + log.debug("Waking for checkpoint to complete."); + latch.await(); + } + } + catch (InterruptedException e) { + log.warn("Request to start checkpoint failed: " + e, e); + } + } + + /** + * This does the actual checkpoint. + * @return + */ + public boolean doCheckpoint() { + CountDownLatch latch = null; + boolean fullCheckpoint; + synchronized(this) { + latch = nextCheckpointCountDownLatch; + nextCheckpointCountDownLatch = new CountDownLatch(1); + fullCheckpoint = this.fullCheckPoint; + this.fullCheckPoint=false; + } + try { + + log.debug("Checkpoint started."); + RecordLocation newMark = null; + + ArrayList futureTasks = new ArrayList(queues.size()+topics.size()); + + // + // We do many partial checkpoints (fullCheckpoint==false) to move topic messages + // to long term store as soon as possible. + // + // We want to avoid doing that for queue messages since removes the come in the same + // checkpoint cycle will nullify the previous message add. Therefore, we only + // checkpoint queues on the fullCheckpoint cycles. + // + if( fullCheckpoint ) { + Iterator iterator = queues.values().iterator(); + while (iterator.hasNext()) { + try { + final QuickJournalMessageStore ms = (QuickJournalMessageStore) iterator.next(); + FutureTask task = new FutureTask(new Callable() { + public Object call() throws Exception { + return ms.checkpoint(); + }}); + futureTasks.add(task); + checkpointExecutor.execute(task); + } + catch (Exception e) { + log.error("Failed to checkpoint a message store: " + e, e); + } + } + } + + Iterator iterator = topics.values().iterator(); + while (iterator.hasNext()) { + try { + final QuickJournalTopicMessageStore ms = (QuickJournalTopicMessageStore) iterator.next(); + FutureTask task = new FutureTask(new Callable() { + public Object call() throws Exception { + return ms.checkpoint(); + }}); + futureTasks.add(task); + checkpointExecutor.execute(task); + } + catch (Exception e) { + log.error("Failed to checkpoint a message store: " + e, e); + } + } + + try { + for (Iterator iter = futureTasks.iterator(); iter.hasNext();) { + FutureTask ft = (FutureTask) iter.next(); + RecordLocation mark = (RecordLocation) ft.get(); + // We only set a newMark on full checkpoints. + if( fullCheckpoint ) { + if (mark != null && (newMark == null || newMark.compareTo(mark) < 0)) { + newMark = mark; + } + } + } + } catch (Throwable e) { + log.error("Failed to checkpoint a message store: " + e, e); + } + + + if( fullCheckpoint ) { + try { + if (newMark != null) { + log.debug("Marking journal at: " + newMark); + journal.setMark(newMark, true); + } + } + catch (Exception e) { + log.error("Failed to mark the Journal: " + e, e); + } + + if (longTermPersistence instanceof JDBCPersistenceAdapter) { + // We may be check pointing more often than the checkpointInterval if under high use + // But we don't want to clean up the db that often. + long now = System.currentTimeMillis(); + if( now > lastCleanup+checkpointInterval ) { + lastCleanup = now; + ((JDBCPersistenceAdapter) longTermPersistence).cleanup(); + } + } + } + + log.debug("Checkpoint done."); + } + finally { + latch.countDown(); + } + synchronized(this) { + return this.fullCheckPoint; + } + + } + + /** + * @param location + * @return + * @throws IOException + */ + public DataStructure readCommand(RecordLocation location) throws IOException { + try { + org.activeio.Packet data = journal.read(location); + return (DataStructure) wireFormat.unmarshal(data); + } + catch (InvalidRecordLocationException e) { + throw createReadException(location, e); + } + catch (IOException e) { + throw createReadException(location, e); + } + } + + /** + * Move all the messages that were in the journal into long term storage. We + * just replay and do a checkpoint. + * + * @throws IOException + * @throws IOException + * @throws InvalidRecordLocationException + * @throws IllegalStateException + */ + private void recover() throws IllegalStateException, InvalidRecordLocationException, IOException, IOException { + + RecordLocation pos = null; + int transactionCounter = 0; + + log.info("Journal Recovery Started."); + ConnectionContext context = new ConnectionContext(); + + // While we have records in the journal. + while ((pos = journal.getNextRecordLocation(pos)) != null) { + org.activeio.Packet data = journal.read(pos); + DataStructure c = (DataStructure) wireFormat.unmarshal(data); + + if (c instanceof Message ) { + Message message = (Message) c; + QuickJournalMessageStore store = (QuickJournalMessageStore) createMessageStore(message.getDestination()); + if ( message.isInTransaction()) { + transactionStore.addMessage(store, message, pos); + } + else { + store.replayAddMessage(context, message, pos); + transactionCounter++; + } + } else { + switch (c.getDataStructureType()) { + case JournalQueueAck.DATA_STRUCTURE_TYPE: + { + JournalQueueAck command = (JournalQueueAck) c; + QuickJournalMessageStore store = (QuickJournalMessageStore) createMessageStore(command.getDestination()); + if (command.getMessageAck().isInTransaction()) { + transactionStore.removeMessage(store, command.getMessageAck(), pos); + } + else { + store.replayRemoveMessage(context, command.getMessageAck()); + transactionCounter++; + } + } + break; + case JournalTopicAck.DATA_STRUCTURE_TYPE: + { + JournalTopicAck command = (JournalTopicAck) c; + QuickJournalTopicMessageStore store = (QuickJournalTopicMessageStore) createMessageStore(command.getDestination()); + if (command.getTransactionId() != null) { + transactionStore.acknowledge(store, command, pos); + } + else { + store.replayAcknowledge(context, command.getClientId(), command.getSubscritionName(), command.getMessageId()); + transactionCounter++; + } + } + break; + case JournalTransaction.DATA_STRUCTURE_TYPE: + { + JournalTransaction command = (JournalTransaction) c; + try { + // Try to replay the packet. + switch (command.getType()) { + case JournalTransaction.XA_PREPARE: + transactionStore.replayPrepare(command.getTransactionId()); + break; + case JournalTransaction.XA_COMMIT: + case JournalTransaction.LOCAL_COMMIT: + Tx tx = transactionStore.replayCommit(command.getTransactionId(), command.getWasPrepared()); + if (tx == null) + break; // We may be trying to replay a commit that + // was already committed. + + // Replay the committed operations. + for (Iterator iter = tx.getOperations().iterator(); iter.hasNext();) { + TxOperation op = (TxOperation) iter.next(); + if (op.operationType == TxOperation.ADD_OPERATION_TYPE) { + op.store.replayAddMessage(context, (Message) op.data, op.location); + } + if (op.operationType == TxOperation.REMOVE_OPERATION_TYPE) { + op.store.replayRemoveMessage(context, (MessageAck) op.data); + } + if (op.operationType == TxOperation.ACK_OPERATION_TYPE) { + JournalTopicAck ack = (JournalTopicAck) op.data; + ((QuickJournalTopicMessageStore) op.store).replayAcknowledge(context, ack.getClientId(), ack.getSubscritionName(), ack + .getMessageId()); + } + } + transactionCounter++; + break; + case JournalTransaction.LOCAL_ROLLBACK: + case JournalTransaction.XA_ROLLBACK: + transactionStore.replayRollback(command.getTransactionId()); + break; + } + } + catch (IOException e) { + log.error("Recovery Failure: Could not replay: " + c + ", reason: " + e, e); + } + } + break; + case JournalTrace.DATA_STRUCTURE_TYPE: + JournalTrace trace = (JournalTrace) c; + log.debug("TRACE Entry: " + trace.getMessage()); + break; + default: + log.error("Unknown type of record in transaction log which will be discarded: " + c); + } + } + } + + RecordLocation location = writeTraceMessage("RECOVERED", true); + journal.setMark(location, true); + + log.info("Journal Recovered: " + transactionCounter + " message(s) in transactions recovered."); + } + + private IOException createReadException(RecordLocation location, Exception e) { + return IOExceptionSupport.create("Failed to read to journal for: " + location + ". Reason: " + e, e); + } + + protected IOException createWriteException(DataStructure packet, Exception e) { + return IOExceptionSupport.create("Failed to write to journal for: " + packet + ". Reason: " + e, e); + } + + protected IOException createWriteException(String command, Exception e) { + return IOExceptionSupport.create("Failed to write to journal for command: " + command + ". Reason: " + e, e); + } + + protected IOException createRecoveryFailedException(Exception e) { + return IOExceptionSupport.create("Failed to recover from journal. Reason: " + e, e); + } + + /** + * + * @param command + * @param sync + * @return + * @throws IOException + */ + public RecordLocation writeCommand(DataStructure command, boolean sync) throws IOException { + if( started.get() ) + return journal.write(wireFormat.marshal(command), sync); + throw new IOException("closed"); + } + + private RecordLocation writeTraceMessage(String message, boolean sync) throws IOException { + JournalTrace trace = new JournalTrace(); + trace.setMessage(message); + return writeCommand(trace, sync); + } + + public void onMemoryUseChanged(UsageManager memoryManager, int oldPercentUsage, int newPercentUsage) { + if (newPercentUsage > 80 && oldPercentUsage < newPercentUsage) { + checkpoint(false, true); + } + } + + public QuickJournalTransactionStore getTransactionStore() { + return transactionStore; + } + + public void deleteAllMessages() throws IOException { + try { + JournalTrace trace = new JournalTrace(); + trace.setMessage("DELETED"); + RecordLocation location = journal.write(wireFormat.marshal(trace), false); + journal.setMark(location, true); + log.info("Journal deleted: "); + } catch (IOException e) { + throw e; + } catch (Throwable e) { + throw IOExceptionSupport.create(e); + } + longTermPersistence.setUseExternalMessageReferences(true); + longTermPersistence.deleteAllMessages(); + } + + public UsageManager getUsageManager() { + return usageManager; + } + + public int getMaxCheckpointMessageAddSize() { + return maxCheckpointMessageAddSize; + } + + public void setMaxCheckpointMessageAddSize(int maxCheckpointMessageAddSize) { + this.maxCheckpointMessageAddSize = maxCheckpointMessageAddSize; + } + + public int getMaxCheckpointWorkers() { + return maxCheckpointWorkers; + } + + public void setMaxCheckpointWorkers(int maxCheckpointWorkers) { + this.maxCheckpointWorkers = maxCheckpointWorkers; + } + + public boolean isUseExternalMessageReferences() { + return false; + } + + public void setUseExternalMessageReferences(boolean enable) { + if( enable ) + throw new IllegalArgumentException("The journal does not support message references."); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalTopicMessageStore.java b/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalTopicMessageStore.java new file mode 100755 index 0000000000..217d70daef --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalTopicMessageStore.java @@ -0,0 +1,195 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.journal; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; + +import org.activeio.journal.RecordLocation; +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.JournalTopicAck; +import org.activemq.command.Message; +import org.activemq.command.MessageId; +import org.activemq.command.SubscriptionInfo; +import org.activemq.store.MessageRecoveryListener; +import org.activemq.store.TopicMessageStore; +import org.activemq.transaction.Synchronization; +import org.activemq.util.Callback; +import org.activemq.util.SubscriptionKey; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A MessageStore that uses a Journal to store it's messages. + * + * @version $Revision: 1.13 $ + */ +public class QuickJournalTopicMessageStore extends QuickJournalMessageStore implements TopicMessageStore { + + private static final Log log = LogFactory.getLog(QuickJournalTopicMessageStore.class); + + private TopicMessageStore longTermStore; + private HashMap ackedLastAckLocations = new HashMap(); + + public QuickJournalTopicMessageStore(QuickJournalPersistenceAdapter adapter, TopicMessageStore checkpointStore, ActiveMQTopic destinationName) { + super(adapter, checkpointStore, destinationName); + this.longTermStore = checkpointStore; + } + + public void recoverSubscription(String clientId, String subscriptionName, final MessageRecoveryListener listener) throws Throwable { + this.peristenceAdapter.checkpoint(true, true); + longTermStore.recoverSubscription(clientId, subscriptionName, new MessageRecoveryListener() { + public void recoverMessage(Message message) throws Throwable { + throw new IOException("Should not get called."); + } + public void recoverMessageReference(String messageReference) throws Throwable { + RecordLocation loc = toRecordLocation(messageReference); + Message message = (Message) peristenceAdapter.readCommand(loc); + listener.recoverMessage(message); + } + }); + + } + + public SubscriptionInfo lookupSubscription(String clientId, String subscriptionName) throws IOException { + return longTermStore.lookupSubscription(clientId, subscriptionName); + } + + public void addSubsciption(String clientId, String subscriptionName, String selector, boolean retroactive) throws IOException { + this.peristenceAdapter.checkpoint(true, true); + longTermStore.addSubsciption(clientId, subscriptionName, selector, retroactive); + } + + public void addMessage(ConnectionContext context, Message message) throws IOException { + super.addMessage(context, message); + this.peristenceAdapter.checkpoint(false, false); + } + + /** + */ + public void acknowledge(ConnectionContext context, String clientId, String subscriptionName, final MessageId messageId) throws IOException { + final boolean debug = log.isDebugEnabled(); + + JournalTopicAck ack = new JournalTopicAck(); + ack.setDestination(destination); + ack.setMessageId(messageId); + ack.setMessageSequenceId(messageId.getBrokerSequenceId()); + ack.setSubscritionName(subscriptionName); + ack.setClientId(clientId); + ack.setTransactionId( context.getTransaction()!=null ? context.getTransaction().getTransactionId():null); + final RecordLocation location = peristenceAdapter.writeCommand(ack, false); + + final SubscriptionKey key = new SubscriptionKey(clientId, subscriptionName); + if( !context.isInTransaction() ) { + if( debug ) + log.debug("Journalled acknowledge for: "+messageId+", at: "+location); + acknowledge(messageId, location, key); + } else { + if( debug ) + log.debug("Journalled transacted acknowledge for: "+messageId+", at: "+location); + synchronized (this) { + inFlightTxLocations.add(location); + } + transactionStore.acknowledge(this, ack, location); + context.getTransaction().addSynchronization(new Synchronization(){ + public void afterCommit() { + if( debug ) + log.debug("Transacted acknowledge commit for: "+messageId+", at: "+location); + synchronized (QuickJournalTopicMessageStore.this) { + inFlightTxLocations.remove(location); + acknowledge(messageId, location, key); + } + } + public void afterRollback() { + if( debug ) + log.debug("Transacted acknowledge rollback for: "+messageId+", at: "+location); + synchronized (QuickJournalTopicMessageStore.this) { + inFlightTxLocations.remove(location); + } + } + }); + } + + } + + public void replayAcknowledge(ConnectionContext context, String clientId, String subscritionName, MessageId messageId) { + try { + SubscriptionInfo sub = longTermStore.lookupSubscription(clientId, subscritionName); + if( sub != null ) { + longTermStore.acknowledge(context, clientId, subscritionName, messageId); + } + } + catch (Throwable e) { + log.debug("Could not replay acknowledge for message '" + messageId + "'. Message may have already been acknowledged. reason: " + e); + } + } + + + /** + * @param messageId + * @param location + * @param key + */ + private void acknowledge(MessageId messageId, RecordLocation location, SubscriptionKey key) { + synchronized(this) { + lastLocation = location; + ackedLastAckLocations.put(key, messageId); + } + } + + public RecordLocation checkpoint() throws IOException { + + final HashMap cpAckedLastAckLocations; + + // swap out the hash maps.. + synchronized (this) { + cpAckedLastAckLocations = this.ackedLastAckLocations; + this.ackedLastAckLocations = new HashMap(); + } + + return super.checkpoint( new Callback() { + public void execute() throws Throwable { + + // Checkpoint the acknowledged messages. + Iterator iterator = cpAckedLastAckLocations.keySet().iterator(); + while (iterator.hasNext()) { + SubscriptionKey subscriptionKey = (SubscriptionKey) iterator.next(); + MessageId identity = (MessageId) cpAckedLastAckLocations.get(subscriptionKey); + longTermStore.acknowledge(transactionTemplate.getContext(), subscriptionKey.clientId, subscriptionKey.subscriptionName, identity); + } + + } + }); + + } + + /** + * @return Returns the longTermStore. + */ + public TopicMessageStore getLongTermTopicMessageStore() { + return longTermStore; + } + + public void deleteSubscription(String clientId, String subscriptionName) throws IOException { + longTermStore.deleteSubscription(clientId, subscriptionName); + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalTransactionStore.java b/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalTransactionStore.java new file mode 100755 index 0000000000..d52fdc8e60 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/journal/QuickJournalTransactionStore.java @@ -0,0 +1,306 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.journal; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +import javax.transaction.xa.XAException; + +import org.activeio.journal.RecordLocation; +import org.activemq.command.JournalTopicAck; +import org.activemq.command.JournalTransaction; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.TransactionId; +import org.activemq.command.XATransactionId; +import org.activemq.store.TransactionRecoveryListener; +import org.activemq.store.TransactionStore; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + */ +public class QuickJournalTransactionStore implements TransactionStore { + + private final QuickJournalPersistenceAdapter peristenceAdapter; + ConcurrentHashMap inflightTransactions = new ConcurrentHashMap(); + ConcurrentHashMap preparedTransactions = new ConcurrentHashMap(); + private boolean doingRecover; + + + public static class TxOperation { + + static final byte ADD_OPERATION_TYPE = 0; + static final byte REMOVE_OPERATION_TYPE = 1; + static final byte ACK_OPERATION_TYPE = 3; + + public byte operationType; + public QuickJournalMessageStore store; + public Object data; + public RecordLocation location; + + public TxOperation(byte operationType, QuickJournalMessageStore store, Object data, RecordLocation location) { + this.operationType=operationType; + this.store=store; + this.data=data; + this.location = location; + } + + } + /** + * Operations + * @version $Revision: 1.6 $ + */ + public static class Tx { + + private final RecordLocation location; + private ArrayList operations = new ArrayList(); + + public Tx(RecordLocation location) { + this.location=location; + } + + public void add(QuickJournalMessageStore store, Message msg, RecordLocation loc) { + operations.add(new TxOperation(TxOperation.ADD_OPERATION_TYPE, store, msg, loc)); + } + + public void add(QuickJournalMessageStore store, MessageAck ack, RecordLocation loc) { + operations.add(new TxOperation(TxOperation.REMOVE_OPERATION_TYPE, store, ack, loc)); + } + + public void add(QuickJournalTopicMessageStore store, JournalTopicAck ack, RecordLocation loc) { + operations.add(new TxOperation(TxOperation.ACK_OPERATION_TYPE, store, ack, loc)); + } + + public Message[] getMessages() { + ArrayList list = new ArrayList(); + for (Iterator iter = operations.iterator(); iter.hasNext();) { + TxOperation op = (TxOperation) iter.next(); + if( op.operationType==TxOperation.ADD_OPERATION_TYPE ) { + list.add(op.data); + } + } + Message rc[] = new Message[list.size()]; + list.toArray(rc); + return rc; + } + + public MessageAck[] getAcks() { + ArrayList list = new ArrayList(); + for (Iterator iter = operations.iterator(); iter.hasNext();) { + TxOperation op = (TxOperation) iter.next(); + if( op.operationType==TxOperation.REMOVE_OPERATION_TYPE ) { + list.add(op.data); + } + } + MessageAck rc[] = new MessageAck[list.size()]; + list.toArray(rc); + return rc; + } + + public ArrayList getOperations() { + return operations; + } + + } + + public QuickJournalTransactionStore(QuickJournalPersistenceAdapter adapter) { + this.peristenceAdapter = adapter; + } + + /** + * @throws IOException + * @see org.activemq.store.TransactionStore#prepare(TransactionId) + */ + public void prepare(TransactionId txid) throws IOException { + Tx tx = (Tx) inflightTransactions.remove(txid); + if (tx == null) + return; + peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.XA_PREPARE, txid, false), true); + preparedTransactions.put(txid, tx); + } + + /** + * @throws IOException + * @see org.activemq.store.TransactionStore#prepare(TransactionId) + */ + public void replayPrepare(TransactionId txid) throws IOException { + Tx tx = (Tx) inflightTransactions.remove(txid); + if (tx == null) + return; + preparedTransactions.put(txid, tx); + } + + public Tx getTx(Object txid, RecordLocation location) { + Tx tx = (Tx) inflightTransactions.get(txid); + if (tx == null) { + tx = new Tx(location); + inflightTransactions.put(txid, tx); + } + return tx; + } + + /** + * @throws XAException + * @see org.activemq.store.TransactionStore#commit(org.activemq.service.Transaction) + */ + public void commit(TransactionId txid, boolean wasPrepared) throws IOException { + Tx tx; + if (wasPrepared) { + tx = (Tx) preparedTransactions.remove(txid); + } else { + tx = (Tx) inflightTransactions.remove(txid); + } + + if (tx == null) + return; + + if (txid.isXATransaction()) { + peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.XA_COMMIT, txid, wasPrepared), + true); + } else { + peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.LOCAL_COMMIT, txid, wasPrepared), + true); + } + } + + /** + * @throws XAException + * @see org.activemq.store.TransactionStore#commit(org.activemq.service.Transaction) + */ + public Tx replayCommit(TransactionId txid, boolean wasPrepared) throws IOException { + if (wasPrepared) { + return (Tx) preparedTransactions.remove(txid); + } else { + return (Tx) inflightTransactions.remove(txid); + } + } + + /** + * @throws IOException + * @see org.activemq.store.TransactionStore#rollback(TransactionId) + */ + public void rollback(TransactionId txid) throws IOException { + + Tx tx = (Tx) inflightTransactions.remove(txid); + if (tx != null) + tx = (Tx) preparedTransactions.remove(txid); + + if (tx != null) { + if (txid.isXATransaction()) { + peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.XA_ROLLBACK, txid, false), + true); + } else { + peristenceAdapter.writeCommand(new JournalTransaction(JournalTransaction.LOCAL_ROLLBACK, txid, false), + true); + } + } + + } + + /** + * @throws IOException + * @see org.activemq.store.TransactionStore#rollback(TransactionId) + */ + public void replayRollback(TransactionId txid) throws IOException { + if (inflightTransactions.remove(txid) != null) + preparedTransactions.remove(txid); + } + + public void start() throws Exception { + } + + public void stop() throws Exception { + } + + synchronized public void recover(TransactionRecoveryListener listener) throws IOException { + // All the in-flight transactions get rolled back.. + inflightTransactions.clear(); + this.doingRecover = true; + try { + for (Iterator iter = preparedTransactions.keySet().iterator(); iter.hasNext();) { + Object txid = (Object) iter.next(); + Tx tx = (Tx) preparedTransactions.get(txid); + listener.recover((XATransactionId) txid, tx.getMessages(), tx.getAcks()); + } + } finally { + this.doingRecover = false; + } + } + + /** + * @param message + * @throws IOException + */ + void addMessage(QuickJournalMessageStore store, Message message, RecordLocation location) throws IOException { + Tx tx = getTx(message.getTransactionId(), location); + tx.add(store, message, location); + } + + /** + * @param ack + * @throws IOException + */ + public void removeMessage(QuickJournalMessageStore store, MessageAck ack, RecordLocation location) throws IOException { + Tx tx = getTx(ack.getTransactionId(), location); + tx.add(store, ack, location); + } + + + public void acknowledge(QuickJournalTopicMessageStore store, JournalTopicAck ack, RecordLocation location) { + Tx tx = getTx(ack.getTransactionId(), location); + tx.add(store, ack, location); + } + + + public RecordLocation checkpoint() throws IOException { + + // Nothing really to checkpoint.. since, we don't + // checkpoint tx operations in to long term store until they are committed. + + // But we keep track of the first location of an operation + // that was associated with an active tx. The journal can not + // roll over active tx records. + RecordLocation rc = null; + for (Iterator iter = inflightTransactions.values().iterator(); iter.hasNext();) { + Tx tx = (Tx) iter.next(); + RecordLocation location = tx.location; + if (rc == null || rc.compareTo(location) < 0) { + rc = location; + } + } + for (Iterator iter = preparedTransactions.values().iterator(); iter.hasNext();) { + Tx tx = (Tx) iter.next(); + RecordLocation location = tx.location; + if (rc == null || rc.compareTo(location) < 0) { + rc = location; + } + } + return rc; + } + + public boolean isDoingRecover() { + return doingRecover; + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/store/journal/package.html b/activemq-core/src/main/java/org/activemq/store/journal/package.html new file mode 100755 index 0000000000..553030d985 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/journal/package.html @@ -0,0 +1,11 @@ + + + + + +

+ Message persistence using a high performance transaction log via the Journal interface. +

+ + + diff --git a/activemq-core/src/main/java/org/activemq/store/memory/MemoryMessageStore.java b/activemq-core/src/main/java/org/activemq/store/memory/MemoryMessageStore.java new file mode 100755 index 0000000000..0929d95adf --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/memory/MemoryMessageStore.java @@ -0,0 +1,105 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.memory; + +import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageId; +import org.activemq.store.MessageRecoveryListener; +import org.activemq.store.MessageStore; + +/** + * An implementation of {@link org.activemq.store.MessageStore} which uses a + * + * @version $Revision: 1.7 $ + */ +public class MemoryMessageStore implements MessageStore { + + protected final ActiveMQDestination destination; + protected final Map messageTable; + + public MemoryMessageStore(ActiveMQDestination destination) { + this(destination, new LinkedHashMap()); + } + + public MemoryMessageStore(ActiveMQDestination destination, Map messageTable) { + this.destination = destination; + this.messageTable = Collections.synchronizedMap(messageTable); + } + + public synchronized void addMessage(ConnectionContext context, Message message) throws IOException { + messageTable.put(message.getMessageId(), message); + } + public void addMessageReference(ConnectionContext context, MessageId messageId, long expirationTime, String messageRef) throws IOException { + messageTable.put(messageId, messageRef); + } + + public Message getMessage(MessageId identity) throws IOException { + return (Message) messageTable.get(identity); + } + public String getMessageReference(MessageId identity) throws IOException { + return (String) messageTable.get(identity); + } + + public void removeMessage(ConnectionContext context, MessageAck ack) throws IOException { + messageTable.remove(ack.getLastMessageId()); + } + + public void removeMessage(MessageId msgId) throws IOException { + messageTable.remove(msgId); + } + + public void recover(MessageRecoveryListener listener) throws Throwable { + for (Iterator iter = messageTable.values().iterator(); iter.hasNext();) { + Object msg = (Object) iter.next(); + if( msg.getClass() == String.class ) { + listener.recoverMessageReference((String) msg); + } else { + listener.recoverMessage((Message) msg); + } + } + } + + public void start() throws IOException { + } + + public void stop(long timeout) throws IOException { + } + + public void removeAllMessages(ConnectionContext context) throws IOException { + messageTable.clear(); + } + + public ActiveMQDestination getDestination() { + return destination; + } + + public void delete() { + messageTable.clear(); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/store/memory/MemoryPersistenceAdapter.java b/activemq-core/src/main/java/org/activemq/store/memory/MemoryPersistenceAdapter.java new file mode 100755 index 0000000000..92e46195cc --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/memory/MemoryPersistenceAdapter.java @@ -0,0 +1,134 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.memory; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.activemq.store.MessageStore; +import org.activemq.store.PersistenceAdapter; +import org.activemq.store.TopicMessageStore; +import org.activemq.store.TransactionStore; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * @org.xbean.XBean + * + * @version $Revision: 1.4 $ + */ +public class MemoryPersistenceAdapter implements PersistenceAdapter { + + MemoryTransactionStore transactionStore; + ConcurrentHashMap topics = new ConcurrentHashMap(); + ConcurrentHashMap queues = new ConcurrentHashMap(); + private boolean useExternalMessageReferences; + + public Set getDestinations() { + Set rc = new HashSet(queues.size()+topics.size()); + for (Iterator iter = queues.keySet().iterator(); iter.hasNext();) { + rc.add( iter.next() ); + } + for (Iterator iter = topics.keySet().iterator(); iter.hasNext();) { + rc.add( iter.next() ); + } + return rc; + } + + public static MemoryPersistenceAdapter newInstance(File file) { + return new MemoryPersistenceAdapter(); + } + + public MessageStore createQueueMessageStore(ActiveMQQueue destination) throws IOException { + MessageStore rc = (MessageStore)queues.get(destination); + if(rc==null) { + rc = new MemoryMessageStore(destination); + if( transactionStore !=null ) { + rc = transactionStore.proxy(rc); + } + queues.put(destination, rc); + } + return rc; + } + + public TopicMessageStore createTopicMessageStore(ActiveMQTopic destination) throws IOException { + TopicMessageStore rc = (TopicMessageStore)topics.get(destination); + if(rc==null) { + rc = new MemoryTopicMessageStore(destination); + if( transactionStore !=null ) { + rc = transactionStore.proxy(rc); + } + topics.put(destination, rc); + } + return rc; + } + + public TransactionStore createTransactionStore() throws IOException { + if( transactionStore==null ) { + transactionStore = new MemoryTransactionStore(); + } + return transactionStore; + } + + public void beginTransaction(ConnectionContext context) { + } + + public void commitTransaction(ConnectionContext context) { + } + + public void rollbackTransaction(ConnectionContext context) { + } + + public void start() throws Exception { + } + + public void stop() throws Exception { + } + + public long getLastMessageBrokerSequenceId() throws IOException { + return 0; + } + + public void deleteAllMessages() throws IOException { + for (Iterator iter = topics.values().iterator(); iter.hasNext();) { + MemoryMessageStore store = (MemoryMessageStore) iter.next(); + store.delete(); + } + for (Iterator iter = queues.values().iterator(); iter.hasNext();) { + MemoryMessageStore store = (MemoryMessageStore) iter.next(); + store.delete(); + } + transactionStore.delete(); + } + + public boolean isUseExternalMessageReferences() { + return useExternalMessageReferences; + } + + public void setUseExternalMessageReferences(boolean useExternalMessageReferences) { + this.useExternalMessageReferences = useExternalMessageReferences; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/store/memory/MemoryTopicMessageStore.java b/activemq-core/src/main/java/org/activemq/store/memory/MemoryTopicMessageStore.java new file mode 100755 index 0000000000..7b53621d3c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/memory/MemoryTopicMessageStore.java @@ -0,0 +1,117 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.memory; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.Message; +import org.activemq.command.MessageId; +import org.activemq.command.SubscriptionInfo; +import org.activemq.store.MessageRecoveryListener; +import org.activemq.store.TopicMessageStore; +import org.activemq.util.SubscriptionKey; + +/** + * @version $Revision: 1.5 $ + */ +public class MemoryTopicMessageStore extends MemoryMessageStore implements TopicMessageStore { + + private Map ackDatabase; + private Map subscriberDatabase; + MessageId lastMessageId; + + public MemoryTopicMessageStore(ActiveMQDestination destination) { + this(destination, new LinkedHashMap(), makeMap(), makeMap()); + } + protected static Map makeMap() { + return Collections.synchronizedMap(new HashMap()); + } + + public MemoryTopicMessageStore(ActiveMQDestination destination, Map messageTable, Map subscriberDatabase, Map ackDatabase) { + super(destination, messageTable); + this.subscriberDatabase = subscriberDatabase; + this.ackDatabase = ackDatabase; + } + + public synchronized void addMessage(ConnectionContext context, Message message) throws IOException { + super.addMessage(context, message); + lastMessageId = message.getMessageId(); + } + + public void acknowledge(ConnectionContext context, String clientId, String subscriptionName, MessageId messageId) throws IOException { + ackDatabase.put(new SubscriptionKey(clientId, subscriptionName), messageId); + } + + public SubscriptionInfo lookupSubscription(String clientId, String subscriptionName) throws IOException { + return (SubscriptionInfo) subscriberDatabase.get(new SubscriptionKey(clientId, subscriptionName)); + } + + public void addSubsciption(String clientId, String subscriptionName, String selector, boolean retroactive) throws IOException { + SubscriptionInfo info = new SubscriptionInfo(); + info.setDestination(destination); + info.setClientId(clientId); + info.setSelector(selector); + info.setSubcriptionName(subscriptionName); + SubscriptionKey key = new SubscriptionKey(clientId, subscriptionName); + subscriberDatabase.put(key, info); + MessageId l=retroactive ? null : lastMessageId; + if( l!=null ) { + ackDatabase.put(key, l); + } + } + + public void deleteSubscription(String clientId, String subscriptionName) { + org.activemq.util.SubscriptionKey key = new SubscriptionKey(clientId, subscriptionName); + ackDatabase.remove(key); + subscriberDatabase.remove(key); + } + + public void recoverSubscription(String clientId, String subscriptionName, MessageRecoveryListener listener) throws Throwable { + MessageId lastAck = (MessageId) ackDatabase.get(new SubscriptionKey(clientId, subscriptionName)); + boolean pastLastAck = lastAck==null; + for (Iterator iter = messageTable.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry = (Entry) iter.next(); + if( pastLastAck ) { + Object msg = entry.getValue(); + if( msg.getClass() == String.class ) { + listener.recoverMessageReference((String) msg); + } else { + listener.recoverMessage((Message) msg); + } + } else { + pastLastAck = entry.getKey().equals(lastAck); + } + } + } + + public void delete() { + super.delete(); + ackDatabase.clear(); + subscriberDatabase.clear(); + lastMessageId=null; + } +} diff --git a/activemq-core/src/main/java/org/activemq/store/memory/MemoryTransactionStore.java b/activemq-core/src/main/java/org/activemq/store/memory/MemoryTransactionStore.java new file mode 100755 index 0000000000..0fd3f6db68 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/memory/MemoryTransactionStore.java @@ -0,0 +1,258 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.store.memory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +import javax.transaction.xa.XAException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.TransactionId; +import org.activemq.command.XATransactionId; +import org.activemq.store.MessageStore; +import org.activemq.store.ProxyMessageStore; +import org.activemq.store.ProxyTopicMessageStore; +import org.activemq.store.TopicMessageStore; +import org.activemq.store.TransactionRecoveryListener; +import org.activemq.store.TransactionStore; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * Provides a TransactionStore implementation that can create transaction aware + * MessageStore objects from non transaction aware MessageStore objects. + * + * @version $Revision: 1.4 $ + */ +public class MemoryTransactionStore implements TransactionStore { + + ConcurrentHashMap inflightTransactions = new ConcurrentHashMap(); + + ConcurrentHashMap preparedTransactions = new ConcurrentHashMap(); + + private boolean doingRecover; + + public static class Tx { + private ArrayList messages = new ArrayList(); + + private ArrayList acks = new ArrayList(); + + public void add(AddMessageCommand msg) { + messages.add(msg); + } + + public void add(RemoveMessageCommand ack) { + acks.add(ack); + } + + public Message[] getMessages() { + Message rc[] = new Message[messages.size()]; + int count=0; + for (Iterator iter = messages.iterator(); iter.hasNext();) { + AddMessageCommand cmd = (AddMessageCommand) iter.next(); + rc[count++] = cmd.getMessage(); + } + return rc; + } + + public MessageAck[] getAcks() { + MessageAck rc[] = new MessageAck[acks.size()]; + int count=0; + for (Iterator iter = acks.iterator(); iter.hasNext();) { + RemoveMessageCommand cmd = (RemoveMessageCommand) iter.next(); + rc[count++] = cmd.getMessageAck(); + } + return rc; + } + + /** + * @throws IOException + */ + public void commit() throws IOException { + // Do all the message adds. + for (Iterator iter = messages.iterator(); iter.hasNext();) { + AddMessageCommand cmd = (AddMessageCommand) iter.next(); + cmd.run(); + } + // And removes.. + for (Iterator iter = acks.iterator(); iter.hasNext();) { + RemoveMessageCommand cmd = (RemoveMessageCommand) iter.next(); + cmd.run(); + } + } + } + + public interface AddMessageCommand { + Message getMessage(); + void run() throws IOException; + } + + public interface RemoveMessageCommand { + MessageAck getMessageAck(); + void run() throws IOException; + } + + public MessageStore proxy(MessageStore messageStore) { + return new ProxyMessageStore(messageStore) { + public void addMessage(ConnectionContext context, final Message send) throws IOException { + MemoryTransactionStore.this.addMessage(getDelegate(), send); + } + + public void removeMessage(ConnectionContext context, final MessageAck ack) throws IOException { + MemoryTransactionStore.this.removeMessage(getDelegate(), ack); + } + }; + } + + public TopicMessageStore proxy(TopicMessageStore messageStore) { + return new ProxyTopicMessageStore(messageStore) { + public void addMessage(ConnectionContext context, final Message send) throws IOException { + MemoryTransactionStore.this.addMessage(getDelegate(), send); + } + public void removeMessage(ConnectionContext context, final MessageAck ack) throws IOException { + MemoryTransactionStore.this.removeMessage(getDelegate(), ack); + } + }; + } + + /** + * @see org.activemq.store.TransactionStore#prepare(TransactionId) + */ + public void prepare(TransactionId txid) { + Tx tx = (Tx) inflightTransactions.remove(txid); + if (tx == null) + return; + preparedTransactions.put(txid, tx); + } + + public Tx getTx(Object txid) { + Tx tx = (Tx) inflightTransactions.get(txid); + if (tx == null) { + tx = new Tx(); + inflightTransactions.put(txid, tx); + } + return tx; + } + + /** + * @throws XAException + * @see org.activemq.store.TransactionStore#commit(org.activemq.service.Transaction) + */ + public void commit(TransactionId txid, boolean wasPrepared) throws IOException { + + Tx tx; + if( wasPrepared ) { + tx = (Tx) preparedTransactions.remove(txid); + } else { + tx = (Tx) inflightTransactions.remove(txid); + } + + if( tx == null ) + return; + tx.commit(); + + } + + /** + * @see org.activemq.store.TransactionStore#rollback(TransactionId) + */ + public void rollback(TransactionId txid) { + preparedTransactions.remove(txid); + inflightTransactions.remove(txid); + } + + public void start() throws Exception { + } + + public void stop() throws Exception { + } + + synchronized public void recover(TransactionRecoveryListener listener) throws IOException { + // All the inflight transactions get rolled back.. + inflightTransactions.clear(); + this.doingRecover = true; + try { + for (Iterator iter = preparedTransactions.keySet().iterator(); iter.hasNext();) { + Object txid = (Object) iter.next(); + Tx tx = (Tx) preparedTransactions.get(txid); + listener.recover((XATransactionId) txid, tx.getMessages(), tx.getAcks()); + } + } finally { + this.doingRecover = false; + } + } + + /** + * @param message + * @throws IOException + */ + void addMessage(final MessageStore destination, final Message message) throws IOException { + + if( doingRecover ) + return; + + if (message.getTransactionId()!=null) { + Tx tx = getTx(message.getTransactionId()); + tx.add(new AddMessageCommand() { + public Message getMessage() { + return message; + } + public void run() throws IOException { + destination.addMessage(null, message); + } + }); + } else { + destination.addMessage(null, message); + } + } + + /** + * @param ack + * @throws IOException + */ + private void removeMessage(final MessageStore destination,final MessageAck ack) throws IOException { + if( doingRecover ) + return; + + if (ack.isInTransaction()) { + Tx tx = getTx(ack.getTransactionId()); + tx.add(new RemoveMessageCommand() { + public MessageAck getMessageAck() { + return ack; + } + public void run() throws IOException { + destination.removeMessage(null, ack); + } + }); + } else { + destination.removeMessage(null, ack); + } + } + + public void delete() { + inflightTransactions.clear(); + preparedTransactions.clear(); + doingRecover=false; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/store/memory/package.html b/activemq-core/src/main/java/org/activemq/store/memory/package.html new file mode 100755 index 0000000000..ab15793b27 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/memory/package.html @@ -0,0 +1,11 @@ + + + + + +

+ VM based implementation of message persistence +

+ + + diff --git a/activemq-core/src/main/java/org/activemq/store/package.html b/activemq-core/src/main/java/org/activemq/store/package.html new file mode 100755 index 0000000000..05b191895d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/store/package.html @@ -0,0 +1,11 @@ + + + + + +

+ The APIs which need to be implemented for persistent message stores for durable messaging +

+ + + diff --git a/activemq-core/src/main/java/org/activemq/thread/DefaultThreadPools.java b/activemq-core/src/main/java/org/activemq/thread/DefaultThreadPools.java new file mode 100755 index 0000000000..a44d7d4237 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/thread/DefaultThreadPools.java @@ -0,0 +1,41 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.thread; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor; + +/** + * + * @version $Revision$ + */ +public class DefaultThreadPools { + + private static final Executor defaultPool = new ScheduledThreadPoolExecutor(5); + private static final TaskRunnerFactory defaultTaskRunnerFactory = new TaskRunnerFactory(defaultPool,10); + + public static Executor getDeaultPool() { + return defaultPool; + } + + public static TaskRunnerFactory getDefaultTaskRunnerFactory() { + return defaultTaskRunnerFactory; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/thread/Scheduler.java b/activemq-core/src/main/java/org/activemq/thread/Scheduler.java new file mode 100755 index 0000000000..c61fe9e724 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/thread/Scheduler.java @@ -0,0 +1,63 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.thread; + +import java.util.HashMap; + +import edu.emory.mathcs.backport.java.util.concurrent.ScheduledFuture; +import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +/** + * @version $Revision$ + */ +public class Scheduler { + + + static public ScheduledThreadPoolExecutor clockDaemon = new ScheduledThreadPoolExecutor(5, new ThreadFactory(){ + public Thread newThread(Runnable runnable) { + Thread thread = new Thread(runnable,"ActiveMQ Scheduler"); + thread.setDaemon(true); + return thread; + } + }); + static { + clockDaemon.setKeepAliveTime(5, TimeUnit.SECONDS); + clockDaemon.allowCoreThreadTimeOut(false); + } + static HashMap clockTickets = new HashMap(); + + synchronized static public void executePeriodically(final Runnable task, long period) { + ScheduledFuture ticket = clockDaemon.scheduleAtFixedRate(task, period, period, TimeUnit.MILLISECONDS); + clockTickets.put(task, ticket); + } + + synchronized static public void cancel(Runnable task) { + ScheduledFuture ticket = (ScheduledFuture) clockTickets.remove(task); + if( ticket!=null ) { + ticket.cancel(true); + } + } + + public static void executeAfterDelay(final Runnable task, long redeliveryDelay) { + clockDaemon.schedule(task, redeliveryDelay, TimeUnit.MILLISECONDS); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/thread/SimpleTaskRunner.java b/activemq-core/src/main/java/org/activemq/thread/SimpleTaskRunner.java new file mode 100644 index 0000000000..9060ab1321 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/thread/SimpleTaskRunner.java @@ -0,0 +1,153 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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. + * + **/ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.thread; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; + +/** + * + * @version $Revision: 1.1 $ + */ +class SimpleTaskRunner implements TaskRunner { + + private final int maxIterationsPerRun; + private final Executor executor; + private final Task task; + private final Runnable runable; + private boolean queued; + private boolean shutdown; + private boolean iterating; + private Thread runningThread; + + public SimpleTaskRunner(Executor executor, Task task, int maxIterationsPerRun) { + this.executor = executor; + this.maxIterationsPerRun = maxIterationsPerRun; + this.task = task; + runable = new Runnable() { + public void run() { + runningThread = Thread.currentThread(); + runTask(); + runningThread = null; + } + }; + } + + + + /** + * We Expect MANY wakeup calls on the same TaskRunner. + */ + public void wakeup() throws InterruptedException { + synchronized( runable ) { + + // When we get in here, we make some assumptions of state: + // queued=false, iterating=false: wakeup() has not be called and therefore task is not executing. + // queued=true, iterating=false: wakeup() was called but, task execution has not started yet + // queued=false, iterating=true : wakeup() was called, which caused task execution to start. + // queued=true, iterating=true : wakeup() called after task execution was started. + + if( queued || shutdown ) + return; + + queued=true; + + // The runTask() method will do this for me once we are done iterating. + if( !iterating ) { + executor.execute(runable); + } + } + } + + /** + * shut down the task + * @throws InterruptedException + */ + public void shutdown() throws InterruptedException{ + synchronized(runable){ + shutdown=true; + //the check on the thread is done + //because a call to iterate can result in + //shutDown() being called, which would wait forever + //waiting for iterating to finish + if(runningThread!=Thread.currentThread()){ + while(iterating==true){ + runable.wait(); + } + } + } + } + + private void runTask() { + + synchronized (runable) { + queued = false; + if( shutdown ) { + iterating = false; + runable.notifyAll(); + return; + } + iterating = true; + } + + // Don't synchronize while we are iterating so that + // multiple wakeup() calls can be executed concurrently. + boolean done=false; + for (int i = 0; i < maxIterationsPerRun; i++) { + if( !task.iterate() ) { + done=true; + break; + } + } + + synchronized (runable) { + iterating=false; + if( shutdown ) { + queued=false; + runable.notifyAll(); + return; + } + + // If we could not iterate all the items + // then we need to re-queue. + if( !done ) + queued = true; + + if( queued ) { + executor.execute(runable); + } + } + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/thread/Task.java b/activemq-core/src/main/java/org/activemq/thread/Task.java new file mode 100755 index 0000000000..6b93b49df4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/thread/Task.java @@ -0,0 +1,28 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.thread; + +/** + * Represents a task that may take a few iterations to complete. + * + * @version $Revision: 1.3 $ + */ +public interface Task { + public boolean iterate(); +} diff --git a/activemq-core/src/main/java/org/activemq/thread/TaskRunner.java b/activemq-core/src/main/java/org/activemq/thread/TaskRunner.java new file mode 100755 index 0000000000..82932449d5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/thread/TaskRunner.java @@ -0,0 +1,29 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.thread; + +/** + * Allows you to request a thread execute the associated Task. + * + * @version $Revision: 1.3 $ + */ +public interface TaskRunner { + public abstract void wakeup() throws InterruptedException; + public abstract void shutdown() throws InterruptedException; +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/thread/TaskRunnerFactory.java b/activemq-core/src/main/java/org/activemq/thread/TaskRunnerFactory.java new file mode 100755 index 0000000000..7886e796b4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/thread/TaskRunnerFactory.java @@ -0,0 +1,91 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.thread; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import edu.emory.mathcs.backport.java.util.concurrent.SynchronousQueue; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +/** + * Manages the thread pool for long running tasks. + * + * Long running tasks are not always active but when they are active, they may + * need a few iterations of processing for them to become idle. The manager + * ensures that each task is processes but that no one task overtakes the + * system. + * + * This is kina like cooperative multitasking. + * + * @version $Revision: 1.5 $ + */ +public class TaskRunnerFactory { + + private Executor executor; + private int maxIterationsPerRun = 1000; + + public TaskRunnerFactory() { + setExecutor(createDefaultExecutor("ActiveMQ Task", Thread.NORM_PRIORITY, true)); + } + + public TaskRunnerFactory(String name, int priority, boolean daemon, int maxIterationsPerRun) { + this.maxIterationsPerRun = maxIterationsPerRun; + setExecutor(createDefaultExecutor(name, priority, daemon)); + } + + public TaskRunnerFactory(Executor executor, int maxIterationsPerRun) { + this.executor = executor; + this.maxIterationsPerRun = maxIterationsPerRun; + } + + public TaskRunner createTaskRunner(Task task) { + return new SimpleTaskRunner(executor, task, maxIterationsPerRun); + } + + public Executor getExecutor() { + return executor; + } + + public void setExecutor(Executor executor) { + this.executor = executor; + } + + public int getMaxIterationsPerRun() { + return maxIterationsPerRun; + } + + public void setMaxIterationsPerRun(int maxIterationsPerRun) { + this.maxIterationsPerRun = maxIterationsPerRun; + } + + protected Executor createDefaultExecutor(final String name, final int priority, final boolean daemon) { + ThreadPoolExecutor rc = new ThreadPoolExecutor(1, Integer.MAX_VALUE, 10, TimeUnit.SECONDS, new SynchronousQueue(), new ThreadFactory() { + public Thread newThread(Runnable runnable) { + Thread thread = new Thread(runnable, name); + thread.setDaemon(daemon); + thread.setPriority(priority); + return thread; + } + }); + rc.allowCoreThreadTimeOut(true); + return rc; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/thread/Valve.java b/activemq-core/src/main/java/org/activemq/thread/Valve.java new file mode 100755 index 0000000000..d72eb8ae32 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/thread/Valve.java @@ -0,0 +1,108 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.thread; + +/** + * A Valve is a synchronization object used enable or disable the "flow" of concurrent + * processing. + * + * + * @version $Revision: 1.2 $ + */ +final public class Valve { + + private final Object mutex = new Object(); + private boolean on; + private int turningOff=0; + private int usage=0; + + public Valve(boolean on) { + this.on = on; + } + + /** + * Turns the valve on. This method blocks until the valve is off. + * @throws InterruptedException + */ + public void turnOn() throws InterruptedException { + synchronized(mutex) { + while( on ) { + mutex.wait(); + } + on=true; + mutex.notifyAll(); + } + } + + boolean isOn() { + synchronized(mutex) { + return on; + } + } + + /** + * Turns the valve off. This method blocks until the valve is on and the valve is not + * in use. + * + * @throws InterruptedException + */ + public void turnOff() throws InterruptedException { + synchronized(mutex) { + try { + ++turningOff; + while( usage > 0 || !on) { + mutex.wait(); + } + on=false; + mutex.notifyAll(); + } finally { + --turningOff; + } + } + } + + /** + * Increments the use counter of the valve. This method blocks if the valve is off, + * or is being turned off. + * + * @throws InterruptedException + */ + public void increment() throws InterruptedException { + synchronized(mutex) { + // Do we have to wait for the value to be on? + while( turningOff>0 || !on ) { + mutex.wait(); + } + usage++; + } + } + + /** + * Decrements the use counter of the valve. + */ + public void decrement() { + synchronized(mutex) { + usage--; + if( turningOff>0 && usage < 1 ) { + mutex.notifyAll(); + } + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transaction/LocalTransaction.java b/activemq-core/src/main/java/org/activemq/transaction/LocalTransaction.java new file mode 100755 index 0000000000..301baba54c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transaction/LocalTransaction.java @@ -0,0 +1,113 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transaction; + +import java.io.IOException; + +import javax.transaction.xa.XAException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.command.LocalTransactionId; +import org.activemq.command.TransactionId; +import org.activemq.store.TransactionStore; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @version $Revision: 1.3 $ + */ +public class LocalTransaction extends Transaction { + + private static final Log log = LogFactory.getLog(LocalTransaction.class); + + private final TransactionStore transactionStore; + private final LocalTransactionId xid; + private final ConnectionContext context; + + public LocalTransaction(TransactionStore transactionStore, LocalTransactionId xid, ConnectionContext context) { + this.transactionStore = transactionStore; + this.xid = xid; + this.context = context; + } + + public void commit(boolean onePhase) throws XAException, IOException { + // Get ready for commit. + try { + prePrepare(); + } + catch (XAException e) { + throw e; + } + catch (Throwable e) { + log.warn("COMMIT FAILED: ", e); + rollback(); + // Let them know we rolled back. + XAException xae = new XAException("COMMIT FAILED: Transaction rolled back."); + xae.errorCode = XAException.XA_RBOTHER; + xae.initCause(e); + throw xae; + } + + setState(Transaction.FINISHED_STATE); + context.getTransactions().remove(xid); + transactionStore.commit(getTransactionId(), false); + + try { + fireAfterCommit(); + } + catch (Throwable e) { + // I guess this could happen. Post commit task failed + // to execute properly. + log.warn("POST COMMIT FAILED: ", e); + XAException xae = new XAException("POST COMMIT FAILED"); + xae.errorCode = XAException.XAER_RMERR; + xae.initCause(e); + throw xae; + } + } + + public void rollback() throws XAException, IOException { + + setState(Transaction.FINISHED_STATE); + context.getTransactions().remove(xid); + transactionStore.rollback(getTransactionId()); + + try { + fireAfterRollback(); + } + catch (Throwable e) { + log.warn("POST ROLLBACK FAILED: ", e); + XAException xae = new XAException("POST ROLLBACK FAILED"); + xae.errorCode = XAException.XAER_RMERR; + xae.initCause(e); + throw xae; + } + } + + public int prepare() throws XAException { + XAException xae = new XAException("Prepare not implemented on Local Transactions."); + xae.errorCode = XAException.XAER_RMERR; + throw xae; + } + + public TransactionId getTransactionId() { + return xid; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transaction/Synchronization.java b/activemq-core/src/main/java/org/activemq/transaction/Synchronization.java new file mode 100755 index 0000000000..4f508fd5df --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transaction/Synchronization.java @@ -0,0 +1,31 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transaction; + +/** + * + * @version $Revision$ + */ +public class Synchronization { + + public void beforeEnd() throws Throwable{} + public void afterCommit() throws Throwable{} + public void afterRollback() throws Throwable{} + +} diff --git a/activemq-core/src/main/java/org/activemq/transaction/Transaction.java b/activemq-core/src/main/java/org/activemq/transaction/Transaction.java new file mode 100755 index 0000000000..6ce8d6d8dc --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transaction/Transaction.java @@ -0,0 +1,108 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transaction; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +import javax.transaction.xa.XAException; + +import org.activemq.command.TransactionId; + +/** + * Keeps track of all the actions the need to be done when + * a transaction does a commit or rollback. + * + * @version $Revision: 1.5 $ + */ +public abstract class Transaction { + + static final public byte START_STATE = 0; // can go to: 1,2,3 + static final public byte IN_USE_STATE = 1; // can go to: 2,3 + static final public byte PREPARED_STATE = 2; // can go to: 3 + static final public byte FINISHED_STATE = 3; + + private ArrayList synchronizations = new ArrayList(); + private byte state = START_STATE; + + public byte getState() { + return state; + } + + public void setState(byte state) { + this.state = state; + } + + public void addSynchronization(Synchronization r) { + synchronizations.add(r); + if (state == START_STATE) { + state = IN_USE_STATE; + } + } + + public void prePrepare() throws Throwable { + + // Is it ok to call prepare now given the state of the + // transaction? + switch (state) { + case START_STATE: + case IN_USE_STATE: + break; + default: + XAException xae = new XAException("Prepare cannot be called now."); + xae.errorCode = XAException.XAER_PROTO; + throw xae; + } + +// // Run the prePrepareTasks +// for (Iterator iter = prePrepareTasks.iterator(); iter.hasNext();) { +// Callback r = (Callback) iter.next(); +// r.execute(); +// } + } + + protected void fireAfterCommit() throws Throwable { + for (Iterator iter = synchronizations.iterator(); iter.hasNext();) { + Synchronization s = (Synchronization) iter.next(); + s.afterCommit(); + } + } + + public void fireAfterRollback() throws Throwable { + for (Iterator iter = synchronizations.iterator(); iter.hasNext();) { + Synchronization s = (Synchronization) iter.next(); + s.afterRollback(); + } + } + + public String toString() { + return super.toString() + "[synchronizations=" + synchronizations +"]"; + } + + abstract public void commit(boolean onePhase) throws XAException, IOException; + abstract public void rollback() throws XAException, IOException; + abstract public int prepare() throws XAException, IOException; + + abstract public TransactionId getTransactionId(); + + public boolean isPrepared() { + return getState()==PREPARED_STATE; + } +} diff --git a/activemq-core/src/main/java/org/activemq/transaction/XATransaction.java b/activemq-core/src/main/java/org/activemq/transaction/XATransaction.java new file mode 100755 index 0000000000..b3cc8d5889 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transaction/XATransaction.java @@ -0,0 +1,194 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transaction; + +import java.io.IOException; + +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; + +import org.activemq.broker.TransactionBroker; +import org.activemq.command.TransactionId; +import org.activemq.command.XATransactionId; +import org.activemq.store.TransactionStore; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @version $Revision: 1.4 $ + */ +public class XATransaction extends Transaction { + + private static final Log log = LogFactory.getLog(XATransaction.class); + + private final TransactionStore transactionStore; + private final XATransactionId xid; + private final TransactionBroker broker; + + public XATransaction(TransactionStore transactionStore, XATransactionId xid, TransactionBroker broker) { + this.transactionStore = transactionStore; + this.xid = xid; + this.broker = broker; + } + + public void commit(boolean onePhase) throws XAException, IOException { + if(log.isDebugEnabled()) + log.debug("XA Transaction commit: "+xid); + + switch (getState()) { + case START_STATE: + // 1 phase commit, no work done. + checkForPreparedState(onePhase); + setStateFinished(); + break; + case IN_USE_STATE: + // 1 phase commit, work done. + checkForPreparedState(onePhase); + doPrePrepare(); + setStateFinished(); + transactionStore.commit(getTransactionId(), false); + doPostCommit(); + break; + case PREPARED_STATE: + // 2 phase commit, work done. + // We would record commit here. + setStateFinished(); + transactionStore.commit(getTransactionId(), true); + doPostCommit(); + break; + default: + illegalStateTransition("commit"); + } + } + + private void illegalStateTransition(String callName) throws XAException { + XAException xae = new XAException("Cannot call " + callName + " now."); + xae.errorCode = XAException.XAER_PROTO; + throw xae; + } + + private void checkForPreparedState(boolean onePhase) throws XAException { + if (!onePhase) { + XAException xae = new XAException("Cannot do 2 phase commit if the transaction has not been prepared."); + xae.errorCode = XAException.XAER_PROTO; + throw xae; + } + } + + private void doPrePrepare() throws XAException, IOException { + try { + prePrepare(); + } catch (XAException e) { + throw e; + } catch (Throwable e) { + log.warn("PRE-PREPARE FAILED: ", e); + rollback(); + XAException xae = new XAException("PRE-PREPARE FAILED: Transaction rolled back."); + xae.errorCode = XAException.XA_RBOTHER; + xae.initCause(e); + throw xae; + } + } + + private void doPostCommit() throws XAException { + try { + fireAfterCommit(); + } + catch (Throwable e) { + // I guess this could happen. Post commit task failed + // to execute properly. + log.warn("POST COMMIT FAILED: ", e); + XAException xae = new XAException("POST COMMIT FAILED"); + xae.errorCode = XAException.XAER_RMERR; + xae.initCause(e); + throw xae; + } + } + + public void rollback() throws XAException, IOException { + + if(log.isDebugEnabled()) + log.debug("XA Transaction rollback: "+xid); + + switch (getState()) { + case START_STATE: + // 1 phase rollback no work done. + setStateFinished(); + break; + case IN_USE_STATE: + // 1 phase rollback work done. + setStateFinished(); + transactionStore.rollback(getTransactionId()); + doPostRollback(); + break; + case PREPARED_STATE: + // 2 phase rollback work done. + setStateFinished(); + transactionStore.rollback(getTransactionId()); + doPostRollback(); + break; + } + + } + + private void doPostRollback() throws XAException { + try { + fireAfterRollback(); + } + catch (Throwable e) { + // I guess this could happen. Post commit task failed + // to execute properly. + log.warn("POST ROLLBACK FAILED: ", e); + XAException xae = new XAException("POST ROLLBACK FAILED"); + xae.errorCode = XAException.XAER_RMERR; + xae.initCause(e); + throw xae; + } + } + + public int prepare() throws XAException, IOException { + if(log.isDebugEnabled()) + log.debug("XA Transaction prepare: "+xid); + + switch (getState()) { + case START_STATE: + // No work done.. no commit/rollback needed. + setStateFinished(); + return XAResource.XA_RDONLY; + case IN_USE_STATE: + // We would record prepare here. + doPrePrepare(); + setState(Transaction.PREPARED_STATE); + transactionStore.prepare(getTransactionId()); + return XAResource.XA_OK; + default : + illegalStateTransition("prepare"); + return XAResource.XA_RDONLY; + } + } + + private void setStateFinished() { + setState(Transaction.FINISHED_STATE); + broker.removeTransaction(xid); + } + + public TransactionId getTransactionId() { + return xid; + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/transport/CompositeTransport.java b/activemq-core/src/main/java/org/activemq/transport/CompositeTransport.java new file mode 100644 index 0000000000..03edaeab2c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/CompositeTransport.java @@ -0,0 +1,26 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport; + +import java.net.URI; + +public interface CompositeTransport extends Transport { + public void add(URI[] uris); + public void remove(URI[] uris); +} diff --git a/activemq-core/src/main/java/org/activemq/transport/FutureResponse.java b/activemq-core/src/main/java/org/activemq/transport/FutureResponse.java new file mode 100755 index 0000000000..c267cec474 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/FutureResponse.java @@ -0,0 +1,60 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport; + +import edu.emory.mathcs.backport.java.util.concurrent.Callable; +import edu.emory.mathcs.backport.java.util.concurrent.ExecutionException; +import edu.emory.mathcs.backport.java.util.concurrent.FutureTask; + +import org.activemq.command.Response; +import org.activemq.util.IOExceptionSupport; + +import java.io.IOException; +import java.io.InterruptedIOException; + +public class FutureResponse extends FutureTask { + + private static final Callable EMPTY_CALLABLE = new Callable() { + public Object call() throws Exception { + return null; + }}; + + public FutureResponse() { + super(EMPTY_CALLABLE); + } + + public synchronized Response getResult() throws IOException { + try { + return (Response) super.get(); + } catch (InterruptedException e) { + throw new InterruptedIOException("Interrupted."); + } catch (ExecutionException e) { + Throwable target = e.getCause(); + if( target instanceof IOException ) { + throw (IOException)target; + } else { + throw IOExceptionSupport.create(target); + } + } + } + + public synchronized void set(Object result) { + super.set(result); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/InactivityIOException.java b/activemq-core/src/main/java/org/activemq/transport/InactivityIOException.java new file mode 100644 index 0000000000..7a167095ac --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/InactivityIOException.java @@ -0,0 +1,48 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport; + +import java.io.IOException; + +/** + * This is exception is thrown when the transport layer detects that the underlying socket has been inactive for + * too long. + * + * @version $Revision$ + */ +public class InactivityIOException extends IOException { + + /** + * + */ + private static final long serialVersionUID = 5816001466763503220L; + + public InactivityIOException() { + super(); + } + + /** + * @param message + */ + public InactivityIOException(String message) { + super(message); + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/InactivityMonitor.java b/activemq-core/src/main/java/org/activemq/transport/InactivityMonitor.java new file mode 100755 index 0000000000..065cbc4772 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/InactivityMonitor.java @@ -0,0 +1,100 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport; + +import java.io.IOException; + +import org.activemq.command.KeepAliveInfo; +import org.activemq.management.CountStatisticImpl; +import org.activemq.thread.Scheduler; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +/** + * Used to make sure that commands are arriving periodically from the peer of the transport. + * + * @version $Revision$ + */ +public class InactivityMonitor extends TransportFilter implements Runnable { + + private final long maxInactivityDuration; + private final AtomicBoolean cancled = new AtomicBoolean(false); + private byte runIteration=0; + + private long lastReadCount; + private long lastWriteCount; + private final CountStatisticImpl readCounter; + private final CountStatisticImpl writeCounter; + + public InactivityMonitor(Transport next, long maxInactivityDuration, CountStatisticImpl readCounter, CountStatisticImpl writeCounter ) { + super(next); + this.maxInactivityDuration = maxInactivityDuration; + this.readCounter = readCounter; + this.writeCounter = writeCounter; + } + + public void start() throws Exception { + next.start(); + Scheduler.executePeriodically(this, maxInactivityDuration/5); + } + + public void stop() throws Exception { + if( cancled.compareAndSet(false, true) ) { + Scheduler.cancel(this); + } + next.stop(); + } + + public void run() { + + switch(runIteration) { + case 1: + case 2: + long wc = writeCounter.getCount(); + if( wc==lastWriteCount ) { + try { + oneway(new KeepAliveInfo()); + } catch (IOException e) { + onException(e); + } + } else { + lastWriteCount = wc; + } + break; + case 4: + long rc = readCounter.getCount(); + if( rc == lastReadCount ) { + onException(new InactivityIOException("Channel was inactive for too long.")); + } else { + lastReadCount = rc; + } + } + + runIteration++; + if(runIteration>=5) + runIteration=0; + } + + public void onException(IOException error) { + if( cancled.compareAndSet(false, true) ) { + Scheduler.cancel(this); + } + commandListener.onException(error); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/MarshallingTransportFilter.java b/activemq-core/src/main/java/org/activemq/transport/MarshallingTransportFilter.java new file mode 100755 index 0000000000..1a3193fac7 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/MarshallingTransportFilter.java @@ -0,0 +1,31 @@ +package org.activemq.transport; + +import java.io.IOException; + +import org.activeio.command.WireFormat; +import org.activemq.command.Command; + +public class MarshallingTransportFilter extends TransportFilter { + + private final WireFormat localWireFormat; + private final WireFormat remoteWireFormat; + + public MarshallingTransportFilter(Transport next, WireFormat localWireFormat, WireFormat remoteWireFormat) { + super(next); + this.localWireFormat = localWireFormat; + this.remoteWireFormat = remoteWireFormat; + } + + public void oneway(Command command) throws IOException { + next.oneway((Command) remoteWireFormat.unmarshal(localWireFormat.marshal(command))); + } + + public void onCommand(Command command) { + try { + commandListener.onCommand((Command)localWireFormat.unmarshal(remoteWireFormat.marshal(command))); + } catch (IOException e) { + commandListener.onException(e); + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/MutexTransport.java b/activemq-core/src/main/java/org/activemq/transport/MutexTransport.java new file mode 100755 index 0000000000..492d2a14a5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/MutexTransport.java @@ -0,0 +1,56 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport; + +import java.io.IOException; + +import org.activemq.command.Command; +import org.activemq.command.Response; + + +/** + * @version $Revision$ + */ +public class MutexTransport extends TransportFilter { + + private final Object writeMutex = new Object(); + + public MutexTransport(Transport next) { + super(next); + } + + public FutureResponse asyncRequest(Command command) throws IOException { + synchronized(writeMutex) { + return next.asyncRequest(command); + } + } + + public void oneway(Command command) throws IOException { + synchronized(writeMutex) { + next.oneway(command); + } + } + + public Response request(Command command) throws IOException { + synchronized(writeMutex) { + return next.request(command); + } + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/transport/ResponseCorrelator.java b/activemq-core/src/main/java/org/activemq/transport/ResponseCorrelator.java new file mode 100755 index 0000000000..b32f2f52a0 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/ResponseCorrelator.java @@ -0,0 +1,87 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport; + +import java.io.IOException; + +import org.activemq.command.Command; +import org.activemq.command.Response; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + + +/** + * Creates a {@see org.activeio.RequestChannel} out of a {@see org.activeio.AsynchChannel}. This + * {@see org.activeio.RequestChannel} is thread safe and mutiplexes concurrent requests and responses over + * the underlying {@see org.activeio.AsynchChannel}. + * + * @version $Revision: 1.4 $ + */ +final public class ResponseCorrelator extends TransportFilter { + + private static final Log log = LogFactory.getLog(ResponseCorrelator.class); + + private final ConcurrentHashMap requestMap = new ConcurrentHashMap(); + private short lastCommandId = 0; + + synchronized short getNextCommandId() { + return ++lastCommandId; + } + + public ResponseCorrelator(Transport next) { + super(next); + } + + public void oneway(Command command) throws IOException { + command.setCommandId(getNextCommandId()); + command.setResponseRequired(false); + next.oneway(command); + } + + public FutureResponse asyncRequest(Command command) throws IOException { + command.setCommandId(getNextCommandId()); + command.setResponseRequired(true); + FutureResponse future = new FutureResponse(); + requestMap.put(new Short(command.getCommandId()), future); + next.oneway(command); + return future; + } + + public Response request(Command command) throws IOException { + FutureResponse response = asyncRequest(command); + return response.getResult(); + } + + public void onCommand(Command command) { + boolean debug = log.isDebugEnabled(); + if( command.isResponse() ) { + Response response = (Response) command; + FutureResponse future = (FutureResponse) requestMap.remove(new Short(response.getCorrelationId())); + if( future!=null ) { + future.set(response); + } else { + if( debug ) log.debug("Received unexpected response for command id: "+response.getCorrelationId()); + } + } else { + commandListener.onCommand(command); + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/Transport.java b/activemq-core/src/main/java/org/activemq/transport/Transport.java new file mode 100755 index 0000000000..c9d15313fe --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/Transport.java @@ -0,0 +1,58 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport; + +import org.activemq.Service; +import org.activemq.command.Command; +import org.activemq.command.Response; + +import java.io.IOException; + +/** + * Represents the client side of a transport allowing messages + * to be sent synchronously, asynchronously and consumed. + * + * @version $Revision: 1.5 $ + */ +public interface Transport extends Service { + + /** + * A one way asynchronous send + */ + public void oneway(Command command) throws IOException; + + /** + * An asynchronous request response where the Receipt will be returned + * in the future + */ + public FutureResponse asyncRequest(Command command) throws IOException; + + /** + * A synchronous request response + */ + public Response request(Command command) throws IOException; + + /** + * Registers an inbound command listener + */ + public void setTransportListener(TransportListener commandListener); + + public Object narrow(Class target); + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/TransportAcceptListener.java b/activemq-core/src/main/java/org/activemq/transport/TransportAcceptListener.java new file mode 100755 index 0000000000..e78cdccdf5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/TransportAcceptListener.java @@ -0,0 +1,28 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport; + + +public interface TransportAcceptListener { + + void onAccept(Transport transport); + + void onAcceptError(Exception error); + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/TransportFactory.java b/activemq-core/src/main/java/org/activemq/transport/TransportFactory.java new file mode 100755 index 0000000000..d8c0cccb4b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/TransportFactory.java @@ -0,0 +1,215 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; +import edu.emory.mathcs.backport.java.util.concurrent.Executor; + +import org.activeio.FactoryFinder; +import org.activeio.command.WireFormat; +import org.activeio.command.WireFormatFactory; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.URISupport; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; + +public abstract class TransportFactory { + + public abstract TransportServer doBind(String brokerId, URI location) throws IOException; + + public Transport doConnect(URI location, Executor ex) throws Exception { + return doConnect(location); + } + + public Transport doCompositeConnect(URI location, Executor ex) throws Exception { + return doCompositeConnect(location); + } + + static final private FactoryFinder transportFactoryFinder = new FactoryFinder("META-INF/services/org/activemq/transport/"); + static final private FactoryFinder wireFormatFactoryFinder = new FactoryFinder("META-INF/services/org/activemq/wireformat/"); + + static final private ConcurrentHashMap transportFactorys = new ConcurrentHashMap(); + + /** + * Creates a normal transport. + * + * @param location + * @return the transport + * @throws Exception + */ + public static Transport connect(URI location) throws Exception { + TransportFactory tf = findTransportFactory(location); + return tf.doConnect(location); + } + + /** + * Creates a normal transport. + * + * @param location + * @param ex + * @return the transport + * @throws Exception + */ + public static Transport connect(URI location, Executor ex) throws Exception { + TransportFactory tf = findTransportFactory(location); + return tf.doConnect(location, ex); + } + + /** + * Creates a slimmed down transport that is more efficient so that it can be + * used by composite transports like reliable and HA. + * + * @param location + * @return the Transport + * @throws Exception + */ + public static Transport compositeConnect(URI location) throws Exception { + TransportFactory tf = findTransportFactory(location); + return tf.doCompositeConnect(location); + } + + /** + * Creates a slimmed down transport that is more efficient so that it can be + * used by composite transports like reliable and HA. + * + * @param location + * @param ex + * @return the Transport + * @throws Exception + */ + public static Transport compositeConnect(URI location, Executor ex) throws Exception { + TransportFactory tf = findTransportFactory(location); + return tf.doCompositeConnect(location, ex); + } + + public static TransportServer bind(String brokerId, URI location) throws IOException { + TransportFactory tf = findTransportFactory(location); + return tf.doBind(brokerId, location); + } + + public Transport doConnect(URI location) throws Exception { + try { + Map options = new HashMap(URISupport.parseParamters(location)); + WireFormat wf = createWireFormat(options); + Transport transport = createTransport(location, wf); + Transport rc = configure(transport, wf, options); + if (!options.isEmpty()) { + throw new IllegalArgumentException("Invalid connect parameters: " + options); + } + return rc; + } + catch (URISyntaxException e) { + throw IOExceptionSupport.create(e); + } + } + + public Transport doCompositeConnect(URI location) throws Exception { + try { + Map options = new HashMap(URISupport.parseParamters(location)); + WireFormat wf = createWireFormat(options); + Transport transport = createTransport(location, wf); + Transport rc = compositeConfigure(transport, wf, options); + if (!options.isEmpty()) { + throw new IllegalArgumentException("Invalid connect parameters: " + options); + } + return rc; + + } + catch (URISyntaxException e) { + throw IOExceptionSupport.create(e); + } + } + + /** + * Factory method to create a new transport + * @throws IOException + * @throws UnknownHostException + */ + protected Transport createTransport(URI location, WireFormat wf) throws MalformedURLException, UnknownHostException, IOException { + throw new IOException("createTransport() method not implemented!"); + } + + /** + * @param location + * @return + * @throws IOException + */ + private static TransportFactory findTransportFactory(URI location) throws IOException { + String scheme = location.getScheme(); + if( scheme == null ) + throw new IOException("Transport not scheme specified: [" + location + "]"); + TransportFactory tf = (TransportFactory) transportFactorys.get(scheme); + if (tf == null) { + // Try to load if from a META-INF property. + try { + tf = (TransportFactory) transportFactoryFinder.newInstance(scheme); + transportFactorys.put(scheme, tf); + } + catch (Throwable e) { + throw IOExceptionSupport.create("Transport scheme NOT recognized: [" + scheme + "]", e); + } + } + return tf; + } + + protected WireFormat createWireFormat(Map options) throws IOException { + WireFormatFactory factory = createWireFormatFactory(options); + WireFormat format = factory.createWireFormat(); + return format; + } + + protected WireFormatFactory createWireFormatFactory(Map options) throws IOException { + String wireFormat = (String) options.get("wireFormat"); + if (wireFormat == null) + wireFormat = getDefaultWireFormatType(); + + try { + WireFormatFactory wff = (WireFormatFactory) wireFormatFactoryFinder.newInstance(wireFormat); + IntrospectionSupport.setProperties(wff, options, "wireFormat."); + return wff; + } + catch (Throwable e) { + throw IOExceptionSupport.create("Could not create wire format factory for: " + wireFormat + ", reason: " + e, e); + } + } + + protected String getDefaultWireFormatType() { + return "default"; + } + + protected Transport configure(Transport transport, WireFormat wf, Map options) { + IntrospectionSupport.setProperties(transport, options); + transport = new MutexTransport(transport); + transport = new ResponseCorrelator(transport); + return transport; + } + + public Transport compositeConfigure(Transport transport, WireFormat format, Map options) { + IntrospectionSupport.setProperties(transport, options); + return transport; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/TransportFilter.java b/activemq-core/src/main/java/org/activemq/transport/TransportFilter.java new file mode 100755 index 0000000000..c94d07a747 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/TransportFilter.java @@ -0,0 +1,114 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport; + +import java.io.IOException; + +import org.activemq.command.Command; +import org.activemq.command.Response; + + +/** + * @version $Revision: 1.5 $ + */ +public class TransportFilter implements Transport, TransportListener { + + final protected Transport next; + protected TransportListener commandListener; + + public TransportFilter(Transport next) { + this.next = next; + } + + /** + */ + public void setTransportListener(TransportListener channelListener) { + this.commandListener = channelListener; + if (channelListener == null) + next.setTransportListener(null); + else + next.setTransportListener(this); + } + + + /** + * @see org.activemq.Service#start() + * @throws IOException if the next channel has not been set. + */ + public void start() throws Exception { + if( next == null ) + throw new IOException("The next channel has not been set."); + if( commandListener == null ) + throw new IOException("The command listener has not been set."); + next.start(); + } + + /** + * @see org.activemq.Service#stop() + */ + public void stop() throws Exception { + next.stop(); + } + + public void onCommand(Command command) { + commandListener.onCommand(command); + } + + /** + * @return Returns the next. + */ + public Transport getNext() { + return next; + } + + /** + * @return Returns the packetListener. + */ + public TransportListener getCommandListener() { + return commandListener; + } + + public String toString() { + return next.toString(); + } + + public void oneway(Command command) throws IOException { + next.oneway(command); + } + + public FutureResponse asyncRequest(Command command) throws IOException { + return next.asyncRequest(command); + } + + public Response request(Command command) throws IOException { + return next.request(command); + } + + public void onException(IOException error) { + commandListener.onException(error); + } + + public Object narrow(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return next.narrow(target); + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/transport/TransportListener.java b/activemq-core/src/main/java/org/activemq/transport/TransportListener.java new file mode 100755 index 0000000000..fddc0473b5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/TransportListener.java @@ -0,0 +1,35 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport; + +import java.io.IOException; + +import org.activemq.command.Command; + +/** + * An asynchronous listener of commands + * + * @version $Revision$ + */ +public interface TransportListener { + + public void onCommand(Command command); + public void onException(IOException error); + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/TransportLogger.java b/activemq-core/src/main/java/org/activemq/transport/TransportLogger.java new file mode 100755 index 0000000000..a985d517cb --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/TransportLogger.java @@ -0,0 +1,55 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport; + +import java.io.IOException; + +import org.activemq.command.Command; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * @version $Revision$ + */ +public class TransportLogger extends TransportFilter { + + private static int lastId=0; + private final Log log; + + public TransportLogger(Transport next) { + this( next, LogFactory.getLog(TransportLogger.class.getName()+"."+getNextId())); + } + + synchronized private static int getNextId() { + return ++lastId; + } + + public TransportLogger(Transport next, Log log) { + super(next); + this.log = log; + } + + public void oneway(Command command) throws IOException { + if( log.isDebugEnabled() ) { + log.debug("SENDING: "+command); + } + next.oneway(command); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/TransportServer.java b/activemq-core/src/main/java/org/activemq/transport/TransportServer.java new file mode 100755 index 0000000000..1c14888c41 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/TransportServer.java @@ -0,0 +1,52 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport; + +import java.net.URI; + +import org.activemq.Service; +import org.activemq.command.BrokerInfo; + + +/** + * A TransportServer asynchronously accepts {@see Transport} objects + * and then delivers those objects to a {@see TransportAcceptListener}. + * + * @version $Revision: 1.4 $ + */ +public interface TransportServer extends Service { + + /** + * Registers an {@see TransportAcceptListener} which is notified of accepted channels. + * + * @param acceptListener + */ + void setAcceptListener(TransportAcceptListener acceptListener); + + /** + * Associates a broker info with the transport server so that the transport can do + * discovery advertisements of the broker. + * + * @param brokerInfo + */ + void setBrokerInfo(BrokerInfo brokerInfo); + + public URI getConnectURI(); + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/TransportServerFilter.java b/activemq-core/src/main/java/org/activemq/transport/TransportServerFilter.java new file mode 100755 index 0000000000..2a16a8c1d4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/TransportServerFilter.java @@ -0,0 +1,56 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport; + +import java.net.URI; + +import org.activemq.command.BrokerInfo; + +public class TransportServerFilter implements TransportServer { + + protected final TransportServer next; + + /** + * @param next + */ + public TransportServerFilter(TransportServer next) { + this.next = next; + } + + public URI getConnectURI() { + return next.getConnectURI(); + } + + public void setAcceptListener(TransportAcceptListener acceptListener) { + next.setAcceptListener(acceptListener); + } + + public void setBrokerInfo(BrokerInfo brokerInfo) { + next.setBrokerInfo(brokerInfo); + } + + public void start() throws Exception { + next.start(); + } + + public void stop() throws Exception { + next.stop(); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/TransportServerSupport.java b/activemq-core/src/main/java/org/activemq/transport/TransportServerSupport.java new file mode 100644 index 0000000000..68571f7996 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/TransportServerSupport.java @@ -0,0 +1,79 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport; + +import java.net.URI; + +/** + * A useful base class for implementations of {@link TransportServer} + * + * @version $Revision: 1.1 $ + */ +public abstract class TransportServerSupport implements TransportServer { + + private URI location; + private TransportAcceptListener acceptListener; + + public TransportServerSupport() { + } + + public TransportServerSupport(URI location) { + this.location = location; + } + + public URI getConnectURI() { + return location; + } + + /** + * @return Returns the acceptListener. + */ + public TransportAcceptListener getAcceptListener() { + return acceptListener; + } + + /** + * Registers an accept listener + * + * @param acceptListener + */ + public void setAcceptListener(TransportAcceptListener acceptListener) { + this.acceptListener = acceptListener; + } + + /** + * @return Returns the location. + */ + public URI getLocation() { + return location; + } + + /** + * @param location + * The location to set. + */ + public void setLocation(URI location) { + this.location = location; + } + + protected void onAcceptError(Exception e) { + if (acceptListener != null) { + acceptListener.onAcceptError(e); + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/TransportServerThreadSupport.java b/activemq-core/src/main/java/org/activemq/transport/TransportServerThreadSupport.java new file mode 100644 index 0000000000..c28bb06c15 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/TransportServerThreadSupport.java @@ -0,0 +1,106 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +import org.activemq.ThreadPriorities; +import org.activemq.util.ServiceStopper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.net.URI; + +/** + * A useful base class for implementations of {@link TransportServer} which uses + * a background thread to accept new connections. + * + * @version $Revision: 1.1 $ + */ +public abstract class TransportServerThreadSupport extends TransportServerSupport implements Runnable { + private static final Log log = LogFactory.getLog(TransportServerThreadSupport.class); + + private AtomicBoolean closed = new AtomicBoolean(false); + private AtomicBoolean started = new AtomicBoolean(false); + private AtomicBoolean closing = new AtomicBoolean(false); + private boolean daemon = true; + private Thread runner; + + public TransportServerThreadSupport() { + } + + public TransportServerThreadSupport(URI location) { + super(location); + } + + public void start() throws Exception { + if (started.compareAndSet(false, true)) { + log.info("Listening for connections at: " + getLocation()); + runner = new Thread(this, toString()); + runner.setDaemon(daemon); + runner.setPriority(ThreadPriorities.BROKER_MANAGEMENT); + runner.start(); + } + } + + public void stop() throws Exception { + if (closed.compareAndSet(false, true)) { + closing.set(true); + ServiceStopper stopper = new ServiceStopper(); + try { + doStop(stopper); + } + catch (Exception e) { + stopper.onException(this, e); + } + if (runner != null) { + runner.join(); + runner = null; + } + closed.set(true); + started.set(false); + closing.set(false); + stopper.throwFirstException(); + } + } + + public boolean isStarted() { + return started.get(); + } + + /** + * @return true if the transport server is in the process of closing down. + */ + public boolean isClosing() { + return closing.get(); + } + + public boolean isClosed() { + return closed.get(); + } + + public boolean isDaemon() { + return daemon; + } + + public void setDaemon(boolean daemon) { + this.daemon = daemon; + } + + protected abstract void doStop(ServiceStopper stopper) throws Exception; +} diff --git a/activemq-core/src/main/java/org/activemq/transport/TransportSupport.java b/activemq-core/src/main/java/org/activemq/transport/TransportSupport.java new file mode 100644 index 0000000000..00f2dab0c4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/TransportSupport.java @@ -0,0 +1,95 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport; + +import org.activemq.command.Command; +import org.activemq.command.Response; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; + +/** + * A useful base class for transport implementations. + * + * @version $Revision: 1.1 $ + */ +public abstract class TransportSupport implements Transport { + private static final Log log = LogFactory.getLog(TransportSupport.class); + + private TransportListener transportListener; + + public TransportListener getTransportListener() { + return transportListener; + } + + /** + * Registers an inbound command listener + * + * @param commandListener + */ + public void setTransportListener(TransportListener commandListener) { + this.transportListener = commandListener; + } + + /** + * narrow acceptance + * + * @param target + * @return 'this' if assignable + */ + public Object narrow(Class target) { + boolean assignableFrom = target.isAssignableFrom(getClass()); + if (assignableFrom) { + return this; + } + return null; + } + + public FutureResponse asyncRequest(Command command) throws IOException { + throw new AssertionError("Unsupported Method"); + } + + public Response request(Command command) throws IOException { + throw new AssertionError("Unsupported Method"); + } + + /** + * Process the inbound command + */ + public void doConsume(Command command) { + if (command != null) { + if (transportListener != null) { + transportListener.onCommand(command); + } + else { + log.error("No transportListener available to process inbound command: " + command); + } + } + } + + /** + * Passes any IO exceptions into the transport listener + */ + public void onException(IOException e) { + if (transportListener != null) { + transportListener.onException(e); + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/TransportThreadSupport.java b/activemq-core/src/main/java/org/activemq/transport/TransportThreadSupport.java new file mode 100644 index 0000000000..bbc046e5f8 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/TransportThreadSupport.java @@ -0,0 +1,77 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport; + +import org.activemq.util.ServiceStopper; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +/** + * A useful base class for a transport implementation which has a background + * reading thread. + * + * @version $Revision: 1.1 $ + */ +public abstract class TransportThreadSupport extends TransportSupport implements Runnable { + + private AtomicBoolean closed = new AtomicBoolean(false); + private AtomicBoolean started = new AtomicBoolean(false); + private boolean daemon = false; + private Thread runner; + + public void start() throws Exception { + if (started.compareAndSet(false, true)) { + runner = new Thread(this, toString()); + runner.setDaemon(daemon); + runner.start(); + } + } + + public void stop() throws Exception { + if (closed.compareAndSet(false, true)) { + started.set(false); + ServiceStopper stopper = new ServiceStopper(); + try { + doStop(stopper); + } + catch (Exception e) { + stopper.onException(this, e); + } + stopper.throwFirstException(); + } + closed.set(true); + } + + public boolean isStarted() { + return started.get(); + } + + public boolean isClosed() { + return closed.get(); + } + + public boolean isDaemon() { + return daemon; + } + + public void setDaemon(boolean daemon) { + this.daemon = daemon; + } + + protected abstract void doStop(ServiceStopper stopper) throws Exception; +} diff --git a/activemq-core/src/main/java/org/activemq/transport/WireFormatNegotiator.java b/activemq-core/src/main/java/org/activemq/transport/WireFormatNegotiator.java new file mode 100755 index 0000000000..552659d024 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/WireFormatNegotiator.java @@ -0,0 +1,109 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport; + +import java.io.IOException; +import java.io.InterruptedIOException; + +import org.activeio.command.WireFormat; +import org.activemq.command.Command; +import org.activemq.command.WireFormatInfo; +import org.activemq.openwire.OpenWireFormat; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; + + +public class WireFormatNegotiator extends TransportFilter { + + private final WireFormat wireFormat; + private final int minimumVersion; + + private boolean firstStart=true; + private CountDownLatch readyCountDownLatch = new CountDownLatch(1); + + /** + * Negotiator + * + * @param next + * @param preferedFormat + */ + public WireFormatNegotiator(Transport next, WireFormat wireFormat, int minimumVersion) { + super(next); + this.wireFormat = wireFormat; + this.minimumVersion = minimumVersion; + } + + + public void start() throws Exception { + super.start(); + if( firstStart ) { + WireFormatInfo info = createWireFormatInfo(); + next.oneway(info); + } + } + + public void oneway(Command command) throws IOException { + try { + readyCountDownLatch.await(); + } catch (InterruptedException e) { + throw new InterruptedIOException(); + } + super.oneway(command); + } + + protected WireFormatInfo createWireFormatInfo() { + WireFormatInfo info = new WireFormatInfo(); + info.setVersion(wireFormat.getVersion()); + if ( wireFormat instanceof OpenWireFormat ) { + info.setStackTraceEnabled(((OpenWireFormat)wireFormat).isStackTraceEnabled()); + info.setTcpNoDelayEnabled(((OpenWireFormat)wireFormat).isTcpNoDelayEnabled()); + info.setCacheEnabled(((OpenWireFormat)wireFormat).isCacheEnabled()); + } + return info; + } + + public void onCommand(Command command) { + if( command.isWireFormatInfo() ) { + WireFormatInfo info = (WireFormatInfo) command; + if( !info.isValid() ) { + commandListener.onException(new IOException("Remote wire format magic is invalid")); + } else if( info.getVersion() < minimumVersion ) { + commandListener.onException(new IOException("Remote wire format ("+info.getVersion()+") is lower the minimum version required ("+minimumVersion+")")); + } else if ( info.getVersion()!=wireFormat.getVersion() ) { + // Match the remote side. + wireFormat.setVersion(info.getVersion()); + } + if ( wireFormat instanceof OpenWireFormat ) { + if( !info.isStackTraceEnabled() ) { + ((OpenWireFormat)wireFormat).setStackTraceEnabled(false); + } + if( info.isTcpNoDelayEnabled() ) { + ((OpenWireFormat)wireFormat).setTcpNoDelayEnabled(true); + } + if( !info.isCacheEnabled() ) { + ((OpenWireFormat)wireFormat).setCacheEnabled(false); + } + } + + readyCountDownLatch.countDown(); + + } + commandListener.onCommand(command); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/activeio/ActiveIOTransport.java b/activemq-core/src/main/java/org/activemq/transport/activeio/ActiveIOTransport.java new file mode 100755 index 0000000000..1b09ccbf44 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/activeio/ActiveIOTransport.java @@ -0,0 +1,195 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.activeio; + +import java.io.IOException; +import java.net.SocketException; + +import org.activeio.command.AsyncCommandChannel; +import org.activeio.net.SocketMetadata; +import org.activemq.command.Command; +import org.activemq.command.Response; +import org.activemq.command.WireFormatInfo; +import org.activemq.management.CountStatisticImpl; +import org.activemq.transport.FutureResponse; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportListener; + +/** + * An implementation of the {@link Transport} interface using ActiveIO + * + * @version $Revision$ + */ +public class ActiveIOTransport implements Transport { + + private AsyncCommandChannel commandChannel; + private TransportListener transportListener; + private long timeout = 2000; + + private int minmumWireFormatVersion = 0; + private long maxInactivityDuration = 60000; + private boolean trace = false; + private long stopTimeout = 2000; + private CountStatisticImpl readCounter; + private CountStatisticImpl writeCounter; + + + public ActiveIOTransport(AsyncCommandChannel commandChannel) { + this.commandChannel = commandChannel; + this.commandChannel.setCommandListener(new org.activeio.command.CommandListener() { + public void onCommand(Object command) { + if (command.getClass() == WireFormatInfo.class) { + WireFormatInfo info = (WireFormatInfo) command; + if (info.isTcpNoDelayEnabled()) { + try { + enableTcpNodeDelay(); + } + catch (SocketException e) { + onError(e); + } + } + } + transportListener.onCommand((Command) command); + } + + public void onError(Exception e) { + if (e instanceof IOException) { + transportListener.onException((IOException) e); + } + else { + transportListener.onException((IOException) new IOException().initCause(e)); + } + } + }); + + } + + private void enableTcpNodeDelay() throws SocketException { + SocketMetadata sm = (SocketMetadata) commandChannel.getAdapter(SocketMetadata.class); + if (sm != null) { + sm.setTcpNoDelay(true); + } + } + + public void oneway(Command command) throws IOException { + if (command.getClass() == WireFormatInfo.class) { + WireFormatInfo info = (WireFormatInfo) command; + if (info.isTcpNoDelayEnabled()) { + enableTcpNodeDelay(); + } + } + commandChannel.writeCommand(command); + } + + public FutureResponse asyncRequest(Command command) throws IOException { + throw new AssertionError("Unsupported Method"); + } + + public Response request(Command command) throws IOException { + throw new AssertionError("Unsupported Method"); + } + + public void start() throws Exception { + commandChannel.start(); + } + + public void stop() throws Exception { + commandChannel.stop(stopTimeout); + commandChannel.dispose(); + } + + // Properties + // ------------------------------------------------------------------------- + + public TransportListener getTransportListener() { + return transportListener; + } + + public void setTransportListener(TransportListener listener) { + this.transportListener = listener; + } + + public AsyncCommandChannel getCommandChannel() { + return commandChannel; + } + + public long getTimeout() { + return timeout; + } + + public void setTimeout(long timeout) { + this.timeout = timeout; + } + + public Object narrow(Class target) { + if (target.isAssignableFrom(getClass())) { + return this; + } + return null; + } + + public int getMinmumWireFormatVersion() { + return minmumWireFormatVersion; + } + + public void setMinmumWireFormatVersion(int minmumWireFormatVersion) { + this.minmumWireFormatVersion = minmumWireFormatVersion; + } + + public long getMaxInactivityDuration() { + return maxInactivityDuration; + } + + public void setMaxInactivityDuration(long maxInactivityDuration) { + this.maxInactivityDuration = maxInactivityDuration; + } + + public long getStopTimeout() { + return stopTimeout; + } + + public void setStopTimeout(long stopTimeout) { + this.stopTimeout = stopTimeout; + } + + public boolean isTrace() { + return trace; + } + + public void setTrace(boolean trace) { + this.trace = trace; + } + + public void setReadCounter(CountStatisticImpl readCounter) { + this.readCounter = readCounter; + } + + public void setWriteCounter(CountStatisticImpl writeCounter) { + this.writeCounter = writeCounter; + } + + public CountStatisticImpl getReadCounter() { + return readCounter; + } + + public CountStatisticImpl getWriteCounter() { + return writeCounter; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/activeio/ActiveIOTransportFactory.java b/activemq-core/src/main/java/org/activemq/transport/activeio/ActiveIOTransportFactory.java new file mode 100755 index 0000000000..6e13808798 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/activeio/ActiveIOTransportFactory.java @@ -0,0 +1,286 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.activeio; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import org.activeio.AsyncChannel; +import org.activeio.Channel; +import org.activeio.ChannelFactory; +import org.activeio.adapter.SyncToAsyncChannel; +import org.activeio.command.AsyncChannelToAsyncCommandChannel; +import org.activeio.command.WireFormat; +import org.activeio.net.SocketMetadata; +import org.activemq.transport.InactivityMonitor; +import org.activemq.transport.MutexTransport; +import org.activemq.transport.ResponseCorrelator; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportLogger; +import org.activemq.transport.TransportServer; +import org.activemq.transport.WireFormatNegotiator; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.URISupport; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; + +public class ActiveIOTransportFactory extends TransportFactory { + + public Transport doConnect(URI location) throws IOException { + try { + Map options = new HashMap(URISupport.parseParamters(location)); + location = convertToActiveIOURI(location); + Transport rc = connect(location, createWireFormat(options), options); + if( !options.isEmpty() ) { + throw new IllegalArgumentException("Invalid connect parameters: "+options); + } + return rc; + } catch (URISyntaxException e) { + throw IOExceptionSupport.create(e); + } + } + + /** + * Do a connect to get the transport + * @param location + * @param ex + * @return the transport + * @throws IOException + * + */ + public Transport doConnect(URI location,Executor ex) throws IOException { + try { + Map options = new HashMap(URISupport.parseParamters(location)); + location = convertToActiveIOURI(location); + Transport rc = connect(location, createWireFormat(options), options,ex); + if( !options.isEmpty() ) { + throw new IllegalArgumentException("Invalid connect parameters: "+options); + } + return rc; + } catch (URISyntaxException e) { + throw IOExceptionSupport.create(e); + } + } + + /** + * do a Composite connect + * @param location + * @return the Transport + * @throws IOException + * + */ + public Transport doCompositeConnect(URI location) throws IOException { + try { + Map options = URISupport.parseParamters(location); + location = convertToActiveIOURI(location); + Transport rc = compositeConnect(location, createWireFormat(options), options); + if( !options.isEmpty() ) { + throw new IllegalArgumentException("Invalid connect parameters: "+options); + } + return rc; + } catch (URISyntaxException e) { + throw IOExceptionSupport.create(e); + } + } + + /** + * Do a Composite Connect + * @param location + * @param ex + * @return the Transport + * @throws IOException + * + */ + public Transport doCompositeConnect(URI location,Executor ex) throws IOException { + try { + Map options = URISupport.parseParamters(location); + location = convertToActiveIOURI(location); + Transport rc = compositeConnect(location, createWireFormat(options), options,ex); + if( !options.isEmpty() ) { + throw new IllegalArgumentException("Invalid connect parameters: "+options); + } + return rc; + } catch (URISyntaxException e) { + throw IOExceptionSupport.create(e); + } + } + + public TransportServer doBind(String brokerId,final URI location) throws IOException { + try { + Map options = new HashMap(URISupport.parseParamters(location)); + + ActiveIOTransportServer server = new ActiveIOTransportServer(convertToActiveIOURI(location), options) { + public URI getBindURI() { + return location; + } + public URI getConnectURI() { + return convertFromActiveIOURI(super.getConnectURI()); + } + }; + server.setWireFormatFactory(createWireFormatFactory(options)); + IntrospectionSupport.setProperties(server, options); + + return server; + } catch (URISyntaxException e) { + throw IOExceptionSupport.create(e); + } + } + + + static private final HashMap toSchemeMap = new HashMap(); + static private final HashMap fromSchemeMap = new HashMap(); + static { + toSchemeMap.put("tcp", "socket"); + fromSchemeMap.put("socket", "tcp"); + toSchemeMap.put("nio", "nio-async"); + fromSchemeMap.put("nio-async", "nio"); + toSchemeMap.put("aio", "aio"); + fromSchemeMap.put("aio", "aio"); + toSchemeMap.put("ssl", "ssl"); + fromSchemeMap.put("ssl", "ssl"); + toSchemeMap.put("vmpipe", "vmpipe"); + fromSchemeMap.put("vmpipe", "vmpipe"); + toSchemeMap.put("jxta", "jxta"); + fromSchemeMap.put("jxta", "jxta"); + } + + static URI convertToActiveIOURI(URI location) { + try { + String scheme = location.getScheme(); + String target = (String) toSchemeMap.get(scheme); + if( target!=null ) { + return new URI(target, + location.getSchemeSpecificPart(), + location.getFragment()); + } + } catch (URISyntaxException e) { + } + return location; + } + + static URI convertFromActiveIOURI(URI location) { + try { + String scheme = location.getScheme(); + String target = (String) fromSchemeMap.get(scheme); + if( target!=null ) { + return new URI(target, + location.getSchemeSpecificPart(), + location.getFragment()); + } + } catch (URISyntaxException e) { + } + return location; + } + + public static Transport connect(URI location, WireFormat format, Map options) throws IOException { + return configure(new ChannelFactory().openAsyncChannel(location), format, options); + } + + public static Transport connect(URI location, WireFormat format, Map options,Executor ex) throws IOException { + return configure(new ChannelFactory().openAsyncChannel(location), format, options,ex); + } + + public static Transport configure(Channel c, WireFormat format, Map options) { + AsyncChannel channel = SyncToAsyncChannel.adapt(c); + return configure(channel,format,options); + } + + public static Transport configure(Channel c, WireFormat format, Map options,Executor ex) { + AsyncChannel channel = SyncToAsyncChannel.adapt(c,ex); + return configure(channel,format,options); + } + public static Transport configure(AsyncChannel channel, WireFormat format, Map options) { + + ActivityMonitor activityMonitor = new ActivityMonitor(channel); + channel = new PacketAggregatingAsyncChannel(activityMonitor); + AsyncChannelToAsyncCommandChannel commandChannel = new AsyncChannelToAsyncCommandChannel(channel,format); + + // Flexible but dangerous! Allows you to configure all the properties of the socket via the URI! + SocketMetadata socketMetadata = (SocketMetadata)commandChannel.getAdapter(SocketMetadata.class); + if( socketMetadata !=null ) { + IntrospectionSupport.setProperties(socketMetadata, options); + } + + ActiveIOTransport activeIOTransport = new ActiveIOTransport(commandChannel); + IntrospectionSupport.setProperties(activeIOTransport, options); + activeIOTransport.setReadCounter(activityMonitor.getReadCounter()); + activeIOTransport.setWriteCounter(activityMonitor.getWriteCounter()); + + Transport transport = activeIOTransport; + if( activeIOTransport.isTrace() ) { + transport = new TransportLogger(transport); + } + transport = new InactivityMonitor(transport, activeIOTransport.getMaxInactivityDuration(), activityMonitor.getReadCounter(), activityMonitor.getWriteCounter()); + transport = new WireFormatNegotiator(transport,format, activeIOTransport.getMinmumWireFormatVersion()); + transport = new MutexTransport(transport); + transport = new ResponseCorrelator(transport); + return transport; + } + + public static Transport compositeConnect(URI location, WireFormat format, Map options) throws IOException { + return compositeConfigure(new ChannelFactory().openAsyncChannel(location), format, options); + } + + public static Transport compositeConnect(URI location, WireFormat format, Map options,Executor ex) throws IOException { + return compositeConfigure(new ChannelFactory().openAsyncChannel(location), format, options,ex); + } + + public static Transport compositeConfigure(Channel c, org.activeio.command.WireFormat format, Map options) { + AsyncChannel channel = SyncToAsyncChannel.adapt(c); + return compositeConfigure(channel, format, options); + } + + public static Transport compositeConfigure(Channel c, org.activeio.command.WireFormat format, Map options,Executor ex) { + AsyncChannel channel = SyncToAsyncChannel.adapt(c,ex); + return compositeConfigure(channel, format, options); + } + + + public static Transport compositeConfigure(AsyncChannel channel, org.activeio.command.WireFormat format, Map options) { + ActivityMonitor activityMonitor = new ActivityMonitor(channel); + channel = new PacketAggregatingAsyncChannel(activityMonitor); + AsyncChannelToAsyncCommandChannel commandChannel = new AsyncChannelToAsyncCommandChannel(channel,format); + + // Flexible but dangerous! Allows you to configure all the properties of the socket via the URI! + SocketMetadata socketMetadata = (SocketMetadata)commandChannel.getAdapter(SocketMetadata.class); + if( socketMetadata !=null ) { + IntrospectionSupport.setProperties(socketMetadata, options, "socket."); + } + + ActiveIOTransport activeIOTransport = new ActiveIOTransport(commandChannel); + IntrospectionSupport.setProperties(activeIOTransport, options); + activeIOTransport.setReadCounter(activityMonitor.getReadCounter()); + activeIOTransport.setWriteCounter(activityMonitor.getWriteCounter()); + + Transport transport = activeIOTransport; + if( activeIOTransport.isTrace() ) { + transport = new TransportLogger(transport); + } + transport = new InactivityMonitor(transport, activeIOTransport.getMaxInactivityDuration(), activityMonitor.getReadCounter(), activityMonitor.getWriteCounter()); + transport = new WireFormatNegotiator(transport,format, activeIOTransport.getMinmumWireFormatVersion()); + return transport; + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/activeio/ActiveIOTransportServer.java b/activemq-core/src/main/java/org/activemq/transport/activeio/ActiveIOTransportServer.java new file mode 100755 index 0000000000..6c62263717 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/activeio/ActiveIOTransportServer.java @@ -0,0 +1,119 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.activeio; + +import java.io.IOException; +import java.net.URI; +import java.util.Map; + +import org.activeio.AcceptListener; +import org.activeio.AsyncChannelServer; +import org.activeio.Channel; +import org.activeio.ChannelFactory; +import org.activeio.command.WireFormat; +import org.activeio.command.WireFormatFactory; +import org.activemq.ThreadPriorities; +import org.activemq.command.BrokerInfo; +import org.activemq.openwire.OpenWireFormatFactory; +import org.activemq.transport.TransportAcceptListener; +import org.activemq.transport.TransportServer; + +import edu.emory.mathcs.backport.java.util.concurrent.Executor; +import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor; +import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory; + +public class ActiveIOTransportServer implements TransportServer { + + private AsyncChannelServer server; + private TransportAcceptListener acceptListener; + private WireFormatFactory wireFormatFactory = new OpenWireFormatFactory(); + private long stopTimeout = 2000; + static protected final Executor BROKER_CONNECTION_EXECUTOR = new ScheduledThreadPoolExecutor(5, new ThreadFactory() { + public Thread newThread(Runnable run) { + Thread thread = new Thread(run); + thread.setPriority(ThreadPriorities.INBOUND_BROKER_CONNECTION); + return thread; + } + }); + + /** + * @param location + * @throws IOException + */ + public ActiveIOTransportServer(URI location, final Map options) throws IOException { + server = new ChannelFactory().bindAsyncChannel(location); + server.setAcceptListener(new AcceptListener(){ + public void onAccept(Channel c) { + if( acceptListener==null ) { + c.dispose(); + } else { + WireFormat format = (WireFormat) wireFormatFactory.createWireFormat(); + acceptListener.onAccept( ActiveIOTransportFactory.configure(c, format, options,BROKER_CONNECTION_EXECUTOR)); + } + } + public void onAcceptError(IOException error) { + if( acceptListener!=null ) { + acceptListener.onAcceptError(error); + } + } + }); + } + + public void setAcceptListener(TransportAcceptListener acceptListener) { + this.acceptListener = acceptListener; + } + + public void start() throws Exception { + server.start(); + } + + public void stop() throws Exception { + server.stop(stopTimeout); + server.dispose(); + } + + public URI getConnectURI() { + return server.getConnectURI(); + } + + public URI getBindURI() { + return server.getBindURI(); + } + + public WireFormatFactory getWireFormatFactory() { + return wireFormatFactory; + } + + public void setWireFormatFactory(WireFormatFactory wireFormatFactory) { + this.wireFormatFactory = wireFormatFactory; + } + + public void setBrokerInfo(BrokerInfo brokerInfo) { + } + + public long getStopTimeout() { + return stopTimeout; + } + + public void setStopTimeout(long stopTimeout) { + this.stopTimeout = stopTimeout; + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/activeio/ActivityMonitor.java b/activemq-core/src/main/java/org/activemq/transport/activeio/ActivityMonitor.java new file mode 100755 index 0000000000..36dea4f707 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/activeio/ActivityMonitor.java @@ -0,0 +1,60 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.activeio; + +import java.io.IOException; + +import org.activeio.AsyncChannel; +import org.activeio.FilterAsyncChannel; +import org.activeio.Packet; +import org.activemq.management.CountStatisticImpl; + +/** + * Used to make sure that commands are arriving periodically from the peer of the transport. + * + * @version $Revision$ + */ +public class ActivityMonitor extends FilterAsyncChannel { + + final CountStatisticImpl writeCounter = new CountStatisticImpl("writeCounter", "The number of bytes written to the transport"); + final CountStatisticImpl readCounter = new CountStatisticImpl("readCoutner", "The number bytes written to the transport"); + + public ActivityMonitor(AsyncChannel next) { + super(next); + } + + public void onPacket(Packet packet) { + readCounter.add(packet.remaining()); + super.onPacket(packet); + } + + public void write(Packet packet) throws IOException { + writeCounter.add(packet.remaining()); + super.write(packet); + } + + public CountStatisticImpl getWriteCounter() { + return writeCounter; + } + + public CountStatisticImpl getReadCounter() { + return readCounter; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/activeio/PacketAggregatingAsyncChannel.java b/activemq-core/src/main/java/org/activemq/transport/activeio/PacketAggregatingAsyncChannel.java new file mode 100644 index 0000000000..ef08abee9c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/activeio/PacketAggregatingAsyncChannel.java @@ -0,0 +1,112 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activemq.transport.activeio; + +import java.io.IOException; + +import org.activeio.AsyncChannel; +import org.activeio.FilterAsyncChannel; +import org.activeio.Packet; +import org.activeio.PacketData; +import org.activeio.packet.AppendedPacket; +import org.activeio.packet.EOSPacket; + +/** + * This PacketAggregatingAsyncChannel can be used when the client is sending a + * 'record' style packet down the channel stack and needs receiving end to + * receive the same 'record' packets. + * + * This is very useful since in general, a channel does not grantee that a + * Packet that is sent down will not be fragmented or combined with other Packet + * objects. + * + * This {@see org.activeio.AsyncChannel} adds a 4 byte header + * to each packet that is sent down. + * + * @version $Revision$ + */ +final public class PacketAggregatingAsyncChannel extends FilterAsyncChannel { + + private static final int HEADER_LENGTH = 4; + + Packet incompleteUpPacket; + boolean headerLoaded; + private int upPacketLength; + + public PacketAggregatingAsyncChannel(AsyncChannel next) { + super(next); + } + + public void onPacket(Packet packet) { + + try { + // Pass through the EOS packet. + if( packet == EOSPacket.EOS_PACKET ) { + channelListener.onPacket(packet); + return; + } + + if (incompleteUpPacket != null) { + packet = AppendedPacket.join(incompleteUpPacket, packet); + incompleteUpPacket = null; + } + + while (true) { + + if (!headerLoaded) { + headerLoaded = packet.remaining() >= HEADER_LENGTH; + if( headerLoaded ) { + int pos = packet.position(); + upPacketLength = PacketData.readIntBig(packet); + packet.position(pos); + + if( upPacketLength < 0 ) { + throw new IOException("Up packet length was invalid: "+upPacketLength); + } + upPacketLength+=4; + } + if( !headerLoaded ) + break; + } + + if (packet.remaining() < upPacketLength ) + break; + + // Get ready to create a slice to send up. + int origLimit = packet.limit(); + packet.limit(upPacketLength); + channelListener.onPacket(packet.slice()); + + // Get a slice of the remaining since that will dump + // the first packets of an AppendedPacket + packet.position(upPacketLength); + packet.limit(origLimit); + packet = packet.slice(); + + // Need to load a header again now. + headerLoaded = false; + } + if (packet.hasRemaining()) { + incompleteUpPacket = packet; + } + } catch (IOException e) { + channelListener.onPacketError(e); + } + + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/transport/activeio/package.html b/activemq-core/src/main/java/org/activemq/transport/activeio/package.html new file mode 100755 index 0000000000..41e4cb666b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/activeio/package.html @@ -0,0 +1,9 @@ + + + + + +ActiveIO based Transport implementation. + + + diff --git a/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryAgent.java b/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryAgent.java new file mode 100755 index 0000000000..a962b201ad --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryAgent.java @@ -0,0 +1,57 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.discovery; + +import java.io.IOException; + +import javax.jms.JMSException; + +import org.activemq.Service; + +/** + * An agent used to discover other instances of a service. + * + * We typically use a discovery agent to auto-discover JMS clients and JMS brokers on a network + * + * @version $Revision$ + */ +public interface DiscoveryAgent extends Service { + + /** + * Sets the discovery listener + * @param listener + */ + public void setDiscoveryListener(DiscoveryListener listener); + + /** + * register a service + * @param name + * @param details + * @throws JMSException + */ + void registerService(String name) throws IOException; + + + String getGroup(); + + void setGroup(String group); + + public void setBrokerName(String brokerName); + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryAgentFactory.java b/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryAgentFactory.java new file mode 100755 index 0000000000..e565b224b0 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryAgentFactory.java @@ -0,0 +1,79 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.discovery; + +import java.io.IOException; +import java.net.URI; + +import org.activeio.FactoryFinder; +import org.activemq.util.IOExceptionSupport; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +public abstract class DiscoveryAgentFactory { + + static final private FactoryFinder discoveryAgentFinder = new FactoryFinder("META-INF/services/org/activemq/transport/discoveryagent/"); + static final private ConcurrentHashMap discoveryAgentFactorys = new ConcurrentHashMap(); + + /** + * @param uri + * @return + * @throws IOException + */ + private static DiscoveryAgentFactory findDiscoveryAgentFactory(URI uri) throws IOException { + String scheme = uri.getScheme(); + if( scheme == null ) + throw new IOException("DiscoveryAgent scheme not specified: [" + uri + "]"); + DiscoveryAgentFactory daf = (DiscoveryAgentFactory) discoveryAgentFactorys.get(scheme); + if (daf == null) { + // Try to load if from a META-INF property. + try { + daf = (DiscoveryAgentFactory) discoveryAgentFinder.newInstance(scheme); + discoveryAgentFactorys.put(scheme, daf); + } + catch (Throwable e) { + throw IOExceptionSupport.create("DiscoveryAgent scheme NOT recognized: [" + scheme + "]", e); + } + } + return daf; + } + + public static DiscoveryAgent createDiscoveryAgent(URI uri) throws IOException { + DiscoveryAgentFactory tf = findDiscoveryAgentFactory(uri); + return tf.doCreateDiscoveryAgent(uri); + + } + + abstract protected DiscoveryAgent doCreateDiscoveryAgent(URI uri) throws IOException; +// { +// try { +// String type = ( uri.getScheme() == null ) ? uri.getPath() : uri.getScheme(); +// DiscoveryAgent rc = (DiscoveryAgent) discoveryAgentFinder.newInstance(type); +// Map options = URISupport.parseParamters(uri); +// IntrospectionSupport.setProperties(rc, options); +// if( rc.getClass() == SimpleDiscoveryAgent.class ) { +// CompositeData data = URISupport.parseComposite(uri); +// ((SimpleDiscoveryAgent)rc).setServices(data.getComponents()); +// } +// return rc; +// } catch (Throwable e) { +// throw IOExceptionSupport.create("Could not create discovery agent: "+uri, e); +// } +// } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryListener.java b/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryListener.java new file mode 100755 index 0000000000..7fd6361fbb --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryListener.java @@ -0,0 +1,33 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.discovery; + +import org.activemq.command.DiscoveryEvent; + +/** + * A listener of services being added or removed from a network + * + * @version $Revision$ + */ +public interface DiscoveryListener { + + public void onServiceAdd(DiscoveryEvent event); + public void onServiceRemove(DiscoveryEvent event); + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryTransport.java b/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryTransport.java new file mode 100755 index 0000000000..57432ef13a --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryTransport.java @@ -0,0 +1,110 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.discovery; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activemq.command.DiscoveryEvent; +import org.activemq.transport.CompositeTransport; +import org.activemq.transport.TransportFilter; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +/** + * A {@link ReliableTransportChannel} which uses a {@link DiscoveryAgent} to + * discover remote broker instances and dynamically connect to them. + * + * @version $Revision$ + */ +public class DiscoveryTransport extends TransportFilter implements DiscoveryListener { + + private static final Log log = LogFactory.getLog(DiscoveryTransport.class); + + private final CompositeTransport next; + private DiscoveryAgent discoveryAgent; + private final ConcurrentHashMap serviceURIs = new ConcurrentHashMap(); + + public DiscoveryTransport(CompositeTransport next) { + super(next); + this.next = next; + } + + public void start() throws Exception { + if (discoveryAgent == null) { + throw new IllegalStateException("discoveryAgent not configured"); + } + + // lets pass into the agent the broker name and connection details + discoveryAgent.setDiscoveryListener(this); + discoveryAgent.start(); + next.start(); + } + + public void stop() throws Exception { + IOException firstError = null; + try { + discoveryAgent.stop(); + } catch (IOException e) { + firstError = e; + } + try { + next.stop(); + } catch (IOException e) { + if (firstError != null) + firstError = e; + } + if (firstError != null) { + throw firstError; + } + } + + public void onServiceAdd(DiscoveryEvent event) { + String url = event.getServiceName(); + if (url != null) { + try { + URI uri = new URI(url); + serviceURIs.put(event.getServiceName(), uri); + log.info("Adding new broker connection URL: " + uri ); + next.add(new URI[]{uri}); + } catch (URISyntaxException e) { + log.warn("Could not connect to remote URI: " + url + " due to bad URI syntax: " + e, e); + } + } + } + + public void onServiceRemove(DiscoveryEvent event) { + URI uri = (URI) serviceURIs.get(event.getServiceName()); + if (uri != null) { + next.remove(new URI[]{uri}); + } + } + + public DiscoveryAgent getDiscoveryAgent() { + return discoveryAgent; + } + + public void setDiscoveryAgent(DiscoveryAgent discoveryAgent) { + this.discoveryAgent = discoveryAgent; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryTransportFactory.java b/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryTransportFactory.java new file mode 100755 index 0000000000..656363b853 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/discovery/DiscoveryTransportFactory.java @@ -0,0 +1,79 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.discovery; + +import java.io.IOException; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import org.activemq.transport.Transport; +import org.activemq.transport.TransportServer; +import org.activemq.transport.failover.FailoverTransportFactory; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.URISupport.CompositeData; + +/** + * @version $Revision$ + */ +public class DiscoveryTransportFactory extends FailoverTransportFactory { + + public Transport createTransport(CompositeData compositData) throws IOException { + Map parameters = new HashMap(compositData.getParameters()); + DiscoveryTransport transport = new DiscoveryTransport(createTransport(parameters)); + + DiscoveryAgent discoveryAgent = DiscoveryAgentFactory.createDiscoveryAgent(compositData.getComponents()[0]); + transport.setDiscoveryAgent(discoveryAgent); + IntrospectionSupport.setProperties(transport,parameters); + + return transport; + } + + public TransportServer doBind(String brokerId,URI location) throws IOException{ + throw new IOException("Invalid server URI: "+location); +// try{ +// CompositeData compositData=URISupport.parseComposite(location); +// URI[] components=compositData.getComponents(); +// if(components.length!=1){ +// throw new IOException("Invalid location: "+location +// +", the location must have 1 and only 1 composite URI in it - components = " +// +components.length); +// } +// Map parameters=new HashMap(compositData.getParameters()); +// DiscoveryTransportServer server=new DiscoveryTransportServer(TransportFactory.bind(brokerId,components[0])); +// IntrospectionSupport.setProperties(server,parameters,"discovery"); +// DiscoveryAgent discoveryAgent=DiscoveryAgentFactory.createDiscoveryAgent(server.getDiscovery()); +// // Use the host name to configure the group of the discovery agent. +// if(!parameters.containsKey("discovery.group")){ +// if(compositData.getHost()!=null){ +// parameters.put("discovery.group",compositData.getHost()); +// } +// } +// if(!parameters.containsKey("discovery.brokerName")){ +// parameters.put("discovery.brokerName",brokerId); +// } +// IntrospectionSupport.setProperties(discoveryAgent,parameters,"discovery."); +// server.setDiscoveryAgent(discoveryAgent); +// return server; +// }catch(URISyntaxException e){ +// throw new IOException("Invalid location: "+location); +// } + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/transport/discovery/multicast/MulticastDiscoveryAgent.java b/activemq-core/src/main/java/org/activemq/transport/discovery/multicast/MulticastDiscoveryAgent.java new file mode 100755 index 0000000000..09e944fd83 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/discovery/multicast/MulticastDiscoveryAgent.java @@ -0,0 +1,351 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.transport.discovery.multicast; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.MulticastSocket; +import java.net.SocketAddress; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.util.Iterator; +import java.util.Map; +import org.activemq.command.DiscoveryEvent; +import org.activemq.transport.discovery.DiscoveryAgent; +import org.activemq.transport.discovery.DiscoveryListener; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicLong; +/** + * A {@link DiscoveryAgent} using Zeroconf via the jmDNS library + * + * @version $Revision$ + */ +public class MulticastDiscoveryAgent implements DiscoveryAgent,Runnable{ + private static final Log log=LogFactory.getLog(MulticastDiscoveryAgent.class); + public static final String DEFAULT_DISCOVERY_URI_STRING="multicast://224.1.2.3:6155"; + private static final String TYPE_SUFFIX="ActiveMQ-4."; + private static final String ALIVE="alive."; + private static final String DEAD="dead."; + private static final String DELIMITER = "%"; + private static final int BUFF_SIZE=8192; + private static final int DEFAULT_IDLE_TIME=500; + private static final int HEARTBEAT_MISS_BEFORE_DEATH=4; + private int timeToLive=1; + private boolean loopBackMode=false; + private Map services=new ConcurrentHashMap(); + private Map brokers = new ConcurrentHashMap(); + private String group="default"; + private String brokerName; + private URI discoveryURI; + private InetAddress inetAddress; + private SocketAddress sockAddress; + private DiscoveryListener discoveryListener; + private String selfService; + private MulticastSocket mcast; + private Thread runner; + private long keepAliveInterval=DEFAULT_IDLE_TIME; + private long lastAdvertizeTime=0; + private AtomicBoolean started=new AtomicBoolean(false); + + /** + * Set the discovery listener + * + * @param listener + */ + public void setDiscoveryListener(DiscoveryListener listener){ + this.discoveryListener=listener; + } + + /** + * register a service + */ + public void registerService(String name) throws IOException{ + this.selfService=name; + if (started.get()){ + doAdvertizeSelf(); + } + } + + /** + * Get the group used for discovery + * + * @return the group + */ + public String getGroup(){ + return group; + } + + /** + * Set the group for discovery + * + * @param group + */ + public void setGroup(String group){ + this.group=group; + } + + /** + * @return Returns the brokerName. + */ + public String getBrokerName(){ + return brokerName; + } + + /** + * @param brokerName The brokerName to set. + */ + public void setBrokerName(String brokerName){ + if (brokerName != null){ + brokerName = brokerName.replace('.','-'); + brokerName = brokerName.replace(':','-'); + brokerName = brokerName.replace('%','-'); + this.brokerName=brokerName; + } + } + + /** + * @return Returns the loopBackMode. + */ + public boolean isLoopBackMode(){ + return loopBackMode; + } + + /** + * @param loopBackMode + * The loopBackMode to set. + */ + public void setLoopBackMode(boolean loopBackMode){ + this.loopBackMode=loopBackMode; + } + + /** + * @return Returns the timeToLive. + */ + public int getTimeToLive(){ + return timeToLive; + } + + /** + * @param timeToLive + * The timeToLive to set. + */ + public void setTimeToLive(int timeToLive){ + this.timeToLive=timeToLive; + } + + /** + * @return the discoveryURI + */ + public URI getDiscoveryURI(){ + return discoveryURI; + } + + /** + * Set the discoveryURI + * + * @param discoveryURI + */ + public void setDiscoveryURI(URI discoveryURI){ + this.discoveryURI=discoveryURI; + } + + public long getKeepAliveInterval(){ + return keepAliveInterval; + } + + public void setKeepAliveInterval(long keepAliveInterval){ + this.keepAliveInterval=keepAliveInterval; + } + + /** + * start the discovery agent + * + * @throws Exception + */ + public void start() throws Exception{ + if(started.compareAndSet(false,true)){ + if(group==null|| group.length()==0){ + throw new IOException("You must specify a group to discover"); + } + if (brokerName == null || brokerName.length()==0){ + log.warn("brokerName not set"); + } + String type=getType(); + if(!type.endsWith(".")){ + log.warn("The type '"+type+"' should end with '.' to be a valid Discovery type"); + type+="."; + } + if(discoveryURI==null){ + discoveryURI=new URI(DEFAULT_DISCOVERY_URI_STRING); + } + this.inetAddress=InetAddress.getByName(discoveryURI.getHost()); + this.sockAddress=new InetSocketAddress(this.inetAddress,discoveryURI.getPort()); + mcast=new MulticastSocket(discoveryURI.getPort()); + mcast.setLoopbackMode(loopBackMode); + mcast.setTimeToLive(getTimeToLive()); + mcast.joinGroup(inetAddress); + mcast.setSoTimeout((int) keepAliveInterval); + runner=new Thread(this); + runner.setName("MulticastDiscovery: "+selfService); + runner.setDaemon(true); + runner.start(); + doAdvertizeSelf(); + } + } + + /** + * stop the channel + * + * @throws Exception + */ + public void stop() throws Exception{ + if(started.compareAndSet(true,false)){ + doAdvertizeSelf(); + mcast.close(); + } + } + + public String getType(){ + return group+"."+TYPE_SUFFIX; + } + + public void run(){ + byte[] buf=new byte[BUFF_SIZE]; + DatagramPacket packet=new DatagramPacket(buf,0,buf.length); + while(started.get()){ + doTimeKeepingServices(); + try{ + mcast.receive(packet); + if(packet.getLength()>0){ + String str=new String(packet.getData(),packet.getOffset(),packet.getLength()); + processData(str); + } + }catch(SocketTimeoutException se){ + // ignore + }catch(IOException e){ + log.error("failed to process packet: "+e); + } + } + } + + private void processData(String str){ + if (discoveryListener != null){ + if(str.startsWith(getType())){ + String payload=str.substring(getType().length()); + if(payload.startsWith(ALIVE)){ + String brokerName=getBrokerName(payload.substring(ALIVE.length())); + String service=payload.substring(ALIVE.length()+brokerName.length()+2); + if(!brokerName.equals(this.brokerName)){ + processAlive(brokerName,service); + } + }else{ + String brokerName=getBrokerName(payload.substring(DEAD.length())); + String service=payload.substring(DEAD.length()+brokerName.length()+2); + if(!brokerName.equals(this.brokerName)){ + processDead(brokerName,service); + } + } + } + } + } + + private void doTimeKeepingServices(){ + if(started.get()){ + long currentTime=System.currentTimeMillis(); + if((currentTime-keepAliveInterval)>lastAdvertizeTime){ + doAdvertizeSelf(); + lastAdvertizeTime = currentTime; + } + doExpireOldServices(); + } + } + + private void doAdvertizeSelf(){ + if(selfService!=null){ + String payload=getType(); + payload+=started.get()?ALIVE:DEAD; + payload+=DELIMITER+brokerName+DELIMITER; + payload+=selfService; + try{ + byte[] data=payload.getBytes(); + DatagramPacket packet=new DatagramPacket(data,0,data.length,sockAddress); + mcast.send(packet); + }catch(IOException e){ + log.error("Failed to advertise our service: "+payload,e); + } + } + } + + private void processAlive(String brokerName,String service){ + if(selfService == null || !service.equals(selfService)){ + AtomicLong lastKeepAlive=(AtomicLong) services.get(service); + if(lastKeepAlive==null){ + lastKeepAlive=new AtomicLong(System.currentTimeMillis()); + services.put(service,lastKeepAlive); + brokers.put(service, brokerName); + if(discoveryListener!=null){ + DiscoveryEvent event=new DiscoveryEvent(service); + event.setBrokerName(brokerName); + discoveryListener.onServiceAdd(event); + } + doAdvertizeSelf(); + + } + lastKeepAlive.set(System.currentTimeMillis()); + } + } + + private void processDead(String brokerName,String service){ + if(!service.equals(selfService)){ + if(services.remove(service)!=null){ + brokers.remove(service); + if(discoveryListener!=null){ + DiscoveryEvent event=new DiscoveryEvent(service); + event.setBrokerName(brokerName); + discoveryListener.onServiceRemove(event); + } + } + } + } + + private void doExpireOldServices(){ + long expireTime=System.currentTimeMillis()-(keepAliveInterval*HEARTBEAT_MISS_BEFORE_DEATH); + for(Iterator i=services.entrySet().iterator();i.hasNext();){ + Map.Entry entry=(Map.Entry) i.next(); + AtomicLong lastHeartBeat=(AtomicLong) entry.getValue(); + if(lastHeartBeat.get()= 0 ){ + int end = str.indexOf(DELIMITER,start+1); + result=str.substring(start+1, end); + } + return result; + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/discovery/multicast/MulticastDiscoveryAgentFactory.java b/activemq-core/src/main/java/org/activemq/transport/discovery/multicast/MulticastDiscoveryAgentFactory.java new file mode 100755 index 0000000000..791ef01ca9 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/discovery/multicast/MulticastDiscoveryAgentFactory.java @@ -0,0 +1,46 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.discovery.multicast; + +import java.io.IOException; +import java.net.URI; +import java.util.Map; + +import org.activemq.transport.discovery.DiscoveryAgent; +import org.activemq.transport.discovery.DiscoveryAgentFactory; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.URISupport; + +public class MulticastDiscoveryAgentFactory extends DiscoveryAgentFactory { + + protected DiscoveryAgent doCreateDiscoveryAgent(URI uri) throws IOException { + try { + + Map options = URISupport.parseParamters(uri); + MulticastDiscoveryAgent rc = new MulticastDiscoveryAgent(); + rc.setGroup(uri.getHost()); + IntrospectionSupport.setProperties(rc, options); + return rc; + + } catch (Throwable e) { + throw IOExceptionSupport.create("Could not create discovery agent: " + uri, e); + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/discovery/package.html b/activemq-core/src/main/java/org/activemq/transport/discovery/package.html new file mode 100755 index 0000000000..da756a5586 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/discovery/package.html @@ -0,0 +1,9 @@ + + + + + +Discovery mechanism to discover brokers and clients. + + + diff --git a/activemq-core/src/main/java/org/activemq/transport/discovery/rendezvous/JmDNSFactory.java b/activemq-core/src/main/java/org/activemq/transport/discovery/rendezvous/JmDNSFactory.java new file mode 100644 index 0000000000..2d5cd67d8e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/discovery/rendezvous/JmDNSFactory.java @@ -0,0 +1,45 @@ +package org.activemq.transport.discovery.rendezvous; + +import java.io.IOException; +import java.net.InetAddress; +import java.util.HashMap; +import java.util.Map; +import javax.jmdns.JmDNS; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +public class JmDNSFactory { + + static Map registry = new HashMap(); + static class UsageTracker { + AtomicInteger count = new AtomicInteger(0); + JmDNS jmDNS; + } + + static synchronized JmDNS create(final InetAddress address) throws IOException { + UsageTracker tracker = (UsageTracker)registry.get(address); + if( tracker == null ) { + tracker = new UsageTracker(); + tracker.jmDNS = new JmDNS(address) { + public void close() { + if( onClose(address) ) { + super.close(); + } + } + }; + registry.put(address, tracker); + } + tracker.count.incrementAndGet(); + return tracker.jmDNS; + } + + static synchronized boolean onClose(InetAddress address){ + UsageTracker tracker=(UsageTracker) registry.get(address); + if(tracker!=null){ + if(tracker.count.decrementAndGet()==0){ + registry.remove(address); + return true; + } + } + return false; + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/discovery/rendezvous/RendezvousDiscoveryAgent.java b/activemq-core/src/main/java/org/activemq/transport/discovery/rendezvous/RendezvousDiscoveryAgent.java new file mode 100755 index 0000000000..6d79247544 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/discovery/rendezvous/RendezvousDiscoveryAgent.java @@ -0,0 +1,240 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.discovery.rendezvous; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.jmdns.JmDNS; +import javax.jmdns.ServiceEvent; +import javax.jmdns.ServiceInfo; +import javax.jmdns.ServiceListener; + +import org.activemq.command.DiscoveryEvent; +import org.activemq.transport.discovery.DiscoveryAgent; +import org.activemq.transport.discovery.DiscoveryListener; +import org.activemq.util.JMSExceptionSupport; +import org.activemq.util.MapHelper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + +/** + * A {@link DiscoveryAgent} using Zeroconf + * via the jmDNS library + * + * @version $Revision$ + */ +public class RendezvousDiscoveryAgent implements DiscoveryAgent, ServiceListener { + private static final Log log = LogFactory.getLog(RendezvousDiscoveryAgent.class); + + private static final String TYPE_SUFFIX = "ActiveMQ-4."; + + private JmDNS jmdns; + private InetAddress localAddress; + private String localhost; + private int weight = 0; + private int priority = 0; + + private DiscoveryListener listener; + private String group = "default"; + private final CopyOnWriteArrayList serviceInfos = new CopyOnWriteArrayList(); + + private String brokerName; + + // DiscoveryAgent interface + //------------------------------------------------------------------------- + public void start() throws Exception { + if (group == null) { + throw new IOException("You must specify a group to discover"); + } + String type = getType(); + if (!type.endsWith(".")) { + log.warn("The type '" + type + "' should end with '.' to be a valid Rendezvous type"); + type += "."; + } + try { + if (jmdns == null) { + jmdns = createJmDNS(); + } + if (listener!=null) { + log.info("Discovering service of type: " +type); + jmdns.addServiceListener(type, this); + } + } + catch (IOException e) { + JMSExceptionSupport.create("Failed to start JmDNS service: " + e, e); + } + } + + public void stop() { + if( jmdns!=null ) { + for (Iterator iter = serviceInfos.iterator(); iter.hasNext();) { + ServiceInfo si = (ServiceInfo) iter.next(); + jmdns.unregisterService(si); + } + + // Close it down async since this could block for a while. + final JmDNS closeTarget = jmdns; + Thread thread = new Thread() { + public void run() { + closeTarget.close(); + } + }; + + thread.setDaemon(true); + thread.start(); + + jmdns=null; + } + } + + public void registerService(String name) throws IOException { + if (jmdns == null) { + throw new IllegalStateException("Not started."); + } + ServiceInfo si = createServiceInfo(name, new HashMap()); + serviceInfos.add(si); + jmdns.registerService(si); + } + + + // ServiceListener interface + //------------------------------------------------------------------------- + public void addService(JmDNS jmDNS, String type, String name) { + if (log.isDebugEnabled()) { + log.debug("addService with type: " + type + " name: " + name); + } + if( listener!=null ) + listener.onServiceAdd(new DiscoveryEvent(name)); + jmDNS.requestServiceInfo(type, name); + } + + public void removeService(JmDNS jmDNS, String type, String name) { + if (log.isDebugEnabled()) { + log.debug("removeService with type: " + type + " name: " + name); + } + if( listener!=null ) + listener.onServiceRemove(new DiscoveryEvent(name)); + } + + public void serviceAdded(ServiceEvent event) { + addService(event.getDNS(), event.getType(), event.getName()); + } + public void serviceRemoved(ServiceEvent event) { + removeService(event.getDNS(), event.getType(), event.getName()); + } + public void serviceResolved(ServiceEvent event) { + } + public void resolveService(JmDNS jmDNS, String type, String name, ServiceInfo serviceInfo) { + } + + public int getPriority() { + return priority; + } + + public void setPriority(int priority) { + this.priority = priority; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public JmDNS getJmdns() { + return jmdns; + } + + public void setJmdns(JmDNS jmdns) { + this.jmdns = jmdns; + } + + + public InetAddress getLocalAddress() throws UnknownHostException { + if (localAddress == null) { + localAddress = createLocalAddress(); + } + return localAddress; + } + + public void setLocalAddress(InetAddress localAddress) { + this.localAddress = localAddress; + } + + public String getLocalhost() { + return localhost; + } + + public void setLocalhost(String localhost) { + this.localhost = localhost; + } + + // Implementation methods + //------------------------------------------------------------------------- + protected ServiceInfo createServiceInfo(String name, Map map) { + int port = MapHelper.getInt(map, "port", 0); + + String type = getType(); + + if (log.isDebugEnabled()) { + log.debug("Registering service type: " + type + " name: " + name + " details: " + map); + } + return new ServiceInfo(type, name+"."+type, port, weight, priority, ""); + } + + protected JmDNS createJmDNS() throws IOException { + return JmDNSFactory.create(getLocalAddress()); + } + + protected InetAddress createLocalAddress() throws UnknownHostException { + if (localhost != null) { + return InetAddress.getByName(localhost); + } + return InetAddress.getLocalHost(); + } + + public void setDiscoveryListener(DiscoveryListener listener) { + this.listener = listener; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group=group; + } + + public String getType() { + return group+"."+TYPE_SUFFIX; + } + + public void setBrokerName(String brokerName) { + this.brokerName = brokerName; + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/discovery/rendezvous/RendezvousDiscoveryAgentFactory.java b/activemq-core/src/main/java/org/activemq/transport/discovery/rendezvous/RendezvousDiscoveryAgentFactory.java new file mode 100755 index 0000000000..bf33733772 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/discovery/rendezvous/RendezvousDiscoveryAgentFactory.java @@ -0,0 +1,45 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.discovery.rendezvous; + +import java.io.IOException; +import java.net.URI; +import java.util.Map; + +import org.activemq.transport.discovery.DiscoveryAgent; +import org.activemq.transport.discovery.DiscoveryAgentFactory; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.URISupport; + +public class RendezvousDiscoveryAgentFactory extends DiscoveryAgentFactory { + + protected DiscoveryAgent doCreateDiscoveryAgent(URI uri) throws IOException { + try { + Map options = URISupport.parseParamters(uri); + RendezvousDiscoveryAgent rc = new RendezvousDiscoveryAgent(); + rc.setGroup(uri.getHost()); + IntrospectionSupport.setProperties(rc, options); + return rc; + + } catch (Throwable e) { + throw IOExceptionSupport.create("Could not create discovery agent: " + uri, e); + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/discovery/rendezvous/package.html b/activemq-core/src/main/java/org/activemq/transport/discovery/rendezvous/package.html new file mode 100755 index 0000000000..f63b764848 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/discovery/rendezvous/package.html @@ -0,0 +1,12 @@ + + + + + +

+ A discovery agent using Zeroconf + via the jmDNS library +

+ + + diff --git a/activemq-core/src/main/java/org/activemq/transport/discovery/simple/SimpleDiscoveryAgent.java b/activemq-core/src/main/java/org/activemq/transport/discovery/simple/SimpleDiscoveryAgent.java new file mode 100755 index 0000000000..5758f2cf31 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/discovery/simple/SimpleDiscoveryAgent.java @@ -0,0 +1,85 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.discovery.simple; + +import java.io.IOException; +import java.net.URI; + +import org.activemq.command.DiscoveryEvent; +import org.activemq.transport.discovery.DiscoveryAgent; +import org.activemq.transport.discovery.DiscoveryListener; + +/** + * A simple DiscoveryAgent that allows static configuration of the discovered services. + * + * @version $Revision$ + */ +public class SimpleDiscoveryAgent implements DiscoveryAgent { + + private DiscoveryListener listener; + String services[] = new String[] {}; + String group = "DEFAULT"; + + public void setDiscoveryListener(DiscoveryListener listener) { + this.listener = listener; + } + + public void registerService(String name) throws IOException { + } + + public void start() throws Exception { + for (int i = 0; i < services.length; i++) { + listener.onServiceAdd(new DiscoveryEvent(services[i])); + } + } + + public void stop() throws Exception { + } + + public String[] getServices() { + return services; + } + + public void setServices(String services) { + this.services = services.split(","); + } + + public void setServices(String services[]) { + this.services = services; + } + + public void setServices(URI services[]) { + this.services = new String[services.length]; + for (int i = 0; i < services.length; i++) { + this.services[i] = services[i].toString(); + } + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public void setBrokerName(String brokerName) { + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/discovery/simple/SimpleDiscoveryAgentFactory.java b/activemq-core/src/main/java/org/activemq/transport/discovery/simple/SimpleDiscoveryAgentFactory.java new file mode 100755 index 0000000000..6fefc05098 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/discovery/simple/SimpleDiscoveryAgentFactory.java @@ -0,0 +1,51 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.discovery.simple; + +import java.io.IOException; +import java.net.URI; +import java.util.Map; + +import org.activemq.transport.discovery.DiscoveryAgent; +import org.activemq.transport.discovery.DiscoveryAgentFactory; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.URISupport; +import org.activemq.util.URISupport.CompositeData; + +public class SimpleDiscoveryAgentFactory extends DiscoveryAgentFactory { + + protected DiscoveryAgent doCreateDiscoveryAgent(URI uri) throws IOException { + try { + + CompositeData data = URISupport.parseComposite(uri); + Map options = URISupport.parseParamters(uri); + + SimpleDiscoveryAgent rc = new SimpleDiscoveryAgent(); + rc.setGroup(uri.getHost()); + IntrospectionSupport.setProperties(rc, options); + rc.setServices(data.getComponents()); + + return rc; + + } catch (Throwable e) { + throw IOExceptionSupport.create("Could not create discovery agent: " + uri, e); + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/failover/FailoverTransport.java b/activemq-core/src/main/java/org/activemq/transport/failover/FailoverTransport.java new file mode 100755 index 0000000000..16a8fdb9cf --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/failover/FailoverTransport.java @@ -0,0 +1,399 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.failover; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Random; + +import org.activemq.command.Command; +import org.activemq.command.Response; +import org.activemq.state.ConnectionStateTracker; +import org.activemq.thread.DefaultThreadPools; +import org.activemq.thread.Task; +import org.activemq.thread.TaskRunner; +import org.activemq.transport.CompositeTransport; +import org.activemq.transport.FutureResponse; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportListener; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.ServiceSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + +/** + * A Transport that is made reliable by being able to fail over to another + * transport when a transport failure is detected. + * + * @version $Revision$ + */ +public class FailoverTransport implements CompositeTransport { + + private static final Log log = LogFactory.getLog(FailoverTransport.class); + + private TransportListener transportListener; + private boolean disposed; + private final CopyOnWriteArrayList uris = new CopyOnWriteArrayList(); + + private final Object reconnectMutex = new Object(); + private final ConnectionStateTracker stateTracker = new ConnectionStateTracker(); + private final ConcurrentHashMap requestMap = new ConcurrentHashMap(); + + private URI connectedTransportURI; + private Transport connectedTransport; + private final TaskRunner reconnectTask; + private boolean started; + + private long initialReconnectDelay = 10; + private long maxReconnectDelay = 1000 * 30; + private long backOffMultiplier = 2; + private boolean useExponentialBackOff = true; + private int maxReconnectAttempts; + private int connectFailures; + private long reconnectDelay = initialReconnectDelay; + private Exception connectionFailure; + + private final TransportListener myTransportListener = new TransportListener() { + public void onCommand(Command command) { + if (command.isResponse()) { + requestMap.remove(new Short(((Response) command).getCorrelationId())); + } + transportListener.onCommand(command); + } + + public void onException(IOException error) { + try { + handleTransportFailure(error); + } + catch (InterruptedException e) { + transportListener.onException(new InterruptedIOException()); + } + } + }; + + public FailoverTransport() throws InterruptedIOException { + + // Setup a task that is used to reconnect the a connection async. + reconnectTask = DefaultThreadPools.getDefaultTaskRunnerFactory().createTaskRunner(new Task() { + + public boolean iterate() { + + Exception failure=null; + synchronized (reconnectMutex) { + + if (disposed || connectionFailure!=null) { + reconnectMutex.notifyAll(); + } + + if (connectedTransport != null || disposed || connectionFailure!=null) { + return false; + } else { + ArrayList connectList = getConnectList(); + if( connectList.isEmpty() ) { + failure = new IOException("No uris available to connect to."); + } else { + Iterator iter = connectList.iterator(); + for (int i = 0; iter.hasNext() && connectedTransport == null && !disposed; i++) { + URI uri = (URI) iter.next(); + try { + log.debug("Attempting connect to: " + uri); + Transport t = TransportFactory.compositeConnect(uri); + t.setTransportListener(myTransportListener); + if (started) { + restoreTransport(t); + } + log.debug("Connection established"); + reconnectDelay = 10; + connectedTransportURI = uri; + connectedTransport = t; + reconnectMutex.notifyAll(); + connectFailures = 0; + return false; + } + catch (Exception e) { + failure = e; + log.debug("Connect fail to: " + uri + ", reason: " + e); + } + } + } + } + + if (maxReconnectAttempts > 0 && ++connectFailures >= maxReconnectAttempts) { + log.error("Failed to connect to transport after: " + connectFailures + " attempt(s)"); + connectionFailure = failure; + reconnectMutex.notifyAll(); + return false; + } + } + + + try { + log.debug("Waiting " + reconnectDelay + " ms before attempting connection. "); + Thread.sleep(reconnectDelay); + } + catch (InterruptedException e1) { + Thread.currentThread().interrupt(); + } + + if (useExponentialBackOff) { + // Exponential increment of reconnect delay. + reconnectDelay *= backOffMultiplier; + if (reconnectDelay > maxReconnectDelay) + reconnectDelay = maxReconnectDelay; + } + return true; + } + + }); + } + + private void handleTransportFailure(IOException e) throws InterruptedException { + synchronized (reconnectMutex) { + log.debug("Transport failed, starting up reconnect task", e); + if (connectedTransport != null) { + ServiceSupport.dispose(connectedTransport); + connectedTransport = null; + connectedTransportURI = null; + reconnectTask.wakeup(); + } + } + } + + public void start() throws Exception { + synchronized (reconnectMutex) { + log.debug("Started."); + if (started) + return; + started = true; + if (connectedTransport != null) { + connectedTransport.start(); + stateTracker.restore(connectedTransport); + } + } + } + + public void stop() throws Exception { + synchronized (reconnectMutex) { + log.debug("Stopped."); + if (!started) + return; + started = false; + disposed = true; + + if (connectedTransport != null) { + connectedTransport.stop(); + } + } + } + + public long getInitialReconnectDelay() { + return initialReconnectDelay; + } + + public void setInitialReconnectDelay(long initialReconnectDelay) { + this.initialReconnectDelay = initialReconnectDelay; + } + + public long getMaxReconnectDelay() { + return maxReconnectDelay; + } + + public void setMaxReconnectDelay(long maxReconnectDelay) { + this.maxReconnectDelay = maxReconnectDelay; + } + + public long getReconnectDelay() { + return reconnectDelay; + } + + public void setReconnectDelay(long reconnectDelay) { + this.reconnectDelay = reconnectDelay; + } + + public long getReconnectDelayExponent() { + return backOffMultiplier; + } + + public void setReconnectDelayExponent(long reconnectDelayExponent) { + this.backOffMultiplier = reconnectDelayExponent; + } + + public Transport getConnectedTransport() { + return connectedTransport; + } + + public URI getConnectedTransportURI() { + return connectedTransportURI; + } + + public int getMaxReconnectAttempts() { + return maxReconnectAttempts; + } + + public void setMaxReconnectAttempts(int maxReconnectAttempts) { + this.maxReconnectAttempts = maxReconnectAttempts; + } + + public void oneway(Command command) throws IOException { + Exception error = null; + try { + + synchronized (reconnectMutex) { + // Keep trying until the message is sent. + for (int i = 0;; i++) { + try { + + // Wait for transport to be connected. + while (connectedTransport == null && !disposed && connectionFailure==null ) { + log.debug("Waiting for transport to reconnect."); + reconnectMutex.wait(1000); + } + + if( connectedTransport==null ) { + // Previous loop may have exited due to use being + // disposed. + if (disposed) { + error = new IOException("Transport disposed."); + } else if (connectionFailure!=null) { + error = connectionFailure; + } else { + error = new IOException("Unexpected failure."); + } + break; + } + + // Send the message. + connectedTransport.oneway(command); + + // If it was a request and it was not being tracked by + // the state tracker, + // then hold it in the requestMap so that we can replay + // it later. + if (!stateTracker.track(command) && command.isResponseRequired()) { + requestMap.put(new Short(command.getCommandId()), command); + } + return; + + } + catch (IOException e) { + log.debug("Send oneway attempt: " + i + " failed."); + handleTransportFailure(e); + } + } + } + } + catch (InterruptedException e) { + // Some one may be trying to stop our thread. + throw new InterruptedIOException(); + } + if( error instanceof IOException ) + throw (IOException)error; + throw IOExceptionSupport.create(error); + } + + public FutureResponse asyncRequest(Command command) throws IOException { + throw new AssertionError("Unsupported Method"); + } + + public Response request(Command command) throws IOException { + throw new AssertionError("Unsupported Method"); + } + + public void add(URI u[]) { + for (int i = 0; i < u.length; i++) { + if( !uris.contains(u[i]) ) + uris.add(u[i]); + } + reconnect(); + } + + public void remove(URI u[]) { + for (int i = 0; i < u.length; i++) { + uris.remove(u[i]); + } + reconnect(); + } + + public void reconnect() { + log.debug("Waking up reconnect task"); + try { + reconnectTask.wakeup(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + private ArrayList getConnectList() { + ArrayList l = new ArrayList(uris); + + // Randomly, reorder the list by random swapping + Random r = new Random(); + r.setSeed(System.currentTimeMillis()); + for (int i = 0; i < l.size(); i++) { + int p = r.nextInt(l.size()); + Object t = l.get(p); + l.set(p, l.get(i)); + l.set(i, t); + } + return l; + } + + public void setTransportListener(TransportListener commandListener) { + this.transportListener = commandListener; + } + + public Object narrow(Class target) { + + if (target.isAssignableFrom(getClass())) { + return this; + } + synchronized (reconnectMutex) { + if (connectedTransport != null) { + return connectedTransport.narrow(target); + } + } + return null; + + } + + protected void restoreTransport(Transport t) throws Exception, IOException { + t.start(); + stateTracker.restore(t); + for (Iterator iter2 = requestMap.values().iterator(); iter2.hasNext();) { + Command command = (Command) iter2.next(); + t.oneway(command); + } + } + + public boolean isUseExponentialBackOff() { + return useExponentialBackOff; + } + + public void setUseExponentialBackOff(boolean useExponentialBackOff) { + this.useExponentialBackOff = useExponentialBackOff; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/failover/FailoverTransportFactory.java b/activemq-core/src/main/java/org/activemq/transport/failover/FailoverTransportFactory.java new file mode 100755 index 0000000000..ef3f08b574 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/failover/FailoverTransportFactory.java @@ -0,0 +1,77 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.failover; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; + +import org.activemq.transport.MutexTransport; +import org.activemq.transport.ResponseCorrelator; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportServer; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.URISupport; +import org.activemq.util.URISupport.CompositeData; + +public class FailoverTransportFactory extends TransportFactory { + + public Transport doConnect(URI location) throws IOException { + try { + Transport transport = createTransport(URISupport.parseComposite(location)); + transport = new MutexTransport(transport); + transport = new ResponseCorrelator(transport); + return transport; + } catch (URISyntaxException e) { + throw new IOException("Invalid location: "+location); + } + } + + public Transport doCompositeConnect(URI location) throws IOException { + try { + return createTransport(URISupport.parseComposite(location)); + } catch (URISyntaxException e) { + throw new IOException("Invalid location: "+location); + } + } + + /** + * @param location + * @return + * @throws IOException + */ + public Transport createTransport(CompositeData compositData) throws IOException { + FailoverTransport transport = createTransport(compositData.getParameters()); + transport.add(compositData.getComponents()); + return transport; + } + + public FailoverTransport createTransport(Map parameters) throws IOException { + FailoverTransport transport = new FailoverTransport(); + IntrospectionSupport.setProperties(transport, parameters); + return transport; + } + + public TransportServer doBind(String brokerId,URI location) throws IOException { + throw new IOException("Invalid server URI: "+location); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/failover/package.html b/activemq-core/src/main/java/org/activemq/transport/failover/package.html new file mode 100755 index 0000000000..b1f8971e9c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/failover/package.html @@ -0,0 +1,9 @@ + + + + + +Fail-Over Transport which will automatically reconnect to a failed transport and choose one of a list of possible transport implementations to use. + + + diff --git a/activemq-core/src/main/java/org/activemq/transport/fanout/FanoutTransport.java b/activemq-core/src/main/java/org/activemq/transport/fanout/FanoutTransport.java new file mode 100755 index 0000000000..8c651dc99c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/fanout/FanoutTransport.java @@ -0,0 +1,506 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.fanout; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Iterator; + +import org.activemq.command.Command; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.Response; +import org.activemq.state.ConnectionStateTracker; +import org.activemq.thread.DefaultThreadPools; +import org.activemq.thread.Task; +import org.activemq.thread.TaskRunner; +import org.activemq.transport.CompositeTransport; +import org.activemq.transport.FutureResponse; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportListener; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.ServiceSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +/** + * A Transport that fans out a connection to multiple brokers. + * + * @version $Revision$ + */ +public class FanoutTransport implements CompositeTransport { + + private static final Log log = LogFactory.getLog(FanoutTransport.class); + + private TransportListener transportListener; + private boolean disposed; + + private final Object reconnectMutex = new Object(); + private final ConnectionStateTracker stateTracker = new ConnectionStateTracker(); + private final ConcurrentHashMap requestMap = new ConcurrentHashMap(); + + private final TaskRunner reconnectTask; + private boolean started; + + private ArrayList transports = new ArrayList(); + private int connectedCount=0; + + private int minAckCount = 2; + + private long initialReconnectDelay = 10; + private long maxReconnectDelay = 1000 * 30; + private long backOffMultiplier = 2; + private boolean useExponentialBackOff = true; + private int maxReconnectAttempts; + private Exception connectionFailure; + private FanoutTransportHandler primary; + + static class RequestCounter { + + final Command command; + final AtomicInteger ackCount; + + RequestCounter(Command command, int count) { + this.command = command; + this.ackCount = new AtomicInteger(count); + } + + public String toString() { + return command.getCommandId()+"="+ackCount.get(); + } + } + + class FanoutTransportHandler implements TransportListener { + + private final URI uri; + private Transport transport; + + private int connectFailures; + private long reconnectDelay = initialReconnectDelay; + private long reconnectDate; + + public FanoutTransportHandler(URI uri) { + this.uri=uri; + } + + public void onCommand(Command command) { + if (command.isResponse()) { + Short id = new Short(((Response) command).getCorrelationId()); + RequestCounter rc = (RequestCounter) requestMap.get(id); + if( rc != null ) { + if( rc.ackCount.decrementAndGet() <= 0 ) { + requestMap.remove(id); + transportListener.onCommand(command); + } + } else { + transportListener.onCommand(command); + } + } else { + transportListener.onCommand(command); + } + } + + public void onException(IOException error) { + try { + synchronized (reconnectMutex) { + if( transport == null ) + return; + + log.debug("Transport failed, starting up reconnect task", error); + + ServiceSupport.dispose(transport); + transport=null; + connectedCount--; + if( primary == this) { + primary = null; + } + reconnectTask.wakeup(); + } + } + catch (InterruptedException e) { + transportListener.onException(new InterruptedIOException()); + } + } + } + + public FanoutTransport() throws InterruptedIOException { + // Setup a task that is used to reconnect the a connection async. + reconnectTask = DefaultThreadPools.getDefaultTaskRunnerFactory().createTaskRunner(new Task() { + public boolean iterate() { + return doConnect(); + } + }); + } + + /** + * @return + */ + private boolean doConnect() { + long closestReconnectDate=0; + synchronized (reconnectMutex) { + + if (disposed || connectionFailure!=null) { + reconnectMutex.notifyAll(); + } + + if (transports.size() == connectedCount || disposed || connectionFailure!=null) { + return false; + } else { + + if( transports.isEmpty() ) { +// connectionFailure = new IOException("No uris available to connect to."); + } else { + + + // Try to connect them up. + Iterator iter = transports.iterator(); + for (int i = 0; iter.hasNext() && !disposed; i++) { + + long now = System.currentTimeMillis(); + + FanoutTransportHandler fanoutHandler = (FanoutTransportHandler) iter.next(); + if( fanoutHandler.transport!=null ) { + continue; + } + + // Are we waiting a little to try to reconnect this one? + if( fanoutHandler.reconnectDate!=0 && fanoutHandler.reconnectDate>now ) { + if( closestReconnectDate==0 || fanoutHandler.reconnectDate < closestReconnectDate ) { + closestReconnectDate = fanoutHandler.reconnectDate; + } + continue; + } + + URI uri = fanoutHandler.uri; + try { + log.debug("Attempting connect to: " + uri); + Transport t = TransportFactory.compositeConnect(uri); + log.debug("Connection established"); + fanoutHandler.transport = t; + fanoutHandler.reconnectDelay = 10; + fanoutHandler.connectFailures = 0; + if( primary == null ) { + primary = fanoutHandler; + } + t.setTransportListener(fanoutHandler); + connectedCount++; + if (started) { + restoreTransport(fanoutHandler); + } + } + catch (Exception e) { + log.debug("Connect fail to: " + uri + ", reason: " + e); + + if (maxReconnectAttempts > 0 && ++fanoutHandler.connectFailures >= maxReconnectAttempts) { + log.error("Failed to connect to transport after: " + fanoutHandler.connectFailures + " attempt(s)"); + connectionFailure = e; + reconnectMutex.notifyAll(); + return false; + } else { + + if (useExponentialBackOff) { + // Exponential increment of reconnect delay. + fanoutHandler.reconnectDelay *= backOffMultiplier; + if (fanoutHandler.reconnectDelay > maxReconnectDelay) + fanoutHandler.reconnectDelay = maxReconnectDelay; + } + + fanoutHandler.reconnectDate = now + fanoutHandler.reconnectDelay; + + if( closestReconnectDate==0 || fanoutHandler.reconnectDate < closestReconnectDate ) { + closestReconnectDate = fanoutHandler.reconnectDate; + } + } + } + } + if (transports.size() == connectedCount || disposed ) { + reconnectMutex.notifyAll(); + return false; + } + + } + } + + } + + try { + long reconnectDelay = closestReconnectDate - System.currentTimeMillis(); + if(reconnectDelay>0) { + log.debug("Waiting " + reconnectDelay + " ms before attempting connection. "); + Thread.sleep(reconnectDelay); + } + } + catch (InterruptedException e1) { + Thread.currentThread().interrupt(); + } + return true; + } + + public void start() throws Exception { + synchronized (reconnectMutex) { + log.debug("Started."); + if (started) + return; + started = true; + for (Iterator iter = transports.iterator(); iter.hasNext();) { + FanoutTransportHandler th = (FanoutTransportHandler) iter.next(); + if( th.transport != null ) { + restoreTransport(th); + } + } + } + } + + public void stop() throws Exception { + synchronized (reconnectMutex) { + log.debug("Stopped."); + if (!started) + return; + started = false; + disposed = true; + + for (Iterator iter = transports.iterator(); iter.hasNext();) { + FanoutTransportHandler th = (FanoutTransportHandler) iter.next(); + if( th.transport != null ) { + th.transport.stop(); + } + } + } + } + + public long getInitialReconnectDelay() { + return initialReconnectDelay; + } + + public void setInitialReconnectDelay(long initialReconnectDelay) { + this.initialReconnectDelay = initialReconnectDelay; + } + + public long getMaxReconnectDelay() { + return maxReconnectDelay; + } + + public void setMaxReconnectDelay(long maxReconnectDelay) { + this.maxReconnectDelay = maxReconnectDelay; + } + + public long getReconnectDelayExponent() { + return backOffMultiplier; + } + + public void setReconnectDelayExponent(long reconnectDelayExponent) { + this.backOffMultiplier = reconnectDelayExponent; + } + + public int getMaxReconnectAttempts() { + return maxReconnectAttempts; + } + + public void setMaxReconnectAttempts(int maxReconnectAttempts) { + this.maxReconnectAttempts = maxReconnectAttempts; + } + + public void oneway(Command command) throws IOException { + try { + synchronized (reconnectMutex) { + + // If it was a request and it was not being tracked by + // the state tracker, + // then hold it in the requestMap so that we can replay + // it later. + boolean fanout = isFanoutCommand(command); + if (!stateTracker.track(command) && command.isResponseRequired() ) { + int size = fanout ? minAckCount : 1; + requestMap.put(new Short(command.getCommandId()), new RequestCounter(command, size)); + } + + // Wait for transport to be connected. + while (connectedCount != minAckCount && !disposed && connectionFailure==null ) { + log.debug("Waiting for at least "+minAckCount+" transports to be connected."); + reconnectMutex.wait(1000); + } + + // Still not fully connected. + if( connectedCount != minAckCount ) { + + Exception error; + + // Throw the right kind of error.. + if (disposed) { + error = new IOException("Transport disposed."); + } else if (connectionFailure!=null) { + error = connectionFailure; + } else { + error = new IOException("Unexpected failure."); + } + + if( error instanceof IOException ) + throw (IOException)error; + throw IOExceptionSupport.create(error); + } + + // Send the message. + if( fanout ) { + for (Iterator iter = transports.iterator(); iter.hasNext();) { + FanoutTransportHandler th = (FanoutTransportHandler) iter.next(); + if( th.transport!=null ) { + try { + th.transport.oneway(command); + } catch (IOException e) { + log.debug("Send attempt: failed."); + th.onException(e); + } + } + } + } else { + try { + primary.transport.oneway(command); + } catch (IOException e) { + log.debug("Send attempt: failed."); + primary.onException(e); + } + } + + } + } catch (InterruptedException e) { + // Some one may be trying to stop our thread. + throw new InterruptedIOException(); + } + } + + /** + * @param command + * @return + */ + private boolean isFanoutCommand(Command command) { + if( command.isMessage() ) { + return ((Message)command).getDestination().isTopic(); + } + if( command.getDataStructureType() == ConsumerInfo.DATA_STRUCTURE_TYPE ) { + return false; + } + return true; + } + + public FutureResponse asyncRequest(Command command) throws IOException { + throw new AssertionError("Unsupported Method"); + } + + public Response request(Command command) throws IOException { + throw new AssertionError("Unsupported Method"); + } + + public void reconnect() { + log.debug("Waking up reconnect task"); + try { + reconnectTask.wakeup(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + public void setTransportListener(TransportListener commandListener) { + this.transportListener = commandListener; + } + + public Object narrow(Class target) { + + if (target.isAssignableFrom(getClass())) { + return this; + } + + synchronized (reconnectMutex) { + for (Iterator iter = transports.iterator(); iter.hasNext();) { + FanoutTransportHandler th = (FanoutTransportHandler) iter.next(); + if( th.transport!=null ) { + Object rc = th.transport.narrow(target); + if( rc !=null ) + return rc; + } + } + } + + return null; + + } + + protected void restoreTransport(FanoutTransportHandler th) throws Exception, IOException { + th.transport.start(); + stateTracker.setRestoreConsumers(th.transport==primary); + stateTracker.restore(th.transport); + for (Iterator iter2 = requestMap.values().iterator(); iter2.hasNext();) { + RequestCounter rc = (RequestCounter) iter2.next(); + th.transport.oneway(rc.command); + } + } + + public void add(URI uris[]) { + + synchronized (reconnectMutex) { + for (int i = 0; i < uris.length; i++) { + URI uri = uris[i]; + + boolean match=false; + for (Iterator iter = transports.iterator(); iter.hasNext();) { + FanoutTransportHandler th = (FanoutTransportHandler) iter.next(); + if( th.uri.equals(uri)) { + match=true; + break; + } + } + if( !match ) { + FanoutTransportHandler th = new FanoutTransportHandler(uri); + transports.add(th); + reconnect(); + } + } + } + + } + + public void remove(URI uris[]) { + + synchronized (reconnectMutex) { + for (int i = 0; i < uris.length; i++) { + URI uri = uris[i]; + + boolean match=false; + for (Iterator iter = transports.iterator(); iter.hasNext();) { + FanoutTransportHandler th = (FanoutTransportHandler) iter.next(); + if( th.uri.equals(uri)) { + if( th.transport!=null ) { + ServiceSupport.dispose(th.transport); + connectedCount--; + } + iter.remove(); + break; + } + } + } + } + + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/fanout/FanoutTransportFactory.java b/activemq-core/src/main/java/org/activemq/transport/fanout/FanoutTransportFactory.java new file mode 100755 index 0000000000..8be1d6c300 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/fanout/FanoutTransportFactory.java @@ -0,0 +1,89 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.fanout; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import org.activemq.transport.MutexTransport; +import org.activemq.transport.ResponseCorrelator; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportServer; +import org.activemq.transport.discovery.DiscoveryAgent; +import org.activemq.transport.discovery.DiscoveryAgentFactory; +import org.activemq.transport.discovery.DiscoveryTransport; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.URISupport; +import org.activemq.util.URISupport.CompositeData; + +public class FanoutTransportFactory extends TransportFactory { + + public Transport doConnect(URI location) throws IOException { + try { + Transport transport = createTransport(location); + transport = new MutexTransport(transport); + transport = new ResponseCorrelator(transport); + return transport; + } catch (URISyntaxException e) { + throw new IOException("Invalid location: "+location); + } + } + + public Transport doCompositeConnect(URI location) throws IOException { + try { + return createTransport(location); + } catch (URISyntaxException e) { + throw new IOException("Invalid location: "+location); + } + } + + /** + * @param location + * @return + * @throws IOException + * @throws URISyntaxException + */ + public Transport createTransport(URI location) throws IOException, URISyntaxException { + + CompositeData compositData = URISupport.parseComposite(location); + Map parameters = new HashMap(compositData.getParameters()); + DiscoveryTransport transport = new DiscoveryTransport(createTransport(parameters)); + + DiscoveryAgent discoveryAgent = DiscoveryAgentFactory.createDiscoveryAgent(compositData.getComponents()[0]); + transport.setDiscoveryAgent(discoveryAgent); + + return transport; + + } + + public FanoutTransport createTransport(Map parameters) throws IOException { + FanoutTransport transport = new FanoutTransport(); + IntrospectionSupport.setProperties(transport, parameters); + return transport; + } + + public TransportServer doBind(String brokerId,URI location) throws IOException { + throw new IOException("Invalid server URI: "+location); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/fanout/package.html b/activemq-core/src/main/java/org/activemq/transport/fanout/package.html new file mode 100755 index 0000000000..4646f1fc48 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/fanout/package.html @@ -0,0 +1,7 @@ + + + + + +Fan-out Transport implementation which ensures that a message is sent to multiple destinations such as to ensure multiple brokers received a message for non-durable topic delivery to improve redundancy + diff --git a/activemq-core/src/main/java/org/activemq/transport/mock/MockTransport.java b/activemq-core/src/main/java/org/activemq/transport/mock/MockTransport.java new file mode 100644 index 0000000000..c1bb3f95c9 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/mock/MockTransport.java @@ -0,0 +1,128 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.mock; + +import java.io.IOException; + +import org.activemq.command.Command; +import org.activemq.command.Response; +import org.activemq.transport.FutureResponse; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFilter; +import org.activemq.transport.TransportListener; + + +/** + * @version $Revision: 1.5 $ + */ +public class MockTransport implements Transport, TransportListener { + + protected Transport next; + protected TransportListener commandListener; + + public MockTransport(Transport next) { + this.next = next; + } + + /** + */ + synchronized public void setTransportListener(TransportListener channelListener) { + this.commandListener = channelListener; + if (channelListener == null) + next.setTransportListener(null); + else + next.setTransportListener(this); + } + + + /** + * @see org.activemq.Service#start() + * @throws IOException if the next channel has not been set. + */ + public void start() throws Exception { + if( next == null ) + throw new IOException("The next channel has not been set."); + if( commandListener == null ) + throw new IOException("The command listener has not been set."); + next.start(); + } + + /** + * @see org.activemq.Service#stop() + */ + public void stop() throws Exception { + next.stop(); + } + + synchronized public void onCommand(Command command) { + commandListener.onCommand(command); + } + + /** + * @return Returns the next. + */ + synchronized public Transport getNext() { + return next; + } + + /** + * @return Returns the packetListener. + */ + synchronized public TransportListener getCommandListener() { + return commandListener; + } + + synchronized public String toString() { + return next.toString(); + } + + synchronized public void oneway(Command command) throws IOException { + next.oneway(command); + } + + synchronized public FutureResponse asyncRequest(Command command) throws IOException { + return next.asyncRequest(command); + } + + synchronized public Response request(Command command) throws IOException { + return next.request(command); + } + + synchronized public void onException(IOException error) { + commandListener.onException(error); + } + + synchronized public Object narrow(Class target) { + if( target.isAssignableFrom(getClass()) ) { + return this; + } + return next.narrow(target); + } + + synchronized public void setNext(Transport next) { + this.next = next; + } + + synchronized public void install(TransportFilter filter) { + filter.setTransportListener(this); + getNext().setTransportListener(filter); + setNext(filter); + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/transport/mock/MockTransportFactory.java b/activemq-core/src/main/java/org/activemq/transport/mock/MockTransportFactory.java new file mode 100755 index 0000000000..0395938189 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/mock/MockTransportFactory.java @@ -0,0 +1,62 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.mock; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.activemq.transport.MutexTransport; +import org.activemq.transport.ResponseCorrelator; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportServer; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.URISupport; +import org.activemq.util.URISupport.CompositeData; + +public class MockTransportFactory extends TransportFactory { + + public Transport doConnect(URI location) throws URISyntaxException, Exception { + Transport transport = createTransport(URISupport.parseComposite(location)); + transport = new MutexTransport(transport); + transport = new ResponseCorrelator(transport); + return transport; + } + + public Transport doCompositeConnect(URI location) throws URISyntaxException, Exception { + return createTransport(URISupport.parseComposite(location)); + } + + /** + * @param location + * @return + * @throws Exception + */ + public Transport createTransport(CompositeData compositData) throws Exception { + MockTransport transport = new MockTransport( TransportFactory.compositeConnect(compositData.getComponents()[0]) ); + IntrospectionSupport.setProperties(transport, compositData.getParameters()); + return transport; + } + + public TransportServer doBind(String brokerId,URI location) throws IOException { + throw new IOException("This protocol does not support being bound."); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/package.html b/activemq-core/src/main/java/org/activemq/transport/package.html new file mode 100755 index 0000000000..50111b9628 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/package.html @@ -0,0 +1,9 @@ + + + + + +The core Transport abstraction and support classes + + + diff --git a/activemq-core/src/main/java/org/activemq/transport/peer/PeerTransportFactory.java b/activemq-core/src/main/java/org/activemq/transport/peer/PeerTransportFactory.java new file mode 100755 index 0000000000..25b33fe2d4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/peer/PeerTransportFactory.java @@ -0,0 +1,118 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.peer; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import org.activemq.broker.BrokerService; +import org.activemq.broker.TransportConnector; +import org.activemq.broker.BrokerFactory.BrokerFactoryHandler; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportServer; +import org.activemq.transport.vm.VMTransportFactory; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.IdGenerator; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.URISupport; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +public class PeerTransportFactory extends TransportFactory { + + final public static ConcurrentHashMap brokers = new ConcurrentHashMap(); + + final public static ConcurrentHashMap connectors = new ConcurrentHashMap(); + + final public static ConcurrentHashMap servers = new ConcurrentHashMap(); + + private IdGenerator idGenerator = new IdGenerator("peer-"); + + + public Transport doConnect(URI location) throws Exception { + VMTransportFactory vmTransportFactory = createTransportFactory(location); + return vmTransportFactory.doConnect(location); + } + + public Transport doCompositeConnect(URI location) throws Exception { + VMTransportFactory vmTransportFactory = createTransportFactory(location); + return vmTransportFactory.doCompositeConnect(location); + } + + /** + * @param location + * @return the converted URI + * @throws URISyntaxException + */ + private VMTransportFactory createTransportFactory(URI location) throws IOException { + try { + String group = location.getHost(); + String broker = location.getPath(); + + if( group == null ) { + group = "default"; + } + if (broker == null || broker.length()==0){ + broker = idGenerator.generateSanitizedId(); + } + + final Map brokerOptions = new HashMap(URISupport.parseParamters(location)); + if (!brokerOptions.containsKey("persistent")){ + brokerOptions.put("persistent", "false"); + } + + final URI finalLocation = new URI("vm://"+broker); + final String finalBroker = broker; + final String finalGroup = group; + VMTransportFactory rc = new VMTransportFactory() { + public Transport doConnect(URI ignore) throws Exception { + return super.doConnect(finalLocation); + }; + public Transport doCompositeConnect(URI ignore) throws Exception { + return super.doCompositeConnect(finalLocation); + }; + }; + rc.setBrokerFactoryHandler(new BrokerFactoryHandler(){ + public BrokerService createBroker(URI brokerURI) throws Exception { + BrokerService service = new BrokerService(); + IntrospectionSupport.setProperties(service, brokerOptions); + service.setBrokerName(finalBroker); + TransportConnector c = service.addConnector("tcp://localhost:0"); + c.setDiscoveryUri(new URI("multicast://"+finalGroup)); + service.addNetworkConnector("multicast://"+finalGroup); + return service; + } + }); + return rc; + + } catch (URISyntaxException e) { + throw IOExceptionSupport.create(e); + } + } + + + public TransportServer doBind(String brokerId,URI location) throws IOException { + throw new IOException("This protocol does not support being bound."); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/peer/package.html b/activemq-core/src/main/java/org/activemq/transport/peer/package.html new file mode 100755 index 0000000000..121825e1dc --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/peer/package.html @@ -0,0 +1,9 @@ + + + + + +Peer based Transport implementation which does not require central servers to connect to + + + diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/Abort.java b/activemq-core/src/main/java/org/activemq/transport/stomp/Abort.java new file mode 100644 index 0000000000..e52ec312dd --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/Abort.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.command.TransactionId; +import org.activemq.command.TransactionInfo; + +import java.io.DataInput; +import java.io.IOException; +import java.net.ProtocolException; +import java.util.Properties; + +class Abort implements StompCommand { + private StompWireFormat format; + private static final HeaderParser parser = new HeaderParser(); + + Abort(StompWireFormat format) { + this.format = format; + } + + public CommandEnvelope build(String commandLine, DataInput in) throws IOException { + Properties headers = parser.parse(in); + while (in.readByte() != 0) { + } + String user_tx_id = headers.getProperty(Stomp.Headers.TRANSACTION); + + if (!headers.containsKey(Stomp.Headers.TRANSACTION)) { + throw new ProtocolException("Must specify the transaction you are aborting"); + } + + TransactionId txnId = format.getTransactionId(user_tx_id); + TransactionInfo tx = new TransactionInfo(); + tx.setTransactionId(txnId); + tx.setType(TransactionInfo.ROLLBACK); + format.clearTransactionId(user_tx_id); + return new CommandEnvelope(tx, headers); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/Ack.java b/activemq-core/src/main/java/org/activemq/transport/stomp/Ack.java new file mode 100644 index 0000000000..041c8eeade --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/Ack.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.MessageAck; +import org.activemq.command.TransactionId; + +import java.io.DataInput; +import java.io.IOException; +import java.net.ProtocolException; +import java.util.List; +import java.util.Properties; + +class Ack implements StompCommand { + private final StompWireFormat format; + private static final HeaderParser parser = new HeaderParser(); + + Ack(StompWireFormat format) { + this.format = format; + } + + public CommandEnvelope build(String commandLine, DataInput in) throws IOException { + Properties headers = parser.parse(in); + String message_id = headers.getProperty(Stomp.Headers.Ack.MESSAGE_ID); + if (message_id == null) + throw new ProtocolException("ACK received without a message-id to acknowledge!"); + + List listeners = format.getAckListeners(); + for (int i = 0; i < listeners.size(); i++) { + AckListener listener = (AckListener) listeners.get(i); + if (listener.handle(message_id)) { + listeners.remove(i); + ActiveMQMessage msg = listener.getMessage(); + MessageAck ack = new MessageAck(); + ack.setDestination((ActiveMQDestination) msg.getJMSDestination()); + ack.setConsumerId(listener.getConsumerId()); + ack.setMessageID(msg.getMessageId()); + ack.setAckType(MessageAck.STANDARD_ACK_TYPE); + + /* + * ack.setMessageRead(true); + * ack.setProducerKey(msg.getProducerKey()); + * ack.setSequenceNumber(msg.getSequenceNumber()); + * ack.setPersistent(msg.getJMSDeliveryMode() == + * DeliveryMode.PERSISTENT); + * ack.setSessionId(format.getSessionId()); + */ + + if (headers.containsKey(Stomp.Headers.TRANSACTION)) { + TransactionId tx_id = format.getTransactionId(headers.getProperty(Stomp.Headers.TRANSACTION)); + if (tx_id == null) + throw new ProtocolException(headers.getProperty(Stomp.Headers.TRANSACTION) + " is an invalid transaction id"); + ack.setTransactionId(tx_id); + } + + while ((in.readByte()) != 0) { + } + return new CommandEnvelope(ack, headers); + } + } + while ((in.readByte()) != 0) { + } + throw new ProtocolException("Unexepected ACK received for message-id [" + message_id + "]"); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/AckListener.java b/activemq-core/src/main/java/org/activemq/transport/stomp/AckListener.java new file mode 100644 index 0000000000..fb08a1f4c5 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/AckListener.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ConsumerId; + +class AckListener { + private final ActiveMQMessage msg; + private final ConsumerId consumerId; + private final String subscriptionId; + + public AckListener(ActiveMQMessage msg, ConsumerId consumerId, String subscriptionId) { + this.msg = msg; + this.consumerId = consumerId; + this.subscriptionId = subscriptionId; + } + + boolean handle(String messageId) { + return msg.getJMSMessageID().equals(messageId); + } + + public ActiveMQMessage getMessage() { + return msg; + } + + public ConsumerId getConsumerId() { + return consumerId; + } + + public String getSubscriptionId() { + return subscriptionId; + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/AsyncHelper.java b/activemq-core/src/main/java/org/activemq/transport/stomp/AsyncHelper.java new file mode 100644 index 0000000000..d89de726e4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/AsyncHelper.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +class AsyncHelper { + public static Object tryUntilNotInterrupted(HelperWithReturn helper) { + while (true) { + try { + return helper.cycle(); + } + catch (InterruptedException e) { /* */ + } + } + } + + static void tryUntilNotInterrupted(final Helper helper) { + tryUntilNotInterrupted(new HelperWithReturn() { + + public Object cycle() throws InterruptedException { + helper.cycle(); + return null; + } + }); + } + + interface HelperWithReturn { + Object cycle() throws InterruptedException; + } + + interface Helper { + void cycle() throws InterruptedException; + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/Begin.java b/activemq-core/src/main/java/org/activemq/transport/stomp/Begin.java new file mode 100644 index 0000000000..568bd1d93c --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/Begin.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.command.TransactionId; +import org.activemq.command.TransactionInfo; + +import java.io.DataInput; +import java.io.IOException; +import java.net.ProtocolException; +import java.util.Properties; + +public class Begin implements StompCommand { + private StompWireFormat format; + private static final HeaderParser parser = new HeaderParser(); + + public Begin(StompWireFormat format) { + this.format = format; + } + + public CommandEnvelope build(String commandLine, DataInput in) throws IOException { + Properties headers = parser.parse(in); + while (in.readByte() != 0) { + } + + TransactionInfo tx = new TransactionInfo(); + String user_tx_id = headers.getProperty(Stomp.Headers.TRANSACTION); + if (!headers.containsKey(Stomp.Headers.TRANSACTION)) { + throw new ProtocolException("Must specify the transaction you are beginning"); + } + int tx_id = StompWireFormat.generateTransactionId(); + TransactionId transactionId = format.registerTransactionId(user_tx_id, tx_id); + tx.setTransactionId(transactionId); + tx.setType(TransactionInfo.BEGIN); + return new CommandEnvelope(tx, headers); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/Command.java b/activemq-core/src/main/java/org/activemq/transport/stomp/Command.java new file mode 100644 index 0000000000..f8be0f673f --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/Command.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import javax.jms.JMSException; + +import java.io.DataInput; +import java.io.IOException; +import java.util.Properties; + +interface Command { + public CommandEnvelope build(String commandLine, DataInput in) throws IOException, JMSException; + + /** + * Returns a command instance which always returns null for a packet + */ + StompCommand NULL_COMMAND = new StompCommand() { + public CommandEnvelope build(String commandLine, DataInput in) { + return new CommandEnvelope(null, new Properties()); + } + }; +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/CommandEnvelope.java b/activemq-core/src/main/java/org/activemq/transport/stomp/CommandEnvelope.java new file mode 100644 index 0000000000..6cdfc7f5ee --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/CommandEnvelope.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.command.Command; + +import java.util.Properties; + +public class CommandEnvelope { + private final Command command; + private final Properties headers; + + CommandEnvelope(Command command, Properties headers) { + this.command = command; + this.headers = headers; + } + + Properties getHeaders() { + return headers; + } + + Command getCommand() { + return command; + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/CommandParser.java b/activemq-core/src/main/java/org/activemq/transport/stomp/CommandParser.java new file mode 100644 index 0000000000..914da10029 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/CommandParser.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.command.Command; +import org.activemq.command.Response; + +import javax.jms.JMSException; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.net.ProtocolException; + +class CommandParser { + private String clientId; + private final StompWireFormat format; + + CommandParser(StompWireFormat wireFormat) { + format = wireFormat; + } + + Command parse(DataInput in) throws IOException, JMSException { + String line; + + // skip white space to next real line + try { + while ((line = in.readLine()).trim().length() == 0) { + } + } + catch (NullPointerException e) { + throw new IOException("connection was closed"); + } + + // figure corrent command and return it + StompCommand command = null; + if (line.startsWith(Stomp.Commands.SUBSCRIBE)) + command = new Subscribe(format); + if (line.startsWith(Stomp.Commands.SEND)) + command = new Send(format); + if (line.startsWith(Stomp.Commands.DISCONNECT)) + command = new Disconnect(); + if (line.startsWith(Stomp.Commands.BEGIN)) + command = new Begin(format); + if (line.startsWith(Stomp.Commands.COMMIT)) + command = new Commit(format); + if (line.startsWith(Stomp.Commands.ABORT)) + command = new Abort(format); + if (line.startsWith(Stomp.Commands.UNSUBSCRIBE)) + command = new Unsubscribe(format); + if (line.startsWith(Stomp.Commands.ACK)) + command = new Ack(format); + + if (command == null) { + while (in.readByte() == 0) { + } + throw new ProtocolException("Unknown command [" + line + "]"); + } + + CommandEnvelope envelope = command.build(line, in); + if (envelope.getHeaders().containsKey(Stomp.Headers.RECEIPT_REQUESTED)) { + final short id = StompWireFormat.generateCommandId(); + envelope.getCommand().setCommandId(id); + envelope.getCommand().setResponseRequired(true); + final String client_packet_key = envelope.getHeaders().getProperty(Stomp.Headers.RECEIPT_REQUESTED); + format.addResponseListener(new ResponseListener() { + public boolean onResponse(Response receipt, DataOutput out) throws IOException { + if (receipt.getCorrelationId() != id) + return false; + + out.write(new FrameBuilder(Stomp.Responses.RECEIPT).addHeader(Stomp.Headers.Response.RECEIPT_ID, client_packet_key).toFrame()); + return true; + } + }); + } + + return envelope.getCommand(); + } + + void setClientId(String clientId) { + this.clientId = clientId; + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/Commit.java b/activemq-core/src/main/java/org/activemq/transport/stomp/Commit.java new file mode 100644 index 0000000000..9c972949a7 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/Commit.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.command.TransactionId; +import org.activemq.command.TransactionInfo; + +import java.io.DataInput; +import java.io.IOException; +import java.net.ProtocolException; +import java.util.Properties; + +class Commit implements StompCommand { + private StompWireFormat format; + private static final HeaderParser parser = new HeaderParser(); + + Commit(StompWireFormat format) { + this.format = format; + } + + public CommandEnvelope build(String commandLine, DataInput in) throws IOException { + Properties headers = parser.parse(in); + while (in.readByte() != 0) { + } + + String user_tx_id = headers.getProperty(Stomp.Headers.TRANSACTION); + + if (!headers.containsKey(Stomp.Headers.TRANSACTION)) { + throw new ProtocolException("Must specify the transaction you are committing"); + } + + TransactionId tx_id = format.getTransactionId(user_tx_id); + TransactionInfo tx = new TransactionInfo(); + tx.setTransactionId(tx_id); + tx.setType(TransactionInfo.COMMIT_ONE_PHASE); + format.clearTransactionId(user_tx_id); + return new CommandEnvelope(tx, headers); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/DestinationNamer.java b/activemq-core/src/main/java/org/activemq/transport/stomp/DestinationNamer.java new file mode 100644 index 0000000000..c1e9496b83 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/DestinationNamer.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.command.ActiveMQDestination; + +import javax.jms.Destination; + +import java.net.ProtocolException; + +class DestinationNamer { + static ActiveMQDestination convert(String name) throws ProtocolException { + if (name == null) { + return null; + } + else if (name.startsWith("/queue/")) { + String q_name = name.substring("/queue/".length(), name.length()); + return ActiveMQDestination.createDestination(q_name, ActiveMQDestination.QUEUE_TYPE); + } + else if (name.startsWith("/topic/")) { + String t_name = name.substring("/topic/".length(), name.length()); + return ActiveMQDestination.createDestination(t_name, ActiveMQDestination.TOPIC_TYPE); + } + else { + throw new ProtocolException("Illegal destination name: [" + name + "] -- ActiveMQ TTMP destinations " + "must begine with /queue/ or /topic/"); + } + + } + + static String convert(Destination d) { + if (d == null) { + return null; + } + ActiveMQDestination amq_d = (ActiveMQDestination) d; + String p_name = amq_d.getPhysicalName(); + + StringBuffer buffer = new StringBuffer(); + if (amq_d.isQueue()) { + buffer.append("/queue/"); + } + if (amq_d.isTopic()) { + buffer.append("/topic/"); + } + buffer.append(p_name); + + return buffer.toString(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/Disconnect.java b/activemq-core/src/main/java/org/activemq/transport/stomp/Disconnect.java new file mode 100644 index 0000000000..26aca7554d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/Disconnect.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.command.ShutdownInfo; + +import java.io.DataInput; +import java.io.IOException; +import java.util.Properties; + +class Disconnect implements StompCommand { + + public CommandEnvelope build(String line, DataInput in) throws IOException { + while (in.readByte() != 0) { + } + return new CommandEnvelope(new ShutdownInfo(), new Properties()); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/FrameBuilder.java b/activemq-core/src/main/java/org/activemq/transport/stomp/FrameBuilder.java new file mode 100644 index 0000000000..a3bcad8461 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/FrameBuilder.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.command.ActiveMQMessage; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; + +class FrameBuilder { + private String command; + private Properties headers = new Properties(); + private byte[] body = new byte[0]; + + public FrameBuilder(String command) { + this.command = command; + } + + public FrameBuilder addHeader(String key, String value) { + if (value != null) { + this.headers.setProperty(key, value); + } + return this; + } + + public FrameBuilder addHeader(String key, long value) { + this.headers.put(key, new Long(value)); + return this; + } + + public FrameBuilder addHeaders(ActiveMQMessage message) throws IOException { + addHeader(Stomp.Headers.Message.DESTINATION, DestinationNamer.convert(message.getDestination())); + addHeader(Stomp.Headers.Message.MESSAGE_ID, message.getJMSMessageID()); + addHeader(Stomp.Headers.Message.CORRELATION_ID, message.getJMSCorrelationID()); + addHeader(Stomp.Headers.Message.EXPIRATION_TIME, message.getJMSExpiration()); + if (message.getJMSRedelivered()) { + addHeader(Stomp.Headers.Message.REDELIVERED, "true"); + } + addHeader(Stomp.Headers.Message.PRORITY, message.getJMSPriority()); + addHeader(Stomp.Headers.Message.REPLY_TO, DestinationNamer.convert(message.getJMSReplyTo())); + addHeader(Stomp.Headers.Message.TIMESTAMP, message.getJMSTimestamp()); + addHeader(Stomp.Headers.Message.TYPE, message.getJMSType()); + + // now lets add all the message headers + Map properties = message.getProperties(); + if (properties != null) { + headers.putAll(properties); + } + return this; + } + + public FrameBuilder setBody(byte[] body) { + this.body = body; + return this; + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(command); + buffer.append(Stomp.NEWLINE); + for (Iterator iterator = headers.keySet().iterator(); iterator.hasNext();) { + String key = (String) iterator.next(); + String property = headers.getProperty(key); + if (property != null) { + buffer.append(key).append(Stomp.Headers.SEPERATOR).append(property).append(Stomp.NEWLINE); + } + } + buffer.append(Stomp.NEWLINE); + buffer.append(body); + buffer.append(Stomp.NULL); + buffer.append(Stomp.NEWLINE); + return buffer.toString(); + } + + byte[] toFrame() { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + try { + bout.write(command.getBytes()); + bout.write(Stomp.NEWLINE.getBytes()); + for (Iterator iterator = headers.keySet().iterator(); iterator.hasNext();) { + String key = (String) iterator.next(); + String property = headers.getProperty(key); + if (property != null) { + bout.write(key.getBytes()); + bout.write(Stomp.Headers.SEPERATOR.getBytes()); + bout.write(property.getBytes()); + bout.write(Stomp.NEWLINE.getBytes()); + } + } + bout.write(Stomp.NEWLINE.getBytes()); + bout.write(body); + bout.write(Stomp.NULL.getBytes()); + bout.write(Stomp.NEWLINE.getBytes()); + } + catch (IOException e) { + throw new RuntimeException("World is caving in, we just got io error writing to" + "a byte array output stream we instantiated!"); + } + return bout.toByteArray(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/HeaderParser.java b/activemq-core/src/main/java/org/activemq/transport/stomp/HeaderParser.java new file mode 100644 index 0000000000..e356f573ba --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/HeaderParser.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import java.io.BufferedReader; +import java.io.DataInput; +import java.io.IOException; +import java.net.ProtocolException; +import java.util.Properties; + +class HeaderParser { + /** + * Reads headers up through the blank line + * + * @param in + * @return + * @throws IOException + */ + Properties parse(BufferedReader in) throws IOException { + Properties props = new Properties(); + String line; + while (((line = in.readLine()).trim().length() > 0)) { + int seperator_index = line.indexOf(Stomp.Headers.SEPERATOR); + String name = line.substring(0, seperator_index).trim(); + String value = line.substring(seperator_index + 1, line.length()).trim(); + props.setProperty(name, value); + } + return props; + } + + Properties parse(DataInput in) throws IOException { + Properties props = new Properties(); + String line; + while (((line = in.readLine()).trim().length() > 0)) { + try { + int seperator_index = line.indexOf(Stomp.Headers.SEPERATOR); + String name = line.substring(0, seperator_index).trim(); + String value = line.substring(seperator_index + 1, line.length()).trim(); + props.setProperty(name, value); + } + catch (Exception e) { + throw new ProtocolException("Unable to parser header line [" + line + "]"); + } + } + return props; + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/ResponseListener.java b/activemq-core/src/main/java/org/activemq/transport/stomp/ResponseListener.java new file mode 100644 index 0000000000..2be7a123c7 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/ResponseListener.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.command.Response; + +import java.io.DataOutput; +import java.io.IOException; + +interface ResponseListener { + /** + * Return true if you handled this, false otherwise + */ + boolean onResponse(Response receipt, DataOutput out) throws IOException; +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/Send.java b/activemq-core/src/main/java/org/activemq/transport/stomp/Send.java new file mode 100644 index 0000000000..c4f1d61cf7 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/Send.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activeio.ByteSequence; +import org.activemq.command.ActiveMQBytesMessage; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ActiveMQTextMessage; +import org.activemq.command.TransactionId; + +import javax.jms.JMSException; + +import java.io.ByteArrayOutputStream; +import java.io.DataInput; +import java.io.IOException; +import java.net.ProtocolException; +import java.util.Properties; + +class Send implements StompCommand { + private final HeaderParser parser = new HeaderParser(); + private final StompWireFormat format; + + Send(StompWireFormat format) { + this.format = format; + } + + public CommandEnvelope build(String commandLine, DataInput in) throws IOException, JMSException { + Properties headers = parser.parse(in); + String destination = (String) headers.remove(Stomp.Headers.Send.DESTINATION); + // now the body + ActiveMQMessage msg; + if (headers.containsKey(Stomp.Headers.CONTENT_LENGTH)) { + ActiveMQBytesMessage bm = new ActiveMQBytesMessage(); + String content_length = headers.getProperty(Stomp.Headers.CONTENT_LENGTH).trim(); + int length; + try { + length = Integer.parseInt(content_length); + } + catch (NumberFormatException e) { + throw new ProtocolException("Specified content-length is not a valid integer"); + } + byte[] bytes = new byte[length]; + in.readFully(bytes); + byte nil = in.readByte(); + if (nil != 0) + throw new ProtocolException("content-length bytes were read and " + "there was no trailing null byte"); + ByteSequence content = new ByteSequence(bytes, 0, bytes.length); + bm.setContent(content); + msg = bm; + } + else { + ActiveMQTextMessage text = new ActiveMQTextMessage(); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + byte b; + while ((b = in.readByte()) != 0) { + bytes.write(b); + } + bytes.close(); + String body = new String(bytes.toByteArray()); + try { + text.setText(body); + } + catch (JMSException e) { + throw new RuntimeException("Something is really wrong, we instantiated this thing!"); + } + msg = text; + } + + msg.setMessageId(format.generateMessageId()); + + ActiveMQDestination d = DestinationNamer.convert(destination); + msg.setDestination(d); + // msg.setJMSClientID(format.getClientId()); + + // the standard JMS headers + msg.setJMSCorrelationID((String) headers.remove(Stomp.Headers.Send.CORRELATION_ID)); + + Object expiration = headers.remove(Stomp.Headers.Send.EXPIRATION_TIME); + if (expiration != null) { + msg.setJMSExpiration(asLong(expiration)); + } + Object priority = headers.remove(Stomp.Headers.Send.PRORITY); + if (priority != null) { + msg.setJMSExpiration(asInt(priority)); + } + + msg.setJMSReplyTo(DestinationNamer.convert((String) headers.remove(Stomp.Headers.Send.REPLY_TO))); + + // now the general headers + msg.setProperties(headers); + + if (headers.containsKey(Stomp.Headers.TRANSACTION)) { + TransactionId tx_id = format.getTransactionId(headers.getProperty(Stomp.Headers.TRANSACTION)); + if (tx_id == null) + throw new ProtocolException(headers.getProperty(Stomp.Headers.TRANSACTION) + " is an invalid transaction id"); + msg.setTransactionId(tx_id); + } + + return new CommandEnvelope(msg, headers); + } + + protected boolean asBool(Object value) { + if (value != null) { + return String.valueOf(value).equals("true"); + } + return false; + } + + protected long asLong(Object value) { + if (value instanceof Number) { + Number n = (Number) value; + return n.longValue(); + } + return Long.parseLong(value.toString()); + } + + protected int asInt(Object value) { + if (value instanceof Number) { + Number n = (Number) value; + return n.intValue(); + } + return Integer.parseInt(value.toString()); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/Stomp.java b/activemq-core/src/main/java/org/activemq/transport/stomp/Stomp.java new file mode 100644 index 0000000000..2a84659e3d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/Stomp.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +public interface Stomp { + String NULL = "\u0000"; + String NEWLINE = "\n"; + + public static interface Commands { + String CONNECT = "CONNECT"; + String SEND = "SEND"; + String DISCONNECT = "DISCONNECT"; + String SUBSCRIBE = "SUB"; + String UNSUBSCRIBE = "UNSUB"; + + String BEGIN_TRANSACTION = "BEGIN"; + String COMMIT_TRANSACTION = "COMMIT"; + String ABORT_TRANSACTION = "ABORT"; + String BEGIN = "BEGIN"; + String COMMIT = "COMMIT"; + String ABORT = "ABORT"; + String ACK = "ACK"; + } + + public interface Responses { + String CONNECTED = "CONNECTED"; + String ERROR = "ERROR"; + String MESSAGE = "MESSAGE"; + String RECEIPT = "RECEIPT"; + } + + public interface Headers { + String SEPERATOR = ":"; + String RECEIPT_REQUESTED = "receipt"; + String TRANSACTION = "transaction"; + String CONTENT_LENGTH = "content-length"; + + public interface Response { + String RECEIPT_ID = "receipt-id"; + } + + public interface Send { + String DESTINATION = "destination"; + String CORRELATION_ID = "correlation-id"; + String REPLY_TO = "reply-to"; + String EXPIRATION_TIME = "expires"; + String PRORITY = "priority"; + String TYPE = "type"; + } + + public interface Message { + String MESSAGE_ID = "message-id"; + String DESTINATION = "destination"; + String CORRELATION_ID = "correlation-id"; + String EXPIRATION_TIME = "expires"; + String REPLY_TO = "reply-to"; + String PRORITY = "priority"; + String REDELIVERED = "redelivered"; + String TIMESTAMP = "timestamp"; + String TYPE = "type"; + String SUBSCRIPTION = "subscription"; + } + + public interface Subscribe { + String DESTINATION = "destination"; + String ACK_MODE = "ack"; + String ID = "id"; + + public interface AckModeValues { + String AUTO = "auto"; + String CLIENT = "client"; + } + } + + public interface Unsubscribe { + String DESTINATION = "destination"; + } + + public interface Connect { + String LOGIN = "login"; + String PASSCODE = "passcode"; + } + + public interface Error { + String MESSAGE = "message"; + } + + public interface Connected { + String SESSION = "session"; + } + + public interface Ack { + String MESSAGE_ID = "message-id"; + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/StompCommand.java b/activemq-core/src/main/java/org/activemq/transport/stomp/StompCommand.java new file mode 100644 index 0000000000..8a386c4e38 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/StompCommand.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import javax.jms.JMSException; + +import java.io.DataInput; +import java.io.IOException; +import java.util.Properties; + +interface StompCommand +{ + public CommandEnvelope build(String commandLine, DataInput in) throws IOException, JMSException; + + /** + * Returns a command instance which always returns null for a packet + */ + StompCommand NULL_COMMAND = new StompCommand() + { + public CommandEnvelope build(String commandLine, DataInput in) + { + return new CommandEnvelope(null, new Properties()); + } + }; +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/StompTransportChannel.java b/activemq-core/src/main/java/org/activemq/transport/stomp/StompTransportChannel.java new file mode 100644 index 0000000000..807ddd80ab --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/StompTransportChannel.java @@ -0,0 +1,66 @@ +/** + * + * Copyright 2005 Protique Ltd + * + * Licensed 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.activemq.transport.stomp; + +import org.activemq.transport.tcp.TcpTransport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.net.Socket; +import java.net.URI; +import java.net.UnknownHostException; + +/** + * A transport for using Stomp to talk to ActiveMQ + * + * @version $Revision: 1.1 $ + */ +public class StompTransportChannel extends TcpTransport { + private static final Log log = LogFactory.getLog(StompTransportChannel.class); + + public StompTransportChannel() { + super(new StompWireFormat()); + } + + public StompTransportChannel(URI remoteLocation) throws UnknownHostException, IOException { + super(new StompWireFormat(), remoteLocation); + } + + public StompTransportChannel(URI remoteLocation, URI localLocation) throws UnknownHostException, IOException { + super(new StompWireFormat(), remoteLocation, localLocation); + } + + public StompTransportChannel(Socket socket) throws IOException { + super(new StompWireFormat(), socket); + } + + /* + * protected void readWireFormat() throws JMSException, IOException { // no + * need to read wire format from wire } + * + * protected void doConsumeCommand(Command packet) { if( packet == + * FlushCommand.PACKET ) { try { doAsyncSend(null); } catch (JMSException e) { + * ExceptionListener listener = getExceptionListener(); if (listener != + * null) { listener.onException(e); } else { log.warn("No listener to report + * error consuming packet: " + e, e); } } } else { + * super.doConsumeCommand(packet); } } + */ + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/StompTransportServerChannel.java b/activemq-core/src/main/java/org/activemq/transport/stomp/StompTransportServerChannel.java new file mode 100644 index 0000000000..f3ca523e52 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/StompTransportServerChannel.java @@ -0,0 +1,54 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.stomp; + +import org.activemq.command.BrokerInfo; +import org.activemq.transport.TransportServerSupport; + +import java.net.URI; + +/** + * A Stomp transport server + * + * @version $Revision: 1.1.1.1 $ + */ +public class StompTransportServerChannel extends TransportServerSupport implements Runnable { + + public StompTransportServerChannel(String brokerId, URI location) { + // TODO... + } + + public void run() { + } + + public void setBrokerInfo(BrokerInfo brokerInfo) { + } + + public void start() throws Exception { + } + + public void stop() throws Exception { + } + + /* + * protected TcpTransportChannel createTransportChannel(Socket socket, + * PooledExecutor executor) throws JMSException { return new + * StompTransportChannel(socket, executor); } + */ +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/StompTransportServerChannelFactory.java b/activemq-core/src/main/java/org/activemq/transport/stomp/StompTransportServerChannelFactory.java new file mode 100644 index 0000000000..837c001393 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/StompTransportServerChannelFactory.java @@ -0,0 +1,37 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.stomp; + +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportServer; + +import java.io.IOException; +import java.net.URI; + +/** + * A Stomp transport factory + * + * @version $Revision: 1.1.1.1 $ + */ +public class StompTransportServerChannelFactory extends TransportFactory { + + public TransportServer doBind(String brokerId, URI location) throws IOException { + return new StompTransportServerChannel(brokerId, location); + } + +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/StompWireFormat.java b/activemq-core/src/main/java/org/activemq/transport/stomp/StompWireFormat.java new file mode 100644 index 0000000000..6fa0fc203d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/StompWireFormat.java @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; +import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +import org.activeio.Packet; +import org.activeio.command.WireFormat; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQTextMessage; +import org.activemq.command.CommandTypes; +import org.activemq.command.ConnectionId; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerId; +import org.activemq.command.Command; +import org.activemq.command.FlushCommand; +import org.activemq.command.LocalTransactionId; +import org.activemq.command.MessageId; +import org.activemq.command.Response; +import org.activemq.command.SessionId; +import org.activemq.command.SessionInfo; +import org.activemq.command.ActiveMQBytesMessage; +import org.activemq.command.TransactionId; +import org.activemq.util.IdGenerator; + +import javax.jms.JMSException; +import java.io.BufferedReader; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.DatagramPacket; +import java.net.ProtocolException; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * Implements the Stomp protocol. + */ +public class StompWireFormat implements WireFormat { + + static final IdGenerator PACKET_IDS = new IdGenerator(); + static final IdGenerator clientIds = new IdGenerator(); + private static int transactionIdCounter; + + private int version = 1; + private CommandParser commandParser = new CommandParser(this); + private HeaderParser headerParser = new HeaderParser(); + + private DataInputStream in; + + private String clientId; + + private BlockingQueue pendingReadCommands = new LinkedBlockingQueue(); + private BlockingQueue pendingWriteFrames = new LinkedBlockingQueue(); + private List receiptListeners = new CopyOnWriteArrayList(); + private SessionId sessionId; + private Map subscriptions = new ConcurrentHashMap(); + private List ackListeners = new CopyOnWriteArrayList(); + private final Map transactions = new ConcurrentHashMap(); + private ConnectionId connectionId; + + void addResponseListener(ResponseListener listener) { + receiptListeners.add(listener); + } + + public Command readCommand(DataInput in) throws IOException, JMSException { + Command pending = (Command) AsyncHelper.tryUntilNotInterrupted(new AsyncHelper.HelperWithReturn() { + public Object cycle() throws InterruptedException { + return pendingReadCommands.poll(0, TimeUnit.MILLISECONDS); + } + }); + if (pending != null) { + return pending; + } + + try { + return commandParser.parse(in); + } + catch (ProtocolException e) { + sendError(e.getMessage()); + return FlushCommand.COMMAND; + } + } + + public Command writeCommand(final Command packet, final DataOutput out) throws IOException, JMSException { + flushPendingFrames(out); + + // It may have just been a flush request. + if (packet == null) + return null; + + if (packet.getDataStructureType() == CommandTypes.RESPONSE) { + assert (packet instanceof Response); + Response receipt = (Response) packet; + for (int i = 0; i < receiptListeners.size(); i++) { + ResponseListener listener = (ResponseListener) receiptListeners.get(i); + if (listener.onResponse(receipt, out)) { + receiptListeners.remove(listener); + return null; + } + } + } + + if (packet.getDataStructureType() == CommandTypes.ACTIVEMQ_TEXT_MESSAGE) { + assert (packet instanceof ActiveMQTextMessage); + ActiveMQTextMessage msg = (ActiveMQTextMessage) packet; + Subscription sub = (Subscription) subscriptions.get(msg.getJMSDestination()); + sub.receive(msg, out); + } + else if (packet.getDataStructureType() == CommandTypes.ACTIVEMQ_BYTES_MESSAGE) { + assert (packet instanceof ActiveMQBytesMessage); + ActiveMQBytesMessage msg = (ActiveMQBytesMessage) packet; + Subscription sub = (Subscription) subscriptions.get(msg.getJMSDestination()); + sub.receive(msg, out); + } + return null; + } + + private void flushPendingFrames(final DataOutput out) throws IOException { + boolean interrupted = false; + do { + try { + byte[] frame = (byte[]) pendingWriteFrames.poll(0, TimeUnit.MILLISECONDS); + if (frame == null) + return; + out.write(frame); + } + catch (InterruptedException e) { + interrupted = true; + } + } + while (interrupted); + } + + private void sendError(final String message) { + // System.err.println("sending error [" + message + "]"); + AsyncHelper.tryUntilNotInterrupted(new AsyncHelper.Helper() { + public void cycle() throws InterruptedException { + pendingWriteFrames.put(new FrameBuilder(Stomp.Responses.ERROR).addHeader(Stomp.Headers.Error.MESSAGE, message).toFrame()); + } + }); + } + + /** + * some transports may register their streams (e.g. Tcp) + * + * @param dataOut + * @param dataIn + */ + public void registerTransportStreams(DataOutputStream dataOut, DataInputStream dataIn) { + this.in = dataIn; + } + + /** + * Some wire formats require a handshake at start-up + * + * @throws java.io.IOException + */ + public void initiateServerSideProtocol() throws IOException { + BufferedReader in = new BufferedReader(new InputStreamReader(this.in)); + String first_line = in.readLine(); + if (!first_line.startsWith(Stomp.Commands.CONNECT)) { + throw new IOException("First line does not begin with with " + Stomp.Commands.CONNECT); + } + + Properties headers = headerParser.parse(in); + // if (!headers.containsKey(TTMP.Headers.Connect.LOGIN)) + // System.err.println("Required header [" + TTMP.Headers.Connect.LOGIN + + // "] missing"); + // if (!headers.containsKey(TTMP.Headers.Connect.PASSCODE)) + // System.err.println("Required header [" + + // TTMP.Headers.Connect.PASSCODE + "] missing"); + + // allow anyone to login for now + + String login = headers.getProperty(Stomp.Headers.Connect.LOGIN); + String passcode = headers.getProperty(Stomp.Headers.Connect.PASSCODE); + + // skip to end of the packet + while (in.read() != 0) { + } + final ConnectionInfo info = new ConnectionInfo(); + clientId = clientIds.generateId(); + commandParser.setClientId(clientId); + + info.setClientId(clientId); + info.setResponseRequired(true); + // info.setClientVersion(Integer.toString(getCurrentWireFormatVersion())); + final short commandId = generateCommandId(); + info.setCommandId(commandId); + info.setUserName(login); + info.setPassword(passcode); + // info.setStartTime(System.currentTimeMillis()); + AsyncHelper.tryUntilNotInterrupted(new AsyncHelper.Helper() { + public void cycle() throws InterruptedException { + pendingReadCommands.put(info); + } + }); + + addResponseListener(new ResponseListener() { + public boolean onResponse(Response receipt, DataOutput out) { + if (receipt.getCorrelationId() != commandId) + return false; + sessionId = generateSessionId(); + + final SessionInfo info = new SessionInfo(); + info.setCommandId(generateCommandId()); + info.setSessionId(sessionId); + info.setResponseRequired(true); + + AsyncHelper.tryUntilNotInterrupted(new AsyncHelper.Helper() { + public void cycle() throws InterruptedException { + pendingReadCommands.put(info); + } + }); + + addResponseListener(new ResponseListener() { + public boolean onResponse(Response receipt, DataOutput out) throws IOException { + if (receipt.getCorrelationId() != commandId) + return false; + StringBuffer buffer = new StringBuffer(); + buffer.append(Stomp.Responses.CONNECTED).append(Stomp.NEWLINE); + buffer.append(Stomp.Headers.Connected.SESSION).append(Stomp.Headers.SEPERATOR).append(clientId).append(Stomp.NEWLINE).append( + Stomp.NEWLINE); + buffer.append(Stomp.NULL); + out.writeBytes(buffer.toString()); + return true; + } + }); + + return true; + } + }); + } + + /** + * Creates a new copy of this wire format so it can be used in another + * thread/context + */ + public WireFormat copy() { + return new StompWireFormat(); + } + + /* Stuff below here is leaky stuff we don't actually need */ + + /** + * Some wire formats require a handshake at start-up + * + * @throws java.io.IOException + */ + public void initiateClientSideProtocol() throws IOException { + throw new UnsupportedOperationException("Not yet implemented!"); + } + + /** + * Can this wireformat process packets of this version + * + * @param version + * the version number to test + * @return true if can accept the version + */ + public boolean canProcessWireFormatVersion(int version) { + return version == getCurrentWireFormatVersion(); + } + + /** + * @return the current version of this wire format + */ + public int getCurrentWireFormatVersion() { + return 1; + } + + /** + * @return Returns the enableCaching. + */ + public boolean isCachingEnabled() { + return false; + } + + /** + * @param enableCaching + * The enableCaching to set. + */ + public void setCachingEnabled(boolean enableCaching) { + // never + } + + /** + * some wire formats will implement their own fragementation + * + * @return true unless a wire format supports it's own fragmentation + */ + public boolean doesSupportMessageFragmentation() { + return false; + } + + /** + * Some wire formats will not be able to understand compressed messages + * + * @return true unless a wire format cannot understand compression + */ + public boolean doesSupportMessageCompression() { + return false; + } + + /** + * Writes the given package to a new datagram + * + * @param channelID + * is the unique channel ID + * @param packet + * is the packet to write + * @return + * @throws java.io.IOException + * @throws javax.jms.JMSException + */ + public DatagramPacket writeCommand(String channelID, Command packet) throws IOException, JMSException { + throw new UnsupportedOperationException("Will not be implemented"); + } + + /** + * Reads the packet from the given byte[] + * + * @param bytes + * @param offset + * @param length + * @return + * @throws java.io.IOException + */ + public Command fromBytes(byte[] bytes, int offset, int length) throws IOException { + throw new UnsupportedOperationException("Will not be implemented"); + } + + /** + * Reads the packet from the given byte[] + * + * @param bytes + * @return + * @throws java.io.IOException + */ + public Command fromBytes(byte[] bytes) throws IOException { + throw new UnsupportedOperationException("Will not be implemented"); + } + + /** + * A helper method which converts a packet into a byte array + * + * @param packet + * @return a byte array representing the packet using some wire protocol + * @throws java.io.IOException + * @throws javax.jms.JMSException + */ + public byte[] toBytes(Command packet) throws IOException, JMSException { + throw new UnsupportedOperationException("Will not be implemented"); + } + + /** + * A helper method for working with sockets where the first byte is read + * first, then the rest of the message is read.

Its common when dealing + * with sockets to have different timeout semantics until the first non-zero + * byte is read of a message, after which time a zero timeout is used. + * + * @param firstByte + * the first byte of the packet + * @param in + * the rest of the packet + * @return + * @throws java.io.IOException + */ + public Command readCommand(int firstByte, DataInput in) throws IOException { + throw new UnsupportedOperationException("Will not be implemented"); + } + + /** + * Read a packet from a Datagram packet from the given channelID. If the + * packet is from the same channel ID as it was sent then we have a + * loop-back so discard the packet + * + * @param channelID + * is the unique channel ID + * @param dpacket + * @return the packet read from the datagram or null if it should be + * discarded + * @throws java.io.IOException + */ + public Command readCommand(String channelID, DatagramPacket dpacket) throws IOException { + throw new UnsupportedOperationException("Will not be implemented"); + } + + void clearTransactionId(String user_tx_id) { + this.transactions.remove(user_tx_id); + } + + String getClientId() { + return this.clientId; + } + + public SessionId getSessionId() { + return sessionId; + } + + public void addSubscription(Subscription s) { + if (subscriptions.containsKey(s.getDestination())) { + Subscription old = (Subscription) subscriptions.get(s.getDestination()); + Command p = old.close(); + enqueueCommand(p); + subscriptions.put(s.getDestination(), s); + } + else { + subscriptions.put(s.getDestination(), s); + } + } + + public void enqueueCommand(final Command ack) { + AsyncHelper.tryUntilNotInterrupted(new AsyncHelper.Helper() { + public void cycle() throws InterruptedException { + pendingReadCommands.put(ack); + } + }); + } + + public Subscription getSubscriptionFor(ActiveMQDestination destination) { + return (Subscription) subscriptions.get(destination); + } + + public void addAckListener(AckListener listener) { + this.ackListeners.add(listener); + } + + public List getAckListeners() { + return ackListeners; + } + + public TransactionId getTransactionId(String key) { + return (TransactionId) transactions.get(key); + } + + public TransactionId registerTransactionId(String user_tx_id, int tx_id) { + LocalTransactionId transactionId = new LocalTransactionId(getConnectionId(), tx_id); + transactions.put(user_tx_id, transactionId); + return transactionId; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public ConnectionId getConnectionId() { + return connectionId; + } + + public void setConnectionId(ConnectionId connectionId) { + this.connectionId = connectionId; + } + + public static synchronized int generateTransactionId() { + return ++transactionIdCounter; + } + + public ConsumerId createConsumerId() { + throw new RuntimeException("TODO!!"); + } + + public MessageId generateMessageId() { + throw new RuntimeException("TODO!!"); + } + + // TODO static??? + public static short generateCommandId() { + throw new RuntimeException("TODO!!"); + } + + public SessionId generateSessionId() { + throw new RuntimeException("TODO!!"); + } + + public Packet marshal(Object arg0) throws IOException { + throw new RuntimeException("TODO!!"); + } + + public Object unmarshal(Packet arg0) throws IOException { + throw new RuntimeException("TODO!!"); + } + + public void marshal(Object arg0, DataOutputStream arg1) throws IOException { + throw new RuntimeException("TODO!!"); + } + + public Object unmarshal(DataInputStream arg0) throws IOException { + throw new RuntimeException("TODO!!"); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/Subscribe.java b/activemq-core/src/main/java/org/activemq/transport/stomp/Subscribe.java new file mode 100644 index 0000000000..b7a3c9e024 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/Subscribe.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConsumerId; +import org.activemq.command.ConsumerInfo; + +import java.io.DataInput; +import java.io.IOException; +import java.util.Properties; + +class Subscribe implements StompCommand { + private HeaderParser headerParser = new HeaderParser(); + private StompWireFormat format; + + Subscribe(StompWireFormat format) { + this.format = format; + } + + public CommandEnvelope build(String commandLine, DataInput in) throws IOException { + ConsumerInfo ci = new ConsumerInfo(); + Properties headers = headerParser.parse(in); + String destination = headers.getProperty(Stomp.Headers.Subscribe.DESTINATION); + ActiveMQDestination actual_dest = DestinationNamer.convert(destination); + ci.setDestination(DestinationNamer.convert(destination)); + ConsumerId consumerId = format.createConsumerId(); + ci.setConsumerId(consumerId); + ci.setResponseRequired(true); + // ci.setSessionId(format.getSessionId()); + while (in.readByte() != 0) { + } + String subscriptionId = headers.getProperty(Stomp.Headers.Subscribe.ID, Subscription.NO_ID); + Subscription s = new Subscription(format, consumerId, subscriptionId); + s.setDestination(actual_dest); + String ack_mode_key = headers.getProperty(Stomp.Headers.Subscribe.ACK_MODE); + if (ack_mode_key != null && ack_mode_key.equals(Stomp.Headers.Subscribe.AckModeValues.CLIENT)) { + s.setAckMode(Subscription.CLIENT_ACK); + } + + format.addSubscription(s); + return new CommandEnvelope(ci, headers); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/Subscription.java b/activemq-core/src/main/java/org/activemq/transport/stomp/Subscription.java new file mode 100644 index 0000000000..0a63e0b3c4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/Subscription.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.command.ActiveMQBytesMessage; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQTextMessage; +import org.activemq.command.ConsumerId; +import org.activemq.command.MessageAck; +import org.activemq.command.RemoveInfo; + +import javax.jms.JMSException; + +import java.io.DataOutput; +import java.io.IOException; + +public class Subscription { + private ActiveMQDestination destination; + private int ackMode = 1; + private StompWireFormat format; + private final ConsumerId consumerId; + private final String subscriptionId; + public static final String NO_ID = "~~ NO SUCH THING ~~%%@#!Q"; + + public Subscription(StompWireFormat format, ConsumerId consumerId, String subscriptionId) { + this.format = format; + this.consumerId = consumerId; + this.subscriptionId = subscriptionId; + } + + void setDestination(ActiveMQDestination actual_dest) { + this.destination = actual_dest; + } + + void receive(ActiveMQTextMessage msg, DataOutput out) throws IOException, JMSException { + if (ackMode == CLIENT_ACK) { + AckListener listener = new AckListener(msg, consumerId, subscriptionId); + format.addAckListener(listener); + } + else if (ackMode == AUTO_ACK) { + MessageAck ack = new MessageAck(); + // if (format.isInTransaction()) + // ack.setTransactionIDString(format.getTransactionId()); + ack.setDestination(msg.getDestination()); + ack.setConsumerId(consumerId); + ack.setMessageID(msg.getMessageId()); + ack.setAckType(MessageAck.STANDARD_ACK_TYPE); + format.enqueueCommand(ack); + } + FrameBuilder builder = new FrameBuilder(Stomp.Responses.MESSAGE).addHeaders(msg).setBody(msg.getText().getBytes()); + if (!subscriptionId.equals(NO_ID)) { + builder.addHeader(Stomp.Headers.Message.SUBSCRIPTION, subscriptionId); + } + out.write(builder.toFrame()); + } + + void receive(ActiveMQBytesMessage msg, DataOutput out) throws IOException, JMSException { + // @todo refactor this and the other receive form to remoce duplication + // -bmc + if (ackMode == CLIENT_ACK) { + AckListener listener = new AckListener(msg, consumerId, subscriptionId); + format.addAckListener(listener); + } + else if (ackMode == AUTO_ACK) { + MessageAck ack = new MessageAck(); + // if (format.isInTransaction()) + // ack.setTransactionIDString(format.getTransactionId()); + ack.setDestination(msg.getDestination()); + ack.setConsumerId(consumerId); + ack.setMessageID(msg.getMessageId()); + ack.setAckType(MessageAck.STANDARD_ACK_TYPE); + format.enqueueCommand(ack); + } + FrameBuilder builder = new FrameBuilder(Stomp.Responses.MESSAGE).addHeaders(msg).setBody(msg.getContent().getData()); + if (!subscriptionId.equals(NO_ID)) { + builder.addHeader(Stomp.Headers.Message.SUBSCRIPTION, subscriptionId); + } + out.write(builder.toFrame()); + } + + ActiveMQDestination getDestination() { + return destination; + } + + static final int AUTO_ACK = 1; + static final int CLIENT_ACK = 2; + + public void setAckMode(int clientAck) { + this.ackMode = clientAck; + } + + public RemoveInfo close() { + RemoveInfo unsub = new RemoveInfo(); + unsub.setObjectId(consumerId); + return unsub; + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/Unsubscribe.java b/activemq-core/src/main/java/org/activemq/transport/stomp/Unsubscribe.java new file mode 100644 index 0000000000..22673fbb52 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/Unsubscribe.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.command.ActiveMQDestination; + +import java.io.DataInput; +import java.io.IOException; +import java.util.Properties; + +public class Unsubscribe implements StompCommand { + private static final HeaderParser parser = new HeaderParser(); + private final StompWireFormat format; + + Unsubscribe(StompWireFormat format) { + this.format = format; + } + + public CommandEnvelope build(String commandLine, DataInput in) throws IOException { + Properties headers = parser.parse(in); + while (in.readByte() == 0) { + } + + String dest_name = headers.getProperty(Stomp.Headers.Unsubscribe.DESTINATION); + ActiveMQDestination destination = DestinationNamer.convert(dest_name); + + Subscription s = format.getSubscriptionFor(destination); + return new CommandEnvelope(s.close(), headers); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/stomp/package.html b/activemq-core/src/main/java/org/activemq/transport/stomp/package.html new file mode 100644 index 0000000000..70f8e90c22 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/stomp/package.html @@ -0,0 +1,10 @@ + + + + + +An implementation of the Stomp protocol which is a simple wire protocol for writing clients for ActiveMQ in different +languages like Ruby, Python, PHP, C etc. + + + diff --git a/activemq-core/src/main/java/org/activemq/transport/tcp/ResponseHolder.java b/activemq-core/src/main/java/org/activemq/transport/tcp/ResponseHolder.java new file mode 100755 index 0000000000..894fbec12b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/tcp/ResponseHolder.java @@ -0,0 +1,87 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.transport.tcp; + +import org.activemq.command.Response; + +/** + * ResponseHolder utility + * + * @version $Revision: 1.1.1.1 $ + */ +public class ResponseHolder { + protected Response response; + protected Object lock = new Object(); + protected boolean notified; + + /** + * Construct a receipt holder + */ + public ResponseHolder() { + } + + /** + * Set the Response for this holder + * + * @param r + */ + public void setResponse(Response r) { + synchronized (lock) { + this.response = r; + notified = true; + lock.notify(); + } + } + + /** + * Get the Response + * + * @return the Response or null if it is closed + */ + public Response getResponse() { + return getResponse(0); + } + + /** + * wait upto timeout timeout ms to get a receipt + * + * @param timeout + * @return + */ + public Response getResponse(int timeout) { + synchronized (lock) { + if (!notified) { + try { + lock.wait(timeout); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + return this.response; + } + + /** + * close this holder + */ + public void close() { + synchronized (lock) { + notified = true; + lock.notifyAll(); + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/tcp/TcpBufferedInputStream.java b/activemq-core/src/main/java/org/activemq/transport/tcp/TcpBufferedInputStream.java new file mode 100755 index 0000000000..65eac11069 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/tcp/TcpBufferedInputStream.java @@ -0,0 +1,124 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.transport.tcp; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +/** + * An optimized buffered input stream for Tcp + * + * @version $Revision: 1.1.1.1 $ + */ +public class TcpBufferedInputStream extends FilterInputStream{ + private static final int DEFAULT_BUFFER_SIZE=8192; + protected byte internalBuffer[]; + protected int count; + protected int position; + + public TcpBufferedInputStream(InputStream in){ + this(in,DEFAULT_BUFFER_SIZE); + } + + public TcpBufferedInputStream(InputStream in,int size){ + super(in); + if(size<=0){ + throw new IllegalArgumentException("Buffer size <= 0"); + } + internalBuffer=new byte[size]; + } + + private void fill() throws IOException{ + byte[] buffer=internalBuffer; + count=position=0; + int n=in.read(buffer,position,buffer.length-position); + if(n>0) + count=n+position; + } + + public int read() throws IOException{ + if(position>=count){ + fill(); + if(position>=count) + return -1; + } + return internalBuffer[position++]&0xff; + } + + private int readStream(byte[] b,int off,int len) throws IOException{ + int avail=count-position; + if(avail<=0){ + if(len>=internalBuffer.length){ + return in.read(b,off,len); + } + fill(); + avail=count-position; + if(avail<=0) + return -1; + } + int cnt=(avail=len) + return n; + // if not closed but no bytes available, return + InputStream input=in; + if(input!=null&&input.available()<=0) + return n; + } + } + + public long skip(long n) throws IOException{ + if(n<=0){ + return 0; + } + long avail=count-position; + if(avail<=0){ + return in.skip(n); + } + long skipped=(availActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.transport.tcp; + +import java.io.EOFException; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * An optimized buffered outputstream for Tcp + * + * @version $Revision: 1.1.1.1 $ + */ + +public class TcpBufferedOutputStream extends FilterOutputStream { + private final static int BUFFER_SIZE = 8192; + private byte[] buffer; + private int count; + private boolean closed; + + /** + * Constructor + * + * @param out + */ + public TcpBufferedOutputStream(OutputStream out) { + this(out, BUFFER_SIZE); + } + + /** + * Creates a new buffered output stream to write data to the specified underlying output stream with the specified + * buffer size. + * + * @param out the underlying output stream. + * @param size the buffer size. + * @throws IllegalArgumentException if size <= 0. + */ + public TcpBufferedOutputStream(OutputStream out, int size) { + super(out); + if (size <= 0) { + throw new IllegalArgumentException("Buffer size <= 0"); + } + buffer = new byte[size]; + } + + /** + * write a byte on to the stream + * + * @param b - byte to write + * @throws IOException + */ + public void write(int b) throws IOException { + checkClosed(); + if (availableBufferToWrite() < 1) { + flush(); + } + buffer[count++] = (byte) b; + } + + + /** + * write a byte array to the stream + * + * @param b the byte buffer + * @param off the offset into the buffer + * @param len the length of data to write + * @throws IOException + */ + public void write(byte b[], int off, int len) throws IOException { + checkClosed(); + if (availableBufferToWrite() < len) { + flush(); + } + if (buffer.length >= len) { + System.arraycopy(b, off, buffer, count, len); + count += len; + } + else { + out.write(b, off, len); + } + } + + /** + * flush the data to the output stream + * This doesn't call flush on the underlying outputstream, because + * Tcp is particularly efficent at doing this itself .... + * + * @throws IOException + */ + public void flush() throws IOException { + if (count > 0 && out != null) { + out.write(buffer, 0, count); + count = 0; + } + } + + /** + * close this stream + * + * @throws IOException + */ + public void close() throws IOException { + super.close(); + closed = true; + } + + + /** + * Checks that the stream has not been closed + * + * @throws IOException + */ + protected void checkClosed() throws IOException { + if (closed) { + throw new EOFException("Cannot write to the stream any more it has already been closed"); + } + } + + /** + * @return the amount free space in the buffer + */ + private int availableBufferToWrite() { + return buffer.length - count; + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/tcp/TcpTransport.java b/activemq-core/src/main/java/org/activemq/transport/tcp/TcpTransport.java new file mode 100755 index 0000000000..58e5604228 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/tcp/TcpTransport.java @@ -0,0 +1,277 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.transport.tcp; + +import org.activeio.command.WireFormat; +import org.activemq.Service; +import org.activemq.command.Command; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportThreadSupport; +import org.activemq.util.ServiceStopper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.net.UnknownHostException; + +/** + * An implementation of the {@link Transport} interface using raw tcp/ip + * + * @version $Revision$ + */ +public class TcpTransport extends TransportThreadSupport implements Transport, Service, Runnable { + private static final Log log = LogFactory.getLog(TcpTransport.class); + + private int soTimeout = 10000; + private int socketBufferSize = 64 * 1024; + private Socket socket; + private DataOutputStream dataOut; + private DataInputStream dataIn; + private WireFormat wireFormat; + private boolean trace; + private boolean useLocalHost = true; + private int minmumWireFormatVersion; + + /** + * Construct basic helpers + * + * @param wireFormat + */ + protected TcpTransport(WireFormat wireFormat) { + this.wireFormat = wireFormat; + } + + /** + * Connect to a remote Node - e.g. a Broker + * + * @param wireFormat + * @param remoteLocation + * @throws IOException + * @throws UnknownHostException + */ + public TcpTransport(WireFormat wireFormat, URI remoteLocation) throws UnknownHostException, IOException { + this(wireFormat); + this.socket = createSocket(remoteLocation); + initializeStreams(); + } + + /** + * Connect to a remote Node - e.g. a Broker + * + * @param wireFormat + * @param remoteLocation + * @param localLocation - + * e.g. local InetAddress and local port + * @throws IOException + * @throws UnknownHostException + */ + public TcpTransport(WireFormat wireFormat, URI remoteLocation, URI localLocation) throws UnknownHostException, IOException { + this(wireFormat); + this.socket = createSocket(remoteLocation, localLocation); + initializeStreams(); + } + + /** + * Initialize from a server Socket + * + * @param wireFormat + * @param socket + * @throws IOException + */ + public TcpTransport(WireFormat wireFormat, Socket socket) throws IOException { + this(wireFormat); + this.socket = socket; + initialiseSocket(socket); + initializeStreams(); + setDaemon(true); + } + + /** + * A one way asynchronous send + */ + public void oneway(Command command) throws IOException { + wireFormat.marshal(command, dataOut); + dataOut.flush(); + } + + /** + * @return pretty print of 'this' + */ + public String toString() { + return "TcpTransport: " + socket; + } + + /** + * reads packets from a Socket + */ + public void run() { + log.trace("TCP consumer thread starting"); + while (!isClosed()) { + try { + Command command = (Command) wireFormat.unmarshal(dataIn); + doConsume(command); + } + catch (SocketTimeoutException e) { + } + catch (InterruptedIOException e) { + } + catch (IOException e) { + try { + stop(); + } + catch (Exception e2) { + log.warn("Caught while closing: " + e2 + ". Now Closed", e2); + } + onException(e); + } + } + } + + // Properties + // ------------------------------------------------------------------------- + + public boolean isTrace() { + return trace; + } + + public void setTrace(boolean trace) { + this.trace = trace; + } + + public int getMinmumWireFormatVersion() { + return minmumWireFormatVersion; + } + + public void setMinmumWireFormatVersion(int minmumWireFormatVersion) { + this.minmumWireFormatVersion = minmumWireFormatVersion; + } + + public boolean isUseLocalHost() { + return useLocalHost; + } + + /** + * Sets whether 'localhost' or the actual local host name should be used to + * make local connections. On some operating systems such as Macs its not + * possible to connect as the local host name so localhost is better. + */ + public void setUseLocalHost(boolean useLocalHost) { + this.useLocalHost = useLocalHost; + } + + // Implementation methods + // ------------------------------------------------------------------------- + + /** + * Factory method to create a new socket + * + * @param remoteLocation + * the URI to connect to + * @return the newly created socket + * @throws UnknownHostException + * @throws IOException + */ + protected Socket createSocket(URI remoteLocation) throws UnknownHostException, IOException { + String host = resolveHostName(remoteLocation.getHost()); + SocketAddress sockAddress = new InetSocketAddress(host, remoteLocation.getPort()); + Socket sock = new Socket(); + initialiseSocket(sock); + sock.connect(sockAddress); + return sock; + } + + /** + * Factory method to create a new socket + * + * @param remoteLocation + * @param localLocation + * @return + * @throws IOException + * @throws IOException + * @throws UnknownHostException + */ + protected Socket createSocket(URI remoteLocation, URI localLocation) throws IOException, UnknownHostException { + String host = resolveHostName(remoteLocation.getHost()); + SocketAddress sockAddress = new InetSocketAddress(host, remoteLocation.getPort()); + SocketAddress localAddress = new InetSocketAddress(InetAddress.getByName(localLocation.getHost()), localLocation.getPort()); + Socket sock = new Socket(); + initialiseSocket(sock); + sock.bind(localAddress); + sock.connect(sockAddress); + return sock; + } + + protected String resolveHostName(String host) throws UnknownHostException { + String localName = InetAddress.getLocalHost().getHostName(); + if (localName != null && isUseLocalHost()) { + if (localName.equals(host)) { + return "localhost"; + } + } + return host; + } + + /** + * Configures the socket for use + * + * @param sock + * @throws SocketException + */ + protected void initialiseSocket(Socket sock) throws SocketException { + try { + sock.setReceiveBufferSize(socketBufferSize); + sock.setSendBufferSize(socketBufferSize); + } + catch (SocketException se) { + log.warn("Cannot set socket buffer size = " + socketBufferSize, se); + } + sock.setSoTimeout(soTimeout); + } + + protected void initializeStreams() throws IOException { + TcpBufferedInputStream buffIn = new TcpBufferedInputStream(socket.getInputStream(), 4096); + this.dataIn = new DataInputStream(buffIn); + TcpBufferedOutputStream buffOut = new TcpBufferedOutputStream(socket.getOutputStream(), 8192); + this.dataOut = new DataOutputStream(buffOut); + } + + protected void doStop(ServiceStopper stopper) throws Exception { + closeStreams(); + if (socket != null) { + socket.close(); + } + } + + protected void closeStreams() throws IOException { + if (dataOut != null) { + dataOut.close(); + } + if (dataIn != null) { + dataIn.close(); + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/tcp/TcpTransportFactory.java b/activemq-core/src/main/java/org/activemq/transport/tcp/TcpTransportFactory.java new file mode 100755 index 0000000000..d548f055b8 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/tcp/TcpTransportFactory.java @@ -0,0 +1,103 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.transport.tcp; + +import org.activeio.command.WireFormat; +import org.activemq.transport.MutexTransport; +import org.activemq.transport.ResponseCorrelator; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportLogger; +import org.activemq.transport.TransportServer; +import org.activemq.transport.WireFormatNegotiator; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.URISupport; + +import javax.net.ServerSocketFactory; +import javax.net.SocketFactory; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; + +public class TcpTransportFactory extends TransportFactory { + + public TransportServer doBind(String brokerId, final URI location) throws IOException { + try { + Map options = new HashMap(URISupport.parseParamters(location)); + + TcpTransportServer server = new TcpTransportServer(location); + server.setWireFormatFactory(createWireFormatFactory(options)); + IntrospectionSupport.setProperties(server, options); + + return server; + } + catch (URISyntaxException e) { + throw IOExceptionSupport.create(e); + } + } + + public Transport configure(Transport transport, WireFormat format, Map options) { + IntrospectionSupport.setProperties(transport, options); + TcpTransport tcpTransport = (TcpTransport) transport; + if (tcpTransport.isTrace()) { + transport = new TransportLogger(transport); + } + + // TODO: missing inactivity monitor + // transport = new InactivityMonitor(transport, + // temp.getMaxInactivityDuration(), activityMonitor.getReadCounter(), + // activityMonitor.getWriteCounter()); + transport = new WireFormatNegotiator(transport, format, tcpTransport.getMinmumWireFormatVersion()); + transport = new MutexTransport(transport); + transport = new ResponseCorrelator(transport); + return transport; + } + + public Transport compositeConfigure(Transport transport, WireFormat format, Map options) { + IntrospectionSupport.setProperties(transport, options); + TcpTransport tcpTransport = (TcpTransport) transport; + if (tcpTransport.isTrace()) { + transport = new TransportLogger(transport); + } + + // TODO: missing inactivity monitor + // transport = new InactivityMonitor(transport, + // temp.getMaxInactivityDuration(), activityMonitor.getReadCounter(), + // activityMonitor.getWriteCounter()); + transport = new WireFormatNegotiator(transport, format, tcpTransport.getMinmumWireFormatVersion()); + return transport; + } + + protected Transport createTransport(URI location, WireFormat wf) throws UnknownHostException, IOException { + return new TcpTransport(wf, location); + } + + protected ServerSocketFactory createServerSocketFactory() { + return ServerSocketFactory.getDefault(); + } + + protected SocketFactory createSocketFactory() { + return SocketFactory.getDefault(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/tcp/TcpTransportServer.java b/activemq-core/src/main/java/org/activemq/transport/tcp/TcpTransportServer.java new file mode 100755 index 0000000000..2254ce0acc --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/tcp/TcpTransportServer.java @@ -0,0 +1,181 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.transport.tcp; + +import org.activeio.command.WireFormat; +import org.activeio.command.WireFormatFactory; +import org.activemq.command.BrokerInfo; +import org.activemq.openwire.OpenWireFormatFactory; +import org.activemq.transport.TransportServerThreadSupport; +import org.activemq.util.ServiceStopper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.HashMap; + +/** + * A TCP based implementation of {@link TransportServer} + * + * @version $Revision: 1.1 $ + */ + +public class TcpTransportServer extends TransportServerThreadSupport { + private static final Log log = LogFactory.getLog(TcpTransportServer.class); + private ServerSocket serverSocket; + private int backlog = 5000; + private WireFormatFactory wireFormatFactory = new OpenWireFormatFactory(); + private TcpTransportFactory transportFactory = new TcpTransportFactory(); + + /** + * Constructor + * + * @param location + * @throws IOException + * @throws URISyntaxException + */ + public TcpTransportServer(URI location) throws IOException, URISyntaxException { + super(location); + serverSocket = createServerSocket(location); + serverSocket.setSoTimeout(2000); + updatePhysicalUri(location); + } + + /** + * @return Returns the wireFormatFactory. + */ + public WireFormatFactory getWireFormatFactory() { + return wireFormatFactory; + } + + /** + * @param wireFormatFactory + * The wireFormatFactory to set. + */ + public void setWireFormatFactory(WireFormatFactory wireFormatFactory) { + this.wireFormatFactory = wireFormatFactory; + } + + /** + * Associates a broker info with the transport server so that the transport + * can do discovery advertisements of the broker. + * + * @param brokerInfo + */ + public void setBrokerInfo(BrokerInfo brokerInfo) { + } + + /** + * pull Sockets from the ServerSocket + */ + public void run() { + while (!isClosed()) { + Socket socket = null; + try { + socket = serverSocket.accept(); + if (socket != null) { + if (isClosed() || getAcceptListener() == null) { + socket.close(); + } + else { + HashMap options = new HashMap(); + WireFormat format = wireFormatFactory.createWireFormat(); + TcpTransport transport = new TcpTransport(format, socket); + getAcceptListener().onAccept(transportFactory.configure(transport, format, options)); + } + } + } + catch (SocketTimeoutException ste) { + // expect this to happen + } + catch (Exception e) { + if (!isClosing()) { + onAcceptError(e); + } else if (!isClosed()) { + log.warn("run()", e); + onAcceptError(e); + } + } + } + } + + /** + * @return pretty print of this + */ + public String toString() { + return "TcpTransportServer@" + getLocation(); + } + + /** + * In cases where we construct ourselves with a zero port we need to + * regenerate the URI with the real physical port so that people can connect + * to us via discovery + * + * @throws UnknownHostException + */ + protected void updatePhysicalUri(URI bindAddr) throws URISyntaxException, UnknownHostException { + setLocation(new URI(bindAddr.getScheme(), bindAddr.getUserInfo(), resolveHostName(bindAddr.getHost()), serverSocket.getLocalPort(), bindAddr.getPath(), + bindAddr.getQuery(), bindAddr.getFragment())); + } + + /** + * + * @param hostName + * @return real hostName + * @throws UnknownHostException + */ + protected String resolveHostName(String hostName) throws UnknownHostException { + String result = hostName; + // hostname can be null for vm:// protocol ... + if (hostName != null && (hostName.equalsIgnoreCase("localhost") || hostName.equals("127.0.0.1"))) { + result = InetAddress.getLocalHost().getHostName(); + } + return result; + } + + /** + * Factory method to create a new ServerSocket + * + * @throws UnknownHostException + * @throws IOException + */ + protected ServerSocket createServerSocket(URI bind) throws UnknownHostException, IOException { + ServerSocket answer = null; + String host = bind.getHost(); + host = (host == null || host.length() == 0) ? "localhost" : host; + InetAddress addr = InetAddress.getByName(host); + if (host.trim().equals("localhost") || addr.equals(InetAddress.getLocalHost())) { + answer = new ServerSocket(bind.getPort(), backlog); + } + else { + answer = new ServerSocket(bind.getPort(), backlog, addr); + } + return answer; + } + + protected void doStop(ServiceStopper stopper) throws Exception { + if (serverSocket != null) { + serverSocket.close(); + } + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/tcp/package.html b/activemq-core/src/main/java/org/activemq/transport/tcp/package.html new file mode 100755 index 0000000000..ee757065be --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/tcp/package.html @@ -0,0 +1,9 @@ + + + + + +TCP/IP based Transport implementation. + + + diff --git a/activemq-core/src/main/java/org/activemq/transport/vm/VMTransport.java b/activemq-core/src/main/java/org/activemq/transport/vm/VMTransport.java new file mode 100755 index 0000000000..2a9eb18702 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/vm/VMTransport.java @@ -0,0 +1,120 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.vm; + +import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import org.activemq.command.Command; +import org.activemq.command.Response; +import org.activemq.transport.FutureResponse; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportListener; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +/** + * A Transport implementation that uses direct method invocations. + * + * @version $Revision$ + */ +public class VMTransport implements Transport{ + private static final Log log=LogFactory.getLog(VMTransport.class); + protected VMTransport peer; + protected TransportListener transportListener; + protected boolean disposed; + protected boolean marshal; + protected boolean network; + protected List queue = Collections.synchronizedList(new LinkedList()); + + + synchronized public VMTransport getPeer(){ + return peer; + } + + synchronized public void setPeer(VMTransport peer){ + this.peer=peer; + } + + public void oneway(Command command) throws IOException{ + if(disposed) + throw new IOException("Transport disposed."); + if(peer==null) + throw new IOException("Peer not connected."); + if (!peer.disposed){ + TransportListener tl = peer.transportListener; + queue = peer.queue; + if (tl != null){ + tl.onCommand(command); + }else { + queue.add(command); + } + } + } + + public FutureResponse asyncRequest(Command command) throws IOException{ + throw new AssertionError("Unsupported Method"); + } + + public Response request(Command command) throws IOException{ + throw new AssertionError("Unsupported Method"); + } + + synchronized public void setTransportListener(TransportListener commandListener){ + this.transportListener=commandListener; + } + + public synchronized void start() throws Exception{ + if(transportListener==null) + throw new IOException("TransportListener not set."); + for (Iterator iter = queue.iterator(); iter.hasNext();) { + Command command = (Command) iter.next(); + transportListener.onCommand(command); + iter.remove(); + } + } + + public void stop() throws Exception{ + if(!disposed){ + disposed=true; + } + } + + public Object narrow(Class target){ + if(target.isAssignableFrom(getClass())){ + return this; + } + return null; + } + + public boolean isMarshal(){ + return marshal; + } + + public void setMarshal(boolean marshal){ + this.marshal=marshal; + } + + public boolean isNetwork(){ + return network; + } + + public void setNetwork(boolean network){ + this.network=network; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/vm/VMTransportFactory.java b/activemq-core/src/main/java/org/activemq/transport/vm/VMTransportFactory.java new file mode 100755 index 0000000000..9e6eb75722 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/vm/VMTransportFactory.java @@ -0,0 +1,171 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.vm; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import org.activemq.broker.BrokerFactory; +import org.activemq.broker.BrokerRegistry; +import org.activemq.broker.BrokerService; +import org.activemq.broker.TransportConnector; +import org.activemq.broker.BrokerFactory.BrokerFactoryHandler; +import org.activemq.transport.MarshallingTransportFilter; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportServer; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.IntrospectionSupport; +import org.activemq.util.ServiceSupport; +import org.activemq.util.URISupport; +import org.activemq.util.URISupport.CompositeData; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +public class VMTransportFactory extends TransportFactory { + + final public static ConcurrentHashMap brokers = new ConcurrentHashMap(); + final public static ConcurrentHashMap connectors = new ConcurrentHashMap(); + final public static ConcurrentHashMap servers = new ConcurrentHashMap(); + + BrokerFactoryHandler brokerFactoryHandler; + + public Transport doConnect(URI location) throws Exception { + return VMTransportServer.configure(doCompositeConnect(location)); + } + + public Transport doCompositeConnect(URI location) throws Exception { + URI brokerURI; + String host; + Map options; + + CompositeData data = URISupport.parseComposite(location); + if( data.getComponents().length==1 && "broker".equals(data.getComponents()[0].getScheme()) ) { + brokerURI = data.getComponents()[0]; + + CompositeData brokerData = URISupport.parseComposite(brokerURI); + host = (String)brokerData.getParameters().get("brokerName"); + if( host == null ) + host = "localhost"; + if( brokerData.getPath()!=null ) + host = data.getPath(); + + options = data.getParameters(); + location = new URI("vm://"+host); + } else { + // If using the less complex vm://localhost?broker.persistent=true form + try { + host = location.getHost(); + options = URISupport.parseParamters(location); + Map brokerOptions = IntrospectionSupport.extractProperties(options, "broker."); + brokerURI = new URI("broker://()/"+host+"?"+URISupport.createQueryString(brokerOptions)); + } catch (URISyntaxException e1) { + throw IOExceptionSupport.create(e1); + } + + location = new URI("vm://"+host); + } + + VMTransportServer server = (VMTransportServer) servers.get(host); + if( server == null ) { + BrokerService broker = BrokerRegistry.getInstance().lookup(host); + if (broker == null) { + try { + if( brokerFactoryHandler !=null ) { + broker = brokerFactoryHandler.createBroker(brokerURI); + } else { + broker = BrokerFactory.createBroker(brokerURI); + } + broker.start(); + } + catch (URISyntaxException e) { + throw IOExceptionSupport.create(e); + } + brokers.put(host, broker); + } + server = (VMTransportServer) servers.get(host); + if (server == null) { + server = (VMTransportServer) bind(location, true); + TransportConnector connector = new TransportConnector(broker.getBroker(), server); + connector.start(); + connectors.put(host, connector); + } + } + + VMTransport vmtransport = server.connect(); + IntrospectionSupport.setProperties(vmtransport, options); + + Transport transport = vmtransport; + if (vmtransport.isMarshal()) { + HashMap optionsCopy = new HashMap(options); + transport = new MarshallingTransportFilter(transport, createWireFormat(options), createWireFormat(optionsCopy)); + } + + if( !options.isEmpty() ) { + throw new IllegalArgumentException("Invalid connect parameters: "+options); + } + + return transport; + } + + public TransportServer doBind(String brokerId,URI location) throws IOException { + return bind(location, false); + } + + /** + * @param location + * @return + * @throws IOException + */ + private TransportServer bind(URI location, boolean dispose) throws IOException { + String host = location.getHost(); + VMTransportServer server = new VMTransportServer(location, dispose); + Object currentBoundValue = servers.get(host); + if (currentBoundValue != null) { + throw new IOException("VMTransportServer already bound at: " + location); + } + servers.put(host, server); + return server; + } + + public static void stopped(VMTransportServer server) { + String host = server.getBindURI().getHost(); + servers.remove(host); + TransportConnector connector = (TransportConnector) connectors.remove(host); + if (connector != null) { + ServiceSupport.dispose(connector); + BrokerService broker = (BrokerService) brokers.remove(host); + if (broker != null) { + ServiceSupport.dispose(broker); + } + } + } + + public BrokerFactoryHandler getBrokerFactoryHandler() { + return brokerFactoryHandler; + } + + public void setBrokerFactoryHandler(BrokerFactoryHandler brokerFactoryHandler) { + this.brokerFactoryHandler = brokerFactoryHandler; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/transport/vm/VMTransportServer.java b/activemq-core/src/main/java/org/activemq/transport/vm/VMTransportServer.java new file mode 100755 index 0000000000..04aa7d205b --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/vm/VMTransportServer.java @@ -0,0 +1,133 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.vm; + +import java.io.IOException; +import java.net.URI; + +import org.activemq.command.BrokerInfo; +import org.activemq.transport.MutexTransport; +import org.activemq.transport.ResponseCorrelator; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportAcceptListener; +import org.activemq.transport.TransportServer; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +/** + * Broker side of the VMTransport + * + */ +public class VMTransportServer implements TransportServer { + + private TransportAcceptListener acceptListener; + private final URI location; + private boolean disposed; + + private final AtomicInteger connectionCount = new AtomicInteger(0); + private final boolean disposeOnDisconnect; + + /** + * @param location + * @param disposeOnDisconnect + */ + public VMTransportServer(URI location, boolean disposeOnDisconnect) { + this.location = location; + this.disposeOnDisconnect=disposeOnDisconnect; + } + + /** + *@return a pretty print of this + */ + public String toString(){ + return "VMTransportServer(" + location +")"; + } + + /** + * @return new VMTransport + * @throws IOException + */ + public VMTransport connect() throws IOException { + TransportAcceptListener al; + synchronized (this) { + if( disposed ) + throw new IOException("Server has been disposed."); + al = acceptListener; + } + if( al == null) + throw new IOException("Server TransportAcceptListener is null."); + + connectionCount.incrementAndGet(); + VMTransport client = new VMTransport() { + public void stop() throws Exception { + if( disposed ) + return; + super.stop(); + if( connectionCount.decrementAndGet()==0 && disposeOnDisconnect ) { + VMTransportServer.this.stop(); + } + }; + }; + + VMTransport server = new VMTransport(); + client.setPeer(server); + server.setPeer(client); + al.onAccept(configure(server)); + return client; + } + + /** + * Configure transport + * @param transport + * @return the Transport + */ + public static Transport configure(Transport transport) { + transport = new MutexTransport(transport); + transport = new ResponseCorrelator(transport); + return transport; + } + + + /** + * Set the Transport accept listener for new Connections + * @param acceptListener + * + */ + synchronized public void setAcceptListener(TransportAcceptListener acceptListener) { + this.acceptListener = acceptListener; + } + + public void start() throws IOException { + } + + public void stop() throws IOException { + VMTransportFactory.stopped(this); + } + + public URI getConnectURI() { + return location; + } + + public URI getBindURI() { + return location; + } + + public void setBrokerInfo(BrokerInfo brokerInfo) { + } +} diff --git a/activemq-core/src/main/java/org/activemq/transport/vm/package.html b/activemq-core/src/main/java/org/activemq/transport/vm/package.html new file mode 100755 index 0000000000..f27d76feae --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/transport/vm/package.html @@ -0,0 +1,9 @@ + + + + + +In-JVM based Transport implementation. + + + diff --git a/activemq-core/src/main/java/org/activemq/util/Callback.java b/activemq-core/src/main/java/org/activemq/util/Callback.java new file mode 100755 index 0000000000..c3a06df163 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/Callback.java @@ -0,0 +1,39 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +/** + * A simple callback object used by the + * {@link org.activemq.util.TransactionTemplate} + * and {@link org.activemq.util.ExceptionTemplate} + objects to provide automatic transactional or exception handling blocks. + * + * @version $Revision: 1.2 $ + */ +public interface Callback { + + /** + * Executes some piece of code within a transaction + * performing a commit if there is no exception thrown + * else a rollback is performed + * + * @throws Throwable + */ + public void execute() throws Throwable; +} diff --git a/activemq-core/src/main/java/org/activemq/util/ClassLoading.java b/activemq-core/src/main/java/org/activemq/util/ClassLoading.java new file mode 100755 index 0000000000..c99b587570 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/ClassLoading.java @@ -0,0 +1,241 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +import java.lang.reflect.Array; +import java.util.HashMap; +import java.util.Map; + +/** + * Utilities for loading classes. + * + * @version $Rev: 109957 $ $Date$ + */ +public class ClassLoading { + + /** + * Load a class for the given name.

+ *

+ * Handles loading primitive types as well as VM class and array syntax. + * + * @param className + * The name of the Class to be loaded. + * @param classLoader + * The class loader to load the Class object from. + * @return The Class object for the given name. + * @throws ClassNotFoundException + * Failed to load Class object. + */ + public static Class loadClass(final String className, final ClassLoader classLoader) throws ClassNotFoundException { + if (className == null) { + throw new IllegalArgumentException("className is null"); + } + + // First just try to load + try { + return load(className, classLoader); + } catch (ClassNotFoundException ignore) { + // handle special cases below + } + + Class type = null; + + // Check if it is a primitive type + type = getPrimitiveType(className); + if (type != null) + return type; + + // Check if it is a vm primitive + type = getVMPrimitiveType(className); + if (type != null) + return type; + + // Handle VM class syntax (Lclassname;) + if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { + String name = className.substring(1, className.length() - 1); + return load(name, classLoader); + } + + // Handle VM array syntax ([type) + if (className.charAt(0) == '[') { + int arrayDimension = className.lastIndexOf('[') + 1; + String componentClassName = className.substring(arrayDimension, className.length()); + type = loadClass(componentClassName, classLoader); + + int dim[] = new int[arrayDimension]; + java.util.Arrays.fill(dim, 0); + return Array.newInstance(type, dim).getClass(); + } + + // Handle user friendly type[] syntax + if (className.endsWith("[]")) { + // get the base component class name and the arrayDimensions + int arrayDimension = 0; + String componentClassName = className; + while (componentClassName.endsWith("[]")) { + componentClassName = componentClassName.substring(0, componentClassName.length() - 2); + arrayDimension++; + } + + // load the base type + type = loadClass(componentClassName, classLoader); + + // return the array type + int[] dim = new int[arrayDimension]; + java.util.Arrays.fill(dim, 0); + return Array.newInstance(type, dim).getClass(); + } + + // Else we can not load (give up) + throw new ClassNotFoundException(className); + } + + private static Class load(final String className, final ClassLoader classLoader) throws ClassNotFoundException { + if (classLoader == null) + return Class.forName(className); + else + return classLoader.loadClass(className); + } + + public static String getClassName(Class clazz) { + StringBuffer rc = new StringBuffer(); + while (clazz.isArray()) { + rc.append('['); + clazz = clazz.getComponentType(); + } + if (!clazz.isPrimitive()) { + rc.append('L'); + rc.append(clazz.getName()); + rc.append(';'); + } else { + rc.append(VM_PRIMITIVES_REVERSE.get(clazz)); + } + return rc.toString(); + } + + /** + * Primitive type name -> class map. + */ + private static final Map PRIMITIVES = new HashMap(); + + /** Setup the primitives map. */ + static { + PRIMITIVES.put("boolean", Boolean.TYPE); + PRIMITIVES.put("byte", Byte.TYPE); + PRIMITIVES.put("char", Character.TYPE); + PRIMITIVES.put("short", Short.TYPE); + PRIMITIVES.put("int", Integer.TYPE); + PRIMITIVES.put("long", Long.TYPE); + PRIMITIVES.put("float", Float.TYPE); + PRIMITIVES.put("double", Double.TYPE); + PRIMITIVES.put("void", Void.TYPE); + } + + /** + * Get the primitive type for the given primitive name. + * + * @param name + * Primitive type name (boolean, byte, int, ...) + * @return Primitive type or null. + */ + private static Class getPrimitiveType(final String name) { + return (Class) PRIMITIVES.get(name); + } + + /** + * VM primitive type name -> primitive type + */ + private static final HashMap VM_PRIMITIVES = new HashMap(); + + /** Setup the vm primitives map. */ + static { + VM_PRIMITIVES.put("B", byte.class); + VM_PRIMITIVES.put("C", char.class); + VM_PRIMITIVES.put("D", double.class); + VM_PRIMITIVES.put("F", float.class); + VM_PRIMITIVES.put("I", int.class); + VM_PRIMITIVES.put("J", long.class); + VM_PRIMITIVES.put("S", short.class); + VM_PRIMITIVES.put("Z", boolean.class); + VM_PRIMITIVES.put("V", void.class); + } + + /** + * VM primitive type primitive type -> name + */ + private static final HashMap VM_PRIMITIVES_REVERSE = new HashMap(); + + /** Setup the vm primitives reverse map. */ + static { + VM_PRIMITIVES_REVERSE.put(byte.class, "B"); + VM_PRIMITIVES_REVERSE.put(char.class, "C"); + VM_PRIMITIVES_REVERSE.put(double.class, "D"); + VM_PRIMITIVES_REVERSE.put(float.class, "F"); + VM_PRIMITIVES_REVERSE.put(int.class, "I"); + VM_PRIMITIVES_REVERSE.put(long.class, "J"); + VM_PRIMITIVES_REVERSE.put(short.class, "S"); + VM_PRIMITIVES_REVERSE.put(boolean.class, "Z"); + VM_PRIMITIVES_REVERSE.put(void.class, "V"); + } + + /** + * Get the primitive type for the given VM primitive name.

+ *

+ * Mapping: + * + *

+     * 
+     *    B - byte
+     *    C - char
+     *    D - double
+     *    F - float
+     *    I - int
+     *    J - long
+     *    S - short
+     *    Z - boolean
+     *    V - void
+     *  
+     * 
+ * + * @param name + * VM primitive type name (B, C, J, ...) + * @return Primitive type or null. + */ + private static Class getVMPrimitiveType(final String name) { + return (Class) VM_PRIMITIVES.get(name); + } + + /** + * Map of primitive types to their wrapper classes + */ + private static final Map PRIMITIVE_WRAPPERS = new HashMap(); + + /** Setup the wrapper map. */ + static { + PRIMITIVE_WRAPPERS.put(Boolean.TYPE, Boolean.class); + PRIMITIVE_WRAPPERS.put(Byte.TYPE, Byte.class); + PRIMITIVE_WRAPPERS.put(Character.TYPE, Character.class); + PRIMITIVE_WRAPPERS.put(Double.TYPE, Double.class); + PRIMITIVE_WRAPPERS.put(Float.TYPE, Float.class); + PRIMITIVE_WRAPPERS.put(Integer.TYPE, Integer.class); + PRIMITIVE_WRAPPERS.put(Long.TYPE, Long.class); + PRIMITIVE_WRAPPERS.put(Short.TYPE, Short.class); + PRIMITIVE_WRAPPERS.put(Void.TYPE, Void.class); + } +} diff --git a/activemq-core/src/main/java/org/activemq/util/HexSupport.java b/activemq-core/src/main/java/org/activemq/util/HexSupport.java new file mode 100755 index 0000000000..7279212048 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/HexSupport.java @@ -0,0 +1,73 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +/** + * Used to convert to hex from byte arrays and back. + * + * @version $Revision: 1.2 $ + */ +public class HexSupport { + + private static final String[] HEX_TABLE = new String[]{ + "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", + "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", + "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", + "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", + "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", + "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", + "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", + "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", + "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", + "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", + "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", + "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", + "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", + "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df", + "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff", + }; + + /** + * @param hex + * @return + */ + static public byte[] toBytesFromHex(String hex) { + byte rc[] = new byte[hex.length() / 2]; + for (int i = 0; i < rc.length; i++) { + String h = hex.substring(i * 2, i * 2 + 2); + int x = Integer.parseInt(h, 16); + rc[i] = (byte) x; + } + return rc; + } + + /** + * @param bytes + * @return + */ + static public String toHexFromBytes(byte[] bytes) { + StringBuffer rc = new StringBuffer(bytes.length * 2); + for (int i = 0; i < bytes.length; i++) { + rc.append(HEX_TABLE[0xFF & bytes[i]]); + } + return rc.toString(); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/util/IOExceptionSupport.java b/activemq-core/src/main/java/org/activemq/util/IOExceptionSupport.java new file mode 100755 index 0000000000..e3d60b7717 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/IOExceptionSupport.java @@ -0,0 +1,49 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +import java.io.IOException; + +final public class IOExceptionSupport { + + public static IOException create(String msg, Throwable cause) { + IOException exception = new IOException(msg); + exception.initCause(cause); + return exception; + } + + public static IOException create(String msg, Exception cause) { + IOException exception = new IOException(msg); + exception.initCause(cause); + return exception; + } + + public static IOException create(Throwable cause) { + IOException exception = new IOException(cause.getMessage()); + exception.initCause(cause); + return exception; + } + + public static IOException create(Exception cause) { + IOException exception = new IOException(cause.getMessage()); + exception.initCause(cause); + return exception; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/util/IdGenerator.java b/activemq-core/src/main/java/org/activemq/util/IdGenerator.java new file mode 100755 index 0000000000..5f03fdc921 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/IdGenerator.java @@ -0,0 +1,114 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Generator for Globally unique Strings. + */ + +public class IdGenerator{ + + private static final Logger log = Logger.getLogger(IdGenerator.class.getName()); + private static final String UNIQUE_STUB; + private static int instanceCount; + private static String hostName; + private String seed; + private long sequence; + + static { + String stub = ""; + boolean canAccessSystemProps = true; + try{ + SecurityManager sm = System.getSecurityManager(); + if(sm != null){ + sm.checkPropertiesAccess(); + } + }catch(SecurityException se){ + canAccessSystemProps = false; + } + + if ( canAccessSystemProps) { + try { + hostName = InetAddress.getLocalHost().getHostName(); + ServerSocket ss = new ServerSocket(0); + stub=hostName + "-" + ss.getLocalPort() + "-" + System.currentTimeMillis() + "-"; + Thread.sleep(100); + ss.close(); + }catch(Exception ioe){ + log.log(Level.WARNING, "could not generate unique stub",ioe); + } + }else{ + hostName="localhost"; + stub = hostName + "-1-" +System.currentTimeMillis() +"-"; + } + UNIQUE_STUB = stub; + } + + /** + * As we have to find the hostname as a side-affect of generating + * a unique stub, we allow it's easy retrevial here + * @return the local host name + */ + + public static String getHostName(){ + return hostName; + } + + /** + * Construct an IdGenerator + * + */ + + public IdGenerator(String prefix){ + synchronized(UNIQUE_STUB){ + this.seed = prefix + UNIQUE_STUB +(instanceCount++) +":"; + } + } + + public IdGenerator(){ + this("ID:"); + } + + /** + * Generate a unqiue id + * @return a unique id + */ + + public synchronized String generateId(){ + return this.seed + (this.sequence++); + } + + /** + * Generate a unique ID - that is friendly for a URL or file system + * @return a unique id + */ + public String generateSanitizedId(){ + String result = generateId(); + result = result.replace(':', '-'); + result = result.replace('_', '-'); + result = result.replace('.', '-'); + return result; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/util/IndentPrinter.java b/activemq-core/src/main/java/org/activemq/util/IndentPrinter.java new file mode 100755 index 0000000000..b9f4853cc0 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/IndentPrinter.java @@ -0,0 +1,90 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +import java.io.PrintWriter; + +/** + * A helper class for printing indented text + * + * @version $Revision: 1.2 $ + */ +public class IndentPrinter { + + private int indentLevel; + private String indent; + private PrintWriter out; + + public IndentPrinter() { + this(new PrintWriter(System.out), " "); + } + + public IndentPrinter(PrintWriter out) { + this(out, " "); + } + + public IndentPrinter(PrintWriter out, String indent) { + this.out = out; + this.indent = indent; + } + + public void println(Object value) { + out.print(value.toString()); + out.println(); + } + + public void println(String text) { + out.print(text); + out.println(); + } + + public void print(String text) { + out.print(text); + } + + public void printIndent() { + for (int i = 0; i < indentLevel; i++) { + out.print(indent); + } + } + + public void println() { + out.println(); + } + + public void incrementIndent() { + ++indentLevel; + } + + public void decrementIndent() { + --indentLevel; + } + + public int getIndentLevel() { + return indentLevel; + } + + public void setIndentLevel(int indentLevel) { + this.indentLevel = indentLevel; + } + + public void flush() { + out.flush(); + } +} diff --git a/activemq-core/src/main/java/org/activemq/util/IntrospectionSupport.java b/activemq-core/src/main/java/org/activemq/util/IntrospectionSupport.java new file mode 100755 index 0000000000..3e90464a70 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/IntrospectionSupport.java @@ -0,0 +1,144 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +import java.beans.PropertyEditor; +import java.beans.PropertyEditorManager; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +public class IntrospectionSupport { + + static public boolean setProperties(Object target, Map props, String optionPrefix) { + boolean rc = false; + if( target == null ) + throw new IllegalArgumentException("target was null."); + if( props == null ) + throw new IllegalArgumentException("props was null."); + + for (Iterator iter = props.keySet().iterator(); iter.hasNext();) { + String name = (String) iter.next(); + if( name.startsWith(optionPrefix) ) { + Object value = props.get(name); + name = name.substring(optionPrefix.length()); + if( setProperty(target, name, value) ) { + iter.remove(); + rc = true; + } + } + } + return rc; + } + + public static Map extractProperties(Map props, String optionPrefix) { + if( props == null ) + throw new IllegalArgumentException("props was null."); + + HashMap rc = new HashMap(props.size()); + + for (Iterator iter = props.keySet().iterator(); iter.hasNext();) { + String name = (String) iter.next(); + if( name.startsWith(optionPrefix) ) { + Object value = props.get(name); + name = name.substring(optionPrefix.length()); + rc.put(name, value); + iter.remove(); + } + } + + return rc; + } + + public static void setProperties(Object target, Map props) { + if( target == null ) + throw new IllegalArgumentException("target was null."); + if( props == null ) + throw new IllegalArgumentException("props was null."); + + for (Iterator iter = props.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry = (Entry) iter.next(); + if( setProperty(target, (String) entry.getKey(), entry.getValue()) ) { + iter.remove(); + } + } + } + + private static boolean setProperty(Object target, String name, Object value) { + try { + Class clazz = target.getClass(); + Method setter = findSetterMethod(clazz, name); + if( setter == null ) + return false; + + // If the type is null or it matches the needed type, just use the value directly + if( value == null || value.getClass()==setter.getParameterTypes()[0] ) { + setter.invoke(target, new Object[]{value}); + } else { + // We need to convert it + setter.invoke(target, new Object[]{ convert(value, setter.getParameterTypes()[0]) }); + } + return true; + } catch (Throwable ignore) { + return false; + } + } + + private static Object convert(Object value, Class type) throws URISyntaxException { + PropertyEditor editor = PropertyEditorManager.findEditor(type); + if( editor != null ) { + editor.setAsText(value.toString()); + return editor.getValue(); + } + if( type == URI.class ) { + return new URI(value.toString()); + } + return null; + } + + private static Method findSetterMethod(Class clazz, String name) { + // Build the method name. + name = "set"+name.substring(0,1).toUpperCase()+name.substring(1); + Method[] methods = clazz.getMethods(); + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + Class params[] = method.getParameterTypes(); + if( method.getName().equals(name) + && params.length==1 + && isSettableType(params[0])) { + return method; + } + } + return null; + } + + private static boolean isSettableType(Class clazz) { + if( PropertyEditorManager.findEditor(clazz)!=null ) + return true; + if( clazz == URI.class ) + return true; + return false; + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/util/JMSExceptionSupport.java b/activemq-core/src/main/java/org/activemq/util/JMSExceptionSupport.java new file mode 100755 index 0000000000..ffad5c1be6 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/JMSExceptionSupport.java @@ -0,0 +1,78 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +import javax.jms.JMSException; +import javax.jms.MessageEOFException; +import javax.jms.MessageFormatException; + +final public class JMSExceptionSupport { + + public static JMSException create(String msg, Throwable cause) { + JMSException exception = new JMSException(msg); + exception.initCause(cause); + return exception; + } + + public static JMSException create(String msg, Exception cause) { + JMSException exception = new JMSException(msg); + exception.setLinkedException(cause); + exception.initCause(cause); + return exception; + } + + public static JMSException create(Throwable cause) { + String msg = cause.getMessage(); + if( msg==null || msg.length()==0 ) + msg = cause.toString(); + JMSException exception = new JMSException(msg); + exception.initCause(cause); + return exception; + } + + public static JMSException create(Exception cause) { + String msg = cause.getMessage(); + if( msg==null || msg.length()==0 ) + msg = cause.toString(); + JMSException exception = new JMSException(msg); + exception.setLinkedException(cause); + exception.initCause(cause); + return exception; + } + + public static MessageEOFException createMessageEOFException(Exception cause) { + String msg = cause.getMessage(); + if( msg==null || msg.length()==0 ) + msg = cause.toString(); + MessageEOFException exception = new MessageEOFException(msg); + exception.setLinkedException(cause); + exception.initCause(cause); + return exception; + } + + public static MessageFormatException createMessageFormatException(Exception cause) { + String msg = cause.getMessage(); + if( msg==null || msg.length()==0 ) + msg = cause.toString(); + MessageFormatException exception = new MessageFormatException(msg); + exception.setLinkedException(cause); + exception.initCause(cause); + return exception; + } +} diff --git a/activemq-core/src/main/java/org/activemq/util/JMXSupport.java b/activemq-core/src/main/java/org/activemq/util/JMXSupport.java new file mode 100755 index 0000000000..8c24bb0864 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/JMXSupport.java @@ -0,0 +1,11 @@ +package org.activemq.util; + +public class JMXSupport { + static public String encodeObjectNamePart(String part) { + String answer = part.replaceAll("[\\:\\,\\'\\\"]", "_"); + answer = answer.replaceAll("\\?", "&qe;"); + answer = answer.replaceAll("=", "&"); + return answer; + + } +} diff --git a/activemq-core/src/main/java/org/activemq/util/LRUCache.java b/activemq-core/src/main/java/org/activemq/util/LRUCache.java new file mode 100755 index 0000000000..cf8a545d41 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/LRUCache.java @@ -0,0 +1,59 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) RAJD Consultancy Ltd + * + * Licensed 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.activemq.util; + +import java.util.LinkedHashMap; +import java.util.Map; +/** + * A Simple LRU Cache + * + * @version $Revision$ + */ + +public class LRUCache extends LinkedHashMap{ + private static final long serialVersionUID=-342098639681884413L; + protected int maxCacheSize=10000; + + + /** + * Constructs LRU Cache + * + */ + public LRUCache(){ + super(1000,0.75f,true); + } + + + + /** + * @return Returns the maxCacheSize. + */ + public int getMaxCacheSize(){ + return maxCacheSize; + } + + /** + * @param maxCacheSize + * The maxCacheSize to set. + */ + public void setMaxCacheSize(int maxCacheSize){ + this.maxCacheSize=maxCacheSize; + } + + protected boolean removeEldestEntry(Map.Entry entry){ + return size() > maxCacheSize; + } +} diff --git a/activemq-core/src/main/java/org/activemq/util/LongSequenceGenerator.java b/activemq-core/src/main/java/org/activemq/util/LongSequenceGenerator.java new file mode 100755 index 0000000000..2b8fda40f7 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/LongSequenceGenerator.java @@ -0,0 +1,37 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +public class LongSequenceGenerator { + + private long lastSequenceId; + + synchronized public long getNextSequenceId() { + return ++lastSequenceId; + } + + synchronized public long getLastSequenceId() { + return lastSequenceId; + } + + synchronized public void setLastSequenceId(long l) { + lastSequenceId = l; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/util/MapHelper.java b/activemq-core/src/main/java/org/activemq/util/MapHelper.java new file mode 100755 index 0000000000..ff9f6bc0f8 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/MapHelper.java @@ -0,0 +1,51 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +import java.util.Map; + +/** + * A bunch of utility methods for working with maps + * + * @version $Revision$ + */ +public class MapHelper { + /** + * Extracts the value from the map and coerces to a String + */ + public static String getString(Map map, String key) { + Object answer = map.get(key); + return (answer != null) ? answer.toString() : null; + } + + /** + * Extracts the value from the map and coerces to an int value + * or returns a default value if one could not be found or coerced + */ + public static int getInt(Map map, String key, int defaultValue) { + Object value = map.get(key); + if (value instanceof Number) { + return ((Number) value).intValue(); + } + else if (value instanceof String) { + return Integer.parseInt((String) value); + } + return defaultValue; + } +} diff --git a/activemq-core/src/main/java/org/activemq/util/MarshallingSupport.java b/activemq-core/src/main/java/org/activemq/util/MarshallingSupport.java new file mode 100755 index 0000000000..c7e9602e0e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/MarshallingSupport.java @@ -0,0 +1,253 @@ +package org.activemq.util; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.UTFDataFormatException; +import java.util.HashMap; +import java.util.Iterator; + +/** + * + * + * The fixed version of the UTF8 encoding function. Some older JVM's UTF8 encoding function + * breaks when handling large strings. + * + * @version $Revision$ + */ +public class MarshallingSupport { + + public static final byte NULL = 0; + public static final byte BOOLEAN_TYPE = 1; + public static final byte BYTE_TYPE = 2; + public static final byte CHAR_TYPE = 3; + public static final byte SHORT_TYPE = 4; + public static final byte INTEGER_TYPE = 5; + public static final byte LONG_TYPE = 6; + public static final byte DOUBLE_TYPE = 7; + public static final byte FLOAT_TYPE = 8; + public static final byte STRING_TYPE = 9; + public static final byte BYTE_ARRAY_TYPE = 10; + + static public void marshalPrimitiveMap(HashMap map, DataOutputStream out) throws IOException { + if( map == null ) { + out.writeInt(-1); + } else { + out.writeInt(map.size()); + for (Iterator iter = map.keySet().iterator(); iter.hasNext();) { + String name = (String) iter.next(); + out.writeUTF(name); + Object value = map.get(name); + marshalPrimitive(out, value); + } + } + } + + /** + * @param in + * @return + * @throws IOException + */ + static public HashMap unmarshalPrimitiveMap(DataInputStream in) throws IOException { + int size = in.readInt(); + if( size < 0 ) { + return null; + } else { + HashMap rc = new HashMap(size); + for(int i=0; i < size; i++) { + String name = in.readUTF(); + rc.put(name, unmarshalPrimitive(in)); + } + return rc; + } + + } + + static public void marshalPrimitive(DataOutputStream out, Object value) throws IOException { + if( value == null ) { + out.writeByte(NULL); + } else if( value.getClass() == Boolean.class ) { + out.writeByte(BOOLEAN_TYPE); + out.writeBoolean(((Boolean)value).booleanValue()); + } else if( value.getClass() == Byte.class ) { + out.writeByte(BYTE_TYPE); + out.writeByte(((Byte)value).byteValue()); + } else if( value.getClass() == Character.class ) { + out.writeByte(CHAR_TYPE); + out.writeChar(((Character)value).charValue()); + } else if( value.getClass() == Short.class ) { + out.writeByte(SHORT_TYPE); + out.writeShort(((Short)value).shortValue()); + } else if( value.getClass() == Integer.class ) { + out.writeByte(INTEGER_TYPE); + out.writeInt(((Integer)value).intValue()); + } else if( value.getClass() == Long.class ) { + out.writeByte(LONG_TYPE); + out.writeLong(((Long)value).longValue()); + } else if( value.getClass() == Float.class ) { + out.writeByte(FLOAT_TYPE); + out.writeFloat(((Float)value).floatValue()); + } else if( value.getClass() == Double.class ) { + out.writeByte(DOUBLE_TYPE); + out.writeDouble(((Double)value).doubleValue()); + } else if( value.getClass() == byte[].class ) { + out.writeByte(BYTE_ARRAY_TYPE); + out.writeInt(((byte[])value).length); + out.write(((byte[])value)); + } else if( value.getClass() == String.class ) { + out.writeByte(STRING_TYPE); + out.writeUTF((String)value); + } else { + throw new IOException("Object is not a primitive: "+value); + } + } + + static public Object unmarshalPrimitive(DataInputStream in) throws IOException { + Object value=null; + switch( in.readByte() ) { + case BYTE_TYPE: + value = new Byte(in.readByte()); + break; + case BOOLEAN_TYPE: + value = in.readBoolean() ? Boolean.TRUE : Boolean.FALSE; + break; + case CHAR_TYPE: + value = new Character(in.readChar()); + break; + case SHORT_TYPE: + value = new Short(in.readShort()); + break; + case INTEGER_TYPE: + value = new Integer(in.readInt()); + break; + case LONG_TYPE: + value = new Long(in.readLong()); + break; + case FLOAT_TYPE: + value = new Float(in.readFloat()); + break; + case DOUBLE_TYPE: + value = new Double(in.readDouble()); + break; + case BYTE_ARRAY_TYPE: + value = new byte[in.readInt()]; + in.readFully((byte[])value); + break; + case STRING_TYPE: + value = in.readUTF(); + break; + } + return value; + } + + static public void writeUTF8(DataOutput dataOut, String text) throws IOException { + if (text != null) { + int strlen = text.length(); + int utflen = 0; + char[] charr = new char[strlen]; + int c, count = 0; + + text.getChars(0, strlen, charr, 0); + + for (int i = 0; i < strlen; i++) { + c = charr[i]; + if ((c >= 0x0001) && (c <= 0x007F)) { + utflen++; + } else if (c > 0x07FF) { + utflen += 3; + } else { + utflen += 2; + } + } + //TODO diff: Sun code - removed + byte[] bytearr = new byte[utflen + 4]; //TODO diff: Sun code + bytearr[count++] = (byte) ((utflen >>> 24) & 0xFF); //TODO diff: Sun code + bytearr[count++] = (byte) ((utflen >>> 16) & 0xFF); //TODO diff: Sun code + bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF); + bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF); + for (int i = 0; i < strlen; i++) { + c = charr[i]; + if ((c >= 0x0001) && (c <= 0x007F)) { + bytearr[count++] = (byte) c; + } else if (c > 0x07FF) { + bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F)); + bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F)); + bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); + } else { + bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F)); + bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); + } + } + dataOut.write(bytearr); + + } else { + dataOut.writeInt(-1); + } + } + + static public String readUTF8(DataInput dataIn) throws IOException { + int utflen = dataIn.readInt(); //TODO diff: Sun code + if (utflen > -1) { + StringBuffer str = new StringBuffer(utflen); + byte bytearr[] = new byte[utflen]; + int c, char2, char3; + int count = 0; + + dataIn.readFully(bytearr, 0, utflen); + + while (count < utflen) { + c = bytearr[count] & 0xff; + switch (c >> 4) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + /* 0xxxxxxx */ + count++; + str.append((char) c); + break; + case 12: + case 13: + /* 110x xxxx 10xx xxxx */ + count += 2; + if (count > utflen) { + throw new UTFDataFormatException(); + } + char2 = bytearr[count - 1]; + if ((char2 & 0xC0) != 0x80) { + throw new UTFDataFormatException(); + } + str.append((char) (((c & 0x1F) << 6) | (char2 & 0x3F))); + break; + case 14: + /* 1110 xxxx 10xx xxxx 10xx xxxx */ + count += 3; + if (count > utflen) { + throw new UTFDataFormatException(); + } + char2 = bytearr[count - 2]; //TODO diff: Sun code + char3 = bytearr[count - 1]; //TODO diff: Sun code + if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) { + throw new UTFDataFormatException(); + } + str.append((char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0))); + break; + default : + /* 10xx xxxx, 1111 xxxx */ + throw new UTFDataFormatException(); + } + } + // The number of chars produced may be less than utflen + return new String(str); + } else { + return null; + } + } + +} diff --git a/activemq-core/src/main/java/org/activemq/util/MessageList.java b/activemq-core/src/main/java/org/activemq/util/MessageList.java new file mode 100644 index 0000000000..f71bc6b3f2 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/MessageList.java @@ -0,0 +1,130 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.util; + +import javax.jms.Message; +import javax.jms.MessageListener; + +import junit.framework.Assert; + +import java.util.ArrayList; +import java.util.List; + +/** + * A simple container for performing testing and rendezvous style code. + * + * @version $Revision: 1.6 $ + */ +public class MessageList extends Assert implements MessageListener { + private List messages = new ArrayList(); + private Object semaphore; + private boolean verbose; + + public MessageList() { + this(new Object()); + } + + public MessageList(Object semaphore) { + this.semaphore = semaphore; + } + + /** + * @return all the messages on the list so far, clearing the buffer + */ + public List flushMessages() { + synchronized (semaphore) { + List answer = new ArrayList(messages); + messages.clear(); + return answer; + } + } + + public synchronized List getMessages() { + synchronized (semaphore) { + return new ArrayList(messages); + } + } + + public void onMessage(Message message) { + synchronized (semaphore) { + messages.add(message); + semaphore.notifyAll(); + } + if (verbose) { + System.out.println("###Êreceived message: " + message); + } + } + + public int getMessageCount() { + synchronized (semaphore) { + return messages.size(); + } + } + + public void waitForMessagesToArrive(int messageCount) { + System.out.println("Waiting for " + messageCount + " message(s) to arrive"); + + long start = System.currentTimeMillis(); + + for (int i = 0; i < messageCount; i++) { + try { + if (hasReceivedMessages(messageCount)) { + break; + } + synchronized (semaphore) { + semaphore.wait(4000); + } + } + catch (InterruptedException e) { + System.out.println("Caught: " + e); + } + } + long end = System.currentTimeMillis() - start; + + System.out.println("End of wait for " + end + " millis"); + } + + /** + * Performs a testing assertion that the correct number of messages have + * been received + * + * @param messageCount + */ + public void assertMessagesReceived(int messageCount) { + waitForMessagesToArrive(messageCount); + + assertEquals("expected number of messages when received: " + getMessages(), messageCount, getMessageCount()); + } + + public boolean hasReceivedMessage() { + return getMessageCount() == 0; + } + + public boolean hasReceivedMessages(int messageCount) { + return getMessageCount() >= messageCount; + } + + public boolean isVerbose() { + return verbose; + } + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/util/ServiceStopper.java b/activemq-core/src/main/java/org/activemq/util/ServiceStopper.java new file mode 100644 index 0000000000..0f60d76888 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/ServiceStopper.java @@ -0,0 +1,103 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.util; + +import org.activemq.Service; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Iterator; +import java.util.List; + +/** + * A helper class used to stop a bunch of services, catching and logging any + * exceptions and then throwing the first exception when everything is stoped. + * + * @version $Revision: 1.1 $ + */ +public class ServiceStopper { + private Throwable firstException; + + /** + * Stops the given service, catching any exceptions that are thrown. + */ + public void stop(Service service) { + try { + service.stop(); + } + catch (Exception e) { + onException(service, e); + } + } + + /** + * Performs the given code to stop some service handling the exceptions + * which may be thrown properly + */ + public void run(Callback stopClosure) { + try { + stopClosure.execute(); + } + catch (Throwable e) { + onException(stopClosure, e); + } + } + + /** + * Stops a list of services + */ + public void stopServices(List services) { + for (Iterator iter = services.iterator(); iter.hasNext();) { + Service service = (Service) iter.next(); + stop(service); + } + } + + public void onException(Object owner, Throwable e) { + logError(owner, e); + if (firstException == null) { + firstException = e; + } + } + + /** + * Throws the first exception that was thrown if there was one. + */ + public void throwFirstException() throws Exception { + if (firstException != null) { + if (firstException instanceof Exception) { + Exception e = (Exception) firstException; + throw e; + } + else if (firstException instanceof RuntimeException) { + RuntimeException e = (RuntimeException) firstException; + throw e; + } + else { + throw new RuntimeException("Unknown type of exception: " + firstException, firstException); + } + } + } + + protected void logError(Object service, Throwable e) { + Log log = LogFactory.getLog(service.getClass()); + log.error("Could not stop service: " + service + ". Reason: " + e, e); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/util/ServiceSupport.java b/activemq-core/src/main/java/org/activemq/util/ServiceSupport.java new file mode 100644 index 0000000000..c05cef22a4 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/ServiceSupport.java @@ -0,0 +1,52 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +import org.activemq.Service; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A helper class for working with services + * + * @version $Revision: 1.1 $ + */ +public abstract class ServiceSupport { + private static final Log log = LogFactory.getLog(ServiceSupport.class); + + public static void dispose(Service service) { + try { + service.stop(); + } + catch (Exception e) { + log.error("Could not stop service: " + service + ". Reason: " + e, e); + } + } + + public void stop() throws Exception { + ServiceStopper stopper = new ServiceStopper(); + stop(stopper); + stopper.throwFirstException(); + } + + /** + * Provides a way for derived classes to stop resources cleanly, handling exceptions + */ + protected abstract void stop(ServiceStopper stopper); +} diff --git a/activemq-core/src/main/java/org/activemq/util/SubscriptionKey.java b/activemq-core/src/main/java/org/activemq/util/SubscriptionKey.java new file mode 100755 index 0000000000..a2af92af3d --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/SubscriptionKey.java @@ -0,0 +1,45 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +public class SubscriptionKey { + public final String clientId; + public final String subscriptionName; + private final int hashValue; + + public SubscriptionKey(String clientId, String subscriptionName) { + this.clientId = clientId; + this.subscriptionName = subscriptionName; + hashValue = clientId.hashCode()^subscriptionName.hashCode(); + } + + public int hashCode() { + return hashValue; + } + + public boolean equals(Object o) { + try { + SubscriptionKey key = (SubscriptionKey) o; + return key.clientId.equals(clientId) && key.subscriptionName.equals(subscriptionName); + } catch (Throwable e) { + return false; + } + } +} \ No newline at end of file diff --git a/activemq-core/src/main/java/org/activemq/util/TransactionTemplate.java b/activemq-core/src/main/java/org/activemq/util/TransactionTemplate.java new file mode 100755 index 0000000000..8656089f47 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/TransactionTemplate.java @@ -0,0 +1,75 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +import java.io.IOException; + +import org.activemq.broker.ConnectionContext; +import org.activemq.store.PersistenceAdapter; + +/** + * A helper class for running code with a PersistenceAdapter + * in a transaction. + * + * @version $Revision: 1.2 $ + */ +public class TransactionTemplate { + private PersistenceAdapter persistenceAdapter; + private ConnectionContext context; + + public TransactionTemplate(PersistenceAdapter persistenceAdapter, ConnectionContext context) { + this.persistenceAdapter = persistenceAdapter; + this.context=context; + } + + public void run(Callback task) throws IOException { + persistenceAdapter.beginTransaction(context); + Throwable throwable = null; + try { + task.execute(); + } + catch (IOException t) { + throwable = t; + throw t; + } + catch (RuntimeException t) { + throwable = t; + throw t; + } + catch (Throwable t) { + throwable = t; + throw IOExceptionSupport.create("Persistence task failed: "+t, t); + } finally { + if (throwable == null) { + persistenceAdapter.commitTransaction(context); + } + else { + persistenceAdapter.rollbackTransaction(context); + } + } + } + + public ConnectionContext getContext() { + return context; + } + + public PersistenceAdapter getPersistenceAdapter() { + return persistenceAdapter; + } +} diff --git a/activemq-core/src/main/java/org/activemq/util/TypeConversionSupport.java b/activemq-core/src/main/java/org/activemq/util/TypeConversionSupport.java new file mode 100755 index 0000000000..4debbd58a8 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/TypeConversionSupport.java @@ -0,0 +1,127 @@ +package org.activemq.util; + +import java.util.HashMap; + +public class TypeConversionSupport { + + static class ConversionKey { + final Class from; + final Class to; + final int hashCode; + + public ConversionKey(Class from, Class to) { + this.from = from; + this.to = to; + this.hashCode = from.hashCode() ^ (to.hashCode() << 1); + } + + public boolean equals(Object o) { + ConversionKey x = (ConversionKey) o; + return x.from == from && x.to == to; + } + + public int hashCode() { + return hashCode; + } + } + + interface Converter { + Object convert(Object value); + } + + static final private HashMap CONVERSION_MAP = new HashMap(); + static { + Converter toStringConverter = new Converter() { + public Object convert(Object value) { + return value.toString(); + } + }; + CONVERSION_MAP.put(new ConversionKey(Boolean.class, String.class), toStringConverter); + CONVERSION_MAP.put(new ConversionKey(Byte.class, String.class), toStringConverter); + CONVERSION_MAP.put(new ConversionKey(Short.class, String.class), toStringConverter); + CONVERSION_MAP.put(new ConversionKey(Integer.class, String.class), toStringConverter); + CONVERSION_MAP.put(new ConversionKey(Long.class, String.class), toStringConverter); + CONVERSION_MAP.put(new ConversionKey(Float.class, String.class), toStringConverter); + CONVERSION_MAP.put(new ConversionKey(Double.class, String.class), toStringConverter); + + CONVERSION_MAP.put(new ConversionKey(String.class, Boolean.class), new Converter() { + public Object convert(Object value) { + return Boolean.valueOf((String) value); + } + }); + CONVERSION_MAP.put(new ConversionKey(String.class, Byte.class), new Converter() { + public Object convert(Object value) { + return Byte.valueOf((String) value); + } + }); + CONVERSION_MAP.put(new ConversionKey(String.class, Short.class), new Converter() { + public Object convert(Object value) { + return Short.valueOf((String) value); + } + }); + CONVERSION_MAP.put(new ConversionKey(String.class, Integer.class), new Converter() { + public Object convert(Object value) { + return Integer.valueOf((String) value); + } + }); + CONVERSION_MAP.put(new ConversionKey(String.class, Long.class), new Converter() { + public Object convert(Object value) { + return Long.valueOf((String) value); + } + }); + CONVERSION_MAP.put(new ConversionKey(String.class, Float.class), new Converter() { + public Object convert(Object value) { + return Float.valueOf((String) value); + } + }); + CONVERSION_MAP.put(new ConversionKey(String.class, Double.class), new Converter() { + public Object convert(Object value) { + return Double.valueOf((String) value); + } + }); + + Converter longConverter = new Converter() { + public Object convert(Object value) { + return new Long(((Number) value).longValue()); + } + }; + CONVERSION_MAP.put(new ConversionKey(Byte.class, Long.class), longConverter); + CONVERSION_MAP.put(new ConversionKey(Short.class, Long.class), longConverter); + CONVERSION_MAP.put(new ConversionKey(Integer.class, Long.class), longConverter); + + Converter intConverter = new Converter() { + public Object convert(Object value) { + return new Integer(((Number) value).intValue()); + } + }; + CONVERSION_MAP.put(new ConversionKey(Byte.class, Integer.class), intConverter); + CONVERSION_MAP.put(new ConversionKey(Short.class, Integer.class), intConverter); + + CONVERSION_MAP.put(new ConversionKey(Byte.class, Short.class), new Converter() { + public Object convert(Object value) { + return new Short(((Number) value).shortValue()); + } + }); + + CONVERSION_MAP.put(new ConversionKey(Float.class, Double.class), new Converter() { + public Object convert(Object value) { + return new Double(((Number) value).doubleValue()); + } + }); + } + + static public Object convert(Object value, Class clazz) { + + assert value != null && clazz != null; + + if (value.getClass() == clazz) + return value; + + Converter c = (Converter) CONVERSION_MAP.get(new ConversionKey(value.getClass(), clazz)); + if (c == null) + return null; + return c.convert(value); + + } + +} diff --git a/activemq-core/src/main/java/org/activemq/util/URISupport.java b/activemq-core/src/main/java/org/activemq/util/URISupport.java new file mode 100755 index 0000000000..16819c4012 --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/URISupport.java @@ -0,0 +1,317 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * @version $Revision$ + */ +public class URISupport { + + public static class CompositeData { + String scheme; + String path; + URI components[]; + Map parameters; + String fragment; + public String host; + + public URI[] getComponents() { + return components; + } + public String getFragment() { + return fragment; + } + public Map getParameters() { + return parameters; + } + public String getScheme() { + return scheme; + } + public String getPath() { + return path; + } + public String getHost() { + return host; + } + + public URI toURI() throws URISyntaxException { + StringBuffer sb = new StringBuffer(); + if( scheme!=null ) { + sb.append(scheme); + sb.append(':'); + } + + if( host!=null && host.length()!=0 ) { + sb.append(host); + } else { + sb.append('('); + for (int i = 0; i < components.length; i++) { + if( i!=0 ) + sb.append(','); + sb.append(components[i].toString()); + } + sb.append(')'); + } + + if( path !=null ) { + sb.append('/'); + sb.append(path); + } + if(!parameters.isEmpty()) { + sb.append("?"); + sb.append(createQueryString(parameters)); + } + if( fragment!=null ) { + sb.append("#"); + sb.append(fragment); + } + return new URI(sb.toString()); + } + } + + public static Map parseQuery(String uri) throws URISyntaxException{ + try{ + Map rc=new HashMap(); + if(uri!=null){ + String[] parameters=uri.split("&"); + for(int i=0;i=0){ + String name=URLDecoder.decode(parameters[i].substring(0,p),"UTF-8"); + String value=URLDecoder.decode(parameters[i].substring(p+1),"UTF-8"); + rc.put(name,value); + }else{ + rc.put(parameters[i],null); + } + } + } + return rc; + }catch(UnsupportedEncodingException e){ + throw (URISyntaxException) new URISyntaxException(e.toString(),"Invalid encoding").initCause(e); + } + } + + public static Map parseParamters(URI uri) throws URISyntaxException { + return uri.getQuery()==null ? Collections.EMPTY_MAP : parseQuery(stripPrefix(uri.getQuery(), "?")); + } + + /** + * Removes any URI query from the given uri + */ + public static URI removeQuery(URI uri) throws URISyntaxException { + return createURIWithQuery(uri, null); + } + + /** + * Creates a URI with the given query + */ + public static URI createURIWithQuery(URI uri, String query) throws URISyntaxException { + return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), query, uri.getFragment()); + } + + public static CompositeData parseComposite(URI uri) throws URISyntaxException { + + CompositeData rc = new CompositeData(); + rc.scheme = uri.getScheme(); + String ssp = stripPrefix(uri.getSchemeSpecificPart().trim(), "//").trim(); + + parseComposite(uri, rc, ssp); + + rc.fragment = uri.getFragment(); + return rc; + } + + /** + * @param uri + * @param rc + * @param ssp + * @param p + * @throws URISyntaxException + */ + private static void parseComposite(URI uri, CompositeData rc, String ssp) throws URISyntaxException { + String componentString; + String params; + + if(!checkParenthesis(ssp)){ + throw new URISyntaxException(uri.toString(), "Not a matching number of '(' and ')' parenthesis"); + } + + int p; + int intialParen = ssp.indexOf("("); + if( intialParen==0 ) { + rc.host = ssp.substring(0, intialParen); + p = rc.host.indexOf("/"); + if( p >= 0 ) { + rc.path = rc.host.substring(p); + rc.host = rc.host.substring(0,p); + } + p = ssp.lastIndexOf(")"); + componentString = ssp.substring(intialParen+1,p); + params = ssp.substring(p+1).trim(); + + } else { + componentString = ssp; + params=""; + } + + String components[] = splitComponents(componentString); + rc.components=new URI[components.length]; + for (int i = 0; i < components.length; i++) { + rc.components[i] = new URI(components[i].trim()); + } + + p = params.indexOf("?"); + if( p >= 0 ) { + if( p > 0) { + rc.path = stripPrefix(params.substring(0, p), "/"); + } + rc.parameters = parseQuery(params.substring(p+1)); + } else { + if( params.length() > 0 ) + rc.path = stripPrefix(params, "/"); + rc.parameters = Collections.EMPTY_MAP; + } + } + + /** + * @param componentString + * @return + */ + private static String[] splitComponents(String str) { + ArrayList l = new ArrayList(); + + int last=0; + int depth = 0; + char chars[] = str.toCharArray(); + for( int i=0; i < chars.length; i ++ ) { + switch( chars[i] ) { + case '(': + depth++; + break; + case ')': + depth--; + break; + case ',': + if( depth == 0 ) { + String s = str.substring(0, i-last); + l.add(s); + last=i+1; + } + } + } + + String s = str.substring(last); + if( s.length() !=0 ) + l.add(s); + + String rc[] = new String[l.size()]; + l.toArray(rc); + return rc; + } + + private static String stripPrefix(String value, String prefix) { + if( value.startsWith(prefix) ) + return value.substring(prefix.length()); + return value; + } + + public static URI stripScheme(URI uri) throws URISyntaxException { + return new URI(stripPrefix(uri.getSchemeSpecificPart().trim(), "//")); + } + + public static String createQueryString(Map options) throws URISyntaxException { + try { + if(options.size()>0) { + StringBuffer rc = new StringBuffer(); + boolean first=true; + for (Iterator iter = options.keySet().iterator(); iter.hasNext();) { + if( first ) + first=false; + else + rc.append("&"); + + String key = (String) iter.next(); + String value = (String)options.get(key); + rc.append(URLEncoder.encode(key, "UTF-8")); + rc.append("="); + rc.append(URLEncoder.encode(value, "UTF-8")); + } + return rc.toString(); + } else { + return ""; + } + } catch (UnsupportedEncodingException e) { + throw (URISyntaxException)new URISyntaxException(e.toString(), "Invalid encoding").initCause(e); + } + } + + /** + * Creates a URI from the original URI and the remaining paramaters + * @throws URISyntaxException + */ + public static URI createRemainingURI(URI originalURI, Map params) throws URISyntaxException { + String s = createQueryString(params); + if( s.length()==0 ) + s = null; + return createURIWithQuery(originalURI, s); + } + + static public URI changeScheme(URI bindAddr, String scheme) throws URISyntaxException { + return new URI(scheme, bindAddr.getUserInfo(), bindAddr.getHost(), bindAddr.getPort(), bindAddr.getPath(), bindAddr.getQuery(), bindAddr.getFragment()); + } + + public static boolean checkParenthesis(String str){ + boolean result=true; + if(str!=null){ + int open=0; + int closed=0; + + int i=0; + while((i=str.indexOf('(',i)) >=0 ){ + i++; + open++; + } + i=0; + while((i=str.indexOf(')',i)) >=0 ){ + i++; + closed++; + } + result = open == closed; + } + return result; + } + + public int indexOfParenthesisMatch(String str){ + int result = -1; + + return result; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/util/WrappedException.java b/activemq-core/src/main/java/org/activemq/util/WrappedException.java new file mode 100755 index 0000000000..1a1afc191e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/util/WrappedException.java @@ -0,0 +1,28 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +public class WrappedException extends RuntimeException { + private static final long serialVersionUID = 3257290240212217905L; + + public WrappedException(Throwable original) { + super(original); + } + +} diff --git a/activemq-core/src/main/java/org/activemq/xbean/BrokerFactoryBean.java b/activemq-core/src/main/java/org/activemq/xbean/BrokerFactoryBean.java new file mode 100644 index 0000000000..6235d9e6fe --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/xbean/BrokerFactoryBean.java @@ -0,0 +1,131 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.xbean; + +import org.activemq.broker.BrokerService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.io.Resource; +import org.xbean.spring.context.ResourceXmlApplicationContext; +import org.xbean.spring.context.impl.URIEditor; + +import java.beans.PropertyEditorManager; +import java.net.URI; + +/** + * A Spring {@link FactoryBean} which creates an embedded broker inside a Spring + * XML using an external XBean Spring XML + * configuration file which provides a much neater and more concise XML + * format. + * + * @version $Revision: 1.1 $ + */ +public class BrokerFactoryBean implements FactoryBean, InitializingBean, DisposableBean { + private static final Log log = LogFactory.getLog(BrokerFactoryBean.class); + + static { + PropertyEditorManager.registerEditor(URI.class, URIEditor.class); + } + + private Resource config; + private XBeanBrokerService broker; + private boolean start=false; + + public BrokerFactoryBean() { + } + + public BrokerFactoryBean(Resource config) { + this.config = config; + } + + public Object getObject() throws Exception { + return broker; + } + + public Class getObjectType() { + return BrokerService.class; + } + + public boolean isSingleton() { + return true; + } + + public void afterPropertiesSet() throws Exception { + if (config == null) { + throw new IllegalArgumentException("config property must be set"); + } + ResourceXmlApplicationContext context = new ResourceXmlApplicationContext(config); + + try { + broker = (XBeanBrokerService) context.getBean("broker"); + } + catch (BeansException e) { + log.trace("No bean named broker available: " + e, e); + } + if (broker == null) { + // lets try find by type + String[] names = context.getBeanNamesForType(BrokerService.class); + for (int i = 0; i < names.length; i++) { + String name = names[i]; + broker = (XBeanBrokerService) context.getBean(name); + if (broker != null) { + break; + } + } + } + if (broker == null) { + throw new IllegalArgumentException("The configuration has no BrokerService instance for resource: " + config); + } + broker.setAbstractApplicationContext(context); + if( start ) + broker.start(); + } + + public void destroy() throws Exception { + if (broker != null) { + broker.stop(); + } + } + + public Resource getConfig() { + return config; + } + + public void setConfig(Resource config) { + this.config = config; + } + + public BrokerService getBroker() { + return broker; + } + + public boolean isStart() { + return start; + } + + public void setStart(boolean start) { + this.start = start; + } + + +} diff --git a/activemq-core/src/main/java/org/activemq/xbean/XBeanBrokerFactory.java b/activemq-core/src/main/java/org/activemq/xbean/XBeanBrokerFactory.java new file mode 100644 index 0000000000..7790ba839e --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/xbean/XBeanBrokerFactory.java @@ -0,0 +1,70 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.xbean; + +import java.beans.PropertyEditorManager; +import java.net.URI; + +import org.activemq.broker.BrokerService; +import org.activemq.broker.BrokerFactory.BrokerFactoryHandler; +import org.springframework.beans.BeansException; +import org.xbean.spring.context.ClassPathXmlApplicationContext; +import org.xbean.spring.context.impl.URIEditor; + +/** + * @version $Revision$ + */ +public class XBeanBrokerFactory implements BrokerFactoryHandler { + + static { + PropertyEditorManager.registerEditor(URI.class, URIEditor.class); + } + + public BrokerService createBroker(URI config) throws Exception { + + String uri = config.getSchemeSpecificPart(); + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(uri); + + XBeanBrokerService broker = null; + try { + broker = (XBeanBrokerService) context.getBean("broker"); + } + catch (BeansException e) { + } + + if (broker == null) { + // lets try find by type + String[] names = context.getBeanNamesForType(BrokerService.class); + for (int i = 0; i < names.length; i++) { + String name = names[i]; + broker = (XBeanBrokerService) context.getBean(name); + if (broker != null) { + break; + } + } + } + if (broker == null) { + throw new IllegalArgumentException("The configuration has no BrokerService instance for resource: " + config); + } + + broker.setAbstractApplicationContext(context); + return broker; + } + +} diff --git a/activemq-core/src/main/java/org/activemq/xbean/XBeanBrokerService.java b/activemq-core/src/main/java/org/activemq/xbean/XBeanBrokerService.java new file mode 100644 index 0000000000..ec6d0f11fe --- /dev/null +++ b/activemq-core/src/main/java/org/activemq/xbean/XBeanBrokerService.java @@ -0,0 +1,52 @@ +package org.activemq.xbean; + +import org.activemq.broker.BrokerService; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.support.AbstractApplicationContext; + +/** + * Represents a running broker service which consists of a number of transport + * connectors, network connectors and a bunch of properties which can be used to + * configure the broker as its lazily created. + * + * @org.xbean.XBean element="broker" rootElement="true" description="An ActiveMQ + * Message Broker which consists of a number of transport + * connectors, network connectors and a persistence adaptor" + * + * @version $Revision: 1.1 $ + */ +public class XBeanBrokerService extends BrokerService implements InitializingBean { + + private boolean start=false; + private AbstractApplicationContext applicationContext; + + public XBeanBrokerService() { + } + + public void setAbstractApplicationContext(AbstractApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + public void afterPropertiesSet() throws Exception { + if( start ) { + start(); + } + } + + public void stop() throws Exception { + super.stop(); + if( applicationContext!=null ) { + applicationContext.destroy(); + applicationContext=null; + } + } + + public boolean isStart() { + return start; + } + public void setStart(boolean start) { + this.start = start; + } + +} diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/broker/broker b/activemq-core/src/main/resources/META-INF/services/org/activemq/broker/broker new file mode 100755 index 0000000000..a04fe5704a --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/broker/broker @@ -0,0 +1 @@ +class=org.activemq.broker.DefaultBrokerFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/broker/spring b/activemq-core/src/main/resources/META-INF/services/org/activemq/broker/spring new file mode 100755 index 0000000000..57ddf29829 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/broker/spring @@ -0,0 +1 @@ +class=org.activemq.spring.SpringBrokerFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/broker/xbean b/activemq-core/src/main/resources/META-INF/services/org/activemq/broker/xbean new file mode 100755 index 0000000000..70c2b4ca64 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/broker/xbean @@ -0,0 +1 @@ +class=org.activemq.xbean.XBeanBrokerFactory \ No newline at end of file diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/apache_derby_embedded_jdbc_driver b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/apache_derby_embedded_jdbc_driver new file mode 100755 index 0000000000..d203deee27 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/apache_derby_embedded_jdbc_driver @@ -0,0 +1 @@ +class=org.activemq.store.jdbc.adapter.DefaultJDBCAdapter \ No newline at end of file diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/axion_jdbc_driver b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/axion_jdbc_driver new file mode 100755 index 0000000000..32a168edb2 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/axion_jdbc_driver @@ -0,0 +1 @@ +class=org.activemq.store.jdbc.adapter.AxionJDBCAdapter \ No newline at end of file diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/hsql_database_engine_driver b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/hsql_database_engine_driver new file mode 100755 index 0000000000..5f82e58b8c --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/hsql_database_engine_driver @@ -0,0 +1 @@ +class=org.activemq.store.jdbc.adapter.HsqldbJDBCAdapter \ No newline at end of file diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/i-net_sprinta_2000 b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/i-net_sprinta_2000 new file mode 100755 index 0000000000..4a5af08353 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/i-net_sprinta_2000 @@ -0,0 +1 @@ +class=org.activemq.store.jdbc.adapter.ImageBasedJDBCAdaptor \ No newline at end of file diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/ibm_informix_jdbc_driver_for_ibm_informix_dynamic_server b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/ibm_informix_jdbc_driver_for_ibm_informix_dynamic_server new file mode 100755 index 0000000000..50693f5bda --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/ibm_informix_jdbc_driver_for_ibm_informix_dynamic_server @@ -0,0 +1 @@ +class=org.activemq.store.jdbc.adapter.InformixJDBCAdapter \ No newline at end of file diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/jconnect__tm__for_jdbc__tm_ b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/jconnect__tm__for_jdbc__tm_ new file mode 100755 index 0000000000..4a5af08353 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/jconnect__tm__for_jdbc__tm_ @@ -0,0 +1 @@ +class=org.activemq.store.jdbc.adapter.ImageBasedJDBCAdaptor \ No newline at end of file diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/jtds_type_4_jdbc_driver_for_ms_sql_server_and_sybase b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/jtds_type_4_jdbc_driver_for_ms_sql_server_and_sybase new file mode 100755 index 0000000000..4a5af08353 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/jtds_type_4_jdbc_driver_for_ms_sql_server_and_sybase @@ -0,0 +1 @@ +class=org.activemq.store.jdbc.adapter.ImageBasedJDBCAdaptor \ No newline at end of file diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/oracle_jdbc_driver b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/oracle_jdbc_driver new file mode 100755 index 0000000000..0df864e4e2 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/oracle_jdbc_driver @@ -0,0 +1 @@ +class=org.activemq.store.jdbc.adapter.OracleJDBCAdapter \ No newline at end of file diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/postgresql_native_driver b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/postgresql_native_driver new file mode 100644 index 0000000000..6c7ab8ec11 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/postgresql_native_driver @@ -0,0 +1 @@ +class=org.activemq.store.jdbc.adapter.PostgresqlJDBCAdapter \ No newline at end of file diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/sqlserver b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/sqlserver new file mode 100755 index 0000000000..4a5af08353 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/store/jdbc/sqlserver @@ -0,0 +1 @@ +class=org.activemq.store.jdbc.adapter.ImageBasedJDBCAdaptor \ No newline at end of file diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/.cvsignore b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/.cvsignore new file mode 100755 index 0000000000..e43b0f9889 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/.cvsignore @@ -0,0 +1 @@ +.DS_Store diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/aio b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/aio new file mode 100755 index 0000000000..bf1f0caef9 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/aio @@ -0,0 +1 @@ +class=org.activemq.transport.activeio.ActiveIOTransportFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discovery b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discovery new file mode 100755 index 0000000000..da91bb5750 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discovery @@ -0,0 +1 @@ +class=org.activemq.transport.discovery.DiscoveryTransportFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discoveryagent/multicast b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discoveryagent/multicast new file mode 100755 index 0000000000..77bc33d70c --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discoveryagent/multicast @@ -0,0 +1 @@ +class=org.activemq.transport.discovery.multicast.MulticastDiscoveryAgentFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discoveryagent/rendezvous b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discoveryagent/rendezvous new file mode 100755 index 0000000000..3777007d68 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discoveryagent/rendezvous @@ -0,0 +1 @@ +class=org.activemq.transport.discovery.rendezvous.RendezvousDiscoveryAgentFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discoveryagent/simple b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discoveryagent/simple new file mode 100755 index 0000000000..3dcbeb71b5 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discoveryagent/simple @@ -0,0 +1 @@ +class=org.activemq.transport.discovery.simple.SimpleDiscoveryAgentFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discoveryagent/static b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discoveryagent/static new file mode 100755 index 0000000000..3dcbeb71b5 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/discoveryagent/static @@ -0,0 +1 @@ +class=org.activemq.transport.discovery.simple.SimpleDiscoveryAgentFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/failover b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/failover new file mode 100755 index 0000000000..59a46490f0 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/failover @@ -0,0 +1 @@ +class=org.activemq.transport.failover.FailoverTransportFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/fanout b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/fanout new file mode 100755 index 0000000000..cfb9de8bd3 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/fanout @@ -0,0 +1 @@ +class=org.activemq.transport.fanout.FanoutTransportFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/jxta b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/jxta new file mode 100755 index 0000000000..bf1f0caef9 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/jxta @@ -0,0 +1 @@ +class=org.activemq.transport.activeio.ActiveIOTransportFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/mock b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/mock new file mode 100755 index 0000000000..7434c70253 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/mock @@ -0,0 +1 @@ +class=org.activemq.transport.mock.MockTransportFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/nio b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/nio new file mode 100755 index 0000000000..bf1f0caef9 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/nio @@ -0,0 +1 @@ +class=org.activemq.transport.activeio.ActiveIOTransportFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/peer b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/peer new file mode 100644 index 0000000000..c03d680bde --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/peer @@ -0,0 +1 @@ +class=org.activemq.transport.peer.PeerTransportFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/ssl b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/ssl new file mode 100755 index 0000000000..bf1f0caef9 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/ssl @@ -0,0 +1 @@ +class=org.activemq.transport.activeio.ActiveIOTransportFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/tcp b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/tcp new file mode 100755 index 0000000000..6942310980 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/tcp @@ -0,0 +1 @@ +class=org.activemq.transport.tcp.TcpTransportFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/vm b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/vm new file mode 100755 index 0000000000..06594a1bff --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/vm @@ -0,0 +1 @@ +class=org.activemq.transport.vm.VMTransportFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/vmpipe b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/vmpipe new file mode 100755 index 0000000000..bf1f0caef9 --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/transport/vmpipe @@ -0,0 +1 @@ +class=org.activemq.transport.activeio.ActiveIOTransportFactory diff --git a/activemq-core/src/main/resources/META-INF/services/org/activemq/wireformat/default b/activemq-core/src/main/resources/META-INF/services/org/activemq/wireformat/default new file mode 100755 index 0000000000..e7e8ac4dab --- /dev/null +++ b/activemq-core/src/main/resources/META-INF/services/org/activemq/wireformat/default @@ -0,0 +1 @@ +class=org.activemq.openwire.OpenWireFormatFactory diff --git a/activemq-core/src/test/java/org/activemq/ActiveMQConnectionFactoryTest.java b/activemq-core/src/test/java/org/activemq/ActiveMQConnectionFactoryTest.java new file mode 100755 index 0000000000..169be1d812 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/ActiveMQConnectionFactoryTest.java @@ -0,0 +1,99 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.net.URI; +import java.net.URISyntaxException; + +import javax.jms.Connection; +import javax.jms.JMSException; + +import org.activemq.broker.BrokerRegistry; +import org.activemq.broker.BrokerService; +import org.activemq.broker.TransportConnector; + +public class ActiveMQConnectionFactoryTest extends CombinationTestSupport { + + public void testUseURIToSetOptionsOnConnectionFactory() throws URISyntaxException, JMSException { + ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("vm://localhost?jms.useAsyncSend=true"); + assertTrue(cf.isUseAsyncSend()); + // the broker url have been adjusted. + assertEquals("vm://localhost", cf.getBrokerURL()); + + cf = new ActiveMQConnectionFactory("vm://localhost?jms.useAsyncSend=false"); + assertFalse(cf.isUseAsyncSend()); + // the broker url have been adjusted. + assertEquals("vm://localhost", cf.getBrokerURL()); + + cf = new ActiveMQConnectionFactory("vm:(broker:()/localhost)?jms.useAsyncSend=true"); + assertTrue(cf.isUseAsyncSend()); + // the broker url have been adjusted. + assertEquals("vm:(broker:()/localhost)", cf.getBrokerURL()); + } + + public void testCreateVMConnectionWithEmbdeddBroker() throws URISyntaxException, JMSException { + ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false"); + // Make sure the broker is not created until the connection is instantiated. + assertNull( BrokerRegistry.getInstance().lookup("localhost") ); + Connection connection = cf.createConnection(); + // This should create the connection. + assertNotNull(connection); + // Verify the broker was created. + assertNotNull( BrokerRegistry.getInstance().lookup("localhost") ); + connection.close(); + // Verify the broker was destroyed. + assertNull( BrokerRegistry.getInstance().lookup("localhost") ); + } + + public void testCreateTcpConnectionUsingAllocatedPort() throws Exception { + assertCreateConnection("tcp://localhost:0?wireFormat.tcpNoDelayEnabled=true"); + } + public void testCreateTcpConnectionUsingKnownPort() throws Exception { + assertCreateConnection("tcp://localhost:61610?wireFormat.tcpNoDelayEnabled=true"); + } + + protected void assertCreateConnection(String uri) throws Exception { + // Start up a broker with a tcp connector. + BrokerService broker = new BrokerService(); + broker.setPersistent(false); + TransportConnector connector = broker.addConnector(uri); + broker.start(); + + URI temp = new URI(uri); + //URI connectURI = connector.getServer().getConnectURI(); + // TODO this sometimes fails when using the actual local host name + URI currentURI = connector.getServer().getConnectURI(); + + // sometimes the actual host name doesn't work in this test case + // e.g. on OS X so lets use the original details but just use the actual port + URI connectURI = new URI(temp.getScheme(), temp.getUserInfo(), temp.getHost(), currentURI.getPort(), temp.getPath(), temp.getQuery(), temp.getFragment()); + + + System.out.println("connection URI is: " + connectURI); + + // This should create the connection. + ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory(connectURI); + Connection connection = cf.createConnection(); + assertNotNull(connection); + connection.close(); + + broker.stop(); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/ClientTestSupport.java b/activemq-core/src/test/java/org/activemq/ClientTestSupport.java new file mode 100755 index 0000000000..9bd64b9758 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/ClientTestSupport.java @@ -0,0 +1,177 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +import org.activemq.broker.Broker; +import org.activemq.broker.BrokerFactory; +import org.activemq.broker.BrokerService; +import org.activemq.broker.StubConnection; +import org.activemq.broker.TransportConnector; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConnectionId; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageDispatch; +import org.activemq.command.RemoveInfo; +import org.activemq.command.SessionInfo; +import org.activemq.transport.TransportFactory; + +import javax.jms.JMSException; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import junit.framework.TestCase; + +public class ClientTestSupport extends TestCase { + + private ActiveMQConnectionFactory connFactory; + protected BrokerService broker; + private String brokerURL = "vm://localhost?broker.persistent=false"; + + protected long idGenerator=0; + + public void setUp() throws Exception { + final AtomicBoolean connected = new AtomicBoolean(false); + TransportConnector connector; + + // Start up a broker with a tcp connector. + try { + broker = BrokerFactory.createBroker(new URI(this.brokerURL)); + String brokerId = broker.getBrokerName(); + connector = new TransportConnector(broker.getBroker(), TransportFactory.bind(brokerId,new URI(this.brokerURL))) { + // Hook into the connector so we can assert that the server accepted a connection. + protected org.activemq.broker.Connection createConnection(org.activemq.transport.Transport transport) throws IOException { + connected.set(true); + return super.createConnection(transport); + } + }; + connector.start(); + broker.start(); + + } catch (IOException e) { + throw new JMSException("Error creating broker " + e); + } catch (URISyntaxException e) { + throw new JMSException("Error creating broker " + e); + } + + URI connectURI; + connectURI = connector.getServer().getConnectURI(); + + // This should create the connection. + connFactory = new ActiveMQConnectionFactory(connectURI); + } + + + protected void tearDown() throws Exception { + super.tearDown(); + if (broker != null) { + broker.stop(); + } + } + + + public ActiveMQConnectionFactory getConnectionFactory() throws JMSException { + if(this.connFactory == null){ + throw new JMSException("ActiveMQConnectionFactory is null "); + } + return this.connFactory; + } + + //Helper Classes + protected ConnectionInfo createConnectionInfo() throws Throwable { + ConnectionInfo info = new ConnectionInfo(); + info.setConnectionId(new ConnectionId("connection:"+(++idGenerator))); + info.setClientId( info.getConnectionId().getConnectionId() ); + return info; + } + + protected SessionInfo createSessionInfo(ConnectionInfo connectionInfo) throws Throwable { + SessionInfo info = new SessionInfo(connectionInfo, ++idGenerator); + return info; + } + + protected ConsumerInfo createConsumerInfo(SessionInfo sessionInfo, ActiveMQDestination destination) throws Throwable { + ConsumerInfo info = new ConsumerInfo(sessionInfo, ++idGenerator); + info.setBrowser(false); + info.setDestination(destination); + info.setPrefetchSize(1000); + info.setDispatchAsync(false); + return info; + } + + protected RemoveInfo closeConsumerInfo(ConsumerInfo consumerInfo) { + return consumerInfo.createRemoveCommand(); + } + + protected MessageAck createAck(ConsumerInfo consumerInfo, Message msg, int count, byte ackType) { + MessageAck ack = new MessageAck(); + ack.setAckType(ackType); + ack.setConsumerId(consumerInfo.getConsumerId()); + ack.setDestination( msg.getDestination() ); + ack.setLastMessageId( msg.getMessageId() ); + ack.setMessageCount(count); + return ack; + } + + protected Message receiveMessage(StubConnection connection, int MAX_WAIT) throws InterruptedException { + while( true ) { + Object o = connection.getDispatchQueue().poll(MAX_WAIT, TimeUnit.MILLISECONDS); + + if( o == null ) + return null; + + if( o instanceof MessageDispatch ) { + MessageDispatch dispatch = (MessageDispatch)o; + return dispatch.getMessage(); + } + } + } + + protected Broker getBroker() throws Exception{ + return this.broker != null?this.broker.getBroker():null; + } + + public static void removeMessageStore() { + if( System.getProperty("activemq.store.dir")!=null ) { + recursiveDelete(new File(System.getProperty("activemq.store.dir"))); + } + if( System.getProperty("derby.system.home")!=null ) { + recursiveDelete(new File(System.getProperty("derby.system.home"))); + } + } + + public static void recursiveDelete(File f) { + if( f.isDirectory() ) { + File[] files = f.listFiles(); + for (int i = 0; i < files.length; i++) { + recursiveDelete(files[i]); + } + } + f.delete(); + } + +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/CombinationTestSupport.java b/activemq-core/src/test/java/org/activemq/CombinationTestSupport.java new file mode 100755 index 0000000000..9f96acb4d1 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/CombinationTestSupport.java @@ -0,0 +1,234 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Poor mans way of getting JUnit to run a test case through a few different + * combinations of options. + * + * + * Usage: If you have a test case called testFoo what you want to run through a + * few combinations, of of values for the attributes age and color, you would + * something like: + * public void initCombosForTestFoo() { + * addCombinationValues( "age", new Object[]{ new Integer(21), new Integer(30) } ); + * addCombinationValues( "color", new Object[]{"blue", "green"} ); + * } + * + * + * The testFoo test case would be run for each possible combination of age and + * color that you setup in the initCombosForTestFoo method. Before each combination is + * run, the age and color fields of the test class are set to one of the values + * defined. This is done before the normal setUp method is called. + * + * If you want the test combinations to show up as separate test runs in the + * JUnit reports, add a suite method to your test case similar to: + * + * + * public static Test suite() { + * return suite(FooTest.class); + * } + * + * + * @version $Revision: 1.5 $ + */ +public class CombinationTestSupport extends TestCase { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(CombinationTestSupport.class); + + private HashMap comboOptions = new HashMap(); + private boolean combosEvaluated; + private Map options; + + static class ComboOption { + final String attribute; + final LinkedHashSet values = new LinkedHashSet(); + + public ComboOption(String attribute, Collection options) { + this.attribute = attribute; + this.values.addAll(options); + } + } + + public void addCombinationValues(String attribute, Object[] options) { + ComboOption co = (ComboOption) this.comboOptions.get(attribute); + if (co == null) { + this.comboOptions.put(attribute, new ComboOption(attribute, Arrays.asList(options))); + } else { + co.values.addAll(Arrays.asList(options)); + } + } + + public void runBare() throws Throwable { + if (combosEvaluated) { + super.runBare(); + } else { + CombinationTestSupport[] combinations = getCombinations(); + for (int i = 0; i < combinations.length; i++) { + CombinationTestSupport test = combinations[i]; + log.info("Running " + test.getName()); + test.runBare(); + } + } + } + + private void setOptions(Map options) throws NoSuchFieldException, IllegalAccessException { + this.options = options; + for (Iterator iterator = options.keySet().iterator(); iterator.hasNext();) { + String attribute = (String) iterator.next(); + Object value = options.get(attribute); + try { + Field field = getClass().getField(attribute); + field.set(this, value); + } catch (Throwable e) { + log.info("Could not set field '" + attribute + "' to value '" + value + + "', make sure the field exists and is public."); + } + } + } + + private CombinationTestSupport[] getCombinations() { + try { + Method method = getClass().getMethod("initCombos", null); + method.invoke(this, null); + } catch (Throwable e) { + } + + String name = getName(); + String comboSetupMethodName = "initCombosFor" + Character.toUpperCase(name.charAt(0)) + name.substring(1); + try { + Method method = getClass().getMethod(comboSetupMethodName, null); + method.invoke(this, null); + } catch (Throwable e) { + } + + try { + ArrayList expandedOptions = new ArrayList(); + expandCombinations(new ArrayList(comboOptions.values()), expandedOptions); + + if (expandedOptions.isEmpty()) { + combosEvaluated = true; + return new CombinationTestSupport[] { this }; + } else { + + ArrayList result = new ArrayList(); + // Run the test case for each possible combination + for (Iterator iter = expandedOptions.iterator(); iter.hasNext();) { + CombinationTestSupport combo = (CombinationTestSupport) TestSuite.createTest(getClass(), getName()); + combo.combosEvaluated = true; + combo.setOptions((Map) iter.next()); + result.add(combo); + } + + CombinationTestSupport rc[] = new CombinationTestSupport[result.size()]; + result.toArray(rc); + return rc; + } + } catch (Throwable e) { + combosEvaluated = true; + return new CombinationTestSupport[] { this }; + } + + } + + private void expandCombinations(List optionsLeft, List expandedCombos) { + if (!optionsLeft.isEmpty()) { + HashMap map; + if (comboOptions.size() == optionsLeft.size()) { + map = new HashMap(); + expandedCombos.add(map); + } else { + map = (HashMap) expandedCombos.get(expandedCombos.size() - 1); + } + + LinkedList l = new LinkedList(optionsLeft); + ComboOption comboOption = (ComboOption) l.removeLast(); + int i = 0; + for (Iterator iter = comboOption.values.iterator(); iter.hasNext();) { + Object value = (Object) iter.next(); + if (i != 0) { + map = new HashMap(map); + expandedCombos.add(map); + } + map.put(comboOption.attribute, value); + expandCombinations(l, expandedCombos); + i++; + } + } + } + + public static Test suite(Class clazz) { + TestSuite suite = new TestSuite(); + + ArrayList names = new ArrayList(); + Method[] methods = clazz.getMethods(); + for (int i = 0; i < methods.length; i++) { + String name = methods[i].getName(); + if (names.contains(name) || !isPublicTestMethod(methods[i])) + continue; + names.add(name); + Test test = TestSuite.createTest(clazz, name); + if (test instanceof CombinationTestSupport) { + CombinationTestSupport[] combinations = ((CombinationTestSupport) test).getCombinations(); + for (int j = 0; j < combinations.length; j++) { + suite.addTest(combinations[j]); + } + } else { + suite.addTest(test); + } + } + return suite; + } + + static private boolean isPublicTestMethod(Method m) { + return isTestMethod(m) && Modifier.isPublic(m.getModifiers()); + } + + static private boolean isTestMethod(Method m) { + String name = m.getName(); + Class[] parameters = m.getParameterTypes(); + Class returnType = m.getReturnType(); + return parameters.length == 0 && name.startsWith("test") && returnType.equals(Void.TYPE); + } + + public String getName() { + if (options != null) { + return super.getName() + " " + options; + } + return super.getName(); + } +} diff --git a/activemq-core/src/test/java/org/activemq/ConnectionCleanupTest.java b/activemq-core/src/test/java/org/activemq/ConnectionCleanupTest.java new file mode 100755 index 0000000000..dc1f962ad2 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/ConnectionCleanupTest.java @@ -0,0 +1,71 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.JMSException; +import javax.jms.Session; + +import junit.framework.TestCase; + +/** + * @version $Revision: 1.2 $ + */ +public class ConnectionCleanupTest extends TestCase { + + private ActiveMQConnection connection; + + protected void setUp() throws Exception { + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://localhost"); + connection = (ActiveMQConnection) factory.createConnection(); + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception { + connection.close(); + } + + /** + * @throws JMSException + */ + public void testChangeClientID() throws JMSException { + + connection.setClientID("test"); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + try { + connection.setClientID("test"); + //fail("Should have received JMSException"); + } catch ( JMSException e ) { + } + + connection.cleanup(); + connection.setClientID("test"); + + connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + try { + connection.setClientID("test"); + //fail("Should have received JMSException"); + } catch ( JMSException e ) { + } + } + +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/ConsumerReceiveWithTimeoutTest.java b/activemq-core/src/test/java/org/activemq/ConsumerReceiveWithTimeoutTest.java new file mode 100644 index 0000000000..f8f3b90633 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/ConsumerReceiveWithTimeoutTest.java @@ -0,0 +1,90 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; + + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class ConsumerReceiveWithTimeoutTest extends TestSupport { + + private Connection connection; + + protected void setUp() throws Exception { + super.setUp(); + connection = createConnection(); + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception { + if (connection != null) { + connection.close(); + connection = null; + } + super.tearDown(); + } + + /** + * Test to check if consumer thread wakes up inside a receive(timeout) after a message is dispatched to the consumer + * + * @throws javax.jms.JMSException + */ + public void testConsumerReceiveBeforeMessageDispatched() throws JMSException { + + connection.start(); + + final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + final Queue queue = session.createQueue("test"); + + Thread t = new Thread() { + public void run(){ + try { + //wait for 10 seconds to allow consumer.receive to be run first + Thread.sleep(10000); + MessageProducer producer = session.createProducer(queue); + producer.send(session.createTextMessage("Hello")); + }catch(Exception e){e.printStackTrace();} + } + }; + + t.start(); + + // Consume the message... + MessageConsumer consumer = session.createConsumer(queue); + Message msg = consumer.receive(60000); + assertNotNull(msg); + session.close(); + + } + + + + + +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/EmbeddedBrokerTestSupport.java b/activemq-core/src/test/java/org/activemq/EmbeddedBrokerTestSupport.java new file mode 100644 index 0000000000..f1169b54a0 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/EmbeddedBrokerTestSupport.java @@ -0,0 +1,140 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import org.activemq.broker.BrokerService; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.activemq.pool.PooledConnectionFactory; +import org.springframework.jms.core.JmsTemplate; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; + +import junit.framework.TestCase; + +/** + * A useful base class which creates and closes an embedded broker + * + * @version $Revision: 1.1 $ + */ +public class EmbeddedBrokerTestSupport extends TestCase { + + protected BrokerService broker; + protected String bindAddress = "tcp://localhost:61616"; + protected ConnectionFactory connectionFactory; + protected boolean useTopic = false; + protected Destination destination; + protected JmsTemplate template; + private boolean usePooledConnectionWithTemplate = true; + + protected void setUp() throws Exception { + if (broker == null) { + broker = createBroker(); + } + broker.start(); + + connectionFactory = createConnectionFactory(); + + destination = createDestination(); + + template = createJmsTemplate(); + template.setDefaultDestination(destination); + template.setPubSubDomain(useTopic); + template.afterPropertiesSet(); + } + + protected void tearDown() throws Exception { + if (broker != null) { + broker.stop(); + } + } + + /** + * Factory method to create a new {@link JmsTemplate} + * + * @return a newly created JmsTemplate + */ + protected JmsTemplate createJmsTemplate() { + if (usePooledConnectionWithTemplate) { + // lets use a pool to avoid creating and closing producers + return new JmsTemplate(new PooledConnectionFactory(bindAddress)); + } + else { + return new JmsTemplate(connectionFactory); + } + } + + /** + * Factory method to create a new {@link Destination} + * + * @return newly created Destinaiton + */ + protected Destination createDestination() { + return createDestination(getDestinationString()); + } + + /** + * Factory method to create the destination in either the queue or topic + * space based on the value of the {@link #useTopic} field + */ + protected Destination createDestination(String subject) { + if (useTopic) { + return new ActiveMQTopic(subject); + } + else { + return new ActiveMQQueue(subject); + } + } + + /** + * Returns the name of the destination used in this test case + */ + protected String getDestinationString() { + return getClass().getName() + "." + getName(); + } + + /** + * Factory method to create a new {@link ConnectionFactory} instance + * + * @return a newly created connection factory + */ + protected ConnectionFactory createConnectionFactory() throws Exception { + return new ActiveMQConnectionFactory(bindAddress); + } + + /** + * Factory method to create a new broker + * + * @throws Exception + */ + protected BrokerService createBroker() throws Exception { + BrokerService answer = new BrokerService(); + answer.addConnector(bindAddress); + return answer; + } + + /** + * Factory method to create a new connection + */ + protected Connection createConnection() throws Exception { + return connectionFactory.createConnection(); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JMSConsumerTest.java b/activemq-core/src/test/java/org/activemq/JMSConsumerTest.java new file mode 100755 index 0000000000..32e4043d7c --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JMSConsumerTest.java @@ -0,0 +1,508 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.BytesMessage; +import javax.jms.DeliveryMode; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; + +import junit.framework.Test; + +import org.activemq.command.ActiveMQDestination; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +/** + * Test cases used to test the JMS message consumer. + * + * @version $Revision$ + */ +public class JMSConsumerTest extends JmsTestSupport { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JMSConsumerTest.class); + + public static Test suite() { + return suite(JMSConsumerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public ActiveMQDestination destination; + public int deliveryMode; + public int prefetch; + public int ackMode; + public byte destinationType; + public boolean durableConsumer; + + + public void initCombosForTestMutiReceiveWithPrefetch1() { + addCombinationValues("deliveryMode", new Object[] { + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) }); + addCombinationValues("ackMode", new Object[] { + new Integer(Session.AUTO_ACKNOWLEDGE), + new Integer(Session.DUPS_OK_ACKNOWLEDGE), + new Integer(Session.CLIENT_ACKNOWLEDGE) }); + addCombinationValues("destinationType", new Object[] { + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE) + }); + } + + public void testMutiReceiveWithPrefetch1() throws Throwable { + + // Set prefetch to 1 + connection.getPrefetchPolicy().setAll(1); + connection.start(); + + // Use all the ack modes + Session session = connection.createSession(false, ackMode); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + + // Send the messages + sendMessages(session, destination, 4); + + // Make sure 4 messages were delivered. + Message message = null; + for (int i = 0; i < 4; i++) { + message = consumer.receive(1000); + assertNotNull(message); + } + assertNull(consumer.receiveNoWait()); + message.acknowledge(); + } + + public void initCombosForTestDurableConsumerSelectorChange() { + addCombinationValues("deliveryMode", new Object[] { + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) }); + addCombinationValues("destinationType", new Object[] { + new Byte(ActiveMQDestination.TOPIC_TYPE)}); + } + public void testDurableConsumerSelectorChange() throws Throwable { + + // Receive a message with the JMS API + connection.setClientID("test"); + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageProducer producer = session.createProducer(destination); + producer.setDeliveryMode(deliveryMode); + MessageConsumer consumer = session.createDurableSubscriber((Topic)destination, "test", "color='red'", false); + + // Send the messages + TextMessage message = session.createTextMessage("1st"); + message.setStringProperty("color", "red"); + producer.send(message); + + Message m = consumer.receive(1000); + assertNotNull(m); + assertEquals("1st", ((TextMessage)m).getText()); + + // Change the subscription. + consumer.close(); + consumer = session.createDurableSubscriber((Topic)destination, "test", "color='blue'", false); + + message = session.createTextMessage("2nd"); + message.setStringProperty("color", "red"); + producer.send(message); + message = session.createTextMessage("3rd"); + message.setStringProperty("color", "blue"); + producer.send(message); + + // Selector should skip the 2nd message. + m = consumer.receive(1000); + assertNotNull(m); + assertEquals("3rd", ((TextMessage)m).getText()); + + assertNull(consumer.receiveNoWait()); + } + + public void initCombosForTestSendReceiveBytesMessage() { + addCombinationValues("deliveryMode", new Object[] { new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) }); + addCombinationValues("destinationType", new Object[] { new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE) }); + } + + public void testSendReceiveBytesMessage() throws Throwable { + + // Receive a message with the JMS API + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + MessageProducer producer = session.createProducer(destination); + + BytesMessage message = session.createBytesMessage(); + message.writeBoolean(true); + message.writeBoolean(false); + producer.send(message); + + // Make sure only 1 message was delivered. + BytesMessage m = (BytesMessage)consumer.receive(1000); + assertNotNull(m); + assertTrue(m.readBoolean()); + assertFalse(m.readBoolean()); + + assertNull(consumer.receiveNoWait()); + } + + + public void initCombosForTestSetMessageListenerAfterStart() { + addCombinationValues("deliveryMode", new Object[] { + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) }); + addCombinationValues("destinationType", new Object[] { + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE) }); + } + public void testSetMessageListenerAfterStart() throws Throwable { + + final AtomicInteger counter = new AtomicInteger(0); + final CountDownLatch done = new CountDownLatch(1); + + // Receive a message with the JMS API + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + + // Send the messages + sendMessages(session, destination, 4); + + // See if the message get sent to the listener + consumer.setMessageListener(new MessageListener() { + public void onMessage(Message m) { + counter.incrementAndGet(); + if( counter.get()==4 ) + done.countDown(); + } + }); + + assertTrue(done.await(1000, TimeUnit.MILLISECONDS)); + Thread.sleep(200); + + // Make sure only 4 messages were delivered. + assertEquals(4, counter.get()); + } + + public void initCombosForTestMessageListenerUnackedWithPrefetch1StayInQueue() { + addCombinationValues("deliveryMode", new Object[] { + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) + }); + addCombinationValues("ackMode", new Object[] { + new Integer(Session.AUTO_ACKNOWLEDGE), + new Integer(Session.DUPS_OK_ACKNOWLEDGE), + new Integer(Session.CLIENT_ACKNOWLEDGE) + }); + addCombinationValues("destinationType", new Object[] { new Byte(ActiveMQDestination.QUEUE_TYPE), }); + } + + public void testMessageListenerUnackedWithPrefetch1StayInQueue() throws Throwable { + + final AtomicInteger counter = new AtomicInteger(0); + final CountDownLatch done = new CountDownLatch(1); + + // Set prefetch to 1 + connection.getPrefetchPolicy().setAll(1); + // This test case does not work if optimized message dispatch is used as the main thread send block until the consumer receives the + // message. This test depends on thread decoupling so that the main thread can stop the consumer thread. + connection.setOptimizedMessageDispatch(false); + connection.start(); + + // Use all the ack modes + Session session = connection.createSession(false, ackMode); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + consumer.setMessageListener(new MessageListener() { + public void onMessage(Message m) { + try { + TextMessage tm = (TextMessage)m; + log.info("Got in first listener: "+tm.getText()); + assertEquals( ""+counter.get(), tm.getText() ); + counter.incrementAndGet(); + m.acknowledge(); + if( counter.get()==2 ) { + done.countDown(); + Thread.sleep(500); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + }); + + // Send the messages + sendMessages(session, destination, 4); + + // Wait for first 2 messages to arrive. + assertTrue(done.await(100000, TimeUnit.MILLISECONDS)); + connection.close(); + + // Re-start connection. + connection = (ActiveMQConnection) factory.createConnection(); + connections.add(connection); + + connection.getPrefetchPolicy().setAll(1); + connection.start(); + + // Pickup the remaining messages. + final CountDownLatch done2 = new CountDownLatch(1); + session = connection.createSession(false, ackMode); + consumer = session.createConsumer(destination); + consumer.setMessageListener(new MessageListener() { + public void onMessage(Message m) { + try { + TextMessage tm = (TextMessage)m; + log.info("Got in second listener: "+tm.getText()); + assertEquals( ""+counter.get(), tm.getText() ); + counter.incrementAndGet(); + if( counter.get()==4 ) + done2.countDown(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + }); + + assertTrue(done2.await(1000, TimeUnit.MILLISECONDS)); + Thread.sleep(200); + + // Make sure only 4 messages were delivered. + assertEquals(4, counter.get()); + + } + + + public void initCombosForTestMessageListenerWithConsumerWithPrefetch1() { + addCombinationValues("deliveryMode", new Object[] { + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) }); + addCombinationValues("destinationType", new Object[] { + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE) }); + } + public void testMessageListenerWithConsumerWithPrefetch1() throws Throwable { + + final AtomicInteger counter = new AtomicInteger(0); + final CountDownLatch done = new CountDownLatch(1); + + // Receive a message with the JMS API + connection.getPrefetchPolicy().setAll(1); + connection.start(); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + consumer.setMessageListener(new MessageListener() { + public void onMessage(Message m) { + counter.incrementAndGet(); + if( counter.get()==4 ) + done.countDown(); + } + }); + + // Send the messages + sendMessages(session, destination, 4); + + assertTrue(done.await(1000, TimeUnit.MILLISECONDS)); + Thread.sleep(200); + + // Make sure only 4 messages were delivered. + assertEquals(4, counter.get()); + } + + public void initCombosForTestMessageListenerWithConsumer() { + addCombinationValues("deliveryMode", new Object[] { + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) }); + addCombinationValues("destinationType", new Object[] { + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE) }); + } + public void testMessageListenerWithConsumer() throws Throwable { + + final AtomicInteger counter = new AtomicInteger(0); + final CountDownLatch done = new CountDownLatch(1); + + // Receive a message with the JMS API + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + consumer.setMessageListener(new MessageListener() { + public void onMessage(Message m) { + counter.incrementAndGet(); + if( counter.get()==4 ) + done.countDown(); + } + }); + + // Send the messages + sendMessages(session, destination, 4); + + assertTrue(done.await(1000, TimeUnit.MILLISECONDS)); + Thread.sleep(200); + + // Make sure only 4 messages were delivered. + assertEquals(4, counter.get()); + } + + public void initCombosForTestUnackedWithPrefetch1StayInQueue() { + addCombinationValues("deliveryMode", new Object[] { new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) }); + addCombinationValues("ackMode", new Object[] { new Integer(Session.AUTO_ACKNOWLEDGE), + new Integer(Session.DUPS_OK_ACKNOWLEDGE), new Integer(Session.CLIENT_ACKNOWLEDGE) }); + addCombinationValues("destinationType", new Object[] { new Byte(ActiveMQDestination.QUEUE_TYPE), }); + } + + public void testUnackedWithPrefetch1StayInQueue() throws Throwable { + + // Set prefetch to 1 + connection.getPrefetchPolicy().setAll(1); + connection.start(); + + // Use all the ack modes + Session session = connection.createSession(false, ackMode); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + + // Send the messages + sendMessages(session, destination, 4); + + // Only pick up the first 2 messages. + Message message = null; + for (int i = 0; i < 2; i++) { + message = consumer.receive(1000); + assertNotNull(message); + } + message.acknowledge(); + + connection.close(); + connection = (ActiveMQConnection) factory.createConnection(); + connections.add(connection); + connection.getPrefetchPolicy().setAll(1); + connection.start(); + + // Use all the ack modes + session = connection.createSession(false, ackMode); + consumer = session.createConsumer(destination); + + // Pickup the rest of the messages. + for (int i = 0; i < 2; i++) { + message = consumer.receive(1000); + assertNotNull(message); + } + message.acknowledge(); + assertNull(consumer.receiveNoWait()); + + } + + public void initCombosForTestDontStart() { + addCombinationValues("deliveryMode", new Object[] { new Integer(DeliveryMode.NON_PERSISTENT), }); + addCombinationValues("destinationType", new Object[] { new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), }); + } + + public void testDontStart() throws Throwable { + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + + // Send the messages + sendMessages(session, destination, 1); + + // Make sure no messages were delivered. + assertNull(consumer.receive(1000)); + } + + public void initCombosForTestStartAfterSend() { + addCombinationValues("deliveryMode", new Object[] { new Integer(DeliveryMode.NON_PERSISTENT), }); + addCombinationValues("destinationType", new Object[] { new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), }); + } + + public void testStartAfterSend() throws Throwable { + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + + // Send the messages + sendMessages(session, destination, 1); + + // Start the conncection after the message was sent. + connection.start(); + + // Make sure only 1 message was delivered. + assertNotNull(consumer.receive(1000)); + assertNull(consumer.receiveNoWait()); + } + + public void initCombosForTestReceiveMessageWithConsumer() { + addCombinationValues("deliveryMode", new Object[] { new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) }); + addCombinationValues("destinationType", new Object[] { new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE) }); + } + + public void testReceiveMessageWithConsumer() throws Throwable { + + // Receive a message with the JMS API + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + + // Send the messages + sendMessages(session, destination, 1); + + // Make sure only 1 message was delivered. + Message m = consumer.receive(1000); + assertNotNull(m); + assertEquals("0", ((TextMessage)m).getText()); + assertNull(consumer.receiveNoWait()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/JMSDurableTopicRedeliverTest.java b/activemq-core/src/test/java/org/activemq/JMSDurableTopicRedeliverTest.java new file mode 100755 index 0000000000..b79966f292 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JMSDurableTopicRedeliverTest.java @@ -0,0 +1,81 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Message; +import javax.jms.Session; +import javax.jms.TextMessage; + +/** + * @version $Revision: 1.4 $ + */ +public class JMSDurableTopicRedeliverTest extends JmsTopicRedeliverTest { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JMSDurableTopicRedeliverTest.class); + + protected void setUp() throws Exception { + durable = true; + super.setUp(); + } + + + /** + * Sends and consumes the messages. + * + * @throws Exception + */ + public void testRedeliverNewSession() throws Exception { + String text = "TEST"; + Message sendMessage = session.createTextMessage(text); + + if (verbose) { + log.info("About to send a message: " + sendMessage + " with text: " + text); + } + producer.send(producerDestination, sendMessage); + + //receive but don't acknowledge + Message unackMessage = consumer.receive(1000); + assertNotNull(unackMessage); + String unackId = unackMessage.getJMSMessageID(); + assertEquals(((TextMessage) unackMessage).getText(), text); + assertFalse(unackMessage.getJMSRedelivered()); + assertEquals(unackMessage.getIntProperty("JMSXDeliveryCount"),1); + consumeSession.close(); + consumer.close(); + + //receive then acknowledge + consumeSession = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + consumer = createConsumer(); + Message ackMessage = consumer.receive(1000); + assertNotNull(ackMessage); + ackMessage.acknowledge(); + String ackId = ackMessage.getJMSMessageID(); + assertEquals(((TextMessage) ackMessage).getText(), text); + assertTrue(ackMessage.getJMSRedelivered()); + assertEquals(ackMessage.getIntProperty("JMSXDeliveryCount"),2); + assertEquals(unackId, ackId); + consumeSession.close(); + consumer.close(); + + consumeSession = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + consumer = createConsumer(); + assertNull(consumer.receiveNoWait()); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JMSExclusiveConsumerTest.java b/activemq-core/src/test/java/org/activemq/JMSExclusiveConsumerTest.java new file mode 100644 index 0000000000..4d8ed50d98 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JMSExclusiveConsumerTest.java @@ -0,0 +1,132 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.DeliveryMode; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import junit.framework.Test; + +import org.activemq.command.ActiveMQQueue; + +/** + * Test cases used to test the JMS message exclusive consumers. + * + * @version $Revision$ + */ +public class JMSExclusiveConsumerTest extends JmsTestSupport { + + public static Test suite() { + return suite(JMSExclusiveConsumerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public int deliveryMode; + + public void initCombosForTestRoundRobinDispatchOnNonExclusive() { + addCombinationValues("deliveryMode", new Object[] { new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) }); + } + + /** + * Shows that by default messages are round robined across a set of consumers. + * + * @throws Throwable + */ + public void testRoundRobinDispatchOnNonExclusive() throws Throwable { + + // Receive a message with the JMS API + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + ActiveMQQueue destination = new ActiveMQQueue("TEST"); + MessageProducer producer = session.createProducer(destination); + producer.setDeliveryMode(deliveryMode); + + MessageConsumer consumer1 = session.createConsumer(destination); + MessageConsumer consumer2 = session.createConsumer(destination); + + // Send the messages + producer.send(session.createTextMessage("1st")); + producer.send(session.createTextMessage("2nd")); + + Message m; + m = consumer2.receive(1000); + assertNotNull(m); + + m = consumer1.receive(1000); + assertNotNull(m); + + assertNull(consumer1.receiveNoWait()); + assertNull(consumer2.receiveNoWait()); + } + + public void initCombosForTestDispatchExclusive() { + addCombinationValues("deliveryMode", new Object[] { new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) }); + } + + /** + * Shows that if the "?consumer.exclusive=true" option is added to destination, + * then all messages are routed to 1 consumer. + * + * @throws Throwable + */ + public void testDispatchExclusive() throws Throwable { + + // Receive a message with the JMS API + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + ActiveMQQueue destination = new ActiveMQQueue("TEST?consumer.exclusive=true"); + MessageProducer producer = session.createProducer(destination); + producer.setDeliveryMode(deliveryMode); + + MessageConsumer consumer1 = session.createConsumer(destination); + MessageConsumer consumer2 = session.createConsumer(destination); + + // Send the messages + producer.send(session.createTextMessage("1st")); + producer.send(session.createTextMessage("2nd")); + producer.send(session.createTextMessage("3nd")); + + Message m; + m = consumer2.receive(1000); + if( m!=null ) { + // Consumer 2 should get all the messages. + for (int i = 0; i < 2; i++) { + m = consumer2.receive(1000); + assertNotNull(m); + } + } else { + // Consumer 1 should get all the messages. + for (int i = 0; i < 3; i++) { + m = consumer1.receive(1000); + assertNotNull(m); + } + } + + assertNull(consumer1.receiveNoWait()); + assertNull(consumer2.receiveNoWait()); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JMSMessageTest.java b/activemq-core/src/test/java/org/activemq/JMSMessageTest.java new file mode 100755 index 0000000000..a8c5c71a23 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JMSMessageTest.java @@ -0,0 +1,461 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 Logic Blaze Inc. + * + * Licensed 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.activemq; + +import java.net.URISyntaxException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Vector; + +import javax.jms.BytesMessage; +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.MessageConsumer; +import javax.jms.MessageEOFException; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Session; +import javax.jms.StreamMessage; +import javax.jms.TextMessage; + +import junit.framework.Test; + +import org.activemq.command.ActiveMQDestination; + +/** + * Test cases used to test the JMS message consumer. + * + * @version $Revision$ + */ +public class JMSMessageTest extends JmsTestSupport { + + public ActiveMQDestination destination; + public int deliveryMode; + public int prefetch; + public int ackMode; + public byte destinationType; + public boolean durableConsumer; + public String connectURL; + + /** + * Run all these tests in both marshaling and non-marshaling mode. + */ + public void initCombos() { + addCombinationValues("connectURL", new Object[] { + "vm://localhost?marshal=false", + "vm://localhost?marshal=true" + }); + addCombinationValues("deliveryMode", new Object[] { + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) }); + addCombinationValues("destinationType", new Object[] { + new Byte(ActiveMQDestination.QUEUE_TYPE)}); + } + + public void testTextMessage() throws Throwable { + + // Receive a message with the JMS API + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + MessageProducer producer = session.createProducer(destination); + + // Send the message. + { + TextMessage message = session.createTextMessage(); + message.setText("Hi"); + producer.send(message); + } + + // Check the Message + { + TextMessage message = (TextMessage)consumer.receive(1000); + assertNotNull(message); + assertEquals( "Hi", message.getText() ); + } + + assertNull(consumer.receiveNoWait()); + } + + public static Test suite() { + return suite(JMSMessageTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + protected ConnectionFactory createConnectionFactory() throws URISyntaxException { + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(connectURL); + return factory; + } + + public void testBytesMessageLength() throws Throwable { + + // Receive a message with the JMS API + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + MessageProducer producer = session.createProducer(destination); + + // Send the message + { + BytesMessage message = session.createBytesMessage(); + message.writeInt(1); + message.writeInt(2); + message.writeInt(3); + message.writeInt(4); + producer.send(message); + } + + // Check the message. + { + BytesMessage message = (BytesMessage) consumer.receive(1000); + assertNotNull(message); + assertEquals(16, message.getBodyLength() ); + } + + assertNull(consumer.receiveNoWait()); + } + + public void testObjectMessage() throws Throwable { + + // Receive a message with the JMS API + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + MessageProducer producer = session.createProducer(destination); + + // send the message. + { + ObjectMessage message = session.createObjectMessage(); + message.setObject("Hi"); + producer.send(message); + } + + // Check the message + { + ObjectMessage message = (ObjectMessage)consumer.receive(1000); + assertNotNull(message); + assertEquals( "Hi", message.getObject() ); + } + assertNull(consumer.receiveNoWait()); + } + + public void testBytesMessage() throws Throwable { + + // Receive a message with the JMS API + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + MessageProducer producer = session.createProducer(destination); + + // Send the message + { + BytesMessage message = session.createBytesMessage(); + message.writeBoolean(true); + producer.send(message); + } + + // Check the message + { + BytesMessage message = (BytesMessage)consumer.receive(1000); + assertNotNull(message); + assertTrue( message.readBoolean() ); + + try { + message.readByte(); + fail("Expected exception not thrown."); + } catch (MessageEOFException e) { + } + + } + assertNull(consumer.receiveNoWait()); + } + + public void testStreamMessage() throws Throwable { + + // Receive a message with the JMS API + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + MessageProducer producer = session.createProducer(destination); + + // Send the message. + { + StreamMessage message = session.createStreamMessage(); + message.writeString("This is a test to see how it works."); + producer.send(message); + } + + // Check the message. + { + StreamMessage message = (StreamMessage) consumer.receive(1000); + assertNotNull(message); + + // Invalid conversion should throw exception and not move the stream position. + try { + message.readByte(); + fail("Should have received NumberFormatException"); + } catch (NumberFormatException e) { + } + + assertEquals("This is a test to see how it works.", message.readString() ); + + // Invalid conversion should throw exception and not move the stream position. + try { + message.readByte(); + fail("Should have received MessageEOFException"); + } catch (MessageEOFException e) { + } + } + assertNull(consumer.receiveNoWait()); + } + + public void testMapMessage() throws Throwable { + + // Receive a message with the JMS API + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + MessageProducer producer = session.createProducer(destination); + + // send the message. + { + MapMessage message = session.createMapMessage(); + message.setBoolean("boolKey", true); + producer.send(message); + } + + // get the message. + { + MapMessage message = (MapMessage)consumer.receive(1000); + assertNotNull(message); + assertTrue( message.getBoolean("boolKey") ); + } + assertNull(consumer.receiveNoWait()); + } + + static class ForeignMessage implements TextMessage { + + private String messageId; + private long timestamp; + private String correlationId; + private Destination replyTo; + private Destination destination; + public int deliveryMode; + private boolean redelivered; + private String type; + private long expiration; + private int priority; + private String text; + HashMap props = new HashMap(); + + public String getJMSMessageID() throws JMSException { + return messageId; + } + + public void setJMSMessageID(String arg0) throws JMSException { + messageId = arg0; + } + + public long getJMSTimestamp() throws JMSException { + return timestamp; + } + + public void setJMSTimestamp(long arg0) throws JMSException { + timestamp = arg0; + } + + public byte[] getJMSCorrelationIDAsBytes() throws JMSException { + return null; + } + public void setJMSCorrelationIDAsBytes(byte[] arg0) throws JMSException { + } + + public void setJMSCorrelationID(String arg0) throws JMSException { + correlationId = arg0; + } + public String getJMSCorrelationID() throws JMSException { + return correlationId; + } + + public Destination getJMSReplyTo() throws JMSException { + return replyTo; + } + public void setJMSReplyTo(Destination arg0) throws JMSException { + replyTo = arg0; + } + + public Destination getJMSDestination() throws JMSException { + return destination; + } + + public void setJMSDestination(Destination arg0) throws JMSException { + destination = arg0; + } + + public int getJMSDeliveryMode() throws JMSException { + return deliveryMode; + } + + public void setJMSDeliveryMode(int arg0) throws JMSException { + deliveryMode = arg0; + } + + public boolean getJMSRedelivered() throws JMSException { + return redelivered; + } + + public void setJMSRedelivered(boolean arg0) throws JMSException { + redelivered=arg0; + } + + public String getJMSType() throws JMSException { + return type; + } + + public void setJMSType(String arg0) throws JMSException { + type=arg0; + } + + public long getJMSExpiration() throws JMSException { + return expiration; + } + + public void setJMSExpiration(long arg0) throws JMSException { + expiration = arg0; + } + + public int getJMSPriority() throws JMSException { + return priority; + } + + public void setJMSPriority(int arg0) throws JMSException { + priority=arg0; + } + + public void clearProperties() throws JMSException { + } + public boolean propertyExists(String arg0) throws JMSException { + return false; + } + public boolean getBooleanProperty(String arg0) throws JMSException { + return false; + } + public byte getByteProperty(String arg0) throws JMSException { + return 0; + } + public short getShortProperty(String arg0) throws JMSException { + return 0; + } + public int getIntProperty(String arg0) throws JMSException { + return 0; + } + public long getLongProperty(String arg0) throws JMSException { + return 0; + } + public float getFloatProperty(String arg0) throws JMSException { + return 0; + } + public double getDoubleProperty(String arg0) throws JMSException { + return 0; + } + public String getStringProperty(String arg0) throws JMSException { + return (String) props.get(arg0); + } + public Object getObjectProperty(String arg0) throws JMSException { + return props.get(arg0); + } + public Enumeration getPropertyNames() throws JMSException { + return new Vector(props.keySet()).elements(); + } + public void setBooleanProperty(String arg0, boolean arg1) throws JMSException { + } + public void setByteProperty(String arg0, byte arg1) throws JMSException { + } + public void setShortProperty(String arg0, short arg1) throws JMSException { + } + public void setIntProperty(String arg0, int arg1) throws JMSException { + } + public void setLongProperty(String arg0, long arg1) throws JMSException { + } + public void setFloatProperty(String arg0, float arg1) throws JMSException { + } + public void setDoubleProperty(String arg0, double arg1) throws JMSException { + } + public void setStringProperty(String arg0, String arg1) throws JMSException { + props.put( arg0, arg1); + } + public void setObjectProperty(String arg0, Object arg1) throws JMSException { + props.put( arg0, arg1); + } + + public void acknowledge() throws JMSException { + } + public void clearBody() throws JMSException { + } + + public void setText(String arg0) throws JMSException { + text = arg0; + } + public String getText() throws JMSException { + return text; + } + } + + public void testForeignMessage() throws Throwable { + + // Receive a message with the JMS API + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer = session.createConsumer(destination); + MessageProducer producer = session.createProducer(destination); + + // Send the message. + { + ForeignMessage message = new ForeignMessage(); + message.text= "Hello"; + message.setStringProperty("test", "value"); + producer.send(message); + } + + // Validate message is OK. + { + TextMessage message = (TextMessage)consumer.receive(1000); + assertNotNull(message); + assertEquals( "Hello", message.getText() ); + assertEquals( "value", message.getStringProperty("test") ); + } + + assertNull(consumer.receiveNoWait()); + } + + +} diff --git a/activemq-core/src/test/java/org/activemq/JMSQueueRedeliverTest.java b/activemq-core/src/test/java/org/activemq/JMSQueueRedeliverTest.java new file mode 100755 index 0000000000..aa271078d9 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JMSQueueRedeliverTest.java @@ -0,0 +1,29 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +/** + * @version $Revision: 1.2 $ + */ +public class JMSQueueRedeliverTest extends JmsTopicRedeliverTest { + protected void setUp() throws Exception { + topic = false; + super.setUp(); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JMSUsecaseTest.java b/activemq-core/src/test/java/org/activemq/JMSUsecaseTest.java new file mode 100755 index 0000000000..8134d705b5 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JMSUsecaseTest.java @@ -0,0 +1,150 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.util.Enumeration; + +import javax.jms.DeliveryMode; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.QueueBrowser; +import javax.jms.Session; +import javax.jms.TextMessage; + +import junit.framework.Test; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQMessage; + +public class JMSUsecaseTest extends JmsTestSupport { + + public static Test suite() { + return suite(JMSUsecaseTest.class); + } + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public ActiveMQDestination destination; + public int deliveryMode; + public int prefetch; + public byte destinationType; + public boolean durableConsumer; + + public void initCombosForTestQueueBrowser() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + } ); + } + public void testQueueBrowser() throws Throwable { + + // Send a message to the broker. + connection.start(); + Session session = connection.createSession(false, Session.SESSION_TRANSACTED); + destination = createDestination(session, destinationType); + sendMessages(session, destination, 5); + + + QueueBrowser browser = session.createBrowser((Queue) destination); + Enumeration enumeration = browser.getEnumeration(); + for(int i=0; i < 5; i++) { + Thread.sleep(100); + assertTrue(enumeration.hasMoreElements()); + Message m = (Message)enumeration.nextElement(); + assertNotNull(m); + assertEquals(""+i, ((TextMessage)m).getText()); + } + assertFalse(enumeration.hasMoreElements()); + } + + public void initCombosForTestSendReceive() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE)} ); + } + public void testSendReceive() throws Throwable { + // Send a message to the broker. + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageProducer producer = session.createProducer(destination); + MessageConsumer consumer = session.createConsumer(destination); + ActiveMQMessage message = new ActiveMQMessage(); + producer.send(message); + + // Make sure only 1 message was delivered. + assertNotNull(consumer.receive(1000)); + assertNull(consumer.receiveNoWait()); + } + + public void initCombosForTestSendReceiveTransacted() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE)} ); + } + public void testSendReceiveTransacted() throws Throwable { + // Send a message to the broker. + connection.start(); + Session session = connection.createSession(true, Session.SESSION_TRANSACTED); + destination = createDestination(session, destinationType); + MessageProducer producer = session.createProducer(destination); + MessageConsumer consumer = session.createConsumer(destination); + producer.send(session.createTextMessage("test")); + + // Message should not be delivered until commit. + assertNull(consumer.receiveNoWait()); + session.commit(); + + // Make sure only 1 message was delivered. + Message message = consumer.receive(1000); + assertNotNull(message); + assertFalse(message.getJMSRedelivered()); + assertNull(consumer.receiveNoWait()); + + // Message should be redelivered is rollback is used. + session.rollback(); + + // Make sure only 1 message was delivered. + message = consumer.receive(2000); + assertNotNull(message); + assertTrue(message.getJMSRedelivered()); + assertNull(consumer.receiveNoWait()); + + // If we commit now, the message should not be redelivered. + session.commit(); + assertNull(consumer.receiveNoWait()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/JmsAutoAckListenerTest.java b/activemq-core/src/test/java/org/activemq/JmsAutoAckListenerTest.java new file mode 100755 index 0000000000..794797515b --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsAutoAckListenerTest.java @@ -0,0 +1,85 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Connection; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; + +/** + * @version $Revision: 1.2 $ + */ +public class JmsAutoAckListenerTest extends TestSupport implements MessageListener{ + + private Connection connection; + + protected void setUp() throws Exception { + super.setUp(); + connection = createConnection(); + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception { + if (connection != null) { + connection.close(); + connection = null; + } + super.tearDown(); + } + + /** + * Tests if acknowleged messages are being consumed. + * + * @throws javax.jms.JMSException + */ + public void testAckedMessageAreConsumed() throws Exception { + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createQueue("test"); + MessageProducer producer = session.createProducer(queue); + producer.send(session.createTextMessage("Hello")); + + // Consume the message... + MessageConsumer consumer = session.createConsumer(queue); + consumer.setMessageListener(this); + + Thread.sleep(10000); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + // Attempt to Consume the message...check if message was acknowledge + consumer = session.createConsumer(queue); + Message msg = consumer.receive(1000); + assertNull(msg); + + + session.close(); + } + + + public void onMessage(Message message){ + assertNotNull(message); + + + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsAutoAckTest.java b/activemq-core/src/test/java/org/activemq/JmsAutoAckTest.java new file mode 100755 index 0000000000..739c63b27f --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsAutoAckTest.java @@ -0,0 +1,82 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; + +/** + * @version $Revision: 1.2 $ + */ +public class JmsAutoAckTest extends TestSupport { + + private Connection connection; + + protected void setUp() throws Exception { + super.setUp(); + connection = createConnection(); + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception { + if (connection != null) { + connection.close(); + connection = null; + } + super.tearDown(); + } + + /** + * Tests if acknowleged messages are being consumed. + * + * @throws javax.jms.JMSException + */ + public void testAckedMessageAreConsumed() throws JMSException { + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createQueue("test"); + MessageProducer producer = session.createProducer(queue); + producer.send(session.createTextMessage("Hello")); + + // Consume the message... + MessageConsumer consumer = session.createConsumer(queue); + Message msg = consumer.receive(1000); + assertNotNull(msg); + + // Reset the session. + session.close(); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Attempt to Consume the message... + consumer = session.createConsumer(queue); + msg = consumer.receive(1000); + assertNull(msg); + + session.close(); + } + + +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsBenchmark.java b/activemq-core/src/test/java/org/activemq/JmsBenchmark.java new file mode 100755 index 0000000000..208921179f --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsBenchmark.java @@ -0,0 +1,213 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import javax.jms.BytesMessage; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import junit.framework.Test; + +import org.activemq.broker.BrokerFactory; +import org.activemq.broker.BrokerService; +import org.activemq.broker.TransportConnector; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; + +import edu.emory.mathcs.backport.java.util.concurrent.Callable; +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.Semaphore; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +/** + * Benchmarks the broker by starting many consumer and producers against the + * same destination. + * + * Make sure you run with jvm option -server (makes a big difference). The tests + * simulate storing 1000 1k jms messages to see the rate of processing msg/sec. + * + * @version $Revision$ + */ +public class JmsBenchmark extends JmsTestSupport { + + private static final long SAMPLE_DELAY = Integer.parseInt(System.getProperty("SAMPLE_DELAY", "" + 1000 * 5)); + private static final long SAMPLES = Integer.parseInt(System.getProperty("SAMPLES", "10")); + private static final long SAMPLE_DURATION = Integer.parseInt(System.getProperty("SAMPLES_DURATION", "" + 1000*60)); + private static final int PRODUCER_COUNT = Integer.parseInt(System.getProperty("PRODUCER_COUNT", "10")); + private static final int CONSUMER_COUNT = Integer.parseInt(System.getProperty("CONSUMER_COUNT", "10")); + + public ActiveMQDestination destination; + + public static Test suite() { + return suite(JmsBenchmark.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(JmsBenchmark.class); + } + + public void initCombos() { + addCombinationValues("destination", new Object[] { +// new ActiveMQTopic("TEST"), + new ActiveMQQueue("TEST"), + }); + } + + protected BrokerService createBroker() throws Exception { + return BrokerFactory.createBroker(new URI("broker://(tcp://localhost:0)?persistent=false")); + } + + protected ConnectionFactory createConnectionFactory() throws URISyntaxException, IOException { + return new ActiveMQConnectionFactory(((TransportConnector) broker.getTransportConnectors().get(0)).getServer().getConnectURI()); + } + + /** + * @throws Throwable + */ + public void testConcurrentSendReceive() throws Throwable { + + final Semaphore connectionsEstablished = new Semaphore(1 - (CONSUMER_COUNT + PRODUCER_COUNT)); + final Semaphore workerDone = new Semaphore(1 - (CONSUMER_COUNT + PRODUCER_COUNT)); + final CountDownLatch sampleTimeDone = new CountDownLatch(1); + + final AtomicInteger producedMessages = new AtomicInteger(0); + final AtomicInteger receivedMessages = new AtomicInteger(0); + + final Callable producer = new Callable() { + public Object call() throws JMSException, InterruptedException { + Connection connection = factory.createConnection(); + connections.add(connection); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = session.createProducer(destination); + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + BytesMessage message = session.createBytesMessage(); + message.writeBytes(new byte[1024]); + connection.start(); + connectionsEstablished.release(); + + while (!sampleTimeDone.await(0, TimeUnit.MILLISECONDS)) { + producer.send(message); + producedMessages.incrementAndGet(); + } + + connection.close(); + workerDone.release(); + return null; + } + }; + + final Callable consumer = new Callable() { + public Object call() throws JMSException, InterruptedException { + Connection connection = factory.createConnection(); + connections.add(connection); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageConsumer consumer = session.createConsumer(destination); + + consumer.setMessageListener(new MessageListener() { + public void onMessage(Message msg) { + receivedMessages.incrementAndGet(); + } + }); + connection.start(); + + connectionsEstablished.release(); + sampleTimeDone.await(); + + connection.close(); + workerDone.release(); + return null; + } + }; + + final Throwable workerError[] = new Throwable[1]; + for (int i = 0; i < PRODUCER_COUNT; i++) { + new Thread("Producer:" + i) { + public void run() { + try { + producer.call(); + } catch (Throwable e) { + e.printStackTrace(); + workerError[0] = e; + } + } + }.start(); + } + + for (int i = 0; i < CONSUMER_COUNT; i++) { + new Thread("Consumer:" + i) { + public void run() { + try { + consumer.call(); + } catch (Throwable e) { + e.printStackTrace(); + workerError[0] = e; + } + } + }.start(); + } + + System.out.println(getName() + ": Waiting for Producers and Consumers to startup."); + connectionsEstablished.acquire(); + System.out.println("Producers and Consumers are now running. Waiting for system to reach steady state: " + + (SAMPLE_DELAY / 1000.0f) + " seconds"); + Thread.sleep(1000 * 10); + + System.out.println("Starting sample: "+SAMPLES+" each lasting "+ (SAMPLE_DURATION / 1000.0f) + " seconds"); + + + long now = System.currentTimeMillis(); + for( int i=0; i < SAMPLES; i ++) { + + long start = System.currentTimeMillis(); + receivedMessages.set(0); + producedMessages.set(0); + + Thread.sleep(SAMPLE_DURATION); + + long end = System.currentTimeMillis(); + int r = receivedMessages.get(); + int p = producedMessages.get(); + + System.out.println("published: " + p + " msgs at "+ (p * 1000f / (end - start)) + " msgs/sec, "+ + "consumed: " + r + " msgs at "+ (r * 1000f / (end - start)) + " msgs/sec"); + } + + System.out.println("Sample done."); + sampleTimeDone.countDown(); + + workerDone.acquire(); + if (workerError[0] != null) { + throw workerError[0]; + } + + } + +} diff --git a/activemq-core/src/test/java/org/activemq/JmsClientAckListenerTest.java b/activemq-core/src/test/java/org/activemq/JmsClientAckListenerTest.java new file mode 100755 index 0000000000..22170ea6db --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsClientAckListenerTest.java @@ -0,0 +1,133 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Connection; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; + +/** + * @version $Revision: 1.3 $ + */ +public class JmsClientAckListenerTest extends TestSupport implements MessageListener { + + private Connection connection; + private boolean dontAck=false; + + protected void setUp() throws Exception { + super.setUp(); + connection = createConnection(); + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception { + if (connection != null) { + connection.close(); + connection = null; + } + super.tearDown(); + } + + /** + * Tests if acknowleged messages are being consumed. + * + * @throws javax.jms.JMSException + */ + public void testAckedMessageAreConsumed() throws Exception { + connection.start(); + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = session.createQueue("test"); + MessageProducer producer = session.createProducer(queue); + producer.send(session.createTextMessage("Hello")); + + // Consume the message... + MessageConsumer consumer = session.createConsumer(queue); + consumer.setMessageListener(this); + + Thread.sleep(10000); + + // Reset the session. + session.close(); + + + + session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Attempt to Consume the message... + consumer = session.createConsumer(queue); + Message msg = consumer.receive(1000); + assertNull(msg); + + session.close(); + } + + /** + * Tests if unacknowleged messages are being redelivered when the consumer connects again. + * + * @throws javax.jms.JMSException + */ + public void testUnAckedMessageAreNotConsumedOnSessionClose() throws Exception { + connection.start(); + //don't aknowledge message on onMessage() call + dontAck=true; + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = session.createQueue("test"); + MessageProducer producer = session.createProducer(queue); + producer.send(session.createTextMessage("Hello")); + + // Consume the message... + MessageConsumer consumer = session.createConsumer(queue); + consumer.setMessageListener(this); + // Don't ack the message. + + // Reset the session. This should cause the Unacked message to be redelivered. + session.close(); + + Thread.sleep(10000); + session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + // Attempt to Consume the message... + consumer = session.createConsumer(queue); + Message msg = consumer.receive(2000); + assertNotNull(msg); + msg.acknowledge(); + + session.close(); + } + + + public void onMessage(Message message){ + + assertNotNull(message); + if(!dontAck) { + try { + message.acknowledge(); + }catch(Exception e){ + e.printStackTrace(); + } + + } + + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsClientAckTest.java b/activemq-core/src/test/java/org/activemq/JmsClientAckTest.java new file mode 100755 index 0000000000..79ccb39174 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsClientAckTest.java @@ -0,0 +1,148 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; + +/** + * @version $Revision: 1.4 $ + */ +public class JmsClientAckTest extends TestSupport { + + private Connection connection; + + protected void setUp() throws Exception { + super.setUp(); + connection = createConnection(); + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception { + if (connection != null) { + connection.close(); + connection = null; + } + super.tearDown(); + } + + /** + * Tests if acknowledged messages are being consumed. + * + * @throws JMSException + */ + public void testAckedMessageAreConsumed() throws JMSException { + connection.start(); + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = session.createQueue("test"); + MessageProducer producer = session.createProducer(queue); + producer.send(session.createTextMessage("Hello")); + + // Consume the message... + MessageConsumer consumer = session.createConsumer(queue); + Message msg = consumer.receive(1000); + assertNotNull(msg); + msg.acknowledge(); + + // Reset the session. + session.close(); + session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Attempt to Consume the message... + consumer = session.createConsumer(queue); + msg = consumer.receive(1000); + assertNull(msg); + + session.close(); + } + + /** + * Tests if acknowledged messages are being consumed. + * + * @throws JMSException + */ + public void testLastMessageAcked() throws JMSException { + connection.start(); + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = session.createQueue("test"); + MessageProducer producer = session.createProducer(queue); + producer.send(session.createTextMessage("Hello")); + producer.send(session.createTextMessage("Hello2")); + producer.send(session.createTextMessage("Hello3")); + + // Consume the message... + MessageConsumer consumer = session.createConsumer(queue); + Message msg = consumer.receive(1000); + assertNotNull(msg); + msg = consumer.receive(1000); + assertNotNull(msg); + msg = consumer.receive(1000); + assertNotNull(msg); + msg.acknowledge(); + + // Reset the session. + session.close(); + session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Attempt to Consume the message... + consumer = session.createConsumer(queue); + msg = consumer.receive(1000); + assertNull(msg); + + session.close(); + } + + /** + * Tests if unacknowledged messages are being re-delivered when the consumer connects again. + * + * @throws JMSException + */ + public void testUnAckedMessageAreNotConsumedOnSessionClose() throws JMSException { + connection.start(); + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = session.createQueue("test"); + MessageProducer producer = session.createProducer(queue); + producer.send(session.createTextMessage("Hello")); + + // Consume the message... + MessageConsumer consumer = session.createConsumer(queue); + Message msg = consumer.receive(1000); + assertNotNull(msg); + // Don't ack the message. + + // Reset the session. This should cause the unacknowledged message to be re-delivered. + session.close(); + session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Attempt to Consume the message... + consumer = session.createConsumer(queue); + msg = consumer.receive(2000); + assertNotNull(msg); + msg.acknowledge(); + + session.close(); + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsConnectionStartStopTest.java b/activemq-core/src/test/java/org/activemq/JmsConnectionStartStopTest.java new file mode 100755 index 0000000000..6d1dcd4a23 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsConnectionStartStopTest.java @@ -0,0 +1,108 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; + +/** + * @version $Revision: 1.3 $ + */ +public class JmsConnectionStartStopTest extends TestSupport { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsConnectionStartStopTest.class); + + private Connection startedConnection; + private Connection stoppedConnection; + + /** + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + + log.info(getClass().getClassLoader().getResource("log4j.properties")); + + ActiveMQConnectionFactory factory = createConnectionFactory(); + startedConnection = factory.createConnection(); + startedConnection.start(); + stoppedConnection = factory.createConnection(); + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception { + stoppedConnection.close(); + startedConnection.close(); + } + + /** + * Tests if the consumer receives the messages that were sent before the connection was started. + * + * @throws JMSException + */ + public void testStoppedConsumerHoldsMessagesTillStarted() throws JMSException { + Session startedSession = startedConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Session stoppedSession = stoppedConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Setup the consumers. + Topic topic = startedSession.createTopic("test"); + MessageConsumer startedConsumer = startedSession.createConsumer(topic); + MessageConsumer stoppedConsumer = stoppedSession.createConsumer(topic); + + // Send the message. + MessageProducer producer = startedSession.createProducer(topic); + TextMessage message = startedSession.createTextMessage("Hello"); + producer.send(message); + + // Test the assertions. + Message m = startedConsumer.receive(1000); + assertNotNull(m); + + m = stoppedConsumer.receive(1000); + assertNull(m); + + stoppedConnection.start(); + m = stoppedConsumer.receive(5000); + assertNotNull(m); + + startedSession.close(); + stoppedSession.close(); + } + + /** + * Tests if the consumer is able to receive messages eveb when the connecction restarts multiple times. + * + * @throws Exception + */ + public void testMultipleConnectionStops() throws Exception { + testStoppedConsumerHoldsMessagesTillStarted(); + stoppedConnection.stop(); + testStoppedConsumerHoldsMessagesTillStarted(); + stoppedConnection.stop(); + testStoppedConsumerHoldsMessagesTillStarted(); + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsCreateConsumerInOnMessageTest.java b/activemq-core/src/test/java/org/activemq/JmsCreateConsumerInOnMessageTest.java new file mode 100755 index 0000000000..ced8b59a5e --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsCreateConsumerInOnMessageTest.java @@ -0,0 +1,104 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Connection; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.Topic; + +import org.activemq.TestSupport; + +/** + * @version $Revision: 1.2 $ + */ +public class JmsCreateConsumerInOnMessageTest extends TestSupport implements MessageListener { + + private Connection connection; + private Session publisherSession; + private Session consumerSession; + private MessageConsumer consumer; + private MessageConsumer testConsumer; + private MessageProducer producer; + private Topic topic; + private Object lock = new Object(); + + /* + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + super.topic = true; + connection = createConnection(); + connection.setClientID("connection:" + getSubject()); + publisherSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + consumerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + topic = (Topic)super.createDestination("Test.Topic"); + consumer = consumerSession.createConsumer(topic); + consumer.setMessageListener(this); + producer = publisherSession.createProducer(topic); + connection.start(); + } + + /* + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + connection.close(); + } + + + /** + * Tests if a consumer can be created asynchronusly + * + * @throws Exception + */ + public void testCreateConsumer() throws Exception{ + Message msg = super.createMessage(); + producer.send(msg); + if (testConsumer == null){ + synchronized(lock){ + lock.wait(3000); + } + } + assertTrue(testConsumer != null); + } + + /** + * Use the asynchronous subscription mechanism + * + * @param message + */ + public void onMessage(Message message) { + try { + testConsumer = consumerSession.createConsumer(topic); + MessageProducer anotherProducer = consumerSession.createProducer(topic); + synchronized(lock){ + lock.notify(); + } + }catch (Exception ex){ + ex.printStackTrace(); + assertTrue(false); + } + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsDurableQueueWildcardSendReceiveTest.java b/activemq-core/src/test/java/org/activemq/JmsDurableQueueWildcardSendReceiveTest.java new file mode 100755 index 0000000000..89f4d0fa1e --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsDurableQueueWildcardSendReceiveTest.java @@ -0,0 +1,55 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.DeliveryMode; + +import org.activemq.test.JmsTopicSendReceiveTest; + + +/** + * @version $Revision: 1.2 $ + */ +public class JmsDurableQueueWildcardSendReceiveTest extends JmsTopicSendReceiveTest { + + /** + * Set up the test with a queue and persistent delivery mode. + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + topic = false; + deliveryMode = DeliveryMode.PERSISTENT; + super.setUp(); + } + + /** + * Returns the consumer subject. + */ + protected String getConsumerSubject(){ + return "FOO.>"; + } + + /** + * Returns the producer subject. + */ + protected String getProducerSubject(){ + return "FOO.BAR.HUMBUG"; + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsDurableTopicSelectorTest.java b/activemq-core/src/test/java/org/activemq/JmsDurableTopicSelectorTest.java new file mode 100755 index 0000000000..8c20a3bc63 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsDurableTopicSelectorTest.java @@ -0,0 +1,29 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +/** + * @version $Revision: 1.2 $ + */ +public class JmsDurableTopicSelectorTest extends JmsTopicSelectorTest { + public void setUp() throws Exception { + durable=true; + super.setUp(); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsDurableTopicSendReceiveTest.java b/activemq-core/src/test/java/org/activemq/JmsDurableTopicSendReceiveTest.java new file mode 100755 index 0000000000..b450f2c60a --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsDurableTopicSendReceiveTest.java @@ -0,0 +1,90 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; + +import org.activemq.test.JmsTopicSendReceiveTest; + +/** + * @version $Revision: 1.5 $ + */ +public class JmsDurableTopicSendReceiveTest extends JmsTopicSendReceiveTest { + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsDurableTopicSendReceiveTest.class); + + protected Connection connection2; + protected Session session2; + protected Session consumeSession2; + protected MessageConsumer consumer2; + protected MessageProducer producer2; + protected Destination consumerDestination2; + protected Destination producerDestination2; + /** + * Set up a durable suscriber test. + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + this.durable = true; + super.setUp(); + } + + /** + * Test if all the messages sent are being received. + * + * @throws Exception + */ + public void testSendWhileClosed() throws Exception { + connection2 = createConnection(); + connection2.setClientID("test"); + connection2.start(); + session2 = connection2.createSession(false, Session.AUTO_ACKNOWLEDGE); + producer2 = session2.createProducer(null); + producer2.setDeliveryMode(deliveryMode); + producerDestination2 = session2.createTopic(getProducerSubject()+"2"); + Thread.sleep(1000); + + consumeSession2 = connection2.createSession(false, Session.AUTO_ACKNOWLEDGE); + consumerDestination2 = session2.createTopic(getConsumerSubject()+"2"); + consumer2 = consumeSession2.createDurableSubscriber((Topic)consumerDestination2, getName()); + Thread.sleep(1000); + consumer2.close(); + TextMessage message = session2.createTextMessage("test"); + message.setStringProperty("test","test"); + message.setJMSType("test"); + producer2.send(producerDestination2, message); + log.info("Creating durable consumer"); + consumer2 = consumeSession2.createDurableSubscriber((Topic)consumerDestination2, getName()); + Message msg = consumer2.receive(1000); + assertNotNull(msg); + assertEquals(((TextMessage) msg).getText(), "test"); + assertEquals(msg.getJMSType(), "test"); + assertEquals(msg.getStringProperty("test"), "test"); + connection2.stop(); + connection2.close(); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsDurableTopicTransactionTest.java b/activemq-core/src/test/java/org/activemq/JmsDurableTopicTransactionTest.java new file mode 100755 index 0000000000..db52bfd55e --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsDurableTopicTransactionTest.java @@ -0,0 +1,41 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.DeliveryMode; +import org.activemq.test.JmsResourceProvider; + +/** + * @version $Revision: 1.2 $ + */ +public class JmsDurableTopicTransactionTest extends JmsTopicTransactionTest { + + /** + * @see JmsTransactionTestSupport#getJmsResourceProvider() + */ + protected JmsResourceProvider getJmsResourceProvider() { + JmsResourceProvider provider = new JmsResourceProvider(); + provider.setTopic(true); + provider.setDeliveryMode(DeliveryMode.PERSISTENT); + provider.setClientID(getClass().getName()); + provider.setDurableName(getName()); + return provider; + } + +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsDurableTopicWildcardSendReceiveTest.java b/activemq-core/src/test/java/org/activemq/JmsDurableTopicWildcardSendReceiveTest.java new file mode 100755 index 0000000000..24abe9d83d --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsDurableTopicWildcardSendReceiveTest.java @@ -0,0 +1,56 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.DeliveryMode; + +import org.activemq.test.JmsTopicSendReceiveTest; + + +/** + * @version $Revision: 1.2 $ + */ +public class JmsDurableTopicWildcardSendReceiveTest extends JmsTopicSendReceiveTest { + + /** + * Sets up a test with a topic destination, durable suscriber and persistent delivery mode. + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + topic = true; + durable = true; + deliveryMode = DeliveryMode.PERSISTENT; + super.setUp(); + } + + /** + * Returns the consumer subject. + */ + protected String getConsumerSubject(){ + return "FOO.>"; + } + + /** + * Returns the producer subject. + */ + protected String getProducerSubject(){ + return "FOO.BAR.HUMBUG"; + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsQueueBrowserTest.java b/activemq-core/src/test/java/org/activemq/JmsQueueBrowserTest.java new file mode 100755 index 0000000000..ca9ccde8b5 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsQueueBrowserTest.java @@ -0,0 +1,101 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.util.Enumeration; + +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.QueueBrowser; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.activemq.command.ActiveMQQueue; + +/** + * @version $Revision: 1.4 $ + */ +public class JmsQueueBrowserTest extends JmsTestSupport { + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsQueueBrowserTest.class); + + + /** + * Tests the queue browser. Browses the messages then the consumer tries to receive them. The messages should still + * be in the queue even when it was browsed. + * + * @throws Exception + */ + public void testReceiveBrowseReceive() throws Exception { + + Session session = connection.createSession(false, 0); + ActiveMQQueue destination = new ActiveMQQueue("TEST"); + MessageProducer producer = session.createProducer(destination); + MessageConsumer consumer = session.createConsumer(destination); + connection.start(); + + Message[] outbound = new Message[]{session.createTextMessage("First Message"), + session.createTextMessage("Second Message"), + session.createTextMessage("Third Message")}; + + // lets consume any outstanding messages from previous test runs + while (consumer.receive(1000) != null) { + } + + producer.send(outbound[0]); + producer.send(outbound[1]); + producer.send(outbound[2]); + + // Get the first. + assertEquals(outbound[0], consumer.receive(1000)); + consumer.close(); + //Thread.sleep(200); + + QueueBrowser browser = session.createBrowser((Queue) destination); + Enumeration enumeration = browser.getEnumeration(); + + // browse the second + assertTrue("should have received the second message", enumeration.hasMoreElements()); + assertEquals(outbound[1], (Message) enumeration.nextElement()); + + // browse the third. + assertTrue("Should have received the third message", enumeration.hasMoreElements()); + assertEquals(outbound[2], (Message) enumeration.nextElement()); + + // There should be no more. + boolean tooMany = false; + while (enumeration.hasMoreElements()) { + log.info("Got extra message: " + ((TextMessage) enumeration.nextElement()).getText()); + tooMany = true; + } + assertFalse(tooMany); + browser.close(); + + // Re-open the consumer. + consumer = session.createConsumer(destination); + // Receive the second. + assertEquals(outbound[1], consumer.receive(1000)); + // Receive the third. + assertEquals(outbound[2], consumer.receive(1000)); + consumer.close(); + + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsQueueCompositeSendReceiveTest.java b/activemq-core/src/test/java/org/activemq/JmsQueueCompositeSendReceiveTest.java new file mode 100755 index 0000000000..8bbdac5e8a --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsQueueCompositeSendReceiveTest.java @@ -0,0 +1,89 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import org.activemq.test.JmsTopicSendReceiveTest; + +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.MessageConsumer; +import javax.jms.Topic; + + +/** + * @version $Revision: 1.3 $ + */ +public class JmsQueueCompositeSendReceiveTest extends JmsTopicSendReceiveTest { + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsQueueCompositeSendReceiveTest.class); + + /** + * Sets a test to have a queue destination and non-persistent delivery mode. + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + topic = false; + deliveryMode = DeliveryMode.NON_PERSISTENT; + super.setUp(); + } + + /** + * Returns the consumer subject. + * + * @return String - consumer subject + * @see org.activemq.test.TestSupport#getConsumerSubject() + */ + protected String getConsumerSubject() { + return "FOO.BAR.HUMBUG"; + } + + /** + * Returns the producer subject. + * + * @return String - producer subject + * @see org.activemq.test.TestSupport#getProducerSubject() + */ + protected String getProducerSubject() { + return "FOO.BAR.HUMBUG,FOO.BAR.HUMBUG2"; + } + + /** + * Test if all the messages sent are being received. + * + * @throws Exception + */ + public void testSendReceive() throws Exception { + super.testSendReceive(); + messages.clear(); + Destination consumerDestination = consumeSession.createQueue("FOO.BAR.HUMBUG2"); + log.info("Created consumer destination: " + consumerDestination + " of type: " + consumerDestination.getClass()); + MessageConsumer consumer = null; + if (durable) { + log.info("Creating durable consumer"); + consumer = consumeSession.createDurableSubscriber((Topic) consumerDestination, getName()); + } else { + consumer = consumeSession.createConsumer(consumerDestination); + } + consumer.setMessageListener(this); + + assertMessagesAreReceived(); + log.info("" + data.length + " messages(s) received, closing down connections"); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsQueueRequestReplyTest.java b/activemq-core/src/test/java/org/activemq/JmsQueueRequestReplyTest.java new file mode 100755 index 0000000000..e204d6752d --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsQueueRequestReplyTest.java @@ -0,0 +1,35 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +/** + * @version $Revision: 1.2 $ + */ +public class JmsQueueRequestReplyTest extends JmsTopicRequestReplyTest { + + /** + * Set up the test with a queue. + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + topic = false; + super.setUp(); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsQueueSelectorTest.java b/activemq-core/src/test/java/org/activemq/JmsQueueSelectorTest.java new file mode 100755 index 0000000000..56cea10b21 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsQueueSelectorTest.java @@ -0,0 +1,30 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +/** + * @version $Revision: 1.2 $ + */ +public class JmsQueueSelectorTest extends JmsTopicSelectorTest { + public void setUp() throws Exception { + topic=false; + super.setUp(); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsQueueSendReceiveTest.java b/activemq-core/src/test/java/org/activemq/JmsQueueSendReceiveTest.java new file mode 100755 index 0000000000..e938abc3ad --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsQueueSendReceiveTest.java @@ -0,0 +1,38 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import org.activemq.test.JmsTopicSendReceiveTest; + + +/** + * @version $Revision: 1.2 $ + */ +public class JmsQueueSendReceiveTest extends JmsTopicSendReceiveTest { + + /** + * Set up the test with a queue. + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + topic = false; + super.setUp(); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsQueueSendReceiveTwoConnectionsTest.java b/activemq-core/src/test/java/org/activemq/JmsQueueSendReceiveTwoConnectionsTest.java new file mode 100755 index 0000000000..1e032b4738 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsQueueSendReceiveTwoConnectionsTest.java @@ -0,0 +1,37 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import org.activemq.test.JmsTopicSendReceiveWithTwoConnectionsTest; + +/** + * @version $Revision: 1.2 $ + */ +public class JmsQueueSendReceiveTwoConnectionsTest extends JmsTopicSendReceiveWithTwoConnectionsTest { + + /** + * Set up the test with a queue and using two connections. + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + topic = false; + super.setUp(); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsQueueSendReceiveUsingTwoSessionsTest.java b/activemq-core/src/test/java/org/activemq/JmsQueueSendReceiveUsingTwoSessionsTest.java new file mode 100755 index 0000000000..9ea6dd44ad --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsQueueSendReceiveUsingTwoSessionsTest.java @@ -0,0 +1,35 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +/** + * @version $Revision: 1.2 $ + */ +public class JmsQueueSendReceiveUsingTwoSessionsTest extends JmsQueueSendReceiveTest { + + /** + * Set up the test using two sessions. + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + useSeparateSession = true; + super.setUp(); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsQueueTopicCompositeSendReceiveTest.java b/activemq-core/src/test/java/org/activemq/JmsQueueTopicCompositeSendReceiveTest.java new file mode 100755 index 0000000000..f5cde2286f --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsQueueTopicCompositeSendReceiveTest.java @@ -0,0 +1,90 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import org.activemq.test.JmsTopicSendReceiveTest; + +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.MessageConsumer; +import javax.jms.Topic; + + +/** + * @version $Revision: 1.3 $ + */ +public class JmsQueueTopicCompositeSendReceiveTest extends JmsTopicSendReceiveTest { + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsQueueTopicCompositeSendReceiveTest.class); + Destination consumerDestination2; + MessageConsumer consumer2; + + /** + * Sets a test to have a queue destination and non-persistent delivery mode. + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + deliveryMode = DeliveryMode.NON_PERSISTENT; + topic = false; + super.setUp(); + consumerDestination2 = consumeSession.createTopic("FOO.BAR.HUMBUG2"); + log.info("Created consumer destination: " + consumerDestination2 + " of type: " + consumerDestination2.getClass()); + if (durable) { + log.info("Creating durable consumer"); + consumer2 = consumeSession.createDurableSubscriber((Topic) consumerDestination2, getName()); + } else { + consumer2 = consumeSession.createConsumer(consumerDestination2); + } + + } + + /** + * Returns the consumer subject. + * + * @return String - consumer subject + * @see org.activemq.test.TestSupport#getConsumerSubject() + */ + protected String getConsumerSubject() { + return "FOO.BAR.HUMBUG"; + } + + /** + * Returns the producer subject. + * + * @return String - producer subject + * @see org.activemq.test.TestSupport#getProducerSubject() + */ + protected String getProducerSubject() { + return "queue://FOO.BAR.HUMBUG,topic://FOO.BAR.HUMBUG2"; + } + + /** + * Test if all the messages sent are being received. + * + * @throws Exception + */ + public void testSendReceive() throws Exception { + super.testSendReceive(); + messages.clear(); + consumer2.setMessageListener(this); + assertMessagesAreReceived(); + log.info("" + data.length + " messages(s) received, closing down connections"); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsQueueTransactionTest.java b/activemq-core/src/test/java/org/activemq/JmsQueueTransactionTest.java new file mode 100755 index 0000000000..4408dae6d4 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsQueueTransactionTest.java @@ -0,0 +1,196 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.util.ArrayList; +import java.util.Enumeration; + +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.QueueBrowser; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.activemq.test.JmsResourceProvider; + + +/** + * @version $Revision: 1.2 $ + */ +public class JmsQueueTransactionTest extends JmsTransactionTestSupport { + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsQueueTransactionTest.class); + + /** + * + * @see org.activemq.JmsTransactionTestSupport#getJmsResourceProvider() + */ + protected JmsResourceProvider getJmsResourceProvider() { + JmsResourceProvider p = new JmsResourceProvider(); + p.setTopic(false); + return p; + } + + /** + * Tests if the the connection gets reset, the messages will still be received. + * + * @throws Exception + */ + public void testReceiveTwoThenCloseConnection() throws Exception { + Message[] outbound = new Message[]{ + session.createTextMessage("First Message"), + session.createTextMessage("Second Message") + }; + + // lets consume any outstanding messages from previous test runs + while (consumer.receive(1000) != null) { + } + session.commit(); + + producer.send(outbound[0]); + producer.send(outbound[1]); + session.commit(); + + log.info("Sent 0: " + outbound[0]); + log.info("Sent 1: " + outbound[1]); + + ArrayList messages = new ArrayList(); + Message message = consumer.receive(1000); + assertEquals(outbound[0], message); + + message = consumer.receive(1000); + assertNotNull(message); + assertEquals(outbound[1], message); + + // Close and reopen connection. + reconnect(); + + // Consume again.. the previous message should + // get redelivered. + message = consumer.receive(5000); + assertNotNull("Should have re-received the first message again!", message); + messages.add(message); + assertEquals(outbound[0], message); + + message = consumer.receive(5000); + assertNotNull("Should have re-received the second message again!", message); + messages.add(message); + assertEquals(outbound[1], message); + session.commit(); + + Message inbound[] = new Message[messages.size()]; + messages.toArray(inbound); + + assertTextMessagesEqual("Rollback did not work", outbound, inbound); + } + + /** + * Tests sending and receiving messages with two sessions(one for producing and another for consuming). + * + * @throws Exception + */ + public void testSendReceiveInSeperateSessionTest() throws Exception { + session.close(); + int batchCount = 10; + + for (int i=0; i < batchCount; i++) { + //Session that sends messages + { + Session session = resourceProvider.createSession(connection); + MessageProducer producer = resourceProvider.createProducer(session, destination); + //consumer = resourceProvider.createConsumer(session, destination); + producer.send(session.createTextMessage("Test Message: "+i)); + session.commit(); + session.close(); + } + + //Session that consumes messages + { + Session session = resourceProvider.createSession(connection); + MessageConsumer consumer = resourceProvider.createConsumer(session, destination); + + TextMessage message = (TextMessage) consumer.receive(1000*5); + assertNotNull("Received only "+i+" messages in batch ", message); + assertEquals("Test Message: "+i, message.getText()); + + session.commit(); + session.close(); + } + } + } + + /** + * Tests the queue browser. Browses the messages then the consumer tries to receive them. + * The messages should still be in the queue even when it was browsed. + * + * @throws Exception + */ + public void testReceiveBrowseReceive() throws Exception { + Message[] outbound = new Message[] { session.createTextMessage("First Message"), + session.createTextMessage("Second Message"), + session.createTextMessage("Third Message") }; + + // lets consume any outstanding messages from previous test runs + while (consumer.receive(1000) != null) { + } + session.commit(); + + producer.send(outbound[0]); + producer.send(outbound[1]); + producer.send(outbound[2]); + session.commit(); + + // Get the first. + assertEquals(outbound[0], consumer.receive(1000)); + consumer.close(); + + QueueBrowser browser = session.createBrowser((Queue) destination); + Enumeration enumeration = browser.getEnumeration(); + + // browse the second + assertTrue("should have received the second message", enumeration.hasMoreElements()); + assertEquals(outbound[1], (Message) enumeration.nextElement()); + + // browse the third. + assertTrue("Should have received the third message", enumeration.hasMoreElements()); + assertEquals(outbound[2], (Message) enumeration.nextElement()); + + // There should be no more. + boolean tooMany = false; + while (enumeration.hasMoreElements()) { + log.info("Got extra message: " + ((TextMessage) enumeration.nextElement()).getText()); + tooMany = true; + } + assertFalse(tooMany); + browser.close(); + + // Re-open the consumer. + consumer = resourceProvider.createConsumer(session, destination); + // Receive the second. + assertEquals(outbound[1], consumer.receive(1000)); + // Receive the third. + assertEquals(outbound[2], consumer.receive(1000)); + consumer.close(); + + session.commit(); + } + +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsQueueWildcardSendReceiveTest.java b/activemq-core/src/test/java/org/activemq/JmsQueueWildcardSendReceiveTest.java new file mode 100755 index 0000000000..58808c9e21 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsQueueWildcardSendReceiveTest.java @@ -0,0 +1,177 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.DeliveryMode; +import javax.jms.Session; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.TextMessage; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageProducer; + +import org.activemq.test.JmsTopicSendReceiveTest; +import org.activemq.command.ActiveMQDestination; + + +/** + * @version $Revision: 1.4 $ + */ +public class JmsQueueWildcardSendReceiveTest extends JmsTopicSendReceiveTest { + + private String destination1String = "TEST.ONE.ONE" ; + private String destination2String = "TEST.ONE.ONE.ONE" ; + private String destination3String = "TEST.ONE.TWO" ; + private String destination4String = "TEST.TWO.ONE" ; + + /** + * Sets a test to have a queue destination and non-persistent delivery mode. + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + topic = false; + deliveryMode = DeliveryMode.NON_PERSISTENT; + super.setUp(); + } + + /** + * Returns the consumer subject. + * + * @return String - consumer subject + * @see org.activemq.test.TestSupport#getConsumerSubject() + */ + protected String getConsumerSubject(){ + return "FOO.>"; + } + + /** + * Returns the producer subject. + * + * @return String - producer subject + * @see org.activemq.test.TestSupport#getProducerSubject() + */ + protected String getProducerSubject(){ + return "FOO.BAR.HUMBUG"; + } + + public void testReceiveWildcardQueueEndAsterisk() throws Throwable { + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + ActiveMQDestination destination1 = (ActiveMQDestination) session.createQueue(destination1String); + ActiveMQDestination destination3 = (ActiveMQDestination) session.createQueue(destination3String); + + Message m = null; + MessageConsumer consumer = null; + String text = null; + + sendMessage(session,destination1,destination1String); + sendMessage(session,destination3,destination3String); + ActiveMQDestination destination6 = (ActiveMQDestination) session.createQueue("TEST.ONE.*"); + consumer = session.createConsumer(destination6); + m = consumer.receive(1000); + assertNotNull(m); + text = ((TextMessage)m).getText(); + if(!(text.equals(destination1String) || text.equals(destination3String))) { + fail("unexpected message:" + text); + } + m = consumer.receive(1000); + assertNotNull(m); + text = ((TextMessage)m).getText(); + if(!(text.equals(destination1String) || text.equals(destination3String))) { + fail("unexpected message:" + text); + } + assertNull(consumer.receiveNoWait()); + } + + public void testReceiveWildcardQueueEndGreaterThan() throws Throwable { + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + ActiveMQDestination destination1 = (ActiveMQDestination) session.createQueue(destination1String); + ActiveMQDestination destination2 = (ActiveMQDestination) session.createQueue(destination2String); + ActiveMQDestination destination3 = (ActiveMQDestination) session.createQueue(destination3String); + + Message m = null; + MessageConsumer consumer = null; + String text = null; + + sendMessage(session,destination1,destination1String); + sendMessage(session,destination2,destination2String); + sendMessage(session,destination3,destination3String); + ActiveMQDestination destination7 = (ActiveMQDestination) session.createQueue("TEST.ONE.>"); + consumer = session.createConsumer(destination7); + m = consumer.receive(1000); + assertNotNull(m); + text = ((TextMessage)m).getText(); + if(!(text.equals(destination1String) || text.equals(destination2String) || text.equals(destination3String))) { + fail("unexpected message:" + text); + } + m = consumer.receive(1000); + assertNotNull(m); + if(!(text.equals(destination1String) || text.equals(destination2String) || text.equals(destination3String))) { + fail("unexpected message:" + text); + } + m = consumer.receive(1000); + assertNotNull(m); + if(!(text.equals(destination1String) || text.equals(destination2String) || text.equals(destination3String))) { + fail("unexpected message:" + text); + } + assertNull(consumer.receiveNoWait()); + } + + public void testReceiveWildcardQueueMidAsterisk() throws Throwable { + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + ActiveMQDestination destination1 = (ActiveMQDestination) session.createQueue(destination1String); + ActiveMQDestination destination4 = (ActiveMQDestination) session.createQueue(destination4String); + + Message m = null; + MessageConsumer consumer = null; + String text = null; + + sendMessage(session,destination1,destination1String); + sendMessage(session,destination4,destination4String); + ActiveMQDestination destination8 = (ActiveMQDestination) session.createQueue("TEST.*.ONE"); + consumer = session.createConsumer(destination8); + m = consumer.receive(1000); + assertNotNull(m); + text = ((TextMessage)m).getText(); + if(!(text.equals(destination1String) || text.equals(destination4String))) { + fail("unexpected message:" + text); + } + m = consumer.receive(1000); + assertNotNull(m); + text = ((TextMessage)m).getText(); + if(!(text.equals(destination1String) || text.equals(destination4String))) { + fail("unexpected message:" + text); + } + assertNull(consumer.receiveNoWait()); + + } + + private void sendMessage(Session session, Destination destination, String text) throws JMSException { + MessageProducer producer = session.createProducer(destination); + producer.send(session.createTextMessage(text)); + producer.close(); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsRedeliveredTest.java b/activemq-core/src/test/java/org/activemq/JmsRedeliveredTest.java new file mode 100755 index 0000000000..0a07792ec1 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsRedeliveredTest.java @@ -0,0 +1,423 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * @version $Revision: 1.5 $ + */ +public class JmsRedeliveredTest extends TestCase { + + private Connection connection; + + /* (non-Javadoc) + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + connection = createConnection(); + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception { + if (connection != null) { + connection.close(); + connection = null; + } + } + + /** + * Creates a connection. + * + * @return connection + * @throws Exception + */ + protected Connection createConnection() throws Exception{ + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false"); + return factory.createConnection(); + } + + /** + * Tests if a message unacknowledged message gets to be resent when the session is closed and + * then a new consumer session is created. + * + */ + public void testQueueSessionCloseMarksMessageRedelivered() throws JMSException { + connection.start(); + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = session.createQueue("queue-"+getName()); + MessageProducer producer = createProducer(session, queue); + producer.send(createTextMessage(session)); + + // Consume the message... + MessageConsumer consumer = session.createConsumer(queue); + Message msg = consumer.receive(1000); + assertNotNull(msg); + assertFalse("Message should not be redelivered.", msg.getJMSRedelivered()); + // Don't ack the message. + + // Reset the session. This should cause the Unacked message to be redelivered. + session.close(); + session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Attempt to Consume the message... + consumer = session.createConsumer(queue); + msg = consumer.receive(2000); + assertNotNull(msg); + assertTrue("Message should be redelivered.", msg.getJMSRedelivered()); + msg.acknowledge(); + + session.close(); + } + + /** + * Tests session recovery and that the redelivered message is marked as such. + * Session uses client acknowledgement, the destination is a queue. + * + * @throws JMSException + */ + public void testQueueRecoverMarksMessageRedelivered() throws JMSException { + connection.start(); + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Queue queue = session.createQueue("queue-"+getName()); + MessageProducer producer = createProducer(session, queue); + producer.send(createTextMessage(session)); + + // Consume the message... + MessageConsumer consumer = session.createConsumer(queue); + Message msg = consumer.receive(1000); + assertNotNull(msg); + assertFalse("Message should not be redelivered.", msg.getJMSRedelivered()); + // Don't ack the message. + + // Reset the session. This should cause the Unacked message to be redelivered. + session.recover(); + + // Attempt to Consume the message... + msg = consumer.receive(2000); + assertNotNull(msg); + assertTrue("Message should be redelivered.", msg.getJMSRedelivered()); + msg.acknowledge(); + + session.close(); + } + + /** + * Tests rollback message to be marked as redelivered. + * Session uses client acknowledgement and the destination is a queue. + * + * @throws JMSException + */ + public void testQueueRollbackMarksMessageRedelivered() throws JMSException { + connection.start(); + + Session session = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE); + Queue queue = session.createQueue("queue-"+getName()); + MessageProducer producer = createProducer(session, queue); + producer.send(createTextMessage(session)); + session.commit(); + + // Get the message... Should not be redelivered. + MessageConsumer consumer = session.createConsumer(queue); + Message msg = consumer.receive(1000); + assertNotNull(msg); + assertFalse("Message should not be redelivered.", msg.getJMSRedelivered()); + + // Rollback.. should cause redelivery. + session.rollback(); + + // Attempt to Consume the message... + msg = consumer.receive(2000); + assertNotNull(msg); + assertTrue("Message should be redelivered.", msg.getJMSRedelivered()); + + session.commit(); + session.close(); + } + + /** + * Tests if the message gets to be re-delivered when the session closes and + * that the re-delivered message is marked as such. + * Session uses client acknowledgment, the destination is a topic and + * the consumer is a durable subscriber. + * + * @throws JMSException + */ + public void testDurableTopicSessionCloseMarksMessageRedelivered() throws JMSException { + connection.setClientID(getName()); + connection.start(); + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Topic topic = session.createTopic("topic-"+getName()); + MessageConsumer consumer = session.createDurableSubscriber(topic, "sub1"); + + MessageProducer producer = createProducer(session, topic); + producer.send(createTextMessage(session)); + + // Consume the message... + Message msg = consumer.receive(1000); + assertNotNull(msg); + assertFalse("Message should not be re-delivered.", msg.getJMSRedelivered()); + // Don't ack the message. + + // Reset the session. This should cause the Unacked message to be re-delivered. + session.close(); + session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Attempt to Consume the message... + consumer = session.createDurableSubscriber(topic, "sub1"); + msg = consumer.receive(2000); + assertNotNull(msg); + assertTrue("Message should be redelivered.", msg.getJMSRedelivered()); + msg.acknowledge(); + + session.close(); + } + + /** + * Tests session recovery and that the redelivered message is marked as such. + * Session uses client acknowledgement, the destination is a topic and + * the consumer is a durable suscriber. + * + * @throws JMSException + */ + public void testDurableTopicRecoverMarksMessageRedelivered() throws JMSException { + connection.setClientID(getName()); + connection.start(); + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Topic topic = session.createTopic("topic-"+getName()); + MessageConsumer consumer = session.createDurableSubscriber(topic, "sub1"); + + MessageProducer producer = createProducer(session, topic); + producer.send(createTextMessage(session)); + + // Consume the message... + Message msg = consumer.receive(1000); + assertNotNull(msg); + assertFalse("Message should not be redelivered.", msg.getJMSRedelivered()); + // Don't ack the message. + + // Reset the session. This should cause the Unacked message to be redelivered. + session.recover(); + + // Attempt to Consume the message... + msg = consumer.receive(2000); + assertNotNull(msg); + assertTrue("Message should be redelivered.", msg.getJMSRedelivered()); + msg.acknowledge(); + + session.close(); + } + + /** + * Tests rollback message to be marked as redelivered. + * Session uses client acknowledgement and the destination is a topic. + * + * @throws JMSException + */ + public void testDurableTopicRollbackMarksMessageRedelivered() throws JMSException { + connection.setClientID(getName()); + connection.start(); + + Session session = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE); + Topic topic = session.createTopic("topic-"+getName()); + MessageConsumer consumer = session.createDurableSubscriber(topic, "sub1"); + + MessageProducer producer = createProducer(session, topic); + producer.send(createTextMessage(session)); + session.commit(); + + // Get the message... Should not be redelivered. + Message msg = consumer.receive(1000); + assertNotNull(msg); + assertFalse("Message should not be redelivered.", msg.getJMSRedelivered()); + + // Rollback.. should cause redelivery. + session.rollback(); + + // Attempt to Consume the message... + msg = consumer.receive(2000); + assertNotNull(msg); + assertTrue("Message should be redelivered.", msg.getJMSRedelivered()); + + + session.commit(); + session.close(); + } + + /** + * + * + * @throws JMSException + */ + public void testTopicRecoverMarksMessageRedelivered() throws JMSException { + + connection.setClientID(getName()); + connection.start(); + + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Topic topic = session.createTopic("topic-"+getName()); + MessageConsumer consumer = session.createConsumer(topic); + + MessageProducer producer = createProducer(session, topic); + producer.send(createTextMessage(session)); + + // Consume the message... + Message msg = consumer.receive(1000); + assertNotNull(msg); + assertFalse("Message should not be redelivered.", msg.getJMSRedelivered()); + // Don't ack the message. + + // Reset the session. This should cause the Unacked message to be redelivered. + session.recover(); + + // Attempt to Consume the message... + msg = consumer.receive(2000); + assertNotNull(msg); + assertTrue("Message should be redelivered.", msg.getJMSRedelivered()); + msg.acknowledge(); + + session.close(); + } + + /** + * Tests rollback message to be marked as redelivered. + * Session uses client acknowledgement and the destination is a topic. + * + * @throws JMSException + */ + public void testTopicRollbackMarksMessageRedelivered() throws JMSException { + connection.setClientID(getName()); + connection.start(); + + Session session = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE); + Topic topic = session.createTopic("topic-"+getName()); + MessageConsumer consumer = session.createConsumer(topic); + + MessageProducer producer = createProducer(session, topic); + producer.send(createTextMessage(session)); + session.commit(); + + // Get the message... Should not be redelivered. + Message msg = consumer.receive(1000); + assertNotNull(msg); + assertFalse("Message should not be redelivered.", msg.getJMSRedelivered()); + + // Rollback.. should cause redelivery. + session.rollback(); + + // Attempt to Consume the message... + msg = consumer.receive(2000); + assertNotNull(msg); + assertTrue("Message should be redelivered.", msg.getJMSRedelivered()); + + session.commit(); + session.close(); + } + + /** + * Creates a text message. + * + * @param session + * @return TextMessage. + * @throws JMSException + */ + private TextMessage createTextMessage(Session session) throws JMSException { + return session.createTextMessage("Hello"); + } + + /** + * Creates a producer. + * + * @param session + * @param queue - destination. + * @return MessageProducer + * @throws JMSException + */ + private MessageProducer createProducer(Session session, Destination queue) throws JMSException { + MessageProducer producer = session.createProducer(queue); + producer.setDeliveryMode(getDeliveryMode()); + return producer; + } + + /** + * Returns delivery mode. + * + * @return int - persistent delivery mode. + */ + protected int getDeliveryMode() { + return DeliveryMode.PERSISTENT; + } + + /** + * Run the JmsRedeliverTest with the delivery mode set as persistent. + */ + static final public class PersistentCase extends JmsRedeliveredTest { + + /** + * Returns delivery mode. + * + * @return int - persistent delivery mode. + */ + protected int getDeliveryMode() { + return DeliveryMode.PERSISTENT; + } + } + + /** + * Run the JmsRedeliverTest with the delivery mode set as non-persistent. + */ + static final public class TransientCase extends JmsRedeliveredTest { + + /** + * Returns delivery mode. + * + * @return int - non-persistent delivery mode. + */ + protected int getDeliveryMode() { + return DeliveryMode.NON_PERSISTENT; + } + } + + public static Test suite() { + TestSuite suite= new TestSuite(); + suite.addTestSuite(PersistentCase.class); + suite.addTestSuite(TransientCase.class); + return suite; + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsSendReceiveTestSupport.java b/activemq-core/src/test/java/org/activemq/JmsSendReceiveTestSupport.java new file mode 100755 index 0000000000..a7ef22a178 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsSendReceiveTestSupport.java @@ -0,0 +1,206 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +/** + * @version $Revision: 1.7 $ + */ +public class JmsSendReceiveTestSupport extends TestSupport implements MessageListener { + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsSendReceiveTestSupport.class); + + protected int messageCount = 100; + protected String[] data; + protected Session session; + protected MessageConsumer consumer; + protected MessageProducer producer; + protected Destination consumerDestination; + protected Destination producerDestination; + protected List messages = createConcurrentList(); + protected boolean topic = true; + protected boolean durable = false; + protected int deliveryMode = DeliveryMode.PERSISTENT; + protected final Object lock = new Object(); + protected boolean verbose = false; + + /* + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + String temp = System.getProperty("messageCount"); + + if (temp != null) { + int i = Integer.parseInt(temp); + if (i > 0) { + messageCount = i; + } + } + + log.info("Message count for test case is: " + messageCount); + data = new String[messageCount]; + + for (int i = 0; i < messageCount; i++) { + data[i] = "Text for message: " + i + " at " + new Date(); + } + } + + /** + * Sends and consumes the messages. + * + * @throws Exception + */ + public void testSendReceive() throws Exception { + messages.clear(); + + for (int i = 0; i < data.length; i++) { + Message message = session.createTextMessage(data[i]); + message.setStringProperty("stringProperty",data[i]); + message.setIntProperty("intProperty",i); + + if (verbose) { + log.info("About to send a message: " + message + " with text: " + data[i]); + } + + producer.send(producerDestination, message); + } + + assertMessagesAreReceived(); + log.info("" + data.length + " messages(s) received, closing down connections"); + } + + /** + * Asserts messages are received. + * + * @throws JMSException + */ + protected void assertMessagesAreReceived() throws JMSException { + waitForMessagesToBeDelivered(); + assertMessagesReceivedAreValid(messages); + } + + /** + * Tests if the messages received are valid. + * + * @param receivedMessages - list of received messages. + * @throws JMSException + */ + protected void assertMessagesReceivedAreValid(List receivedMessages) throws JMSException { + List copyOfMessages = Arrays.asList(receivedMessages.toArray()); + int counter = 0; + + if (data.length != copyOfMessages.size()) { + for (Iterator iter = copyOfMessages.iterator(); iter.hasNext();) { + TextMessage message = (TextMessage) iter.next(); + log.info("<== " + counter++ + " = " + message); + } + } + + assertEquals("Not enough messages received", data.length, receivedMessages.size()); + + for (int i = 0; i < data.length; i++) { + TextMessage received = (TextMessage) receivedMessages.get(i); + String text = received.getText(); + String stringProperty = received.getStringProperty("stringProperty"); + int intProperty = received.getIntProperty("intProperty"); + + if (verbose) { + log.info("Received Text: " + text); + } + + assertEquals("Message: " + i, data[i], text); + assertEquals(data[i],stringProperty); + assertEquals(i,intProperty) ; + } + } + + /** + * Waits for messages to be delivered. + */ + protected void waitForMessagesToBeDelivered() { + long maxWaitTime = 30000; + long waitTime = maxWaitTime; + long start = (maxWaitTime <= 0) ? 0 : System.currentTimeMillis(); + + synchronized (lock) { + while (messages.size() < data.length && waitTime >= 0) { + try { + lock.wait(200); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + + waitTime = maxWaitTime - (System.currentTimeMillis() - start); + } + } + } + + /* (non-Javadoc) + * @see javax.jms.MessageListener#onMessage(javax.jms.Message) + */ + public synchronized void onMessage(Message message) { + consumeMessage(message, messages); + } + + /** + * Consumes messages. + * + * @param message - message to be consumed. + * @param messageList -list of consumed messages. + */ + protected void consumeMessage(Message message, List messageList) { + if (verbose) { + log.info("Received message: " + message); + } + + messageList.add(message); + + if (messageList.size() >= data.length) { + synchronized (lock) { + lock.notifyAll(); + } + } + } + + /** + * Returns the ArrayList as a synchronized list. + * + * @return List + */ + protected List createConcurrentList() { + return Collections.synchronizedList(new ArrayList()); + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsSessionRecoverTest.java b/activemq-core/src/test/java/org/activemq/JmsSessionRecoverTest.java new file mode 100755 index 0000000000..e94b64b504 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsSessionRecoverTest.java @@ -0,0 +1,292 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; + +import junit.framework.TestCase; +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +/** + * Testcases to see if Session.recover() work. + * @version $Revision: 1.3 $ + */ +public class JmsSessionRecoverTest extends TestCase { + + private Connection connection; + private ActiveMQConnectionFactory factory; + private Destination dest; + + /** + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + factory = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false"); + connection = factory.createConnection(); + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception { + if (connection != null) { + connection.close(); + connection = null; + } + } + + /** + * + * @throws JMSException + * @throws InterruptedException + */ + public void testQueueSynchRecover() throws JMSException, InterruptedException { + dest = new ActiveMQQueue("Queue-"+System.currentTimeMillis()); + doTestSynchRecover(); + } + + /** + * + * @throws JMSException + * @throws InterruptedException + */ + public void testQueueAsynchRecover() throws JMSException, InterruptedException { + dest = new ActiveMQQueue("Queue-"+System.currentTimeMillis()); + doTestAsynchRecover(); + } + + /** + * + * @throws JMSException + * @throws InterruptedException + */ + public void testTopicSynchRecover() throws JMSException, InterruptedException { + dest = new ActiveMQTopic("Topic-"+System.currentTimeMillis()); + doTestSynchRecover(); + } + + /** + * + * @throws JMSException + * @throws InterruptedException + */ + public void testTopicAsynchRecover() throws JMSException, InterruptedException { + dest = new ActiveMQTopic("Topic-"+System.currentTimeMillis()); + doTestAsynchRecover(); + } + + /** + * + * @throws JMSException + * @throws InterruptedException + */ + public void testQueueAsynchRecoverWithAutoAck() throws JMSException, InterruptedException { + dest = new ActiveMQQueue("Queue-"+System.currentTimeMillis()); + doTestAsynchRecoverWithAutoAck(); + } + + /** + * + * @throws JMSException + * @throws InterruptedException + */ + public void testTopicAsynchRecoverWithAutoAck() throws JMSException, InterruptedException { + dest = new ActiveMQTopic("Topic-"+System.currentTimeMillis()); + doTestAsynchRecoverWithAutoAck(); + } + + /** + * Test to make sure that a Sync recover works. + * + * @throws JMSException + */ + public void doTestSynchRecover() throws JMSException { + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + MessageConsumer consumer = session.createConsumer(dest); + connection.start(); + + MessageProducer producer = session.createProducer(dest); + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + producer.send(session.createTextMessage("First")); + producer.send(session.createTextMessage("Second")); + + TextMessage message = (TextMessage)consumer.receive(1000); + assertEquals("First", message.getText()); + assertFalse(message.getJMSRedelivered()); + message.acknowledge(); + + message = (TextMessage)consumer.receive(1000); + assertEquals("Second", message.getText()); + assertFalse(message.getJMSRedelivered()); + + session.recover(); + + message = (TextMessage)consumer.receive(2000); + assertEquals("Second", message.getText()); + assertTrue(message.getJMSRedelivered()); + + message.acknowledge(); + } + + /** + * Test to make sure that a Async recover works. + * + * @throws JMSException + * @throws InterruptedException + */ + public void doTestAsynchRecover() throws JMSException, InterruptedException { + + final Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + final String errorMessage[] = new String[]{null}; + final CountDownLatch doneCountDownLatch = new CountDownLatch(1); + + MessageConsumer consumer = session.createConsumer(dest); + + MessageProducer producer = session.createProducer(dest); + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + producer.send(session.createTextMessage("First")); + producer.send(session.createTextMessage("Second")); + + consumer.setMessageListener(new MessageListener(){ + int counter; + public void onMessage(Message msg) { + counter++; + try { + TextMessage message = (TextMessage)msg; + switch( counter ) { + case 1: + assertEquals("First", message.getText()); + assertFalse(message.getJMSRedelivered()); + message.acknowledge(); + + break; + case 2: + assertEquals("Second", message.getText()); + assertFalse(message.getJMSRedelivered()); + session.recover(); + break; + + case 3: + assertEquals("Second", message.getText()); + assertTrue(message.getJMSRedelivered()); + message.acknowledge(); + doneCountDownLatch.countDown(); + break; + + default: + errorMessage[0]="Got too many messages: "+counter; + doneCountDownLatch.countDown(); + } + } catch (Throwable e) { + e.printStackTrace(); + errorMessage[0]="Got exception: "+e; + doneCountDownLatch.countDown(); + } + } + }); + connection.start(); + + if( doneCountDownLatch.await(5, TimeUnit.SECONDS) ) { + if( errorMessage[0]!=null ) { + fail(errorMessage[0]); + } + } else { + fail("Timeout waiting for async message delivery to complete."); + } + + } + + /** + * Test to make sure that a Async recover works when using AUTO_ACKNOWLEDGE. + * + * @throws JMSException + * @throws InterruptedException + */ + public void doTestAsynchRecoverWithAutoAck() throws JMSException, InterruptedException { + + final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + final String errorMessage[] = new String[]{null}; + final CountDownLatch doneCountDownLatch = new CountDownLatch(1); + + MessageConsumer consumer = session.createConsumer(dest); + + MessageProducer producer = session.createProducer(dest); + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + producer.send(session.createTextMessage("First")); + producer.send(session.createTextMessage("Second")); + + consumer.setMessageListener(new MessageListener(){ + int counter; + public void onMessage(Message msg) { + counter++; + try { + TextMessage message = (TextMessage)msg; + switch( counter ) { + case 1: + assertEquals("First", message.getText()); + assertFalse(message.getJMSRedelivered()); + break; + case 2: + // This should rollback the delivery of this message.. and re-deliver. + assertEquals("Second", message.getText()); + assertFalse(message.getJMSRedelivered()); + session.recover(); + break; + + case 3: + assertEquals("Second", message.getText()); + assertTrue(message.getJMSRedelivered()); + doneCountDownLatch.countDown(); + break; + + default: + errorMessage[0]="Got too many messages: "+counter; + doneCountDownLatch.countDown(); + } + } catch (Throwable e) { + e.printStackTrace(); + errorMessage[0]="Got exception: "+e; + doneCountDownLatch.countDown(); + } + } + }); + connection.start(); + + if( doneCountDownLatch.await(5000, TimeUnit.SECONDS) ) { + if( errorMessage[0]!=null ) { + fail(errorMessage[0]); + } + } else { + fail("Timeout waiting for async message delivery to complete."); + } + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsTempDestinationTest.java b/activemq-core/src/test/java/org/activemq/JmsTempDestinationTest.java new file mode 100755 index 0000000000..56dfba3e1b --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsTempDestinationTest.java @@ -0,0 +1,260 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.util.ArrayList; +import javax.jms.*; + +import junit.framework.TestCase; + +/** + * @version + */ +public class JmsTempDestinationTest extends TestCase { + + private Connection connection; + private ActiveMQConnectionFactory factory; + + protected void setUp() throws Exception { + factory = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false"); + factory.setUseAsyncSend(false); + connection = factory.createConnection(); + } + + /** + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception { + if (connection != null) { + connection.close(); + connection = null; + } + } + + + /** + * Make sure Temp destination can only be consumed by local connection + * + * @throws JMSException + */ + public void testTempDestOnlyConsumedByLocalConn() throws JMSException { + connection.start(); + + Session tempSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + TemporaryQueue queue = tempSession.createTemporaryQueue(); + MessageProducer producer = tempSession.createProducer(queue); + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + TextMessage message = tempSession.createTextMessage("First"); + producer.send(message); + + //temp destination should not be consume when using another connection + Connection otherConnection = factory.createConnection(); + Session otherSession = otherConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + TemporaryQueue otherQueue = otherSession.createTemporaryQueue(); + MessageConsumer consumer = otherSession.createConsumer(otherQueue); + Message msg = consumer.receive(3000); + assertNull(msg); + + //should throw InvalidDestinationException when consuming a temp destination from another connection + try{ + consumer = otherSession.createConsumer(queue); + fail("Send should fail since temp destination should be used from another connection"); + }catch(InvalidDestinationException e){ + assertTrue("failed to throw an exception",true); + } + + + //should be able to consume temp destination from the same connection + consumer = tempSession.createConsumer(queue); + msg = consumer.receive(3000); + assertNotNull(msg); + + + } + + /** + * Make sure that a temp queue does not drop message if there is an active + * consumers. + * + * @throws JMSException + */ + public void testTempQueueHoldsMessagesWithConsumers() throws JMSException { + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createTemporaryQueue(); + MessageConsumer consumer = session.createConsumer(queue); + connection.start(); + + MessageProducer producer = session.createProducer(queue); + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + TextMessage message = session.createTextMessage("Hello"); + producer.send(message); + + Message message2 = consumer.receive(1000); + assertNotNull(message2); + assertTrue("Expected message to be a TextMessage", message2 instanceof TextMessage); + assertTrue("Expected message to be a '" + message.getText() + "'", + ((TextMessage) message2).getText().equals(message.getText())); + } + + /** + * Make sure that a temp queue does not drop message if there are no active + * consumers. + * + * @throws JMSException + */ + public void testTempQueueHoldsMessagesWithoutConsumers() throws JMSException { + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createTemporaryQueue(); + MessageProducer producer = session.createProducer(queue); + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + TextMessage message = session.createTextMessage("Hello"); + producer.send(message); + + connection.start(); + MessageConsumer consumer = session.createConsumer(queue); + Message message2 = consumer.receive(3000); + assertNotNull(message2); + assertTrue("Expected message to be a TextMessage", message2 instanceof TextMessage); + assertTrue("Expected message to be a '" + message.getText() + "'", + ((TextMessage) message2).getText().equals(message.getText())); + + } + + /** + * Test temp queue works under load + * @throws JMSException + */ + public void testTmpQueueWorksUnderLoad() throws JMSException { + int count = 500; + int dataSize = 1024; + + ArrayList list = new ArrayList(count); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createTemporaryQueue(); + MessageProducer producer = session.createProducer(queue); + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + + byte[] data = new byte[dataSize]; + for (int i =0; i < count; i++){ + BytesMessage message = session.createBytesMessage(); + message.writeBytes(data); + producer.send(message); + list.add(message); + } + + + connection.start(); + MessageConsumer consumer = session.createConsumer(queue); + for (int i =0; i < count; i++){ + Message message2 = consumer.receive(2000); + + assertTrue(message2 != null); + assertTrue(message2.equals(list.get(i))); + } + } + + /** + * Make sure you cannot publish to a temp destination that does not exist anymore. + * + * @throws JMSException + */ + public void testPublishFailsForClosedConnection() throws JMSException { + + Connection tempConnection = factory.createConnection(); + Session tempSession = tempConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + TemporaryQueue queue = tempSession.createTemporaryQueue(); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + connection.start(); + + // This message delivery should work since the temp connection is still open. + MessageProducer producer = session.createProducer(queue); + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + TextMessage message = session.createTextMessage("First"); + producer.send(message); + + // Closing the connection should destroy the temp queue that was created. + tempConnection.close(); + + // This message delivery NOT should work since the temp connection is now closed. + try { + message = session.createTextMessage("Hello"); + producer.send(message); + fail("Send should fail since temp destination should not exist anymore."); + } catch ( JMSException e ) { + assertTrue("failed to throw an exception",true); + } + } + + /** + * Make sure you cannot publish to a temp destination that does not exist anymore. + * + * @throws JMSException + */ + public void testPublishFailsForDestoryedTempDestination() throws JMSException { + + Connection tempConnection = factory.createConnection(); + Session tempSession = tempConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + TemporaryQueue queue = tempSession.createTemporaryQueue(); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + connection.start(); + + // This message delivery should work since the temp connection is still open. + MessageProducer producer = session.createProducer(queue); + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + TextMessage message = session.createTextMessage("First"); + producer.send(message); + + // deleting the Queue will cause sends to fail + queue.delete(); + + // This message delivery NOT should work since the temp connection is now closed. + try { + message = session.createTextMessage("Hello"); + producer.send(message); + fail("Send should fail since temp destination should not exist anymore."); + } catch ( JMSException e ) { + assertTrue("failed to throw an exception",true); + } + } + + /** + * Test you can't delete a Destination with Active Subscribers + * @throws JMSException + */ + public void testDeleteDestinationWithSubscribersFails() throws JMSException { + Connection connection = factory.createConnection(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + TemporaryQueue queue = session.createTemporaryQueue(); + + connection.start(); + + session.createConsumer(queue); + + // This message delivery should NOT work since the temp connection is now closed. + try { + queue.delete(); + fail("Should fail as Subscribers are active"); + } catch ( JMSException e ) { + assertTrue("failed to throw an exception",true); + } + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsTestSupport.java b/activemq-core/src/test/java/org/activemq/JmsTestSupport.java new file mode 100755 index 0000000000..d0e6117b0e --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsTestSupport.java @@ -0,0 +1,165 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import org.activemq.broker.BrokerFactory; +import org.activemq.broker.BrokerService; +import org.activemq.command.ActiveMQDestination; + +/** + * Test cases used to test the JMS message comsumer. + * + * @version $Revision$ + */ +public class JmsTestSupport extends CombinationTestSupport { + + public String userName; + public String password; + + protected ConnectionFactory factory; + protected ActiveMQConnection connection; + protected BrokerService broker; + + protected List connections = Collections.synchronizedList(new ArrayList()); + + // ///////////////////////////////////////////////////////////////// + // + // Test support methods. + // + // ///////////////////////////////////////////////////////////////// + protected ActiveMQDestination createDestination(Session session, byte type) throws JMSException { + switch (type) { + case ActiveMQDestination.QUEUE_TYPE: + return (ActiveMQDestination) session.createQueue("TEST"); + case ActiveMQDestination.TOPIC_TYPE: + return (ActiveMQDestination) session.createTopic("TEST"); + case ActiveMQDestination.TEMP_QUEUE_TYPE: + return (ActiveMQDestination) session.createTemporaryQueue(); + case ActiveMQDestination.TEMP_TOPIC_TYPE: + return (ActiveMQDestination) session.createTemporaryTopic(); + } + throw new IllegalArgumentException("type: " + type); + } + + protected void sendMessages(Destination destination, int count) throws Exception { + ConnectionFactory factory = createConnectionFactory(); + Connection connection = factory.createConnection(); + connection.start(); + sendMessages(connection, destination, count); + connection.close(); + } + + protected void sendMessages(Connection connection, Destination destination, int count) throws JMSException { + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + sendMessages(session, destination, count); + session.close(); + } + + protected void sendMessages(Session session, Destination destination, int count) throws JMSException { + MessageProducer producer = session.createProducer(destination); + for (int i = 0; i < count; i++) { + producer.send(session.createTextMessage(""+i)); + } + producer.close(); + } + + protected ConnectionFactory createConnectionFactory() throws Exception { + return new ActiveMQConnectionFactory("vm://localhost"); + } + + protected BrokerService createBroker() throws Exception { + return BrokerFactory.createBroker(new URI("broker://()/localhost?persistent=false")); + } + + protected void setUp() throws Exception { + broker = createBroker(); + broker.start(); + factory = createConnectionFactory(); + connection = (ActiveMQConnection) factory.createConnection(userName, password); + connections.add(connection); + } + + protected void tearDown() throws Exception { + for (Iterator iter = connections.iterator(); iter.hasNext();) { + Connection conn= (Connection) iter.next(); + try { + conn.close(); + } catch (Throwable e) { + } + } + broker.stop(); + } + + protected void safeClose(Connection c) { + try { + c.close(); + } catch (Throwable e) { + } + } + + protected void safeClose(Session s) { + try { + s.close(); + } catch (Throwable e) { + } + } + + protected void safeClose(MessageConsumer c) { + try { + c.close(); + } catch (Throwable e) { + } + } + + protected void safeClose(MessageProducer p) { + try { + p.close(); + } catch (Throwable e) { + } + } + + protected void profilerPause(String prompt) throws IOException { + if( System.getProperty("profiler")!=null ) { + pause(prompt); + } + } + + protected void pause(String prompt) throws IOException { + System.out.println(); + System.out.println(prompt+"> Press enter to continue: "); + while( System.in.read()!='\n' ) { + } + } + +} diff --git a/activemq-core/src/test/java/org/activemq/JmsTopicCompositeSendReceiveTest.java b/activemq-core/src/test/java/org/activemq/JmsTopicCompositeSendReceiveTest.java new file mode 100755 index 0000000000..a1ca9b9732 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsTopicCompositeSendReceiveTest.java @@ -0,0 +1,90 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import org.activemq.test.JmsTopicSendReceiveTest; + +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.MessageConsumer; +import javax.jms.Topic; + + +/** + * @version $Revision: 1.3 $ + */ +public class JmsTopicCompositeSendReceiveTest extends JmsTopicSendReceiveTest { + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsTopicCompositeSendReceiveTest.class); + + Destination consumerDestination2; + MessageConsumer consumer2; + + /** + * Sets a test to have a queue destination and non-persistent delivery mode. + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + deliveryMode = DeliveryMode.NON_PERSISTENT; + super.setUp(); + consumerDestination2 = consumeSession.createTopic("FOO.BAR.HUMBUG2"); + log.info("Created consumer destination: " + consumerDestination2 + " of type: " + consumerDestination2.getClass()); + if (durable) { + log.info("Creating durable consumer"); + consumer2 = consumeSession.createDurableSubscriber((Topic) consumerDestination2, getName()); + } else { + consumer2 = consumeSession.createConsumer(consumerDestination2); + } + + } + + /** + * Returns the consumer subject. + * + * @return String - consumer subject + * @see org.activemq.test.TestSupport#getConsumerSubject() + */ + protected String getConsumerSubject() { + return "FOO.BAR.HUMBUG"; + } + + /** + * Returns the producer subject. + * + * @return String - producer subject + * @see org.activemq.test.TestSupport#getProducerSubject() + */ + protected String getProducerSubject() { + return "FOO.BAR.HUMBUG,FOO.BAR.HUMBUG2"; + } + + /** + * Test if all the messages sent are being received. + * + * @throws Exception + */ + public void testSendReceive() throws Exception { + super.testSendReceive(); + messages.clear(); + consumer2.setMessageListener(this); + assertMessagesAreReceived(); + log.info("" + data.length + " messages(s) received, closing down connections"); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsTopicRedeliverTest.java b/activemq-core/src/test/java/org/activemq/JmsTopicRedeliverTest.java new file mode 100755 index 0000000000..c55a23b43f --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsTopicRedeliverTest.java @@ -0,0 +1,155 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; + +/** + * @version $Revision: 1.4 $ + */ +public class JmsTopicRedeliverTest extends TestSupport { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsTopicRedeliverTest.class); + + + protected Connection connection; + protected Session session; + protected Session consumeSession; + protected MessageConsumer consumer; + protected MessageProducer producer; + protected Destination consumerDestination; + protected Destination producerDestination; + protected boolean topic = true; + protected boolean durable = false; + protected boolean verbose = false; + protected long initRedeliveryDelay = 0; + + protected void setUp() throws Exception { + super.setUp(); + + connectionFactory = createConnectionFactory(); + connection = createConnection(); + initRedeliveryDelay = ((ActiveMQConnection)connection).getRedeliveryPolicy().getInitialRedeliveryDelay(); + + if (durable) { + connection.setClientID(getClass().getName()); + } + + log.info("Created connection: " + connection); + + session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + consumeSession = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + log.info("Created session: " + session); + log.info("Created consumeSession: " + consumeSession); + producer = session.createProducer(null); + //producer.setDeliveryMode(deliveryMode); + + log.info("Created producer: " + producer); + + if (topic) { + consumerDestination = session.createTopic(getConsumerSubject()); + producerDestination = session.createTopic(getProducerSubject()); + } else { + consumerDestination = session.createQueue(getConsumerSubject()); + producerDestination = session.createQueue(getProducerSubject()); + } + + log.info("Created consumer destination: " + consumerDestination + " of type: " + consumerDestination.getClass()); + log.info("Created producer destination: " + producerDestination + " of type: " + producerDestination.getClass()); + consumer = createConsumer(); + connection.start(); + + log.info("Created connection: " + connection); + } + + /** + * Returns the consumer subject. + * + * @return String - consumer subject + * @see org.activemq.test.TestSupport#getConsumerSubject() + */ + protected String getConsumerSubject() { + return "TEST"; + } + + /** + * Returns the producer subject. + * + * @return String - producer subject + * @see org.activemq.test.TestSupport#getProducerSubject() + */ + protected String getProducerSubject() { + return "TEST"; + } + + /** + * Sends and consumes the messages. + * + * @throws Exception + */ + public void testRecover() throws Exception { + String text = "TEST"; + Message sendMessage = session.createTextMessage(text); + + if (verbose) { + log.info("About to send a message: " + sendMessage + " with text: " + text); + } + producer.send(producerDestination, sendMessage); + + //receive but don't acknowledge + Message unackMessage = consumer.receive(initRedeliveryDelay + 1000); + assertNotNull(unackMessage); + String unackId = unackMessage.getJMSMessageID(); + assertEquals(((TextMessage) unackMessage).getText(), text); + assertFalse(unackMessage.getJMSRedelivered()); + //assertEquals(unackMessage.getIntProperty("JMSXDeliveryCount"),1); + + //receive then acknowledge + consumeSession.recover(); + Message ackMessage = consumer.receive(initRedeliveryDelay + 1000); + assertNotNull(ackMessage); + ackMessage.acknowledge(); + String ackId = ackMessage.getJMSMessageID(); + assertEquals(((TextMessage) ackMessage).getText(), text); + assertTrue(ackMessage.getJMSRedelivered()); + //assertEquals(ackMessage.getIntProperty("JMSXDeliveryCount"),2); + assertEquals(unackId, ackId); + consumeSession.recover(); + assertNull(consumer.receiveNoWait()); + } + + protected MessageConsumer createConsumer() throws JMSException { + if (durable) { + log.info("Creating durable consumer"); + return consumeSession.createDurableSubscriber((Topic) consumerDestination, getName()); + } + return consumeSession.createConsumer(consumerDestination); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/JmsTopicRequestReplyTest.java b/activemq-core/src/test/java/org/activemq/JmsTopicRequestReplyTest.java new file mode 100755 index 0000000000..5be4e22e19 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsTopicRequestReplyTest.java @@ -0,0 +1,221 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.util.List; +import java.util.Vector; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.activemq.test.TestSupport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @version $Revision: 1.3 $ + */ +public class JmsTopicRequestReplyTest extends TestSupport implements MessageListener { + private final Log log = LogFactory.getLog(getClass()); + + private Connection serverConnection; + private Connection clientConnection; + private MessageProducer replyProducer; + private Session serverSession; + private Destination requestDestination; + private List failures = new Vector(); + private boolean dynamicallyCreateProducer; + protected boolean useAsyncConsume = false; + private String clientSideClientID; + + public void testSendAndReceive() throws Exception { + clientConnection = createConnection(); + clientConnection.setClientID("ClientConnection:" + getSubject()); + + Session session = clientConnection.createSession(false, + Session.AUTO_ACKNOWLEDGE); + + clientConnection.start(); + + Destination replyDestination = createTemporaryDestination(session); + + + // lets test the destination + clientSideClientID = clientConnection.getClientID(); + + //TODO + //String value = ActiveMQDestination.getClientId((ActiveMQDestination) replyDestination); + //assertEquals("clientID from the temporary destination must be the same", clientSideClientID, value); + log.info("Both the clientID and destination clientID match properly: " + clientSideClientID); + + + /* build queues */ + MessageProducer requestProducer = + session.createProducer(requestDestination); + MessageConsumer replyConsumer = + session.createConsumer(replyDestination); + + + /* build requestmessage */ + TextMessage requestMessage = session.createTextMessage("Olivier"); + requestMessage.setJMSReplyTo(replyDestination); + requestProducer.send(requestMessage); + + log.info("Sent request."); + log.info(requestMessage.toString()); + + Message msg = replyConsumer.receive(5000); + + + if (msg instanceof TextMessage) { + TextMessage replyMessage = (TextMessage) msg; + log.info("Received reply."); + log.info(replyMessage.toString()); + assertEquals("Wrong message content", "Hello: Olivier", replyMessage.getText()); + } + else { + fail("Should have received a reply by now"); + } + + assertEquals("Should not have had any failures: " + failures, 0, failures.size()); + } + + public void testSendAndReceiveWithDynamicallyCreatedProducer() throws Exception { + dynamicallyCreateProducer = true; + testSendAndReceive(); + } + + /** + * Use the asynchronous subscription mechanism + */ + public void onMessage(Message message) { + try { + TextMessage requestMessage = (TextMessage) message; + + log.info("Received request."); + log.info(requestMessage.toString()); + + Destination replyDestination = requestMessage.getJMSReplyTo(); + + //TODO + //String value = ActiveMQDestination.getClientId((ActiveMQDestination) replyDestination); + //assertEquals("clientID from the temporary destination must be the same", clientSideClientID, value); + + TextMessage replyMessage = serverSession.createTextMessage("Hello: " + requestMessage.getText()); + + replyMessage.setJMSCorrelationID(requestMessage.getJMSMessageID()); + + if (dynamicallyCreateProducer) { + replyProducer = serverSession.createProducer(replyDestination); + replyProducer.send(replyMessage); + } + else { + replyProducer.send(replyDestination, replyMessage); + } + + log.info("Sent reply."); + log.info(replyMessage.toString()); + } + catch (JMSException e) { + onException(e); + } + } + + /** + * Use the synchronous subscription mechanism + */ + protected void syncConsumeLoop(MessageConsumer requestConsumer) { + try { + Message message = requestConsumer.receive(5000); + if (message != null) { + onMessage(message); + } + else { + log.error("No message received"); + } + } + catch (JMSException e) { + onException(e); + } + } + + + protected void setUp() throws Exception { + super.setUp(); + + serverConnection = createConnection(); + serverConnection.setClientID("serverConnection:" + getSubject()); + serverSession = serverConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + replyProducer = serverSession.createProducer(null); + + requestDestination = createDestination(serverSession); + + /* build queues */ + final MessageConsumer requestConsumer = serverSession.createConsumer(requestDestination); + if (useAsyncConsume) { + requestConsumer.setMessageListener(this); + } + else { + Thread thread = new Thread(new Runnable() { + public void run() { + syncConsumeLoop(requestConsumer); + } + }); + thread.start(); + } + serverConnection.start(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + serverConnection.close(); + clientConnection.stop(); + clientConnection.close(); + } + + protected void onException(JMSException e) { + log.info("Caught: " + e); + e.printStackTrace(); + failures.add(e); + } + + protected Destination createDestination(Session session) throws JMSException { + if (topic) { + return session.createTopic(getSubject()); + } + return session.createQueue(getSubject()); + } + + protected Destination createTemporaryDestination(Session session) throws JMSException { + if (topic) { + return session.createTemporaryTopic(); + } + return session.createTemporaryQueue(); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/JmsTopicSelectorTest.java b/activemq-core/src/test/java/org/activemq/JmsTopicSelectorTest.java new file mode 100755 index 0000000000..683983abf0 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsTopicSelectorTest.java @@ -0,0 +1,199 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; + +/** + * @version $Revision: 1.2 $ + */ +public class JmsTopicSelectorTest extends TestSupport { + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsTopicSelectorTest.class); + + protected Connection connection; + protected Session session; + protected MessageConsumer consumer; + protected MessageProducer producer; + protected Destination consumerDestination; + protected Destination producerDestination; + protected boolean topic = true; + protected boolean durable = false; + protected int deliveryMode = DeliveryMode.PERSISTENT; + + public JmsTopicSelectorTest() { + super(); + } + + public JmsTopicSelectorTest(String name) { + super(name); + } + + public void setUp() throws Exception { + super.setUp(); + + connectionFactory = createConnectionFactory(); + connection = createConnection(); + if (durable) { + connection.setClientID(getClass().getName()); + } + + log.info("Created connection: " + connection); + + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + log.info("Created session: " + session); + + if (topic) { + consumerDestination = session.createTopic(getConsumerSubject()); + producerDestination = session.createTopic(getProducerSubject()); + } else { + consumerDestination = session.createQueue(getConsumerSubject()); + producerDestination = session.createQueue(getProducerSubject()); + } + + log.info("Created consumer destination: " + consumerDestination + " of type: " + consumerDestination.getClass()); + log.info("Created producer destination: " + producerDestination + " of type: " + producerDestination.getClass()); + producer = session.createProducer(producerDestination); + producer.setDeliveryMode(deliveryMode); + + log.info("Created producer: " + producer + " delivery mode = " + + (deliveryMode == DeliveryMode.PERSISTENT ? "PERSISTENT" : "NON_PERSISTENT")); + connection.start(); + } + + public void tearDown() throws Exception { + session.close(); + connection.close(); + } + + protected MessageConsumer createConsumer(String selector) throws JMSException { + if (durable) { + log.info("Creating durable consumer"); + return session.createDurableSubscriber((Topic) consumerDestination, getName(), selector, false); + } + return session.createConsumer(consumerDestination, selector); + } + + public void sendMessages() throws Exception { + TextMessage message = session.createTextMessage("1"); + message.setIntProperty("id", 1); + message.setJMSType("a"); + message.setStringProperty("stringProperty", "a"); + message.setLongProperty("longProperty", 1); + message.setBooleanProperty("booleanProperty", true); + producer.send(message); + + message = session.createTextMessage("2"); + message.setIntProperty("id", 2); + message.setJMSType("a"); + message.setStringProperty("stringProperty", "a"); + message.setLongProperty("longProperty", 1); + message.setBooleanProperty("booleanProperty", false); + producer.send(message); + + message = session.createTextMessage("3"); + message.setIntProperty("id", 3); + message.setJMSType("a"); + message.setStringProperty("stringProperty", "a"); + message.setLongProperty("longProperty", 1); + message.setBooleanProperty("booleanProperty", true); + producer.send(message); + + message = session.createTextMessage("4"); + message.setIntProperty("id", 4); + message.setJMSType("b"); + message.setStringProperty("stringProperty", "b"); + message.setLongProperty("longProperty", 2); + message.setBooleanProperty("booleanProperty", false); + producer.send(message); + + message = session.createTextMessage("5"); + message.setIntProperty("id", 5); + message.setJMSType("c"); + message.setStringProperty("stringProperty", "c"); + message.setLongProperty("longProperty", 3); + message.setBooleanProperty("booleanProperty", true); + producer.send(message); + } + + public void consumeMessages(int remaining) throws Exception { + consumer = createConsumer(null); + for (int i = 0; i < remaining; i++) { + consumer.receive(1000); + } + consumer.close(); + + } + + public void testPropertySelector() throws Exception { + int remaining = 5; + Message message = null; + consumer = createConsumer("stringProperty = 'a' and longProperty = 1 and booleanProperty = true"); + sendMessages(); + while (true) { + message = consumer.receive(1000); + if (message == null) { + break; + } + String text = ((TextMessage) message).getText(); + if (!text.equals("1") && !text.equals("3")) { + fail("unexpected message: " + text); + } + remaining--; + } + assertEquals(remaining, 3); + consumer.close(); + consumeMessages(remaining); + + } + + public void testJMSPropertySelector() throws Exception { + int remaining = 5; + Message message = null; + consumer = createConsumer("JMSType = 'a' and stringProperty = 'a'"); + sendMessages(); + while (true) { + message = consumer.receive(1000); + if (message == null) { + break; + } + String text = ((TextMessage) message).getText(); + if (!text.equals("1") && !text.equals("2") && !text.equals("3")) { + fail("unexpected message: " + text); + } + remaining--; + } + assertEquals(remaining, 2); + consumer.close(); + consumeMessages(remaining); + + } + +} diff --git a/activemq-core/src/test/java/org/activemq/JmsTopicSendReceiveSubscriberTest.java b/activemq-core/src/test/java/org/activemq/JmsTopicSendReceiveSubscriberTest.java new file mode 100755 index 0000000000..7e4cccc48f --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsTopicSendReceiveSubscriberTest.java @@ -0,0 +1,39 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.Topic; +import javax.jms.TopicSession; + +/** + * @version $Revision: 1.4 $ + */ +public class JmsTopicSendReceiveSubscriberTest extends JmsTopicSendReceiveTest { + protected MessageConsumer createConsumer() throws JMSException { + if (durable) { + return super.createConsumer(); + } + else { + TopicSession topicSession = (TopicSession)session; + return topicSession.createSubscriber((Topic) consumerDestination, null, false); + } + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsTopicSendReceiveTest.java b/activemq-core/src/test/java/org/activemq/JmsTopicSendReceiveTest.java new file mode 100755 index 0000000000..40a3c9aeb2 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsTopicSendReceiveTest.java @@ -0,0 +1,94 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.Session; +import javax.jms.Topic; + +/** + * @version $Revision: 1.3 $ + */ +public class JmsTopicSendReceiveTest extends JmsSendReceiveTestSupport { + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsTopicSendReceiveTest.class); + + protected Connection connection; + + protected void setUp() throws Exception { + super.setUp(); + + connectionFactory = createConnectionFactory(); + connection = createConnection(); + if (durable) { + connection.setClientID(getClass().getName()); + } + + log.info("Created connection: " + connection); + + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + log.info("Created session: " + session); + producer = session.createProducer(null); + producer.setDeliveryMode(deliveryMode); + + log.info("Created producer: " + producer + " delivery mode = " + + (deliveryMode == DeliveryMode.PERSISTENT ? "PERSISTENT" : "NON_PERSISTENT")); + + if (topic) { + consumerDestination = session.createTopic(getConsumerSubject()); + producerDestination = session.createTopic(getProducerSubject()); + } + else { + consumerDestination = session.createQueue(getConsumerSubject()); + producerDestination = session.createQueue(getProducerSubject()); + } + + log.info("Created consumer destination: " + consumerDestination + " of type: " + consumerDestination.getClass()); + log.info("Created producer destination: " + producerDestination + " of type: " + producerDestination.getClass()); + consumer = createConsumer(); + consumer.setMessageListener(this); + connection.start(); + + //log.info("Created connection: " + connection); + } + + protected MessageConsumer createConsumer() throws JMSException { + if (durable) { + log.info("Creating durable consumer"); + return session.createDurableSubscriber((Topic) consumerDestination, getName()); + } + return session.createConsumer(consumerDestination); + } + + protected void tearDown() throws Exception { + log.info("Dumping stats..."); + //connectionFactory.getStats().reset(); + + log.info("Closing down connection"); + + /** TODO we should be able to shut down properly */ + session.close(); + connection.close(); + } + +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsTopicSendReceiveWithTwoConnectionsTest.java b/activemq-core/src/test/java/org/activemq/JmsTopicSendReceiveWithTwoConnectionsTest.java new file mode 100755 index 0000000000..0b851d214d --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsTopicSendReceiveWithTwoConnectionsTest.java @@ -0,0 +1,100 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Session; + +/** + * @version + */ +public class JmsTopicSendReceiveWithTwoConnectionsTest extends JmsSendReceiveTestSupport { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsTopicSendReceiveWithTwoConnectionsTest.class); + + protected Connection sendConnection; + protected Connection receiveConnection; + protected Session receiveSession; + + protected void setUp() throws Exception { + super.setUp(); + + connectionFactory = createConnectionFactory(); + + sendConnection = createSendConnection(); + sendConnection.start(); + + receiveConnection = createReceiveConnection(); + receiveConnection.start(); + + log.info("Created sendConnection: " + sendConnection); + log.info("Created receiveConnection: " + receiveConnection); + + session = sendConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + receiveSession = receiveConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + log.info("Created sendSession: " + session); + log.info("Created receiveSession: " + receiveSession); + + producer = session.createProducer(null); + producer.setDeliveryMode(deliveryMode); + + log.info("Created producer: " + producer + " delivery mode = " + + (deliveryMode == DeliveryMode.PERSISTENT ? "PERSISTENT" : "NON_PERSISTENT")); + + if (topic) { + consumerDestination = session.createTopic(getConsumerSubject()); + producerDestination = session.createTopic(getProducerSubject()); + } + else { + consumerDestination = session.createQueue(getConsumerSubject()); + producerDestination = session.createQueue(getProducerSubject()); + } + + log.info("Created consumer destination: " + consumerDestination + " of type: " + consumerDestination.getClass()); + log.info("Created producer destination: " + producerDestination + " of type: " + producerDestination.getClass()); + + consumer = receiveSession.createConsumer(consumerDestination); + consumer.setMessageListener(this); + + + log.info("Started connections"); + } + + protected Connection createReceiveConnection() throws Exception { + return createConnection(); + } + + protected Connection createSendConnection() throws Exception { + return createConnection(); + } + + protected ActiveMQConnectionFactory createConnectionFactory() throws Exception { + return new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false"); + } + + protected void tearDown() throws Exception { + session.close(); + receiveSession.close(); + sendConnection.close(); + receiveConnection.close(); + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsTopicSendSameMessageTest.java b/activemq-core/src/test/java/org/activemq/JmsTopicSendSameMessageTest.java new file mode 100755 index 0000000000..d86a99534e --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsTopicSendSameMessageTest.java @@ -0,0 +1,52 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.TextMessage; + +import org.activemq.JmsTopicSendReceiveWithTwoConnectionsTest; + +/** + * @version $Revision: 1.3 $ + */ +public class JmsTopicSendSameMessageTest extends JmsTopicSendReceiveWithTwoConnectionsTest { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsTopicSendSameMessageTest.class); + + public void testSendReceive() throws Exception { + messages.clear(); + + TextMessage message = session.createTextMessage(); + + for (int i = 0; i < data.length; i++) { + message.setText(data[i]); + message.setStringProperty("stringProperty",data[i]); + message.setIntProperty("intProperty",i); + + if (verbose) { + log.info("About to send a message: " + message + " with text: " + data[i]); + } + + producer.send(producerDestination, message); + } + + assertMessagesAreReceived(); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsTopicTransactionTest.java b/activemq-core/src/test/java/org/activemq/JmsTopicTransactionTest.java new file mode 100755 index 0000000000..7ed4f7d2cc --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsTopicTransactionTest.java @@ -0,0 +1,40 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import org.activemq.test.JmsResourceProvider; + + +/** + * @version $Revision: 1.3 $ + */ +public class JmsTopicTransactionTest extends JmsTransactionTestSupport { + + /** + * @see org.activemq.JmsTransactionTestSupport#getJmsResourceProvider() + */ + protected JmsResourceProvider getJmsResourceProvider() { + JmsResourceProvider p = new JmsResourceProvider(); + p.setTopic(true); + p.setDurableName("testsub"); + p.setClientID("testclient"); + return p; + } + +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/JmsTopicWildcardSendReceiveTest.java b/activemq-core/src/test/java/org/activemq/JmsTopicWildcardSendReceiveTest.java new file mode 100755 index 0000000000..b468cd541f --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsTopicWildcardSendReceiveTest.java @@ -0,0 +1,161 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.DeliveryMode; +import javax.jms.Session; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.TextMessage; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageProducer; + +import org.activemq.test.JmsTopicSendReceiveTest; +import org.activemq.command.ActiveMQDestination; + + +/** + * @version $Revision: 1.4 $ + */ +public class JmsTopicWildcardSendReceiveTest extends JmsTopicSendReceiveTest { + + private String destination1String = "TEST.ONE.ONE" ; + private String destination2String = "TEST.ONE.ONE.ONE" ; + private String destination3String = "TEST.ONE.TWO" ; + private String destination4String = "TEST.TWO.ONE" ; + + protected void setUp() throws Exception { + topic = true; + durable = false; + deliveryMode = DeliveryMode.NON_PERSISTENT; + super.setUp(); + } + + protected String getConsumerSubject(){ + return "FOO.>"; + } + + protected String getProducerSubject(){ + return "FOO.BAR.HUMBUG"; + } + + public void testReceiveWildcardTopicEndAsterisk() throws Throwable { + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + ActiveMQDestination destination1 = (ActiveMQDestination) session.createTopic(destination1String); + ActiveMQDestination destination3 = (ActiveMQDestination) session.createTopic(destination3String); + + Message m = null; + MessageConsumer consumer = null; + String text = null; + + ActiveMQDestination destination6 = (ActiveMQDestination) session.createTopic("TEST.ONE.*"); + consumer = session.createConsumer(destination6); + sendMessage(session,destination1,destination1String); + sendMessage(session,destination3,destination3String); + m = consumer.receive(1000); + assertNotNull(m); + text = ((TextMessage)m).getText(); + if(!(text.equals(destination1String) || text.equals(destination3String))) { + fail("unexpected message:" + text); + } + m = consumer.receive(1000); + assertNotNull(m); + text = ((TextMessage)m).getText(); + if(!(text.equals(destination1String) || text.equals(destination3String))) { + fail("unexpected message:" + text); + } + assertNull(consumer.receiveNoWait()); + } + + public void testReceiveWildcardTopicEndGreaterThan() throws Throwable { + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + ActiveMQDestination destination1 = (ActiveMQDestination) session.createTopic(destination1String); + ActiveMQDestination destination2 = (ActiveMQDestination) session.createTopic(destination2String); + ActiveMQDestination destination3 = (ActiveMQDestination) session.createTopic(destination3String); + + Message m = null; + MessageConsumer consumer = null; + String text = null; + + ActiveMQDestination destination7 = (ActiveMQDestination) session.createTopic("TEST.ONE.>"); + consumer = session.createConsumer(destination7); + sendMessage(session,destination1,destination1String); + sendMessage(session,destination2,destination2String); + sendMessage(session,destination3,destination3String); + m = consumer.receive(1000); + assertNotNull(m); + text = ((TextMessage)m).getText(); + if(!(text.equals(destination1String) || text.equals(destination2String) || text.equals(destination3String))) { + fail("unexpected message:" + text); + } + m = consumer.receive(1000); + assertNotNull(m); + if(!(text.equals(destination1String) || text.equals(destination2String) || text.equals(destination3String))) { + fail("unexpected message:" + text); + } + m = consumer.receive(1000); + assertNotNull(m); + if(!(text.equals(destination1String) || text.equals(destination2String) || text.equals(destination3String))) { + fail("unexpected message:" + text); + } + assertNull(consumer.receiveNoWait()); + } + + public void testReceiveWildcardTopicMidAsterisk() throws Throwable { + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + ActiveMQDestination destination1 = (ActiveMQDestination) session.createTopic(destination1String); + ActiveMQDestination destination4 = (ActiveMQDestination) session.createTopic(destination4String); + + Message m = null; + MessageConsumer consumer = null; + String text = null; + + ActiveMQDestination destination8 = (ActiveMQDestination) session.createTopic("TEST.*.ONE"); + consumer = session.createConsumer(destination8); + sendMessage(session,destination1,destination1String); + sendMessage(session,destination4,destination4String); + m = consumer.receive(1000); + assertNotNull(m); + text = ((TextMessage)m).getText(); + if(!(text.equals(destination1String) || text.equals(destination4String))) { + fail("unexpected message:" + text); + } + m = consumer.receive(1000); + assertNotNull(m); + text = ((TextMessage)m).getText(); + if(!(text.equals(destination1String) || text.equals(destination4String))) { + fail("unexpected message:" + text); + } + assertNull(consumer.receiveNoWait()); + + } + + private void sendMessage(Session session, Destination destination, String text) throws JMSException { + MessageProducer producer = session.createProducer(destination); + producer.send(session.createTextMessage(text)); + producer.close(); + } +} diff --git a/activemq-core/src/test/java/org/activemq/JmsTransactionTestSupport.java b/activemq-core/src/test/java/org/activemq/JmsTransactionTestSupport.java new file mode 100755 index 0000000000..4e571956c2 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/JmsTransactionTestSupport.java @@ -0,0 +1,508 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import org.activemq.test.JmsResourceProvider; +import org.activemq.test.TestSupport; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.MessageListener; +import java.util.ArrayList; +import java.util.List; + +/** + * @version $Revision: 1.9 $ + */ +abstract public class JmsTransactionTestSupport extends TestSupport implements MessageListener { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsTransactionTestSupport.class); + + protected ConnectionFactory connectionFactory; + protected Connection connection; + protected Session session; + protected MessageConsumer consumer; + protected MessageProducer producer; + protected JmsResourceProvider resourceProvider; + protected Destination destination; + + // for message listener test + private final int messageCount = 5; + private final String messageText = "message"; + private List unackMessages = new ArrayList(messageCount); + private List ackMessages = new ArrayList(messageCount); + private boolean resendPhase = false; + + public JmsTransactionTestSupport() { + super(); + } + + public JmsTransactionTestSupport(String name) { + super(name); + } + + + /* (non-Javadoc) + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + resourceProvider = getJmsResourceProvider(); + topic = resourceProvider.isTopic(); + // We will be using transacted sessions. + resourceProvider.setTransacted(true); + connectionFactory = resourceProvider.createConnectionFactory(); + reconnect(); + } + + /* (non-Javadoc) + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception { + //TODO + //log.info("Test Done. Stats"); + //((ActiveMQConnectionFactory) connectionFactory).getFactoryStats().dump(new IndentPrinter()); + log.info("Closing down connection"); + + session.close(); + connection.close(); + log.info("Connection closed."); + } + + protected abstract JmsResourceProvider getJmsResourceProvider(); + + /** + * Sends a batch of messages and validates that the messages are received. + * + * @throws Exception + */ + public void testSendReceiveTransactedBatches() throws Exception { + int batchCount = 10; + int batchSize = 20; + TextMessage message = session.createTextMessage("Batch Message"); + + for (int j = 0; j < batchCount; j++) { + log.info("Producing bacth " + j + " of " + batchSize + " messages"); + + for (int i = 0; i < batchSize; i++) { + producer.send(message); + } + + session.commit(); + log.info("Consuming bacth " + j + " of " + batchSize + " messages"); + + for (int i = 0; i < batchSize; i++) { + message = (TextMessage) consumer.receive(1000 * 5); + assertNotNull("Received only " + i + " messages in batch " + j, message); + assertEquals("Batch Message", message.getText()); + } + + session.commit(); + } + } + + /** + * Sends a batch of messages and validates that the rollbacked message was not consumed. + * + * @throws Exception + */ + public void testSendRollback() throws Exception { + Message[] outbound = new Message[]{ + session.createTextMessage("First Message"), + session.createTextMessage("Second Message") + }; + + //sends a message + producer.send(outbound[0]); + session.commit(); + + //sends a message that gets rollbacked + producer.send(session.createTextMessage("I'm going to get rolled back.")); + session.rollback(); + + //sends a message + producer.send(outbound[1]); + session.commit(); + + //receives the first message + ArrayList messages = new ArrayList(); + log.info("About to consume message 1"); + Message message = consumer.receive(1000); + messages.add(message); + log.info("Received: " + message); + + //receives the second message + log.info("About to consume message 2"); + message = consumer.receive(4000); + messages.add(message); + log.info("Received: " + message); + + //validates that the rollbacked was not consumed + session.commit(); + Message inbound[] = new Message[messages.size()]; + messages.toArray(inbound); + assertTextMessagesEqual("Rollback did not work.", outbound, inbound); + } + + /** + * Sends a batch of messages and validates that the message sent before session close is not consumed. + * + * @throws Exception + */ + public void testSendSessionClose() throws Exception { + Message[] outbound = new Message[]{ + session.createTextMessage("First Message"), + session.createTextMessage("Second Message") + }; + + //sends a message + producer.send(outbound[0]); + session.commit(); + + //sends a message that gets rollbacked + producer.send(session.createTextMessage("I'm going to get rolled back.")); + consumer.close(); + session.close(); + + reconnect(); + + //sends a message + producer.send(outbound[1]); + session.commit(); + + //receives the first message + ArrayList messages = new ArrayList(); + log.info("About to consume message 1"); + Message message = consumer.receive(1000); + messages.add(message); + log.info("Received: " + message); + + //receives the second message + log.info("About to consume message 2"); + message = consumer.receive(4000); + messages.add(message); + log.info("Received: " + message); + + //validates that the rollbacked was not consumed + session.commit(); + Message inbound[] = new Message[messages.size()]; + messages.toArray(inbound); + assertTextMessagesEqual("Rollback did not work.", outbound, inbound); + } + + /** + * Sends a batch of messages and validates that the rollbacked message was redelivered. + * + * @throws Exception + */ + public void testReceiveRollback() throws Exception { + Message[] outbound = new Message[]{ + session.createTextMessage("First Message"), + session.createTextMessage("Second Message") + }; + + // lets consume any outstanding messages from previous test runs + while (consumer.receive(1000) != null) { + } + session.commit(); + + //sent both messages + producer.send(outbound[0]); + producer.send(outbound[1]); + session.commit(); + + log.info("Sent 0: " + outbound[0]); + log.info("Sent 1: " + outbound[1]); + + ArrayList messages = new ArrayList(); + Message message = consumer.receive(1000); + messages.add(message); + assertEquals(outbound[0], message); + session.commit(); + + // rollback so we can get that last message again. + message = consumer.receive(1000); + assertNotNull(message); + assertEquals(outbound[1], message); + session.rollback(); + + // Consume again.. the previous message should + // get redelivered. + message = consumer.receive(5000); + assertNotNull("Should have re-received the message again!", message); + messages.add(message); + session.commit(); + + Message inbound[] = new Message[messages.size()]; + messages.toArray(inbound); + assertTextMessagesEqual("Rollback did not work", outbound, inbound); + } + + /** + * Sends a batch of messages and validates that the rollbacked message was redelivered. + * + * @throws Exception + */ + public void testReceiveTwoThenRollback() throws Exception { + Message[] outbound = new Message[]{ + session.createTextMessage("First Message"), + session.createTextMessage("Second Message") + }; + + // lets consume any outstanding messages from previous test runs + while (consumer.receive(1000) != null) { + } + session.commit(); + + // + producer.send(outbound[0]); + producer.send(outbound[1]); + session.commit(); + + log.info("Sent 0: " + outbound[0]); + log.info("Sent 1: " + outbound[1]); + + ArrayList messages = new ArrayList(); + Message message = consumer.receive(1000); + assertEquals(outbound[0], message); + + message = consumer.receive(1000); + assertNotNull(message); + assertEquals(outbound[1], message); + session.rollback(); + + // Consume again.. the previous message should + // get redelivered. + message = consumer.receive(5000); + assertNotNull("Should have re-received the first message again!", message); + messages.add(message); + assertEquals(outbound[0], message); + message = consumer.receive(5000); + assertNotNull("Should have re-received the second message again!", message); + messages.add(message); + assertEquals(outbound[1], message); + + assertNull(consumer.receiveNoWait()); + session.commit(); + + Message inbound[] = new Message[messages.size()]; + messages.toArray(inbound); + assertTextMessagesEqual("Rollback did not work", outbound, inbound); + } + + /** + * Sends a batch of messages and validates that the rollbacked message was not consumed. + * + * @throws Exception + */ + public void testSendReceiveWithPrefetchOne() throws Exception { + setPrefetchToOne(); + Message[] outbound = new Message[]{ + session.createTextMessage("First Message"), + session.createTextMessage("Second Message"), + session.createTextMessage("Third Message"), + session.createTextMessage("Fourth Message") + }; + + for (int i = 0; i < outbound.length; i++) { + //sends a message + producer.send(outbound[i]); + } + session.commit(); + + //receives the first message + for (int i = 0; i < outbound.length; i++) { + log.info("About to consume message 1"); + Message message = consumer.receive(1000); + assertNotNull(message); + log.info("Received: " + message); + } + + //validates that the rollbacked was not consumed + session.commit(); + } + + /** + * Perform the test that validates if the rollbacked message was redelivered multiple times. + * + * @throws Exception + */ + public void testReceiveTwoThenRollbackManyTimes() throws Exception { + for (int i = 0; i < 5; i++) + testReceiveTwoThenRollback(); + } + + /** + * Sends a batch of messages and validates that the rollbacked message was not consumed. This test differs by + * setting the message prefetch to one. + * + * @throws Exception + */ + public void testSendRollbackWithPrefetchOfOne() throws Exception { + setPrefetchToOne(); + testSendRollback(); + } + + /** + * Sends a batch of messages and and validates that the rollbacked message was redelivered. This test differs by + * setting the message prefetch to one. + * + * @throws Exception + */ + public void testReceiveRollbackWithPrefetchOfOne() throws Exception { + setPrefetchToOne(); + testReceiveRollback(); + } + + /** + * Tests if the messages can still be received if the consumer is closed (session is not closed). + * + * @throws Exception see http://jira.codehaus.org/browse/AMQ-143 + */ + public void testCloseConsumerBeforeCommit() throws Exception { + TextMessage[] outbound = new TextMessage[]{ + session.createTextMessage("First Message"), + session.createTextMessage("Second Message") + }; + + // lets consume any outstanding messages from previous test runs + while (consumer.receiveNoWait() != null) { + } + + session.commit(); + + //sends the messages + producer.send(outbound[0]); + producer.send(outbound[1]); + session.commit(); + log.info("Sent 0: " + outbound[0]); + log.info("Sent 1: " + outbound[1]); + + TextMessage message = (TextMessage) consumer.receive(1000); + assertEquals(outbound[0].getText(), message.getText()); + // Close the consumer before the commit. This should not cause the received message + // to rollback. + consumer.close(); + session.commit(); + + // Create a new consumer + consumer = resourceProvider.createConsumer(session, destination); + log.info("Created consumer: " + consumer); + + message = (TextMessage) consumer.receive(1000); + assertEquals(outbound[1].getText(), message.getText()); + session.commit(); + } + + + /** + * Recreates the connection. + * + * @throws JMSException + */ + protected void reconnect() throws JMSException { + + Connection t = resourceProvider.createConnection(connectionFactory); + if (connection != null) { + // Close the previous connection. + connection.close(); + } + connection = t; + + session = resourceProvider.createSession(connection); + destination = resourceProvider.createDestination(session, getSubject()); + producer = resourceProvider.createProducer(session, destination); + consumer = resourceProvider.createConsumer(session, destination); + connection.start(); + } + + /** + * Sets the prefeftch policy to one. + */ + protected void setPrefetchToOne() { + ActiveMQPrefetchPolicy prefetchPolicy = ((ActiveMQConnection) connection).getPrefetchPolicy(); + prefetchPolicy.setQueuePrefetch(1); + prefetchPolicy.setTopicPrefetch(1); + prefetchPolicy.setDurableTopicPrefetch(1); + } + + public void testMessageListener() throws Exception { + //send messages + for(int i = 0;iActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq; + +import javax.jms.BytesMessage; +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.Topic; + +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.activemq.util.IdGenerator; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +/** + * @version $Revision: 1.4 $ + */ +public class LargeMessageTestSupport extends ClientTestSupport implements MessageListener { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LargeMessageTestSupport.class); + + protected static final int LARGE_MESSAGE_SIZE = 128 * 1024; + protected static final int MESSAGE_COUNT = 100; + protected Connection producerConnection; + protected Connection consumerConnection; + protected MessageConsumer consumer; + protected MessageProducer producer; + protected Session producerSession; + protected Session consumerSession; + protected byte[] largeMessageData; + protected Destination destination; + protected boolean isTopic = true; + protected boolean isDurable = true; + protected int deliveryMode = DeliveryMode.PERSISTENT; + protected IdGenerator idGen = new IdGenerator(); + protected boolean validMessageConsumption = true; + protected AtomicInteger messageCount = new AtomicInteger(0); + + protected int prefetchValue = 10000000; + + protected Destination createDestination() { + String subject = getClass().getName(); + if (isTopic) { + return new ActiveMQTopic(subject); + } + else { + return new ActiveMQQueue(subject); + } + } + + protected MessageConsumer createConsumer() throws JMSException { + if (isTopic && isDurable) { + return consumerSession.createDurableSubscriber((Topic) destination, idGen.generateId()); + } + else { + return consumerSession.createConsumer(destination); + } + } + + public void setUp() throws Exception { + super.setUp(); + ClientTestSupport.removeMessageStore(); + log.info("Setting up . . . . . "); + messageCount.set(0); + + destination = createDestination(); + largeMessageData = new byte[LARGE_MESSAGE_SIZE]; + for (int i = 0; i < LARGE_MESSAGE_SIZE; i++) { + if (i % 2 == 0) { + largeMessageData[i] = 'a'; + } + else { + largeMessageData[i] = 'z'; + } + } + + try { + Thread.sleep(1000);// allow the broker to start + } + catch (InterruptedException e) { + throw new JMSException(e.getMessage()); + } + + ActiveMQConnectionFactory fac = getConnectionFactory(); + producerConnection = fac.createConnection(); + setPrefetchPolicy((ActiveMQConnection) producerConnection); + producerConnection.start(); + + consumerConnection = fac.createConnection(); + setPrefetchPolicy((ActiveMQConnection) consumerConnection); + consumerConnection.setClientID(idGen.generateId()); + consumerConnection.start(); + producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + producer = producerSession.createProducer(createDestination()); + producer.setDeliveryMode(deliveryMode); + consumerSession = consumerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + consumer = createConsumer(); + consumer.setMessageListener(this); + log.info("Setup complete"); + } + + protected void setPrefetchPolicy(ActiveMQConnection activeMQConnection) { + activeMQConnection.getPrefetchPolicy().setTopicPrefetch(prefetchValue); + activeMQConnection.getPrefetchPolicy().setQueuePrefetch(prefetchValue); + activeMQConnection.getPrefetchPolicy().setDurableTopicPrefetch(prefetchValue); + activeMQConnection.getPrefetchPolicy().setQueueBrowserPrefetch(prefetchValue); + } + + public void tearDown() throws Exception { + Thread.sleep(1000); + producerConnection.close(); + consumerConnection.close(); + + super.tearDown(); + + largeMessageData = null; + } + + protected boolean isSame(BytesMessage msg1) throws Exception { + boolean result = false; + ((ActiveMQMessage) msg1).setReadOnlyBody(true); + + for (int i = 0; i < LARGE_MESSAGE_SIZE; i++) { + result = msg1.readByte() == largeMessageData[i]; + if (!result) + break; + } + + return result; + } + + public void onMessage(Message msg) { + try { + BytesMessage ba = (BytesMessage) msg; + validMessageConsumption &= isSame(ba); + assertTrue(ba.getBodyLength() == LARGE_MESSAGE_SIZE); + if (messageCount.incrementAndGet() >= MESSAGE_COUNT) { + synchronized (messageCount) { + messageCount.notify(); + } + } + log.info("got message = " + messageCount); + if (messageCount.get() % 50 == 0) { + log.info("count = " + messageCount); + } + } + catch (Exception e) { + e.printStackTrace(); + } + } + + public void testLargeMessages() throws Exception { + for (int i = 0; i < MESSAGE_COUNT; i++) { + log.info("Sending message: " + i); + BytesMessage msg = producerSession.createBytesMessage(); + msg.writeBytes(largeMessageData); + producer.send(msg); + } + long now = System.currentTimeMillis(); + while (now + 60000 > System.currentTimeMillis() && messageCount.get() < MESSAGE_COUNT) { + log.info("message count = " + messageCount); + synchronized (messageCount) { + messageCount.wait(1000); + } + } + log.info("Finished count = " + messageCount); + assertTrue("Not enough messages - expected " + MESSAGE_COUNT + " but got " + messageCount, messageCount.get() == MESSAGE_COUNT); + assertTrue("received messages are not valid", validMessageConsumption); + Thread.sleep(1000); + log.info("FINAL count = " + messageCount); + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/LoadTestBurnIn.java b/activemq-core/src/test/java/org/activemq/LoadTestBurnIn.java new file mode 100644 index 0000000000..da4afebd6e --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/LoadTestBurnIn.java @@ -0,0 +1,182 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import javax.jms.BytesMessage; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.Topic; + +import junit.framework.Test; + +import org.activemq.broker.BrokerFactory; +import org.activemq.broker.BrokerService; +import org.activemq.broker.TransportConnector; +import org.activemq.command.ActiveMQDestination; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +/** + * Small burn test moves sends a moderate amount of messages through the broker, to + * checking to make sure that the broker does not lock up after a while of sustained + * messaging. + * + * @version $Revision$ + */ +public class LoadTestBurnIn extends JmsTestSupport { + + public static Test suite() { + return suite(LoadTestBurnIn.class); + } + + protected void setUp() throws Exception { + System.out.println("Start: "+getName()); + super.setUp(); + } + + protected void tearDown() throws Exception { + try { + super.tearDown(); + } catch (Throwable e) { + e.printStackTrace(System.out); + } finally { + System.out.println("End: "+getName()); + } + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + protected BrokerService createBroker() throws Exception { + return BrokerFactory.createBroker(new URI("broker://(tcp://localhost:0)?useJmx=true")); +// return BrokerFactory.createBroker(new URI("xbean:org/activemq/broker/store/loadtester.xml")); + } + + protected ConnectionFactory createConnectionFactory() throws URISyntaxException, IOException { + return new ActiveMQConnectionFactory(((TransportConnector) broker.getTransportConnectors().get(0)).getServer().getConnectURI()); + } + + public ActiveMQDestination destination; + public int deliveryMode; + public byte destinationType; + public boolean durableConsumer; + + public int messageCount = 50000; + public int messageSize = 1024; + + public void initCombosForTestSendReceive() { + addCombinationValues("deliveryMode", new Object[] { + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) }); + addCombinationValues("destinationType", new Object[] { + new Byte(ActiveMQDestination.TOPIC_TYPE), +// new Byte(ActiveMQDestination.QUEUE_TYPE), + }); + addCombinationValues("durableConsumer", new Object[] { + Boolean.TRUE, +// Boolean.FALSE, + }); + addCombinationValues("messageSize", new Object[] { + new Integer(101), + new Integer(102), + new Integer(103), + new Integer(104), + new Integer(105), + new Integer(106), + new Integer(107), + new Integer(108), + }); + } + + public void testSendReceive() throws Throwable { + + // Durable consumer combination is only valid with topics + if( durableConsumer && destinationType!=ActiveMQDestination.TOPIC_TYPE) + return; + + connection.setClientID(getName()); + connection.getPrefetchPolicy().setAll(1000); + connection.start(); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + destination = createDestination(session, destinationType); + MessageConsumer consumer; + if( durableConsumer ) { + consumer = session.createDurableSubscriber((Topic) destination, "sub1:"+System.currentTimeMillis()); + } else { + consumer = session.createConsumer(destination); + } + profilerPause("Ready: "); + + final CountDownLatch producerDoneLatch = new CountDownLatch(1); + + // Send the messages, async + new Thread() { + public void run() { + Connection connection2=null; + try { + connection2 = factory.createConnection(); + Session session = connection2.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = session.createProducer(destination); + producer.setDeliveryMode(deliveryMode); + for (int i = 0; i < messageCount; i++) { + BytesMessage m = session.createBytesMessage(); + m.writeBytes(new byte[messageSize]); + producer.send(m); + } + producer.close(); + } catch (JMSException e) { + e.printStackTrace(); + } finally { + safeClose(connection2); + producerDoneLatch.countDown(); + } + + } + }.start(); + + // Make sure all the messages were delivered. + Message message = null; + for (int i = 0; i < messageCount; i++) { + message = consumer.receive(5000); + assertNotNull("Did not get message: "+i, message); + } + + profilerPause("Done: "); + + assertNull(consumer.receiveNoWait()); + message.acknowledge(); + + // Make sure the producer thread finishes. + assertTrue(producerDoneLatch.await(5, TimeUnit.SECONDS)); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/MessageTransformationTest.java b/activemq-core/src/test/java/org/activemq/MessageTransformationTest.java new file mode 100755 index 0000000000..0c69daa4c4 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/MessageTransformationTest.java @@ -0,0 +1,93 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.BytesMessage; +import javax.jms.MapMessage; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.StreamMessage; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.TextMessage; +import javax.jms.Topic; + +import junit.framework.TestCase; + +import org.activemq.command.ActiveMQBytesMessage; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQMapMessage; +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ActiveMQObjectMessage; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQStreamMessage; +import org.activemq.command.ActiveMQTempQueue; +import org.activemq.command.ActiveMQTempTopic; +import org.activemq.command.ActiveMQTextMessage; +import org.activemq.command.ActiveMQTopic; + +public class MessageTransformationTest extends TestCase { + + /** + * Sets up the resources of the unit test. + * + * @throws Exception + */ + protected void setUp() throws Exception { + } + + /** + * Clears up the resources used in the unit test. + */ + protected void tearDown() throws Exception { + } + + /** + * Tests transforming destinations into ActiveMQ's destination implementation. + */ + public void testTransformDestination() throws Exception { + assertTrue("Transforming a TempQueue destination to an ActiveMQTempQueue", ActiveMQMessageTransformation.transformDestination((TemporaryQueue)new ActiveMQTempQueue()) instanceof ActiveMQTempQueue); + + assertTrue("Transforming a TempTopic destination to an ActiveMQTempTopic", ActiveMQMessageTransformation.transformDestination((TemporaryTopic)new ActiveMQTempTopic()) instanceof ActiveMQTempTopic); + + assertTrue("Transforming a Queue destination to an ActiveMQQueue", ActiveMQMessageTransformation.transformDestination((Queue)new ActiveMQQueue()) instanceof ActiveMQQueue); + + assertTrue("Transforming a Topic destination to an ActiveMQTopic", ActiveMQMessageTransformation.transformDestination((Topic)new ActiveMQTopic()) instanceof ActiveMQTopic); + + assertTrue("Transforming a Destination to an ActiveMQDestination", ActiveMQMessageTransformation.transformDestination((ActiveMQDestination)new ActiveMQTopic()) instanceof ActiveMQDestination); + } + + /** + * Tests transforming messages into ActiveMQ's message implementation. + */ + public void testTransformMessage() throws Exception { + assertTrue("Transforming a BytesMessage message into an ActiveMQBytesMessage",ActiveMQMessageTransformation.transformMessage((BytesMessage)new ActiveMQBytesMessage(), null) instanceof ActiveMQBytesMessage); + + assertTrue("Transforming a MapMessage message to an ActiveMQMapMessage",ActiveMQMessageTransformation.transformMessage((MapMessage)new ActiveMQMapMessage(), null) instanceof ActiveMQMapMessage); + + assertTrue("Transforming an ObjectMessage message to an ActiveMQObjectMessage",ActiveMQMessageTransformation.transformMessage((ObjectMessage)new ActiveMQObjectMessage(), null) instanceof ActiveMQObjectMessage); + + assertTrue("Transforming a StreamMessage message to an ActiveMQStreamMessage",ActiveMQMessageTransformation.transformMessage((StreamMessage)new ActiveMQStreamMessage(), null) instanceof ActiveMQStreamMessage); + + assertTrue("Transforming a TextMessage message to an ActiveMQTextMessage",ActiveMQMessageTransformation.transformMessage((TextMessage)new ActiveMQTextMessage(), null) instanceof ActiveMQTextMessage); + + assertTrue("Transforming an ActiveMQMessage message to an ActiveMQMessage",ActiveMQMessageTransformation.transformMessage(new ActiveMQMessage(), null) instanceof ActiveMQMessage); + } +} diff --git a/activemq-core/src/test/java/org/activemq/RedeliveryPolicyTest.java b/activemq-core/src/test/java/org/activemq/RedeliveryPolicyTest.java new file mode 100644 index 0000000000..c22bb16da8 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/RedeliveryPolicyTest.java @@ -0,0 +1,193 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import junit.framework.Test; + +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.RedeliveryPolicy; + +/** + * Test cases used to test the JMS message exclusive consumers. + * + * @version $Revision$ + */ +public class RedeliveryPolicyTest extends JmsTestSupport { + + public static Test suite() { + return suite(RedeliveryPolicyTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + /** + * @throws Throwable + */ + public void testExponentialRedeliveryPolicyDelaysDeliveryOnRollback() throws Throwable { + + // Receive a message with the JMS API + RedeliveryPolicy policy = connection.getRedeliveryPolicy(); + policy.setInitialRedeliveryDelay(500); + policy.setBackOffMultiplier((short) 2); + policy.setUseExponentialBackOff(true); + + connection.start(); + Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE); + ActiveMQQueue destination = new ActiveMQQueue("TEST"); + MessageProducer producer = session.createProducer(destination); + + MessageConsumer consumer = session.createConsumer(destination); + + // Send the messages + producer.send(session.createTextMessage("1st")); + producer.send(session.createTextMessage("2nd")); + session.commit(); + + TextMessage m; + m = (TextMessage)consumer.receive(1000); + assertNotNull(m); + assertEquals("1st", m.getText()); + session.rollback(); + + // Show re-delivery delay is incrementing. + m = (TextMessage)consumer.receive(100); + assertNull(m); + m = (TextMessage)consumer.receive(500); + assertNotNull(m); + assertEquals("1st", m.getText()); + session.rollback(); + + // Show re-delivery delay is incrementing exponentially + m = (TextMessage)consumer.receive(100); + assertNull(m); + m = (TextMessage)consumer.receive(500); + assertNull(m); + m = (TextMessage)consumer.receive(500); + assertNotNull(m); + assertEquals("1st", m.getText()); + + } + + + /** + * @throws Throwable + */ + public void testNornalRedeliveryPolicyDelaysDeliveryOnRollback() throws Throwable { + + // Receive a message with the JMS API + RedeliveryPolicy policy = connection.getRedeliveryPolicy(); + policy.setInitialRedeliveryDelay(500); + + connection.start(); + Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE); + ActiveMQQueue destination = new ActiveMQQueue("TEST"); + MessageProducer producer = session.createProducer(destination); + + MessageConsumer consumer = session.createConsumer(destination); + + // Send the messages + producer.send(session.createTextMessage("1st")); + producer.send(session.createTextMessage("2nd")); + session.commit(); + + TextMessage m; + m = (TextMessage)consumer.receive(1000); + assertNotNull(m); + assertEquals("1st", m.getText()); + session.rollback(); + + // Show re-delivery delay is incrementing. + m = (TextMessage)consumer.receive(100); + assertNull(m); + m = (TextMessage)consumer.receive(500); + assertNotNull(m); + assertEquals("1st", m.getText()); + session.rollback(); + + // The message gets redelivered after 500 ms every time since + // we are not using exponential backoff. + m = (TextMessage)consumer.receive(100); + assertNull(m); + m = (TextMessage)consumer.receive(500); + assertNotNull(m); + assertEquals("1st", m.getText()); + + } + + /** + * @throws Throwable + */ + public void testDLQHandling() throws Throwable { + + // Receive a message with the JMS API + RedeliveryPolicy policy = connection.getRedeliveryPolicy(); + policy.setInitialRedeliveryDelay(100); + policy.setUseExponentialBackOff(false); + policy.setMaximumRedeliveries(2); + + connection.start(); + Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE); + ActiveMQQueue destination = new ActiveMQQueue("TEST"); + MessageProducer producer = session.createProducer(destination); + + MessageConsumer consumer = session.createConsumer(destination); + MessageConsumer dlqConsumer = session.createConsumer(new ActiveMQQueue("ActiveMQ.DLQ")); + + // Send the messages + producer.send(session.createTextMessage("1st")); + producer.send(session.createTextMessage("2nd")); + session.commit(); + + TextMessage m; + m = (TextMessage)consumer.receive(1000); + assertNotNull(m); + assertEquals("1st", m.getText()); + session.rollback(); + + m = (TextMessage)consumer.receive(1000); + assertNotNull(m); + assertEquals("1st", m.getText()); + session.rollback(); + + m = (TextMessage)consumer.receive(1000); + assertNotNull(m); + assertEquals("1st", m.getText()); + session.rollback(); + + // The last rollback should cause the 1st message to get sent to the DLQ + m = (TextMessage)consumer.receive(1000); + assertNotNull(m); + assertEquals("2nd", m.getText()); + session.commit(); + + // We should be able to get the message off the DLQ now. + m = (TextMessage)dlqConsumer.receive(1000); + assertNotNull(m); + assertEquals("1st", m.getText()); + session.commit(); + + } +} diff --git a/activemq-core/src/test/java/org/activemq/SpringTestSupport.java b/activemq-core/src/test/java/org/activemq/SpringTestSupport.java new file mode 100755 index 0000000000..9eacf35a33 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/SpringTestSupport.java @@ -0,0 +1,69 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.support.AbstractApplicationContext; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import junit.framework.TestCase; + +/** + * A useful base class for spring based unit test cases + * + * @version $Revision: 1.1 $ + */ +public abstract class SpringTestSupport extends TestCase { + + protected final Log log = LogFactory.getLog(getClass()); + + protected AbstractApplicationContext context; + + protected void setUp() throws Exception { + context = createApplicationContext(); + } + + protected abstract AbstractApplicationContext createApplicationContext();; + + protected void tearDown() throws Exception { + if (context != null) { + context.destroy(); + } + } + + protected Object getBean(String name) { + Object bean = context.getBean(name); + if (bean == null) { + fail("Should have found bean named '" + name + "' in the Spring ApplicationContext"); + } + return bean; + } + + protected void assertSetEquals(String description, Object[] expected, Set actual) { + Set expectedSet = new HashSet(); + expectedSet.addAll(Arrays.asList(expected)); + assertEquals(description, expectedSet, actual); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/TestSupport.java b/activemq-core/src/test/java/org/activemq/TestSupport.java new file mode 100755 index 0000000000..d9a58b8c97 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/TestSupport.java @@ -0,0 +1,135 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq; + +import java.io.File; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.TextMessage; + +import junit.framework.TestCase; + +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * Useful base class for unit test cases + * + * @version $Revision: 1.5 $ + */ +public class TestSupport extends TestCase { + + protected Log log = LogFactory.getLog(getClass()); + protected ActiveMQConnectionFactory connectionFactory; + protected boolean topic = true; + + public TestSupport() { + super(); + } + + public TestSupport(String name) { + super(name); + } + + protected ActiveMQMessage createMessage() { + return new ActiveMQMessage(); + } + + protected Destination createDestination(String subject) { + if (topic) { + return new ActiveMQTopic(subject); + } + else { + return new ActiveMQQueue(subject); + } + } + + /** + * @param messsage + * @param firstSet + * @param secondSet + */ + protected void assertTextMessagesEqual(String messsage, Message[] firstSet, Message[] secondSet) throws JMSException { + assertEquals("Message count does not match: " + messsage, firstSet.length, secondSet.length); + for (int i = 0; i < secondSet.length; i++) { + TextMessage m1 = (TextMessage) firstSet[i]; + TextMessage m2 = (TextMessage) secondSet[i]; + assertFalse("Message " + (i + 1) + " did not match : " + messsage + ": expected {" + m1 + "}, but was {" + m2 + "}", m1 == null ^ m2 == null); + assertEquals("Message " + (i + 1) + " did not match: " + messsage + ": expected {" + m1 + "}, but was {" + m2 + "}", m1.getText(), m2.getText()); + } + } + + protected ActiveMQConnectionFactory createConnectionFactory() throws Exception { + return new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false"); + } + + /** + * Factory method to create a new connection + */ + protected Connection createConnection() throws Exception { + return getConnectionFactory().createConnection(); + } + + public ActiveMQConnectionFactory getConnectionFactory() throws Exception { + if (connectionFactory == null) { + connectionFactory = createConnectionFactory(); + assertTrue("Should have created a connection factory!", connectionFactory != null); + } + return connectionFactory; + } + + protected String getConsumerSubject() { + return getSubject(); + } + + protected String getProducerSubject() { + return getSubject(); + } + + protected String getSubject() { + return getClass().getName() + "." + getName(); + } + + + public static void recursiveDelete(File f) { + if( f.isDirectory() ) { + File[] files = f.listFiles(); + for (int i = 0; i < files.length; i++) { + recursiveDelete(files[i]); + } + } + f.delete(); + } + + public static void removeMessageStore() { + if( System.getProperty("activemq.store.dir")!=null ) { + recursiveDelete(new File(System.getProperty("activemq.store.dir"))); + } + if( System.getProperty("derby.system.home")!=null ) { + recursiveDelete(new File(System.getProperty("derby.system.home"))); + } + } +} diff --git a/activemq-core/src/test/java/org/activemq/broker/BrokerBenchmark.java b/activemq-core/src/test/java/org/activemq/broker/BrokerBenchmark.java new file mode 100755 index 0000000000..02d22752f6 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/BrokerBenchmark.java @@ -0,0 +1,210 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + + +import junit.framework.Test; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.ProducerInfo; +import org.activemq.command.SessionInfo; + +import edu.emory.mathcs.backport.java.util.concurrent.Semaphore; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +/** + * BrokerBenchmark is used to get an idea of the raw performance of a broker. Since + * the broker data structures using in message dispatching are under high contention from + * client requests, it's performance should be monitored closely since it typically is the + * biggest bottleneck in a high performance messaging fabric. + * + * The benchmarks are run under all the following combinations options: + * + * Queue vs. Topic, 1 vs. 10 producer threads, 1 vs. 10 consumer threads, and + * Persistent vs. Non-Persistent messages. + * + * Message Acking uses client ack style batch acking since that typically has the + * best ack performance. + * + * @version $Revision: 1.9 $ + */ +public class BrokerBenchmark extends BrokerTestSupport { + + public int PRODUCE_COUNT=Integer.parseInt(System.getProperty("PRODUCE_COUNT","10000")); + public ActiveMQDestination destination; + public int PRODUCER_COUNT; + public int CONSUMER_COUNT; + public boolean deliveryMode; + + public void initCombosForTestPerformance() { + addCombinationValues("destination", new Object[]{ + new ActiveMQQueue("TEST"), + new ActiveMQTopic("TEST") + }); + addCombinationValues("PRODUCER_COUNT", new Object[]{ + new Integer("1"), + new Integer("10")}); + addCombinationValues("CONSUMER_COUNT", new Object[]{ + new Integer("1"), + new Integer("10")}); + addCombinationValues("CONSUMER_COUNT", new Object[]{ + new Integer("1"), + new Integer("10")}); + addCombinationValues( "deliveryMode", new Object[]{ +// Boolean.FALSE, + Boolean.TRUE + } ); + } + + public void testPerformance() throws Throwable { + + System.out.println("Running Benchmark for destination="+destination+", producers="+PRODUCER_COUNT+", consumers="+CONSUMER_COUNT+", deliveryMode="+deliveryMode); + final int CONSUME_COUNT = destination.isTopic() ? CONSUMER_COUNT*PRODUCE_COUNT : PRODUCE_COUNT; + + final Semaphore consumersStarted = new Semaphore(1-(CONSUMER_COUNT)); + final Semaphore producersFinished = new Semaphore(1-(PRODUCER_COUNT)); + final Semaphore consumersFinished = new Semaphore(1-(CONSUMER_COUNT)); + final ProgressPrinter printer = new ProgressPrinter(PRODUCE_COUNT+CONSUME_COUNT, 10); + + // Start a producer and consumer + + profilerPause("Benchmark ready. Start profiler "); + + long start = System.currentTimeMillis(); + + + final AtomicInteger receiveCounter = new AtomicInteger(0); + for( int i=0; i < CONSUMER_COUNT; i++) { + new Thread() { + public void run() { + try { + + // Consume the messages + StubConnection connection = new StubConnection(broker); + ConnectionInfo connectionInfo = createConnectionInfo(); + connection.send(connectionInfo); + + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + consumerInfo.setPrefetchSize(1000); + connection.send(sessionInfo); + connection.send(consumerInfo); + + consumersStarted.release(); + + while( receiveCounter.get() < CONSUME_COUNT ) { + + int counter=0; + // Get a least 1 message. + Message msg = receiveMessage(connection, 2000); + if( msg!=null ) { + printer.increment(); + receiveCounter.incrementAndGet(); + + counter++; + + // Try to piggy back a few extra message acks if they are ready. + Message extra=null; + while( (extra = receiveMessage(connection,0))!=null ) { + msg=extra; + printer.increment(); + receiveCounter.incrementAndGet(); + counter++; + } + } + + + if(msg!=null) { + connection.send(createAck(consumerInfo, msg, counter, MessageAck.STANDARD_ACK_TYPE)); + } else if ( receiveCounter.get() < CONSUME_COUNT ) { + System.out.println("Consumer stall, waiting for message #"+receiveCounter.get()+1); + } + } + + connection.send(closeConsumerInfo(consumerInfo)); + } catch (Throwable e) { + e.printStackTrace(); + } finally { + consumersFinished.release(); + } + } + + }.start(); + } + + // Make sure that the consumers are started first to avoid sending messages + // before a topic is subscribed so that those messages are not missed. + consumersStarted.acquire(); + + // Send the messages in an async thread. + for( int i=0; i < PRODUCER_COUNT; i++) { + new Thread() { + public void run() { + try { + StubConnection connection = new StubConnection(broker); + ConnectionInfo connectionInfo = createConnectionInfo(); + connection.send(connectionInfo); + + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + for(int i=0; i < PRODUCE_COUNT/PRODUCER_COUNT; i++) { + Message message = createMessage(producerInfo, destination); + message.setPersistent(deliveryMode); + message.setResponseRequired(false); + connection.send(message); + printer.increment(); + } + } catch (Throwable e) { + e.printStackTrace(); + } finally { + producersFinished.release(); + } + }; + }.start(); + } + + producersFinished.acquire(); + long end1 = System.currentTimeMillis(); + consumersFinished.acquire(); + long end2 = System.currentTimeMillis(); + + System.out.println("Results for destination="+destination+", producers="+PRODUCER_COUNT+", consumers="+CONSUMER_COUNT+", deliveryMode="+deliveryMode); + System.out.println("Produced at messages/sec: "+ (PRODUCE_COUNT*1000.0/(end1-start))); + System.out.println("Consumed at messages/sec: "+ (CONSUME_COUNT*1000.0/(end2-start))); + profilerPause("Benchmark done. Stop profiler "); + } + + public static Test suite() { + return suite(BrokerBenchmark.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/broker/BrokerRestartTestSupport.java b/activemq-core/src/test/java/org/activemq/broker/BrokerRestartTestSupport.java new file mode 100644 index 0000000000..9aa8776757 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/BrokerRestartTestSupport.java @@ -0,0 +1,43 @@ +package org.activemq.broker; + +import java.io.IOException; +import java.net.URISyntaxException; + +import org.activemq.store.PersistenceAdapter; + +public class BrokerRestartTestSupport extends BrokerTestSupport { + + private PersistenceAdapter persistenceAdapter; + + protected BrokerService createBroker() throws Exception { + BrokerService broker = new BrokerService(); + broker.setPersistent(false); + persistenceAdapter = broker.getPersistenceAdapter(); + return broker; + } + + /** + * @return + * @throws Exception + */ + protected BrokerService createRestartedBroker() throws Exception { + BrokerService broker = new BrokerService(); + broker.setPersistenceAdapter(persistenceAdapter); + return broker; + } + + /** + * Simulates a broker restart. The memory based persistence adapter is + * reused so that it does not "loose" it's "persistent" messages. + * + * @throws IOException + * @throws URISyntaxException + */ + protected void restartBroker() throws Exception { + broker.stop(); + broker = createRestartedBroker(); + broker.start(); + } + + +} diff --git a/activemq-core/src/test/java/org/activemq/broker/BrokerTest.java b/activemq-core/src/test/java/org/activemq/broker/BrokerTest.java new file mode 100755 index 0000000000..6ab3545836 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/BrokerTest.java @@ -0,0 +1,1664 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import javax.jms.DeliveryMode; +import javax.jms.JMSException; + +import junit.framework.Test; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.DestinationInfo; +import org.activemq.command.LocalTransactionId; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.ProducerInfo; +import org.activemq.command.SessionInfo; + +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +public class BrokerTest extends BrokerTestSupport { + + public ActiveMQDestination destination; + public int deliveryMode; + public int prefetch; + public byte destinationType; + public boolean durableConsumer; + + public void initCombosForTestConsumerPrefetchAndStandardAck() { + addCombinationValues( "deliveryMode", new Object[]{ +// new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE) + } ); + } + + public void testConsumerPrefetchAndStandardAck() throws Throwable { + + // Start a producer and consumer + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + destination = createDestinationInfo(connection, connectionInfo, destinationType); + + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + consumerInfo.setPrefetchSize(1); + connection.send(consumerInfo); + + // Send 3 messages to the broker. + connection.send(createMessage(producerInfo, destination, deliveryMode)); + connection.send(createMessage(producerInfo, destination, deliveryMode)); + connection.send(createMessage(producerInfo, destination, deliveryMode)); + + // Make sure only 1 message was delivered. + Message m1 = receiveMessage(connection); + assertNotNull(m1); + assertNoMessagesLeft(connection); + + // Acknowledge the first message. This should cause the next message to get dispatched. + connection.send(createAck(consumerInfo, m1, 1, MessageAck.STANDARD_ACK_TYPE)); + + Message m2 = receiveMessage(connection); + assertNotNull(m2); + connection.send(createAck(consumerInfo, m2, 1, MessageAck.STANDARD_ACK_TYPE)); + + Message m3 = receiveMessage(connection); + assertNotNull(m3); + connection.send(createAck(consumerInfo, m3, 1, MessageAck.STANDARD_ACK_TYPE)); + + connection.send(closeConnectionInfo(connectionInfo)); + } + + + public void initCombosForTestTransactedAckWithPrefetchOfOne() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE) + } ); + } + + public void testTransactedAckWithPrefetchOfOne() throws Throwable { + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + destination = createDestinationInfo(connection1, connectionInfo1, destinationType); + + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(1); + connection1.send(consumerInfo1); + + // Send the messages + for( int i=0; i < 4 ; i++ ) { + Message message = createMessage(producerInfo1, destination, deliveryMode); + connection1.send(message); + } + + // Begin the transaction. + LocalTransactionId txid = createLocalTransaction(sessionInfo1); + connection1.send(createBeginTransaction(connectionInfo1, txid)); + + // Now get the messages. + for( int i=0; i < 4 ; i++ ) { + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + MessageAck ack = createAck(consumerInfo1, m1, 1, MessageAck.STANDARD_ACK_TYPE); + ack.setTransactionId(txid); + connection1.send(ack); + } + + // Commit the transaction. + connection1.send(createCommitTransaction1Phase(connectionInfo1, txid)); + + assertNoMessagesLeft(connection1); + } + + public void initCombosForTestTransactedSend() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE)} ); + } + public void testTransactedSend() throws Throwable { + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + destination = createDestinationInfo(connection1, connectionInfo1, destinationType); + + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + connection1.send(consumerInfo1); + + // Begin the transaction. + LocalTransactionId txid = createLocalTransaction(sessionInfo1); + connection1.send(createBeginTransaction(connectionInfo1, txid)); + + // Send the messages + for( int i=0; i < 4 ; i++ ) { + Message message = createMessage(producerInfo1, destination, deliveryMode); + message.setTransactionId(txid); + connection1.send(message); + } + + // The point of this test is that message should not be delivered until + // send is committed. + assertNull(receiveMessage(connection1)); + + // Commit the transaction. + connection1.send(createCommitTransaction1Phase(connectionInfo1, txid)); + + // Now get the messages. + for( int i=0; i < 4 ; i++ ) { + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + } + + assertNoMessagesLeft(connection1); + } + + public void initCombosForTestQueueTransactedAck() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + } ); + } + + public void testQueueTransactedAck() throws Throwable { + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + destination = createDestinationInfo(connection1, connectionInfo1, destinationType); + + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + connection1.send(consumerInfo1); + + // Send the messages + for( int i=0; i < 4 ; i++ ) { + Message message = createMessage(producerInfo1, destination, deliveryMode); + connection1.send(message); + } + + // Begin the transaction. + LocalTransactionId txid = createLocalTransaction(sessionInfo1); + connection1.send(createBeginTransaction(connectionInfo1, txid)); + + // Acknowledge the first 2 messages. + for( int i=0; i < 2 ; i++ ) { + Message m1 = receiveMessage(connection1); + assertNotNull("m1 is null for index: " + i, m1); + MessageAck ack = createAck(consumerInfo1, m1, 1, MessageAck.STANDARD_ACK_TYPE); + ack.setTransactionId(txid); + connection1.request(ack); + } + + // Commit the transaction. + connection1.send(createCommitTransaction1Phase(connectionInfo1, txid)); + + // The queue should now only have the remaining 2 messages + assertEquals(2, countMessagesInQueue(connection1, connectionInfo1, destination)); + } + + public void initCombosForTestConsumerCloseCausesRedelivery() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destination", new Object[]{ + new ActiveMQQueue("TEST")} ); + } + + public void testConsumerCloseCausesRedelivery() throws Throwable { + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + connection1.request(consumerInfo1); + + // Send the messages + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + + // Receive the messages. + for( int i=0; i < 4 ; i++ ) { + Message m1 = receiveMessage(connection1); + assertNotNull("m1 is null for index: " + i, m1); + assertFalse(m1.isRedelivered()); + } + + // Close the consumer without acking.. this should cause re-delivery of the messages. + connection1.send(consumerInfo1.createRemoveCommand()); + + // Create another consumer that should get the messages again. + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo1, destination); + consumerInfo2.setPrefetchSize(100); + connection1.request(consumerInfo2); + + // Receive the messages. + for( int i=0; i < 4 ; i++ ) { + Message m1 = receiveMessage(connection1); + assertNotNull("m1 is null for index: " + i, m1); + assertTrue(m1.isRedelivered()); + } + assertNoMessagesLeft(connection1); + + } + + public void testTopicDurableSubscriptionCanBeRestored() throws Throwable { + + ActiveMQDestination destination = new ActiveMQTopic("TEST"); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + connectionInfo1.setClientId("clientid1"); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + consumerInfo1.setSubcriptionName("test"); + connection1.send(consumerInfo1); + + // Send the messages + connection1.send(createMessage(producerInfo1, destination, DeliveryMode.PERSISTENT)); + connection1.send(createMessage(producerInfo1, destination, DeliveryMode.PERSISTENT)); + connection1.send(createMessage(producerInfo1, destination, DeliveryMode.PERSISTENT)); + connection1.send(createMessage(producerInfo1, destination, DeliveryMode.PERSISTENT)); + + // Get the messages + Message m=null; + for( int i=0; i < 2 ; i++ ) { + m = receiveMessage(connection1); + assertNotNull(m); + } + // Ack the last message. + connection1.send(createAck(consumerInfo1, m, 2, MessageAck.STANDARD_ACK_TYPE)); + // Close the connection. + connection1.send(closeConnectionInfo(connectionInfo1)); + connection1.stop(); + + // Setup a second connection + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + connectionInfo2.setClientId("clientid1"); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + consumerInfo2.setPrefetchSize(100); + consumerInfo2.setSubcriptionName("test"); + + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.send(consumerInfo2); + + // Get the rest of the messages + for( int i=0; i < 2 ; i++ ) { + Message m1 = receiveMessage(connection2); + assertNotNull("m1 is null for index: " + i, m1); + } + assertNoMessagesLeft(connection2); + } + + + public void initCombosForTestGroupedMessagesDeliveredToOnlyOneConsumer() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + } + public void testGroupedMessagesDeliveredToOnlyOneConsumer() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo); + + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(1); + connection1.send(consumerInfo1); + + // Send the messages. + for( int i=0; i < 4 ; i++ ) { + Message message = createMessage(producerInfo, destination, deliveryMode); + message.setGroupID("TEST-GROUP"); + message.setGroupSequence(i+1); + connection1.request(message); + } + + // Setup a second connection + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + consumerInfo2.setPrefetchSize(1); + connection2.send(consumerInfo2); + + // All the messages should have been sent down connection 1.. just get the first 3 + for( int i=0; i < 3 ; i++ ) { + Message m1 = receiveMessage(connection1); + assertNotNull("m1 is null for index: " + i, m1); + connection1.send(createAck(consumerInfo1, m1, 1, MessageAck.STANDARD_ACK_TYPE)); + } + + // Close the first consumer. + connection1.send(closeConsumerInfo(consumerInfo1)); + + // The last messages should now go the the second consumer. + for( int i=0; i < 1 ; i++ ) { + Message m1 = receiveMessage(connection2); + assertNotNull("m1 is null for index: " + i, m1); + connection2.send(createAck(consumerInfo2, m1, 1, MessageAck.STANDARD_ACK_TYPE)); + } + + assertNoMessagesLeft(connection2); + } + + public void initCombosForTestTopicConsumerOnlySeeMessagesAfterCreation() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "durableConsumer", new Object[]{ + Boolean.TRUE, + Boolean.FALSE}); + } + + public void testTopicConsumerOnlySeeMessagesAfterCreation() throws Throwable { + + ActiveMQDestination destination = new ActiveMQTopic("TEST"); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + connectionInfo1.setClientId("A"); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + // Send the 1st message + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + + // Create the durable subscription. + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + if( durableConsumer ) { + consumerInfo1.setSubcriptionName("test"); + } + consumerInfo1.setPrefetchSize(100); + connection1.send(consumerInfo1); + + Message m = createMessage(producerInfo1, destination, deliveryMode); + connection1.send(m); + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + + // Subscription should skip over the first message + Message m2 = receiveMessage(connection1); + assertNotNull(m2); + assertEquals(m.getMessageId(), m2.getMessageId()); + m2 = receiveMessage(connection1); + assertNotNull(m2); + + assertNoMessagesLeft(connection1); + } + + public void initCombosForTestTopicRetroactiveConsumerSeeMessagesBeforeCreation() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "durableConsumer", new Object[]{ + Boolean.TRUE, + Boolean.FALSE}); + } + + public void testTopicRetroactiveConsumerSeeMessagesBeforeCreation() throws Throwable { + + ActiveMQDestination destination = new ActiveMQTopic("TEST"); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + connectionInfo1.setClientId("A"); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + // Send the messages + Message m = createMessage(producerInfo1, destination, deliveryMode); + connection1.send(m); + + // Create the durable subscription. + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + if( durableConsumer ) { + consumerInfo1.setSubcriptionName("test"); + } + consumerInfo1.setPrefetchSize(100); + consumerInfo1.setRetroactive(true); + connection1.send(consumerInfo1); + + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + + if( deliveryMode == DeliveryMode.NON_PERSISTENT && durableConsumer ) { + // Durable subs don't keep non persistent messages around! + for( int i=0; i < 2 ; i++ ) { + Message m2 = receiveMessage(connection1); + assertNotNull(m2); + } + + } else { + + // Subscription should see all messages sent. + Message m2 = receiveMessage(connection1); + assertNotNull(m2); + assertEquals(m.getMessageId(), m2.getMessageId()); + for( int i=0; i < 2 ; i++ ) { + m2 = receiveMessage(connection1); + assertNotNull(m2); + } + + } + + assertNoMessagesLeft(connection1); + } + + public void initCombosForTestTempDestinationsRemovedOnConnectionClose() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE)} ); + } + + public void testTempDestinationsRemovedOnConnectionClose() throws Throwable { + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + destination = createDestinationInfo(connection1, connectionInfo1, destinationType); + + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ProducerInfo producerInfo2 = createProducerInfo(sessionInfo2); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.send(producerInfo2); + + // Send from connection2 to connection1's temp destination. Should succeed. + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + + // Close connection 1 + connection1.request(closeConnectionInfo(connectionInfo1)); + + try { + // Send from connection2 to connection1's temp destination. Should not succeed. + connection2.request(createMessage(producerInfo2, destination, deliveryMode)); + fail("Expected JMSException."); + } catch ( JMSException success ) { + } + + } + + public void initCombosForTestTempDestinationsAreNotAutoCreated() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE)} ); + } + + public void testTempDestinationsAreNotAutoCreated() throws Throwable { + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + destination = ActiveMQDestination.createDestination(connectionInfo1.getConnectionId()+":1", destinationType); + + // Should not be able to send to a non-existant temp destination. + try { + connection1.request(createMessage(producerInfo1, destination, deliveryMode)); + fail("Expected JMSException."); + } catch ( JMSException success ) { + } + + } + + public void initCombosForTestTempDestinationsOnlyAllowsLocalConsumers() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE)} ); + } + + public void testTempDestinationsOnlyAllowsLocalConsumers() throws Throwable { + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + + DestinationInfo destinationInfo = createTempDestinationInfo(connectionInfo1, destinationType); + connection1.request(destinationInfo); + destination = destinationInfo.getDestination(); + + // Setup a second connection + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + + // Only consumers local to the temp destination should be allowed to subscribe. + try { + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + connection2.request(consumerInfo2); + fail("Expected JMSException."); + } catch ( JMSException success ) { + } + + // This should succeed since it's local. + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + connection1.send(consumerInfo1); + } + + public void initCombosForTestExclusiveQueueDeliversToOnlyOneConsumer() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + } + public void testExclusiveQueueDeliversToOnlyOneConsumer() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo); + + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(1); + consumerInfo1.setExclusive(true); + connection1.send(consumerInfo1); + + // Send a message.. this should make consumer 1 the exclusive owner. + connection1.request(createMessage(producerInfo, destination, deliveryMode)); + + // Setup a second connection + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + consumerInfo2.setPrefetchSize(1); + consumerInfo2.setExclusive(true); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.request(consumerInfo2); + + // Second message should go to consumer 1 even though consumer 2 is ready + // for dispatch. + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + + // Acknowledge the first 2 messages + for( int i=0; i < 2 ; i++ ) { + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + connection1.send(createAck(consumerInfo1, m1, 1, MessageAck.STANDARD_ACK_TYPE)); + } + + // Close the first consumer. + connection1.send(closeConsumerInfo(consumerInfo1)); + + // The last two messages should now go the the second consumer. + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + + for( int i=0; i < 2 ; i++ ) { + Message m1 = receiveMessage(connection2); + assertNotNull(m1); + connection2.send(createAck(consumerInfo2, m1, 1, MessageAck.STANDARD_ACK_TYPE)); + } + + assertNoMessagesLeft(connection2); + } + + public void initCombosForTestWildcardConsume() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE)} ); + } + + public void testWildcardConsume() throws Throwable { + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + // setup the wildcard consumer. + ActiveMQDestination compositeDestination = ActiveMQDestination.createDestination("WILD.*.TEST", destinationType); + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, compositeDestination); + consumerInfo1.setPrefetchSize(100); + connection1.send(consumerInfo1); + + // These two message should NOT match the wild card. + connection1.send(createMessage(producerInfo1, ActiveMQDestination.createDestination("WILD.CARD", destinationType), deliveryMode)); + connection1.send(createMessage(producerInfo1, ActiveMQDestination.createDestination("WILD.TEST", destinationType), deliveryMode)); + + // These two message should match the wild card. + ActiveMQDestination d1 = ActiveMQDestination.createDestination("WILD.CARD.TEST", destinationType); + connection1.send(createMessage(producerInfo1, d1, deliveryMode)); + ActiveMQDestination d2 = ActiveMQDestination.createDestination("WILD.FOO.TEST", destinationType); + connection1.send(createMessage(producerInfo1, d2, deliveryMode)); + + Message m = receiveMessage(connection1); + assertNotNull(m); + assertEquals(d1,m.getDestination()); + m = receiveMessage(connection1); + assertNotNull(m); + assertEquals(d2,m.getDestination()); + + assertNoMessagesLeft(connection1); + connection1.send(closeConnectionInfo(connectionInfo1)); + } + + public void initCombosForTestCompositeConsume() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE)} ); + } + + public void testCompositeConsume() throws Throwable { + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + // setup the composite consumer. + ActiveMQDestination compositeDestination = ActiveMQDestination.createDestination("A,B", destinationType); + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, compositeDestination); + consumerInfo1.setPrefetchSize(100); + connection1.send(consumerInfo1); + + // Publish to the two destinations + ActiveMQDestination destinationA = ActiveMQDestination.createDestination("A", destinationType); + ActiveMQDestination destinationB = ActiveMQDestination.createDestination("B", destinationType); + + // Send a message to each destination . + connection1.send(createMessage(producerInfo1, destinationA, deliveryMode)); + connection1.send(createMessage(producerInfo1, destinationB, deliveryMode)); + + // The consumer should get both messages. + for( int i=0; i < 2 ; i++ ) { + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + } + + assertNoMessagesLeft(connection1); + connection1.send(closeConnectionInfo(connectionInfo1)); + } + + public void initCombosForTestCompositeSend() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE)} ); + } + + public void testCompositeSend() throws Throwable { + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + ActiveMQDestination destinationA = ActiveMQDestination.createDestination("A", destinationType); + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destinationA); + consumerInfo1.setPrefetchSize(100); + connection1.send(consumerInfo1); + + // Setup a second connection + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + + ActiveMQDestination destinationB = ActiveMQDestination.createDestination("B", destinationType); + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destinationB); + consumerInfo2.setPrefetchSize(100); + connection2.send(consumerInfo2); + + // Send the messages to the composite destination. + ActiveMQDestination compositeDestination = ActiveMQDestination.createDestination("A,B", destinationType); + for( int i=0; i < 4 ; i++ ) { + connection1.send(createMessage(producerInfo1, compositeDestination, deliveryMode)); + } + + // The messages should have been delivered to both the A and B destination. + for( int i=0; i < 4 ; i++ ) { + Message m1 = receiveMessage(connection1); + Message m2 = receiveMessage(connection2); + + assertNotNull(m1); + assertNotNull(m2); + + assertEquals( m1.getMessageId(), m2.getMessageId() ); + assertEquals( compositeDestination, m1.getOriginalDestination()); + assertEquals( compositeDestination, m2.getOriginalDestination()); + + connection1.send(createAck(consumerInfo1, m1, 1, MessageAck.STANDARD_ACK_TYPE)); + connection2.send(createAck(consumerInfo2, m2, 1, MessageAck.STANDARD_ACK_TYPE)); + + } + + assertNoMessagesLeft(connection1); + assertNoMessagesLeft(connection2); + + connection1.send(closeConnectionInfo(connectionInfo1)); + connection2.send(closeConnectionInfo(connectionInfo2)); + } + + public void initCombosForTestConnectionCloseCascades() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destination", new Object[]{ + new ActiveMQTopic("TEST"), + new ActiveMQQueue("TEST")} ); + } + + public void testConnectionCloseCascades() throws Throwable { + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + consumerInfo1.setNoLocal(true); + connection1.request(consumerInfo1); + + // Setup a second connection + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ProducerInfo producerInfo2 = createProducerInfo(sessionInfo2); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.send(producerInfo2); + + // Send the messages + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + + for( int i=0; i < 4 ; i++ ) { + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + connection1.send(createAck(consumerInfo1, m1, 1, MessageAck.STANDARD_ACK_TYPE)); + } + + // Close the connection, this should in turn close the consumer. + connection1.request(closeConnectionInfo(connectionInfo1)); + + // Send another message, connection1 should not get the message. + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + + assertNull(connection1.getDispatchQueue().poll(MAX_WAIT, TimeUnit.MILLISECONDS)); + } + + public void initCombosForTestSessionCloseCascades() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destination", new Object[]{ + new ActiveMQTopic("TEST"), + new ActiveMQQueue("TEST")} ); + } + + public void testSessionCloseCascades() throws Throwable { + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + consumerInfo1.setNoLocal(true); + connection1.request(consumerInfo1); + + // Setup a second connection + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ProducerInfo producerInfo2 = createProducerInfo(sessionInfo2); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.send(producerInfo2); + + // Send the messages + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + + for( int i=0; i < 4 ; i++ ) { + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + connection1.send(createAck(consumerInfo1, m1, 1, MessageAck.STANDARD_ACK_TYPE)); + } + + // Close the session, this should in turn close the consumer. + connection1.request(closeSessionInfo(sessionInfo1)); + + // Send another message, connection1 should not get the message. + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + + assertNull(connection1.getDispatchQueue().poll(MAX_WAIT, TimeUnit.MILLISECONDS)); + } + + public void initCombosForTestConsumerClose() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destination", new Object[]{ + new ActiveMQTopic("TEST"), + new ActiveMQQueue("TEST")} ); + } + + public void testConsumerClose() throws Throwable { + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + consumerInfo1.setNoLocal(true); + connection1.request(consumerInfo1); + + // Setup a second connection + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ProducerInfo producerInfo2 = createProducerInfo(sessionInfo2); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.send(producerInfo2); + + // Send the messages + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + + for( int i=0; i < 4 ; i++ ) { + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + connection1.send(createAck(consumerInfo1, m1, 1, MessageAck.STANDARD_ACK_TYPE)); + } + + // Close the consumer. + connection1.request(closeConsumerInfo(consumerInfo1)); + + // Send another message, connection1 should not get the message. + connection2.send(createMessage(producerInfo2, destination, deliveryMode)); + + assertNull(connection1.getDispatchQueue().poll(MAX_WAIT, TimeUnit.MILLISECONDS)); + } + public void initCombosForTestTopicNoLocal() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + } + + public void testTopicNoLocal() throws Throwable { + + ActiveMQDestination destination = new ActiveMQTopic("TEST"); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + consumerInfo1.setNoLocal(true); + connection1.send(consumerInfo1); + + // Setup a second connection + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ProducerInfo producerInfo2 = createProducerInfo(sessionInfo2); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.send(producerInfo2); + + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + consumerInfo2.setPrefetchSize(100); + consumerInfo2.setNoLocal(true); + connection2.send(consumerInfo2); + + // Send the messages + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + + // The 2nd connection should get the messages. + for( int i=0; i < 4 ; i++ ) { + Message m1 = receiveMessage(connection2); + assertNotNull(m1); + } + + // Send a message with the 2nd connection + Message message = createMessage(producerInfo2, destination, deliveryMode); + connection2.send(message); + + // The first connection should not see the initial 4 local messages sent but should + // see the messages from connection 2. + Message m = receiveMessage(connection1); + assertNotNull(m); + assertEquals(message.getMessageId(), m.getMessageId()); + + assertNoMessagesLeft(connection1); + assertNoMessagesLeft(connection2); + } + + + public void setUpTopicDispatchIsBroadcast() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + } + + public void testTopicDispatchIsBroadcast() throws Throwable { + + ActiveMQDestination destination = new ActiveMQTopic("TEST"); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + connection1.send(consumerInfo1); + + // Setup a second connection + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + consumerInfo2.setPrefetchSize(100); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.send(consumerInfo2); + + // Send the messages + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + connection1.send(createMessage(producerInfo1, destination, deliveryMode)); + + // Get the messages + for( int i=0; i < 4 ; i++ ) { + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + m1 = receiveMessage(connection2); + assertNotNull(m1); + } + } + + public void initCombosForTestQueueDispatchedAreRedeliveredOnConsumerClose() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + } ); + } + + public void testQueueDispatchedAreRedeliveredOnConsumerClose() throws Throwable { + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo); + + destination = createDestinationInfo(connection1, connectionInfo1, destinationType); + + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + connection1.send(consumerInfo1); + + // Send the messages + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + + // Get the messages + for( int i=0; i < 4 ; i++ ) { + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + assertFalse(m1.isRedelivered()); + } + // Close the consumer without sending any ACKS. + connection1.send(closeConsumerInfo(consumerInfo1)); + + // Drain any in flight messages.. + while(connection1.getDispatchQueue().poll(0, TimeUnit.MILLISECONDS)!=null){ + } + + // Add the second consumer + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo1, destination); + consumerInfo2.setPrefetchSize(100); + connection1.send(consumerInfo2); + + // Make sure the messages were re delivered to the 2nd consumer. + for( int i=0; i < 4 ; i++ ) { + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + assertTrue(m1.isRedelivered()); + } + } + + public void initCombosForTestQueueBrowseMessages() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + } ); + } + public void testQueueBrowseMessages() throws Throwable { + + // Start a producer and consumer + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + destination = createDestinationInfo(connection, connectionInfo, destinationType); + + connection.send(createMessage(producerInfo, destination, deliveryMode)); + connection.send(createMessage(producerInfo, destination, deliveryMode)); + connection.send(createMessage(producerInfo, destination, deliveryMode)); + connection.send(createMessage(producerInfo, destination, deliveryMode)); + + // Use selector to skip first message. + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + consumerInfo.setBrowser(true); + connection.send(consumerInfo); + + for( int i=0; i < 4; i++ ) { + Message m = receiveMessage(connection); + assertNotNull(m); + connection.send(createAck(consumerInfo, m, 1, MessageAck.DELIVERED_ACK_TYPE)); + } + + assertNoMessagesLeft(connection); + } + + public void initCombosForTestQueuBrowserWith2Consumers() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + } + + public void testQueueBrowserWith2Consumers() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo); + + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(1); + connection1.send(consumerInfo1); + + // Send the messages + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + + // Setup a second connection with a queue browser. + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + consumerInfo2.setPrefetchSize(1); + consumerInfo2.setBrowser(true); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.send(consumerInfo2); + + for( int i=0; i < 4; i++ ) { + Message m1 = receiveMessage(connection1); + Message m2 = receiveMessage(connection2); + assertNotNull("m1 is null for index: " + i, m1); + assertNotNull("m2 is null for index: " + i, m2); + assertEquals(m1.getMessageId(), m2.getMessageId()); + connection1.send(createAck(consumerInfo1, m1, 1, MessageAck.STANDARD_ACK_TYPE)); + connection2.send(createAck(consumerInfo2, m2, 1, MessageAck.DELIVERED_ACK_TYPE)); + } + + assertNoMessagesLeft(connection1); + assertNoMessagesLeft(connection2); + } + + public void initCombosForTestQueueOnlyOnceDeliveryWith2Consumers() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + } + public void testQueueOnlyOnceDeliveryWith2Consumers() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo); + + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(1); + connection1.send(consumerInfo1); + + // Setup a second connection + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + consumerInfo2.setPrefetchSize(1); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.send(consumerInfo2); + + // Send the messages + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + + for( int i=0; i < 2 ; i++ ) { + Message m1 = receiveMessage(connection1); + Message m2 = receiveMessage(connection2); + + assertNotNull("m1 is null for index: " + i, m1); + assertNotNull("m2 is null for index: " + i, m2); + + assertNotSame(m1.getMessageId(), m2.getMessageId()); + connection1.send(createAck(consumerInfo1, m1, 1, MessageAck.STANDARD_ACK_TYPE)); + connection2.send(createAck(consumerInfo2, m2, 1, MessageAck.STANDARD_ACK_TYPE)); + } + + assertNoMessagesLeft(connection1); + assertNoMessagesLeft(connection2); + } + + public void initCombosForTestQueueSendThenAddConsumer() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + } ); + } + public void testQueueSendThenAddConsumer() throws Throwable { + + // Start a producer + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + destination = createDestinationInfo(connection, connectionInfo, destinationType); + + // Send a message to the broker. + connection.send(createMessage(producerInfo, destination, deliveryMode)); + + // Start the consumer + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // Make sure the message was delivered. + Message m = receiveMessage(connection); + assertNotNull(m); + + } + + public void initCombosForTestQueueAckRemovesMessage() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + } ); + } + + public void testQueueAckRemovesMessage() throws Throwable { + + // Start a producer and consumer + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + destination = createDestinationInfo(connection, connectionInfo, destinationType); + + Message message1 = createMessage(producerInfo, destination, deliveryMode); + Message message2 = createMessage(producerInfo, destination, deliveryMode); + connection.send(message1); + connection.send(message2); + + // Make sure the message was delivered. + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.request(consumerInfo); + Message m = receiveMessage(connection); + assertNotNull(m); assertEquals(m.getMessageId(), message1.getMessageId()); + + assertTrue(countMessagesInQueue(connection, connectionInfo, destination)==2); + connection.send(createAck(consumerInfo, m, 1, MessageAck.DELIVERED_ACK_TYPE)); + assertTrue(countMessagesInQueue(connection, connectionInfo, destination)==2); + connection.send(createAck(consumerInfo, m, 1, MessageAck.STANDARD_ACK_TYPE)); + assertTrue(countMessagesInQueue(connection, connectionInfo, destination)==1); + + } + + public void initCombosForTestSelectorSkipsMessages() { + addCombinationValues( "destination", new Object[]{ + new ActiveMQTopic("TEST_TOPIC"), + new ActiveMQQueue("TEST_QUEUE")} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE)} ); + } + + public void testSelectorSkipsMessages() throws Throwable { + + // Start a producer and consumer + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + destination = createDestinationInfo(connection, connectionInfo, destinationType); + + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + consumerInfo.setSelector("JMSType='last'"); + connection.send(consumerInfo); + + Message message1 = createMessage(producerInfo, destination, deliveryMode); + message1.setType("first"); + Message message2 = createMessage(producerInfo, destination, deliveryMode); + message2.setType("last"); + connection.send(message1); + connection.send(message2); + + // Use selector to skip first message. + Message m = receiveMessage(connection); + assertNotNull(m); assertEquals(m.getMessageId(), message2.getMessageId()); + connection.send(createAck(consumerInfo, m, 1, MessageAck.STANDARD_ACK_TYPE)); + connection.send(closeConsumerInfo(consumerInfo)); + + assertNoMessagesLeft(connection); + } + + public void initCombosForTestAddConsumerThenSend() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE)} ); + } + + public void testAddConsumerThenSend() throws Throwable { + + // Start a producer and consumer + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + destination = createDestinationInfo(connection, connectionInfo, destinationType); + + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + connection.send(createMessage(producerInfo, destination, deliveryMode)); + + // Make sure the message was delivered. + Message m = receiveMessage(connection); + assertNotNull(m); + } + + public void initCombosForTestConsumerPrefetchAtOne() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE)} ); + } + + public void testConsumerPrefetchAtOne() throws Throwable { + + // Start a producer and consumer + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + destination = createDestinationInfo(connection, connectionInfo, destinationType); + + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + consumerInfo.setPrefetchSize(1); + connection.send(consumerInfo); + + // Send 2 messages to the broker. + connection.send(createMessage(producerInfo, destination, deliveryMode)); + connection.send(createMessage(producerInfo, destination, deliveryMode)); + + // Make sure only 1 message was delivered. + Message m = receiveMessage(connection); + assertNotNull(m); + assertNoMessagesLeft(connection); + + } + + public void initCombosForTestConsumerPrefetchAtTwo() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE)} ); + } + + public void testConsumerPrefetchAtTwo() throws Throwable { + + // Start a producer and consumer + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + destination = createDestinationInfo(connection, connectionInfo, destinationType); + + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + consumerInfo.setPrefetchSize(2); + connection.send(consumerInfo); + + // Send 3 messages to the broker. + connection.send(createMessage(producerInfo, destination, deliveryMode)); + connection.send(createMessage(producerInfo, destination, deliveryMode)); + connection.send(createMessage(producerInfo, destination, deliveryMode)); + + // Make sure only 1 message was delivered. + Message m = receiveMessage(connection); + assertNotNull(m); + m = receiveMessage(connection); + assertNotNull(m); + assertNoMessagesLeft(connection); + + } + + public void initCombosForTestConsumerPrefetchAndDeliveredAck() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + new Byte(ActiveMQDestination.TEMP_QUEUE_TYPE), + new Byte(ActiveMQDestination.TEMP_TOPIC_TYPE)} ); + } + + public void testConsumerPrefetchAndDeliveredAck() throws Throwable { + + // Start a producer and consumer + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + destination = createDestinationInfo(connection, connectionInfo, destinationType); + + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + consumerInfo.setPrefetchSize(1); + connection.send(consumerInfo); + + // Send 3 messages to the broker. + connection.send(createMessage(producerInfo, destination, deliveryMode)); + connection.send(createMessage(producerInfo, destination, deliveryMode)); + connection.send(createMessage(producerInfo, destination, deliveryMode)); + + // Make sure only 1 message was delivered. + Message m1 = receiveMessage(connection); + assertNotNull(m1); + + assertNoMessagesLeft(connection); + + // Acknowledge the first message. This should cause the next message to get dispatched. + connection.send(createAck(consumerInfo, m1, 1, MessageAck.DELIVERED_ACK_TYPE)); + + Message m2 = receiveMessage(connection); + assertNotNull(m2); + connection.send(createAck(consumerInfo, m2, 1, MessageAck.DELIVERED_ACK_TYPE)); + + Message m3 = receiveMessage(connection); + assertNotNull(m3); + connection.send(createAck(consumerInfo, m3, 1, MessageAck.DELIVERED_ACK_TYPE)); + } + + public static Test suite() { + return suite(BrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/broker/BrokerTestSupport.java b/activemq-core/src/test/java/org/activemq/broker/BrokerTestSupport.java new file mode 100755 index 0000000000..95eb7f90cf --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/BrokerTestSupport.java @@ -0,0 +1,340 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Iterator; + +import javax.jms.DeliveryMode; +import javax.jms.MessageNotWriteableException; + +import org.activemq.CombinationTestSupport; +import org.activemq.broker.region.RegionBroker; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQTextMessage; +import org.activemq.command.ConnectionId; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.DestinationInfo; +import org.activemq.command.LocalTransactionId; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.MessageDispatch; +import org.activemq.command.MessageId; +import org.activemq.command.ProducerInfo; +import org.activemq.command.RemoveInfo; +import org.activemq.command.SessionInfo; +import org.activemq.command.TransactionId; +import org.activemq.command.TransactionInfo; +import org.activemq.command.XATransactionId; +import org.activemq.memory.UsageManager; +import org.activemq.store.PersistenceAdapter; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class BrokerTestSupport extends CombinationTestSupport { + private static final Log log = LogFactory.getLog(BrokerTestSupport.class); + + /** + * Setting this to false makes the test run faster but they may be less accurate. + */ + public static boolean FAST_NO_MESSAGE_LEFT_ASSERT = System.getProperty("FAST_NO_MESSAGE_LEFT_ASSERT", "true").equals("true"); + + protected RegionBroker regionBroker; + protected BrokerService broker; + protected long idGenerator=0; + protected int msgIdGenerator=0; + protected int txGenerator=0; + protected int tempDestGenerator=0; + protected PersistenceAdapter persistenceAdapter; + + protected int MAX_WAIT = 1000; + + protected UsageManager memoryManager; + + + protected void setUp() throws Exception { + broker = createBroker(); + broker.start(); + } + + protected BrokerService createBroker() throws Exception { + return BrokerFactory.createBroker(new URI("broker:()/localhost?persistent=false")); + } + + protected void tearDown() throws Exception { + broker.stop(); + } + + protected ConsumerInfo createConsumerInfo(SessionInfo sessionInfo, ActiveMQDestination destination) throws Throwable { + ConsumerInfo info = new ConsumerInfo(sessionInfo, ++idGenerator); + info.setBrowser(false); + info.setDestination(destination); + info.setPrefetchSize(1000); + info.setDispatchAsync(false); + return info; + } + + protected RemoveInfo closeConsumerInfo(ConsumerInfo consumerInfo) { + return consumerInfo.createRemoveCommand(); + } + + protected ProducerInfo createProducerInfo(SessionInfo sessionInfo) throws Throwable { + ProducerInfo info = new ProducerInfo(sessionInfo, ++idGenerator); + return info; + } + + protected SessionInfo createSessionInfo(ConnectionInfo connectionInfo) throws Throwable { + SessionInfo info = new SessionInfo(connectionInfo, ++idGenerator); + return info; + } + + protected ConnectionInfo createConnectionInfo() throws Throwable { + ConnectionInfo info = new ConnectionInfo(); + info.setConnectionId(new ConnectionId("connection:"+(++idGenerator))); + info.setClientId( info.getConnectionId().getConnectionId() ); + return info; + } + + protected Message createMessage(ProducerInfo producerInfo, ActiveMQDestination destination) { + ActiveMQTextMessage message = new ActiveMQTextMessage(); + message.setMessageId(new MessageId(producerInfo, ++msgIdGenerator)); + message.setDestination(destination); + message.setPersistent(false); + try { + message.setText("Test Message Payload."); + } catch (MessageNotWriteableException e) { + } + return message; + } + + protected MessageAck createAck(ConsumerInfo consumerInfo, Message msg, int count, byte ackType) { + MessageAck ack = new MessageAck(); + ack.setAckType(ackType); + ack.setConsumerId(consumerInfo.getConsumerId()); + ack.setDestination( msg.getDestination() ); + ack.setLastMessageId( msg.getMessageId() ); + ack.setMessageCount(count); + return ack; + } + + protected void gc() { + regionBroker.gc(); + } + + protected void profilerPause(String prompt) throws IOException { + if( System.getProperty("profiler")!=null ) { + System.out.println(); + System.out.println(prompt+"> Press enter to continue: "); + while( System.in.read()!='\n' ) { + } + log.info(prompt+"> Done."); + } + } + + protected RemoveInfo closeConnectionInfo(ConnectionInfo info) { + return info.createRemoveCommand(); + } + + protected RemoveInfo closeSessionInfo(SessionInfo info) { + return info.createRemoveCommand(); + } + + protected RemoveInfo closeProducerInfo(ProducerInfo info) { + return info.createRemoveCommand(); + } + + protected Message createMessage(ProducerInfo producerInfo, ActiveMQDestination destination, int deliveryMode) { + Message message = createMessage(producerInfo, destination); + message.setPersistent(deliveryMode == DeliveryMode.PERSISTENT); + return message; + } + + protected LocalTransactionId createLocalTransaction(SessionInfo info) { + LocalTransactionId id = new LocalTransactionId(info.getSessionId().getParentId(), ++txGenerator); + return id; + } + + protected XATransactionId createXATransaction(SessionInfo info) throws IOException { + long id = txGenerator; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream os = new DataOutputStream(baos); + os.writeLong(++txGenerator); + os.close(); + byte[] bs = baos.toByteArray(); + + XATransactionId xid = new XATransactionId(); + xid.setBranchQualifier(bs); + xid.setGlobalTransactionId(bs); + xid.setFormatId(55); + return xid; + } + + protected TransactionInfo createBeginTransaction(ConnectionInfo connectionInfo, TransactionId txid) { + TransactionInfo info = new TransactionInfo(connectionInfo.getConnectionId(), txid, TransactionInfo.BEGIN); + return info; + } + + protected TransactionInfo createPrepareTransaction(ConnectionInfo connectionInfo, TransactionId txid) { + TransactionInfo info = new TransactionInfo(connectionInfo.getConnectionId(), txid, TransactionInfo.PREPARE); + return info; + } + + protected TransactionInfo createCommitTransaction1Phase(ConnectionInfo connectionInfo, TransactionId txid) { + TransactionInfo info = new TransactionInfo(connectionInfo.getConnectionId(), txid, TransactionInfo.COMMIT_ONE_PHASE); + return info; + } + + protected TransactionInfo createCommitTransaction2Phase(ConnectionInfo connectionInfo, TransactionId txid) { + TransactionInfo info = new TransactionInfo(connectionInfo.getConnectionId(), txid, TransactionInfo.COMMIT_TWO_PHASE); + return info; + } + + protected TransactionInfo createRollbackTransaction(ConnectionInfo connectionInfo, TransactionId txid) { + TransactionInfo info = new TransactionInfo(connectionInfo.getConnectionId(), txid, TransactionInfo.ROLLBACK); + return info; + } + + protected int countMessagesInQueue(StubConnection connection, ConnectionInfo connectionInfo, ActiveMQDestination destination) throws Throwable { + + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + connection.send(sessionInfo); + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + consumerInfo.setPrefetchSize(1); + consumerInfo.setBrowser(true); + connection.send(consumerInfo); + + ArrayList skipped = new ArrayList(); + + // Now get the messages. + Object m = connection.getDispatchQueue().poll(MAX_WAIT, TimeUnit.MILLISECONDS); + int i=0; + while( m!=null ) { + if( m instanceof MessageDispatch && ((MessageDispatch)m).getConsumerId().equals(consumerInfo.getConsumerId()) ) { + MessageDispatch md = (MessageDispatch) m; + if( md.getMessage()!=null ) { + i++; + connection.send(createAck(consumerInfo, md.getMessage(), 1, MessageAck.STANDARD_ACK_TYPE)); + } else { + break; + } + } else { + skipped.add(m); + } + m = connection.getDispatchQueue().poll(MAX_WAIT, TimeUnit.MILLISECONDS); + } + + for (Iterator iter = skipped.iterator(); iter.hasNext();) { + connection.getDispatchQueue().put(iter.next()); + } + + connection.send(closeSessionInfo(sessionInfo)); + return i; + + } + + protected DestinationInfo createTempDestinationInfo(ConnectionInfo connectionInfo, byte destinationType) { + DestinationInfo info = new DestinationInfo(); + info.setConnectionId(connectionInfo.getConnectionId()); + info.setOperationType(DestinationInfo.ADD_OPERATION_TYPE); + info.setDestination(ActiveMQDestination.createDestination(info.getConnectionId()+":"+(++tempDestGenerator), destinationType)); + return info; + } + + protected ActiveMQDestination createDestinationInfo(StubConnection connection, ConnectionInfo connectionInfo1, byte destinationType) throws Throwable { + if( (destinationType & ActiveMQDestination.TEMP_MASK)!=0 ) { + DestinationInfo info = createTempDestinationInfo(connectionInfo1, destinationType); + connection.send(info); + return info.getDestination(); + } else { + return ActiveMQDestination.createDestination("TEST", destinationType); + } + } + + + protected DestinationInfo closeDestinationInfo(DestinationInfo info) { + info.setOperationType(DestinationInfo.REMOVE_OPERATION_TYPE); + info.setTimeout(0); + return info; + } + + public static void recursiveDelete(File f) { + if( f.isDirectory() ) { + File[] files = f.listFiles(); + for (int i = 0; i < files.length; i++) { + recursiveDelete(files[i]); + } + } + f.delete(); + } + + protected StubConnection createConnection() throws Exception { + return new StubConnection(broker); + } + + /** + * @param connection + * @return + * @throws InterruptedException + */ + public Message receiveMessage(StubConnection connection) throws InterruptedException { + return receiveMessage(connection, MAX_WAIT); + } + + public Message receiveMessage(StubConnection connection, long timeout) throws InterruptedException { + while( true ) { + Object o = connection.getDispatchQueue().poll(timeout, TimeUnit.MILLISECONDS); + + if( o == null ) + return null; + + if( o instanceof MessageDispatch ) { + + MessageDispatch dispatch = (MessageDispatch)o; + if( dispatch.getMessage()==null ) + return null; + + dispatch.setMessage(dispatch.getMessage().copy()); + dispatch.getMessage().setRedeliveryCounter(dispatch.getRedeliveryCounter()); + return dispatch.getMessage(); + } + } + }; + + protected void assertNoMessagesLeft(StubConnection connection) throws InterruptedException { + long wait = FAST_NO_MESSAGE_LEFT_ASSERT ? 0 : MAX_WAIT; + while( true ) { + Object o = connection.getDispatchQueue().poll(wait, TimeUnit.MILLISECONDS); + if( o == null ) + return; + if( o instanceof MessageDispatch && ((MessageDispatch)o).getMessage()!=null ) { + fail("Received a message."); + } + } + } + + +} diff --git a/activemq-core/src/test/java/org/activemq/broker/MarshallingBrokerTest.java b/activemq-core/src/test/java/org/activemq/broker/MarshallingBrokerTest.java new file mode 100755 index 0000000000..f0590110ca --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/MarshallingBrokerTest.java @@ -0,0 +1,72 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import java.io.IOException; + +import junit.framework.Test; + +import org.activeio.command.WireFormat; +import org.activemq.command.Command; +import org.activemq.command.Response; +import org.activemq.openwire.OpenWireFormat; + +/** + * Runs against the broker but marshals all request and response commands. + * + * @version $Revision$ + */ +public class MarshallingBrokerTest extends BrokerTest { + + public WireFormat wireFormat = new OpenWireFormat(); + + public void initCombos() { + addCombinationValues( "wireFormat", new Object[]{ + new OpenWireFormat(true), + new OpenWireFormat(false), + }); + } + + protected StubConnection createConnection() throws Exception { + return new StubConnection(broker) { + public Response request(Command command) throws Throwable { + Response r = super.request((Command) wireFormat.unmarshal(wireFormat.marshal(command))); + if( r != null ) { + r = (Response) wireFormat.unmarshal(wireFormat.marshal(r)); + } + return r; + } + public void send(Command command) throws Throwable { + super.send((Command) wireFormat.unmarshal(wireFormat.marshal(command))); + } + protected void dispatch(Command command) throws InterruptedException, IOException { + super.dispatch((Command) wireFormat.unmarshal(wireFormat.marshal(command))); + }; + }; + } + public static Test suite() { + return suite(MarshallingBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + +} diff --git a/activemq-core/src/test/java/org/activemq/broker/ProgressPrinter.java b/activemq-core/src/test/java/org/activemq/broker/ProgressPrinter.java new file mode 100755 index 0000000000..73278104b4 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/ProgressPrinter.java @@ -0,0 +1,46 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + + +public class ProgressPrinter { + + private final long total; + private final long interval; + long percentDone=0; + long counter=0; + + public ProgressPrinter(long total, long interval) { + this.total=total; + this.interval = interval; + } + + synchronized public void increment() { + update(++counter); + } + + synchronized public void update(long current) { + long at = 100*current/total; + if( (percentDone/interval) != (at/interval) ) { + percentDone=at; + System.out.println("Completed: "+percentDone+"%"); + } + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/broker/RecoveryBrokerTest.java b/activemq-core/src/test/java/org/activemq/broker/RecoveryBrokerTest.java new file mode 100755 index 0000000000..f3fc887d8b --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/RecoveryBrokerTest.java @@ -0,0 +1,412 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import javax.jms.DeliveryMode; + +import junit.framework.Test; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.Message; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.LocalTransactionId; +import org.activemq.command.MessageAck; +import org.activemq.command.ProducerInfo; +import org.activemq.command.SessionInfo; + +/** + * Used to simulate the recovery that occurs when a broker shuts down. + * + * @version $Revision$ + */ +public class RecoveryBrokerTest extends BrokerRestartTestSupport { + + public void testConsumedQueuePersistentMessagesLostOnRestart() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup the producer and send the message. + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + for( int i=0; i < 4; i++) { + Message message = createMessage(producerInfo, destination); + message.setPersistent(true); + connection.send(message); + } + + // Setup the consumer and receive the message. + connection = createConnection(); + connectionInfo = createConnectionInfo(); + sessionInfo = createSessionInfo(connectionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // The we should get the messages. + for( int i=0; i < 4 ; i++ ) { + Message m2 = receiveMessage(connection); + assertNotNull(m2); + } + + // restart the broker. + restartBroker(); + + // No messages should be delivered. + Message m = receiveMessage(connection); + assertNull(m); + } + + public void testQueuePersistentUncommitedMessagesLostOnRestart() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup the producer and send the message. + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + // Begin the transaction. + LocalTransactionId txid = createLocalTransaction(sessionInfo); + connection.send(createBeginTransaction(connectionInfo, txid)); + + for( int i=0; i < 4; i++) { + Message message = createMessage(producerInfo, destination); + message.setPersistent(true); + message.setTransactionId(txid); + connection.send(message); + } + + // Don't commit + + // restart the broker. + restartBroker(); + + // Setup the consumer and receive the message. + connection = createConnection(); + connectionInfo = createConnectionInfo(); + sessionInfo = createSessionInfo(connectionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // No messages should be delivered. + Message m = receiveMessage(connection); + assertNull(m); + } + + public void testTopicDurableConsumerHoldsPersistentMessageAfterRestart() throws Throwable { + + ActiveMQDestination destination = new ActiveMQTopic("TEST"); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + connectionInfo1.setClientId("A"); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo1 = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo1); + + // Create the durable subscription. + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setSubcriptionName("test"); + consumerInfo1.setPrefetchSize(100); + connection1.send(consumerInfo1); + + // Close the subscription. + connection1.send(closeConsumerInfo(consumerInfo1)); + + // Send the messages + connection1.send(createMessage(producerInfo1, destination, DeliveryMode.PERSISTENT)); + connection1.send(createMessage(producerInfo1, destination, DeliveryMode.PERSISTENT)); + connection1.send(createMessage(producerInfo1, destination, DeliveryMode.PERSISTENT)); + connection1.send(createMessage(producerInfo1, destination, DeliveryMode.PERSISTENT)); + + // Restart the broker. + restartBroker(); + + // Get a connection to the new broker. + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + connectionInfo2.setClientId("A"); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + + // Re-open the subscription. + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + consumerInfo2.setSubcriptionName("test"); + consumerInfo2.setPrefetchSize(100); + connection2.send(consumerInfo2); + + // The we should get the messages. + for( int i=0; i < 4 ; i++ ) { + Message m2 = receiveMessage(connection2); + assertNotNull(m2); + } + assertNoMessagesLeft(connection2); + } + + public void testQueuePersistentMessagesNotLostOnRestart() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup the producer and send the message. + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + Message message = createMessage(producerInfo, destination); + message.setPersistent(true); + connection.send(message); + + // restart the broker. + restartBroker(); + + // Setup the consumer and receive the message. + connection = createConnection(); + connectionInfo = createConnectionInfo(); + sessionInfo = createSessionInfo(connectionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // Message should have been dropped due to broker restart. + Message m = receiveMessage(connection); + assertNotNull(m); + assertEquals( m.getMessageId(), message.getMessageId() ); + } + + public void testQueueNonPersistentMessagesLostOnRestart() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup the producer and send the message. + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + Message message = createMessage(producerInfo, destination); + message.setPersistent(false); + connection.send(message); + + // restart the broker. + restartBroker(); + + // Setup the consumer and receive the message. + connection = createConnection(); + connectionInfo = createConnectionInfo(); + sessionInfo = createSessionInfo(connectionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // Message should have been dropped due to broker restart. + assertNoMessagesLeft(connection); + } + + public void testQueuePersistentCommitedMessagesNotLostOnRestart() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup the producer and send the message. + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + // Begin the transaction. + LocalTransactionId txid = createLocalTransaction(sessionInfo); + connection.send(createBeginTransaction(connectionInfo, txid)); + + for( int i=0; i < 4; i++) { + Message message = createMessage(producerInfo, destination); + message.setPersistent(true); + message.setTransactionId(txid); + connection.send(message); + } + + // Commit + connection.send(createCommitTransaction1Phase(connectionInfo, txid)); + + // restart the broker. + restartBroker(); + + // Setup the consumer and receive the message. + connection = createConnection(); + connectionInfo = createConnectionInfo(); + sessionInfo = createSessionInfo(connectionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + for( int i=0; i < 4 ;i ++ ) { + Message m = receiveMessage(connection); + assertNotNull(m); + } + + assertNoMessagesLeft(connection); + } + + public void testQueuePersistentCommitedAcksNotLostOnRestart() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup the producer and send the message. + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + for( int i=0; i < 4; i++) { + Message message = createMessage(producerInfo, destination); + message.setPersistent(true); + connection.send(message); + } + + // Setup the consumer and receive the message. + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // Begin the transaction. + LocalTransactionId txid = createLocalTransaction(sessionInfo); + connection.send(createBeginTransaction(connectionInfo, txid)); + for( int i=0; i < 4 ;i ++ ) { + Message m = receiveMessage(connection); + assertNotNull(m); + MessageAck ack = createAck(consumerInfo, m, 1, MessageAck.STANDARD_ACK_TYPE); + ack.setTransactionId(txid); + connection.send(ack); + } + // Commit + connection.send(createCommitTransaction1Phase(connectionInfo, txid)); + + // restart the broker. + restartBroker(); + + // Setup the consumer and receive the message. + connection = createConnection(); + connectionInfo = createConnectionInfo(); + sessionInfo = createSessionInfo(connectionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // No messages should be delivered. + Message m = receiveMessage(connection); + assertNull(m); + } + + public void testQueuePersistentUncommitedAcksLostOnRestart() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup the producer and send the message. + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + for( int i=0; i < 4; i++) { + Message message = createMessage(producerInfo, destination); + message.setPersistent(true); + connection.send(message); + } + + // Setup the consumer and receive the message. + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // Begin the transaction. + LocalTransactionId txid = createLocalTransaction(sessionInfo); + connection.send(createBeginTransaction(connectionInfo, txid)); + for( int i=0; i < 4 ;i ++ ) { + Message m = receiveMessage(connection); + assertNotNull(m); + MessageAck ack = createAck(consumerInfo, m, 1, MessageAck.STANDARD_ACK_TYPE); + ack.setTransactionId(txid); + connection.send(ack); + } + // Don't commit + + // restart the broker. + restartBroker(); + + // Setup the consumer and receive the message. + connection = createConnection(); + connectionInfo = createConnectionInfo(); + sessionInfo = createSessionInfo(connectionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // All messages should be re-delivered. + for( int i=0; i < 4 ;i ++ ) { + Message m = receiveMessage(connection); + assertNotNull(m); + } + + assertNoMessagesLeft(connection); + } + + public static Test suite() { + return suite(RecoveryBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/broker/SpringTest.java b/activemq-core/src/test/java/org/activemq/broker/SpringTest.java new file mode 100755 index 0000000000..a6a36feb21 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/SpringTest.java @@ -0,0 +1,98 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import java.util.Iterator; +import java.util.List; + +import junit.framework.TestCase; + +import org.activemq.spring.SpringConsumer; +import org.activemq.spring.SpringProducer; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class SpringTest extends TestCase { + + private static final Log log = LogFactory.getLog(SpringTest.class); + + protected AbstractApplicationContext context; + protected SpringConsumer consumer; + protected SpringProducer producer; + + public void testSenderWithSpringXml() throws Exception { + assertSenderConfig("org/activemq/broker/spring.xml"); + } + /** + * assert method that is used by all the test method to send and receive messages + * based on each spring configuration. + * + * @param config + * @throws Exception + */ + protected void assertSenderConfig(String config) throws Exception { + context = new ClassPathXmlApplicationContext(config); + + consumer = (SpringConsumer) context.getBean("consumer"); + assertTrue("Found a valid consumer", consumer != null); + + consumer.start(); + + producer = (SpringProducer) context.getBean("producer"); + assertTrue("Found a valid producer", producer != null); + + consumer.flushMessages(); + producer.start(); + + // lets sleep a little to give the JMS time to dispatch stuff + consumer.waitForMessagesToArrive(producer.getMessageCount()); + + // now lets check that the consumer has received some messages + List messages = consumer.flushMessages(); + log.info("Consumer has received messages...."); + for (Iterator iter = messages.iterator(); iter.hasNext();) { + Object message = iter.next(); + log.info("Received: " + message); + } + + assertEquals("Message count", producer.getMessageCount(), messages.size()); + } + + /** + * Clean up method. + * + * @throws Exception + */ + protected void tearDown() throws Exception { + if (consumer != null) { + consumer.stop(); + } + if (producer != null) { + producer.stop(); + } + + if (context != null) { + context.destroy(); + } + } + +} diff --git a/activemq-core/src/test/java/org/activemq/broker/StubConnection.java b/activemq-core/src/test/java/org/activemq/broker/StubConnection.java new file mode 100755 index 0000000000..58e437fdc2 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/StubConnection.java @@ -0,0 +1,163 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.broker; + +import java.io.IOException; + +import org.activemq.Service; +import org.activemq.command.Command; +import org.activemq.command.ExceptionResponse; +import org.activemq.command.Message; +import org.activemq.command.Response; +import org.activemq.command.ShutdownInfo; +import org.activemq.thread.TaskRunnerFactory; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportListener; +import org.activemq.util.ServiceSupport; +import org.axiondb.engine.commands.ShutdownCommand; + +import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue; + +public class StubConnection implements Service { + + private final BlockingQueue dispatchQueue = new LinkedBlockingQueue(); + private Connection connection; + private Transport transport; + boolean shuttingDown = false; + + public StubConnection(BrokerService broker) throws Exception { + this(broker, null); + } + + public StubConnection(BrokerService broker, TaskRunnerFactory taskRunnerFactory) throws Exception { + connection = new AbstractConnection(null, broker.getBroker(), null) { + protected void dispatch(Command command) { + try { + StubConnection.this.dispatch(command); + } + catch (Exception e) { + serviceException(e); + } + } + }; + } + + protected void dispatch(Command command) throws InterruptedException, IOException { + dispatchQueue.put(command); + } + + public StubConnection(Connection connection) { + this.connection = connection; + } + + public StubConnection(Transport transport) throws Exception { + this.transport = transport; + transport.setTransportListener(new TransportListener() { + public void onCommand(Command command) { + try { + if (command.getClass() == ShutdownCommand.class) { + shuttingDown = true; + } + StubConnection.this.dispatch(command); + } + catch (Exception e) { + onException(new IOException("" + e)); + } + } + + public void onException(IOException error) { + if (!shuttingDown) { + error.printStackTrace(); + } + } + }); + transport.start(); + } + + public BlockingQueue getDispatchQueue() { + return dispatchQueue; + } + + public void send(Command command) throws Throwable { + if( command instanceof Message ) { + Message message = (Message) command; + message.setProducerId(message.getMessageId().getProducerId()); + } + command.setResponseRequired(false); + if (connection != null) { + Response response = connection.service(command); + if (response != null && response.isException()) { + ExceptionResponse er = (ExceptionResponse) response; + throw er.getException(); + } + } + else if (transport != null) { + transport.oneway(command); + } + } + + public Response request(Command command) throws Throwable { + if( command instanceof Message ) { + Message message = (Message) command; + message.setProducerId(message.getMessageId().getProducerId()); + } + command.setResponseRequired(true); + if (connection != null) { + Response response = connection.service(command); + if (response != null && response.isException()) { + ExceptionResponse er = (ExceptionResponse) response; + throw er.getException(); + } + return response; + } + else if (transport != null) { + Response response = transport.request(command); + if (response != null && response.isException()) { + ExceptionResponse er = (ExceptionResponse) response; + throw er.getException(); + } + return response; + } + return null; + } + + public Connection getConnection() { + return connection; + } + + public Transport getTransport() { + return transport; + } + + public void start() throws Exception { + } + + public void stop() throws Exception { + shuttingDown = true; + if (transport != null) { + try { + transport.oneway(new ShutdownInfo()); + } + catch (IOException e) { + } + ServiceSupport.dispose(transport); + } + } +} diff --git a/activemq-core/src/test/java/org/activemq/broker/XARecoveryBrokerTest.java b/activemq-core/src/test/java/org/activemq/broker/XARecoveryBrokerTest.java new file mode 100755 index 0000000000..66cd506150 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/XARecoveryBrokerTest.java @@ -0,0 +1,276 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker; + +import junit.framework.Test; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.DataArrayResponse; +import org.activemq.command.Message; +import org.activemq.command.MessageAck; +import org.activemq.command.ProducerInfo; +import org.activemq.command.Response; +import org.activemq.command.SessionInfo; +import org.activemq.command.TransactionId; +import org.activemq.command.TransactionInfo; +import org.activemq.command.XATransactionId; + +/** + * Used to simulate the recovery that occurs when a broker shuts down. + * + * @version $Revision$ + */ +public class XARecoveryBrokerTest extends BrokerRestartTestSupport { + + + public void testPreparedTransactionRecoveredOnRestart() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup the producer and send the message. + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // Prepare 4 message sends. + for( int i=0; i < 4; i++) { + // Begin the transaction. + XATransactionId txid = createXATransaction(sessionInfo); + connection.send(createBeginTransaction(connectionInfo, txid)); + + Message message = createMessage(producerInfo, destination); + message.setPersistent(true); + message.setTransactionId(txid); + connection.send(message); + + // Prepare + connection.send(createPrepareTransaction(connectionInfo, txid)); + } + + // Since prepared but not committed.. they should not get delivered. + assertNoMessagesLeft(connection); + + // restart the broker. + restartBroker(); + + // Setup the consumer and receive the message. + connection = createConnection(); + connectionInfo = createConnectionInfo(); + sessionInfo = createSessionInfo(connectionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // Since prepared but not committed.. they should not get delivered. + assertNoMessagesLeft(connection); + + Response response = connection.request( new TransactionInfo(connectionInfo.getConnectionId(), null, TransactionInfo.RECOVER) ); + assertNotNull(response); + DataArrayResponse dar = (DataArrayResponse) response; + assertEquals(4, dar.getData().length); + + // Commit the prepared transactions. + for( int i=0; i < dar.getData().length ;i ++ ) { + connection.send( createCommitTransaction2Phase(connectionInfo, (TransactionId)dar.getData()[i]) ); + } + + // We should not get the committed transactions. + for( int i=0; i < 4 ;i ++ ) { + Message m = receiveMessage(connection); + assertNotNull(m); + } + + assertNoMessagesLeft(connection); + } + + public void testQueuePersistentCommitedMessagesNotLostOnRestart() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup the producer and send the message. + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + // Begin the transaction. + XATransactionId txid = createXATransaction(sessionInfo); + connection.send(createBeginTransaction(connectionInfo, txid)); + + for( int i=0; i < 4; i++) { + Message message = createMessage(producerInfo, destination); + message.setPersistent(true); + message.setTransactionId(txid); + connection.send(message); + } + + // Commit + connection.send(createCommitTransaction1Phase(connectionInfo, txid)); + + // restart the broker. + restartBroker(); + + // Setup the consumer and receive the message. + connection = createConnection(); + connectionInfo = createConnectionInfo(); + sessionInfo = createSessionInfo(connectionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + for( int i=0; i < 4 ;i ++ ) { + Message m = receiveMessage(connection); + assertNotNull(m); + } + + assertNoMessagesLeft(connection); + } + + public void testQueuePersistentCommitedAcksNotLostOnRestart() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup the producer and send the message. + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + for( int i=0; i < 4; i++) { + Message message = createMessage(producerInfo, destination); + message.setPersistent(true); + connection.send(message); + } + + // Setup the consumer and receive the message. + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // Begin the transaction. + XATransactionId txid = createXATransaction(sessionInfo); + connection.send(createBeginTransaction(connectionInfo, txid)); + for( int i=0; i < 4 ;i ++ ) { + Message m = receiveMessage(connection); + assertNotNull(m); + MessageAck ack = createAck(consumerInfo, m, 1, MessageAck.STANDARD_ACK_TYPE); + ack.setTransactionId(txid); + connection.send(ack); + } + // Commit + connection.send(createCommitTransaction1Phase(connectionInfo, txid)); + + // restart the broker. + restartBroker(); + + // Setup the consumer and receive the message. + connection = createConnection(); + connectionInfo = createConnectionInfo(); + sessionInfo = createSessionInfo(connectionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // No messages should be delivered. + Message m = receiveMessage(connection); + assertNull(m); + } + + public void testQueuePersistentUncommittedAcksLostOnRestart() throws Throwable { + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + // Setup the producer and send the message. + StubConnection connection = createConnection(); + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + for( int i=0; i < 4; i++) { + Message message = createMessage(producerInfo, destination); + message.setPersistent(true); + connection.send(message); + } + + // Setup the consumer and receive the message. + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // Begin the transaction. + XATransactionId txid = createXATransaction(sessionInfo); + connection.send(createBeginTransaction(connectionInfo, txid)); + for( int i=0; i < 4 ;i ++ ) { + Message m = receiveMessage(connection); + assertNotNull(m); + MessageAck ack = createAck(consumerInfo, m, 1, MessageAck.STANDARD_ACK_TYPE); + ack.setTransactionId(txid); + connection.send(ack); + } + // Don't commit + + // restart the broker. + restartBroker(); + + // Setup the consumer and receive the message. + connection = createConnection(); + connectionInfo = createConnectionInfo(); + sessionInfo = createSessionInfo(connectionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + consumerInfo = createConsumerInfo(sessionInfo, destination); + connection.send(consumerInfo); + + // All messages should be re-delivered. + for( int i=0; i < 4 ;i ++ ) { + Message m = receiveMessage(connection); + assertNotNull(m); + } + + assertNoMessagesLeft(connection); + } + + public static Test suite() { + return suite(XARecoveryBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/broker/advisory/AdvisoryBrokerTest.java b/activemq-core/src/test/java/org/activemq/broker/advisory/AdvisoryBrokerTest.java new file mode 100755 index 0000000000..35dd4ba3b0 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/advisory/AdvisoryBrokerTest.java @@ -0,0 +1,332 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.advisory; + +import junit.framework.Test; + +import org.activemq.advisory.AdvisorySupport; +import org.activemq.broker.BrokerTestSupport; +import org.activemq.broker.StubConnection; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.ProducerInfo; +import org.activemq.command.RemoveInfo; +import org.activemq.command.SessionInfo; + +public class AdvisoryBrokerTest extends BrokerTestSupport { + + public void testConnectionAdvisories() throws Throwable { + + ActiveMQDestination destination = AdvisorySupport.getConnectionAdvisoryTopic(); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(consumerInfo1); + + // We should get an advisory of our own connection. + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + assertNotNull(m1.getDataStructure()); + assertEquals(((ConnectionInfo)m1.getDataStructure()).getConnectionId(), connectionInfo1.getConnectionId()); + + // Setup a second connection + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + connection2.send(connectionInfo2); + + // We should get an advisory of the second connection. + m1 = receiveMessage(connection1); + assertNotNull(m1); + assertNotNull(m1.getDataStructure()); + assertEquals(((ConnectionInfo)m1.getDataStructure()).getConnectionId(), connectionInfo2.getConnectionId()); + + // Close the second connection. + connection2.send(closeConnectionInfo(connectionInfo2)); + connection2.stop(); + + // We should get an advisory of the second connection closing + m1 = receiveMessage(connection1); + assertNotNull(m1); + assertNotNull(m1.getDataStructure()); + RemoveInfo r = (RemoveInfo) m1.getDataStructure(); + assertEquals(r.getObjectId(), connectionInfo2.getConnectionId()); + + assertNoMessagesLeft(connection1); + } + + public void testConsumerAdvisories() throws Throwable { + + ActiveMQDestination queue = new ActiveMQQueue("test"); + ActiveMQDestination destination = AdvisorySupport.getConsumerAdvisoryTopic(queue); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(consumerInfo1); + + // We should not see and advisory for the advisory consumer. + assertNoMessagesLeft(connection1); + + // Setup a second consumer. + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, queue); + consumerInfo1.setPrefetchSize(100); + + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.send(consumerInfo2); + + // We should get an advisory of the new consumer. + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + assertNotNull(m1.getDataStructure()); + assertEquals(((ConsumerInfo)m1.getDataStructure()).getConsumerId(), consumerInfo2.getConsumerId()); + + // Close the second connection. + connection2.send(closeConnectionInfo(connectionInfo2)); + connection2.stop(); + + // We should get an advisory of the consumer closing + m1 = receiveMessage(connection1); + assertNotNull(m1); + assertNotNull(m1.getDataStructure()); + RemoveInfo r = (RemoveInfo) m1.getDataStructure(); + assertEquals(r.getObjectId(), consumerInfo2.getConsumerId()); + + assertNoMessagesLeft(connection2); + } + + public void testConsumerAdvisoriesReplayed() throws Throwable { + + ActiveMQDestination queue = new ActiveMQQueue("test"); + ActiveMQDestination destination = AdvisorySupport.getConsumerAdvisoryTopic(queue); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + + // Setup a second consumer. + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, queue); + consumerInfo2.setPrefetchSize(100); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.send(consumerInfo2); + + // We should get an advisory of the previous consumer. + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + connection1.send(consumerInfo1); + + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + assertNotNull(m1.getDataStructure()); + assertEquals(((ConsumerInfo)m1.getDataStructure()).getConsumerId(), consumerInfo2.getConsumerId()); + + // Close the second connection. + connection2.send(closeConnectionInfo(connectionInfo2)); + connection2.stop(); + + // We should get an advisory of the consumer closing + m1 = receiveMessage(connection1); + assertNotNull(m1); + assertNotNull(m1.getDataStructure()); + RemoveInfo r = (RemoveInfo) m1.getDataStructure(); + assertEquals(r.getObjectId(), consumerInfo2.getConsumerId()); + + assertNoMessagesLeft(connection2); + } + + + public void testProducerAdvisories() throws Throwable { + + ActiveMQDestination queue = new ActiveMQQueue("test"); + ActiveMQDestination destination = AdvisorySupport.getProducerAdvisoryTopic(queue); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(consumerInfo1); + + assertNoMessagesLeft(connection1); + + // Setup a producer. + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ProducerInfo producerInfo2 = createProducerInfo(sessionInfo2); + producerInfo2.setDestination(queue); + + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.send(producerInfo2); + + // We should get an advisory of the new produver. + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + assertNotNull(m1.getDataStructure()); + assertEquals(((ProducerInfo)m1.getDataStructure()).getProducerId(), producerInfo2.getProducerId()); + + // Close the second connection. + connection2.send(closeConnectionInfo(connectionInfo2)); + connection2.stop(); + + // We should get an advisory of the producer closing + m1 = receiveMessage(connection1); + assertNotNull(m1); + assertNotNull(m1.getDataStructure()); + RemoveInfo r = (RemoveInfo) m1.getDataStructure(); + assertEquals(r.getObjectId(), producerInfo2.getProducerId()); + + assertNoMessagesLeft(connection2); + } + + public void testProducerAdvisoriesReplayed() throws Throwable { + + ActiveMQDestination queue = new ActiveMQQueue("test"); + ActiveMQDestination destination = AdvisorySupport.getProducerAdvisoryTopic(queue); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + + // Setup a producer. + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ProducerInfo producerInfo2 = createProducerInfo(sessionInfo2); + producerInfo2.setDestination(queue); + + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.send(producerInfo2); + + // Create the advisory consumer.. it should see the previous producer + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + connection1.send(consumerInfo1); + + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + assertNotNull(m1.getDataStructure()); + assertEquals(((ProducerInfo)m1.getDataStructure()).getProducerId(), producerInfo2.getProducerId()); + + // Close the second connection. + connection2.send(closeConnectionInfo(connectionInfo2)); + connection2.stop(); + + // We should get an advisory of the producer closing + m1 = receiveMessage(connection1); + assertNotNull(m1); + assertNotNull(m1.getDataStructure()); + RemoveInfo r = (RemoveInfo) m1.getDataStructure(); + assertEquals(r.getObjectId(), producerInfo2.getProducerId()); + + assertNoMessagesLeft(connection2); + } + + public void testProducerAdvisoriesReplayedOnlyTargetNewConsumer() throws Throwable { + + ActiveMQDestination queue = new ActiveMQQueue("test"); + ActiveMQDestination destination = AdvisorySupport.getProducerAdvisoryTopic(queue); + + // Setup a first connection + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + // Create the first consumer.. + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + consumerInfo1.setPrefetchSize(100); + connection1.send(consumerInfo1); + + // Setup a producer. + StubConnection connection2 = createConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ProducerInfo producerInfo2 = createProducerInfo(sessionInfo2); + producerInfo2.setDestination(queue); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.send(producerInfo2); + + Message m1 = receiveMessage(connection1); + assertNotNull(m1); + assertNotNull(m1.getDataStructure()); + assertEquals(((ProducerInfo)m1.getDataStructure()).getProducerId(), producerInfo2.getProducerId()); + + // Create the 2nd consumer.. + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + consumerInfo2.setPrefetchSize(100); + connection2.send(consumerInfo2); + + // The second consumer should se a replay + m1 = receiveMessage(connection2); + assertNotNull(m1); + assertNotNull(m1.getDataStructure()); + assertEquals(((ProducerInfo)m1.getDataStructure()).getProducerId(), producerInfo2.getProducerId()); + + // But the first consumer should not see the replay. + assertNoMessagesLeft(connection1); + } + + public static Test suite() { + return suite(AdvisoryBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/broker/store/DefaultStoreBrokerTest.java b/activemq-core/src/test/java/org/activemq/broker/store/DefaultStoreBrokerTest.java new file mode 100755 index 0000000000..2777dc218b --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/store/DefaultStoreBrokerTest.java @@ -0,0 +1,83 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.store; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import javax.sql.DataSource; + +import junit.framework.Test; + +import org.activemq.broker.BrokerFactory; +import org.activemq.broker.BrokerService; +import org.activemq.broker.BrokerTest; +import org.activemq.store.DefaultPersistenceAdapterFactory; +import org.activemq.store.PersistenceAdapter; +import org.activemq.store.jdbc.JDBCPersistenceAdapter; +import org.activemq.store.journal.JournalPersistenceAdapter; + +/** + * Once the wire format is completed we can test against real persistence storage. + * + * @version $Revision$ + */ +public class DefaultStoreBrokerTest extends BrokerTest { + + protected BrokerService createBroker() throws Exception { + return BrokerFactory.createBroker(new URI("broker://()/localhost?deleteAllMessagesOnStartup=true")); + } + + protected PersistenceAdapter createPersistenceAdapter(boolean clean) throws IOException { + File dataDir = new File("test-data"); + if( clean ) { + recursiveDelete(new File(dataDir, "journal")); + } + DefaultPersistenceAdapterFactory factory = new DefaultPersistenceAdapterFactory(); + factory.setDataDirectory(dataDir); + // Use a smaller journal so that tests are quicker. + factory.setJournalLogFileSize(1024*64); + PersistenceAdapter adapter = factory.createPersistenceAdapter(); + if( clean ) { + DataSource ds = ((JDBCPersistenceAdapter)((JournalPersistenceAdapter)adapter).getLongTermPersistence()).getDataSource(); + try { + Connection c = ds.getConnection(); + Statement s = c.createStatement(); + try { s.executeUpdate("DROP TABLE ACTIVEMQ_MSGS");} catch (SQLException e) {} + try { s.executeUpdate("DROP TABLE ACTIVEMQ_TXS");} catch (SQLException e) {} + try { s.executeUpdate("DROP TABLE ACTIVEMQ_ACKS");} catch (SQLException e) {} + } catch (SQLException e) { + } + } + return adapter; + } + + public static Test suite() { + return suite(DefaultStoreBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/broker/store/DefaultStoreRecoveryBrokerTest.java b/activemq-core/src/test/java/org/activemq/broker/store/DefaultStoreRecoveryBrokerTest.java new file mode 100755 index 0000000000..1447493b9b --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/store/DefaultStoreRecoveryBrokerTest.java @@ -0,0 +1,52 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.store; + +import java.net.URI; + +import junit.framework.Test; + +import org.activemq.broker.BrokerFactory; +import org.activemq.broker.BrokerService; +import org.activemq.broker.RecoveryBrokerTest; + +/** + * Used to verify that recovery works correctly against + * + * @version $Revision$ + */ +public class DefaultStoreRecoveryBrokerTest extends RecoveryBrokerTest { + + protected BrokerService createBroker() throws Exception { + return BrokerFactory.createBroker(new URI("broker://()/localhost?deleteAllMessagesOnStartup=true")); + } + + protected BrokerService createRestartedBroker() throws Exception { + return BrokerFactory.createBroker(new URI("broker://()/localhost")); + } + + public static Test suite() { + return suite(DefaultStoreRecoveryBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/broker/store/DefaultStoreXARecoveryBrokerTest.java b/activemq-core/src/test/java/org/activemq/broker/store/DefaultStoreXARecoveryBrokerTest.java new file mode 100755 index 0000000000..2787735544 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/store/DefaultStoreXARecoveryBrokerTest.java @@ -0,0 +1,52 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.store; + +import java.net.URI; + +import junit.framework.Test; + +import org.activemq.broker.BrokerFactory; +import org.activemq.broker.BrokerService; +import org.activemq.broker.XARecoveryBrokerTest; + +/** + * Used to verify that recovery works correctly against + * + * @version $Revision$ + */ +public class DefaultStoreXARecoveryBrokerTest extends XARecoveryBrokerTest { + + public static Test suite() { + return suite(DefaultStoreXARecoveryBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + protected BrokerService createBroker() throws Exception { + return BrokerFactory.createBroker(new URI("broker://()/localhost?deleteAllMessagesOnStartup=true")); + } + + protected BrokerService createRestartedBroker() throws Exception { + return BrokerFactory.createBroker(new URI("broker://()/localhost")); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/broker/store/LoadTester.java b/activemq-core/src/test/java/org/activemq/broker/store/LoadTester.java new file mode 100755 index 0000000000..085ce66579 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/store/LoadTester.java @@ -0,0 +1,107 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.store; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import javax.jms.BytesMessage; +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import junit.framework.Test; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.JmsTestSupport; +import org.activemq.broker.BrokerFactory; +import org.activemq.broker.BrokerService; +import org.activemq.broker.ProgressPrinter; +import org.activemq.broker.TransportConnector; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; + +/** + * + * @version $Revision$ + */ +public class LoadTester extends JmsTestSupport { + + protected BrokerService createBroker() throws Exception { + return BrokerFactory.createBroker(new URI("xbean:org/activemq/broker/store/loadtester.xml")); + } + + protected ConnectionFactory createConnectionFactory() throws URISyntaxException, IOException { + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(((TransportConnector)broker.getTransportConnectors().get(0)).getServer().getConnectURI()); + factory.setUseAsyncSend(true); + return factory; + } + + public void testQueueSendThenAddConsumer() throws Throwable { + int MESSAGE_SIZE=1024*64; + int PRODUCE_COUNT=10000; + ProgressPrinter printer = new ProgressPrinter(PRODUCE_COUNT, 20); + + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + + connection.setUseCompression(false); + connection.getPrefetchPolicy().setAll(10); + connection.start(); + Session session = connection.createSession(false, Session.DUPS_OK_ACKNOWLEDGE); + MessageProducer producer = session.createProducer(destination); + producer.setDeliveryMode(DeliveryMode.PERSISTENT); + + System.out.println("Sending "+ PRODUCE_COUNT+" messages that are "+(MESSAGE_SIZE/1024.0)+"k large, for a total of "+(PRODUCE_COUNT*MESSAGE_SIZE/(1024.0*1024.0))+" megs of data."); + // Send a message to the broker. + long start = System.currentTimeMillis(); + for( int i=0; i < PRODUCE_COUNT; i++) { + printer.increment(); + BytesMessage msg = session.createBytesMessage(); + msg.writeBytes(new byte[MESSAGE_SIZE]); + producer.send(msg); + } + long end1 = System.currentTimeMillis(); + + System.out.println("Produced messages/sec: "+ (PRODUCE_COUNT*1000.0/(end1-start))); + + printer = new ProgressPrinter(PRODUCE_COUNT, 10); + start = System.currentTimeMillis(); + MessageConsumer consumer = session.createConsumer(destination); + for( int i=0; i < PRODUCE_COUNT; i++) { + printer.increment(); + assertNotNull("Getting message: "+i,consumer.receive(5000)); + } + end1 = System.currentTimeMillis(); + System.out.println("Consumed messages/sec: "+ (PRODUCE_COUNT*1000.0/(end1-start))); + + + } + + public static Test suite() { + return suite(LoadTester.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/broker/store/QuickJournalRecoveryBrokerTest.java b/activemq-core/src/test/java/org/activemq/broker/store/QuickJournalRecoveryBrokerTest.java new file mode 100644 index 0000000000..ba84e8e6fb --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/store/QuickJournalRecoveryBrokerTest.java @@ -0,0 +1,54 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.store; + +import junit.framework.Test; + +import org.activemq.broker.BrokerService; +import org.activemq.broker.RecoveryBrokerTest; + +/** + * Used to verify that recovery works correctly against + * + * @version $Revision$ + */ +public class QuickJournalRecoveryBrokerTest extends RecoveryBrokerTest { + + protected BrokerService createBroker() throws Exception { + BrokerService service = new BrokerService(); + service.setDeleteAllMessagesOnStartup(true); + service.getPersistenceFactory().setUseQuickJournal(true); + return service; + } + + protected BrokerService createRestartedBroker() throws Exception { + BrokerService service = new BrokerService(); + service.getPersistenceFactory().setUseQuickJournal(true); + return service; + } + + public static Test suite() { + return suite(QuickJournalRecoveryBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/broker/store/QuickJournalXARecoveryBrokerTest.java b/activemq-core/src/test/java/org/activemq/broker/store/QuickJournalXARecoveryBrokerTest.java new file mode 100644 index 0000000000..0c6e4acd4c --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/broker/store/QuickJournalXARecoveryBrokerTest.java @@ -0,0 +1,54 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.broker.store; + +import junit.framework.Test; + +import org.activemq.broker.BrokerService; +import org.activemq.broker.XARecoveryBrokerTest; + +/** + * Used to verify that recovery works correctly against + * + * @version $Revision$ + */ +public class QuickJournalXARecoveryBrokerTest extends XARecoveryBrokerTest { + + public static Test suite() { + return suite(QuickJournalXARecoveryBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + protected BrokerService createBroker() throws Exception { + BrokerService service = new BrokerService(); + service.setDeleteAllMessagesOnStartup(true); + service.getPersistenceFactory().setUseQuickJournal(true); + return service; + } + + protected BrokerService createRestartedBroker() throws Exception { + BrokerService service = new BrokerService(); + service.getPersistenceFactory().setUseQuickJournal(true); + return service; + } + +} diff --git a/activemq-core/src/test/java/org/activemq/command/ActiveMQBytesMessageTest.java b/activemq-core/src/test/java/org/activemq/command/ActiveMQBytesMessageTest.java new file mode 100755 index 0000000000..92cb1b82e7 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/command/ActiveMQBytesMessageTest.java @@ -0,0 +1,494 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import javax.jms.JMSException; +import javax.jms.MessageFormatException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; + +import junit.framework.TestCase; + +/** + * @version $Revision$ + */ +public class ActiveMQBytesMessageTest extends TestCase { + public static void main(String[] args) { + junit.textui.TestRunner.run(ActiveMQBytesMessageTest.class); + } + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + } + + /* + * @see TestCase#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Constructor for ActiveMQBytesMessageTest. + * + * @param arg0 + */ + public ActiveMQBytesMessageTest(String arg0) { + super(arg0); + } + + public void testGetDataStructureType() { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + assertEquals(msg.getDataStructureType(), CommandTypes.ACTIVEMQ_BYTES_MESSAGE); + } + + public void testGetBodyLength() { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + int len = 10; + try { + for (int i = 0; i < len; i++) { + msg.writeLong(5l); + } + } catch (JMSException ex) { + ex.printStackTrace(); + } + try { + msg.reset(); + assertTrue(msg.getBodyLength() == (len * 8)); + } catch (Throwable e) { + e.printStackTrace(); + assertTrue(false); + } + } + + public void testReadBoolean() { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + try { + msg.writeBoolean(true); + msg.reset(); + assertTrue(msg.readBoolean()); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadByte() { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + try { + msg.writeByte((byte) 2); + msg.reset(); + assertTrue(msg.readByte() == 2); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadUnsignedByte() { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + try { + msg.writeByte((byte) 2); + msg.reset(); + assertTrue(msg.readUnsignedByte() == 2); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadShort() { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + try { + msg.writeShort((short) 3000); + msg.reset(); + assertTrue(msg.readShort() == 3000); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadUnsignedShort() { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + try { + msg.writeShort((short) 3000); + msg.reset(); + assertTrue(msg.readUnsignedShort() == 3000); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadChar() { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + try { + msg.writeChar('a'); + msg.reset(); + assertTrue(msg.readChar() == 'a'); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadInt() { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + try { + msg.writeInt(3000); + msg.reset(); + assertTrue(msg.readInt() == 3000); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadLong() { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + try { + msg.writeLong(3000); + msg.reset(); + assertTrue(msg.readLong() == 3000); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadFloat() { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + try { + msg.writeFloat(3.3f); + msg.reset(); + assertTrue(msg.readFloat() == 3.3f); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadDouble() { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + try { + msg.writeDouble(3.3d); + msg.reset(); + assertTrue(msg.readDouble() == 3.3d); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadUTF() { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + try { + String str = "this is a test"; + msg.writeUTF(str); + msg.reset(); + assertTrue(msg.readUTF().equals(str)); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + /* + * Class to test for int readBytes(byte[]) + */ + public void testReadBytesbyteArray() { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + try { + byte[] data = new byte[50]; + for (int i = 0; i < data.length; i++) { + data[i] = (byte) i; + } + msg.writeBytes(data); + msg.reset(); + byte[] test = new byte[data.length]; + msg.readBytes(test); + for (int i = 0; i < test.length; i++) { + assertTrue(test[i] == i); + } + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testWriteObject() throws JMSException { + ActiveMQBytesMessage msg = new ActiveMQBytesMessage(); + try { + msg.writeObject("fred"); + msg.writeObject(Boolean.TRUE); + msg.writeObject(new Character('q')); + msg.writeObject(new Byte((byte) 1)); + msg.writeObject(new Short((short) 3)); + msg.writeObject(new Integer(3)); + msg.writeObject(new Long(300l)); + msg.writeObject(new Float(3.3f)); + msg.writeObject(new Double(3.3)); + msg.writeObject(new byte[3]); + } catch (MessageFormatException mfe) { + fail("objectified primitives should be allowed"); + } + try { + msg.writeObject(new Object()); + fail("only objectified primitives are allowed"); + } catch (MessageFormatException mfe) { + } + } + + + /* new */ + public void testClearBody() throws JMSException { + ActiveMQBytesMessage bytesMessage = new ActiveMQBytesMessage(); + try { + bytesMessage.writeInt(1); + bytesMessage.clearBody(); + assertFalse(bytesMessage.isReadOnlyBody()); + bytesMessage.writeInt(1); + bytesMessage.readInt(); + } catch (MessageNotReadableException mnwe) { + } catch (MessageNotWriteableException mnwe) { + assertTrue(false); + } + } + + public void testReset() throws JMSException { + ActiveMQBytesMessage message = new ActiveMQBytesMessage(); + try { + message.writeDouble(24.5); + message.writeLong(311); + } catch (MessageNotWriteableException mnwe) { + fail("should be writeable"); + } + message.reset(); + try { + assertTrue(message.isReadOnlyBody()); + assertEquals(message.readDouble(), 24.5, 0); + assertEquals(message.readLong(), 311); + } catch (MessageNotReadableException mnre) { + fail("should be readable"); + } + try { + message.writeInt(33); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + } + + public void testReadOnlyBody() throws JMSException { + ActiveMQBytesMessage message = new ActiveMQBytesMessage(); + try { + message.writeBoolean(true); + message.writeByte((byte) 1); + message.writeByte((byte) 1); + message.writeBytes(new byte[1]); + message.writeBytes(new byte[3], 0, 2); + message.writeChar('a'); + message.writeDouble(1.5); + message.writeFloat((float) 1.5); + message.writeInt(1); + message.writeLong(1); + message.writeObject("stringobj"); + message.writeShort((short) 1); + message.writeShort((short) 1); + message.writeUTF("utfstring"); + } catch (MessageNotWriteableException mnwe) { + fail("Should be writeable"); + } + message.reset(); + try { + message.readBoolean(); + message.readByte(); + message.readUnsignedByte(); + message.readBytes(new byte[1]); + message.readBytes(new byte[2], 2); + message.readChar(); + message.readDouble(); + message.readFloat(); + message.readInt(); + message.readLong(); + message.readUTF(); + message.readShort(); + message.readUnsignedShort(); + message.readUTF(); + } catch (MessageNotReadableException mnwe) { + fail("Should be readable"); + } + try { + message.writeBoolean(true); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeByte((byte) 1); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeBytes(new byte[1]); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeBytes(new byte[3], 0, 2); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeChar('a'); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeDouble(1.5); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeFloat((float) 1.5); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeInt(1); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeLong(1); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeObject("stringobj"); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeShort((short) 1); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeUTF("utfstring"); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + } + + public void testWriteOnlyBody() throws JMSException { + ActiveMQBytesMessage message = new ActiveMQBytesMessage(); + message.clearBody(); + try { + message.writeBoolean(true); + message.writeByte((byte) 1); + message.writeByte((byte) 1); + message.writeBytes(new byte[1]); + message.writeBytes(new byte[3], 0, 2); + message.writeChar('a'); + message.writeDouble(1.5); + message.writeFloat((float) 1.5); + message.writeInt(1); + message.writeLong(1); + message.writeObject("stringobj"); + message.writeShort((short) 1); + message.writeShort((short) 1); + message.writeUTF("utfstring"); + } catch (MessageNotWriteableException mnwe) { + fail("Should be writeable"); + } + try { + message.readBoolean(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException mnwe) { + } + try { + message.readByte(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readUnsignedByte(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readBytes(new byte[1]); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readBytes(new byte[2], 2); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readChar(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readDouble(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readFloat(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readInt(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readLong(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readUTF(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readShort(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readUnsignedShort(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readUTF(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + } +} diff --git a/activemq-core/src/test/java/org/activemq/command/ActiveMQDestinationTest.java b/activemq-core/src/test/java/org/activemq/command/ActiveMQDestinationTest.java new file mode 100755 index 0000000000..0780a888e4 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/command/ActiveMQDestinationTest.java @@ -0,0 +1,71 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.IOException; +import java.util.Map; + +import junit.framework.Test; + +public class ActiveMQDestinationTest extends DataStructureTestSupport { + + public ActiveMQDestination destination; + + public void initCombosForTestDesintaionMarshaling() { + addCombinationValues("destination", new Object[]{ + new ActiveMQQueue("TEST"), + new ActiveMQTopic("TEST"), + new ActiveMQTempQueue("TEST:1"), + new ActiveMQTempTopic("TEST:1"), + new ActiveMQQueue("TEST?option=value"), + new ActiveMQTopic("TEST?option=value"), + new ActiveMQTempQueue("TEST:1?option=value"), + new ActiveMQTempTopic("TEST:1?option=value"), + }); + } + + public void testDesintaionMarshaling() throws IOException { + assertBeanMarshalls(destination); + } + + public void initCombosForTestDesintaionOptions() { + addCombinationValues("destination", new Object[]{ + new ActiveMQQueue("TEST?k1=v1&k2=v2"), + new ActiveMQTopic("TEST?k1=v1&k2=v2"), + new ActiveMQTempQueue("TEST:1?k1=v1&k2=v2"), + new ActiveMQTempTopic("TEST:1?k1=v1&k2=v2"), + }); + } + + public void testDesintaionOptions() throws IOException { + Map options = destination.getOptions(); + assertNotNull(options); + assertEquals("v1", options.get("k1")); + assertEquals("v2", options.get("k2")); + } + + public static Test suite() { + return suite(ActiveMQDestinationTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/command/ActiveMQMapMessageTest.java b/activemq-core/src/test/java/org/activemq/command/ActiveMQMapMessageTest.java new file mode 100755 index 0000000000..7740defc6f --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/command/ActiveMQMapMessageTest.java @@ -0,0 +1,483 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; + +import javax.jms.JMSException; +import javax.jms.MessageFormatException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; + +import junit.framework.TestCase; + +/** + * @version $Revision$ + */ +public class ActiveMQMapMessageTest extends TestCase { + private String name = "testName"; + + public static void main(String[] args) { + junit.textui.TestRunner.run(ActiveMQMapMessageTest.class); + } + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + } + + /* + * @see TestCase#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Constructor for ActiveMQMapMessageTest. + * + * @param arg0 + */ + public ActiveMQMapMessageTest(String arg0) { + super(arg0); + } + + public void testBytesConversion() throws JMSException, IOException { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + msg.setBoolean("boolean", true); + msg.setByte("byte", (byte) 1); + msg.setBytes("bytes", new byte[1]); + msg.setChar("char", 'a'); + msg.setDouble("double", 1.5); + msg.setFloat("float", 1.5f); + msg.setInt("int", 1); + msg.setLong("long", 1); + msg.setObject("object", "stringObj"); + msg.setShort("short", (short) 1); + msg.setString("string", "string"); + + msg.onSend(); + msg.setContent(msg.getContent()); + + assertEquals(msg.getBoolean("boolean"), true); + assertEquals(msg.getByte("byte"), (byte) 1); + assertEquals(msg.getBytes("bytes").length, 1); + assertEquals(msg.getChar("char"), 'a'); + assertEquals(msg.getDouble("double"), 1.5, 0); + assertEquals(msg.getFloat("float"), 1.5f, 0); + assertEquals(msg.getInt("int"), 1); + assertEquals(msg.getLong("long"), 1); + assertEquals(msg.getObject("object"), "stringObj"); + assertEquals(msg.getShort("short"), (short) 1); + assertEquals(msg.getString("string"), "string"); + } + + public void testGetBoolean() throws JMSException { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + msg.setBoolean(name, true); + msg.setReadOnlyBody(true); + assertTrue(msg.getBoolean(name)); + msg.clearBody(); + msg.setString(name, "true"); + + msg.onSend(); + msg.setContent(msg.getContent()); + + assertTrue(msg.getBoolean(name)); + } + + public void testGetByte() throws JMSException { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + msg.setByte(this.name, (byte) 1); + msg.setReadOnlyBody(true); + assertTrue(msg.getByte(this.name) == (byte) 1); + } + + public void testGetShort() { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + try { + msg.setShort(this.name, (short) 1); + msg.setReadOnlyBody(true); + assertTrue(msg.getShort(this.name) == (short) 1); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testGetChar() { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + try { + msg.setChar(this.name, 'a'); + msg.setReadOnlyBody(true); + assertTrue(msg.getChar(this.name) == 'a'); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testGetInt() { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + try { + msg.setInt(this.name, 1); + msg.setReadOnlyBody(true); + assertTrue(msg.getInt(this.name) == 1); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testGetLong() { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + try { + msg.setLong(this.name, 1); + msg.setReadOnlyBody(true); + assertTrue(msg.getLong(this.name) == 1); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testGetFloat() { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + try { + msg.setFloat(this.name, 1.5f); + msg.setReadOnlyBody(true); + assertTrue(msg.getFloat(this.name) == 1.5f); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testGetDouble() { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + try { + msg.setDouble(this.name, 1.5); + msg.setReadOnlyBody(true); + assertTrue(msg.getDouble(this.name) == 1.5); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testGetString() { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + try { + String str = "test"; + msg.setString(this.name, str); + msg.setReadOnlyBody(true); + assertTrue(msg.getString(this.name) == str); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testGetBytes() { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + try { + byte[] bytes1 = new byte[3]; + byte[] bytes2 = new byte[2]; + System.arraycopy(bytes1, 0, bytes2, 0, 2); + msg.setBytes(this.name, bytes1); + msg.setBytes(this.name + "2", bytes1, 0, 2); + msg.setReadOnlyBody(true); + assertTrue(msg.getBytes(this.name) == bytes1); + assertEquals(msg.getBytes(this.name + "2").length, bytes2.length); + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testGetObject() throws JMSException { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + Boolean booleanValue = Boolean.TRUE; + Byte byteValue = Byte.valueOf("1"); + byte[] bytesValue = new byte[3]; + Character charValue = new Character('a'); + Double doubleValue = Double.valueOf("1.5"); + Float floatValue = Float.valueOf("1.5"); + Integer intValue = Integer.valueOf("1"); + Long longValue = Long.valueOf("1"); + Short shortValue = Short.valueOf("1"); + String stringValue = "string"; + + try { + msg.setObject("boolean", booleanValue); + msg.setObject("byte", byteValue); + msg.setObject("bytes", bytesValue); + msg.setObject("char", charValue); + msg.setObject("double", doubleValue); + msg.setObject("float", floatValue); + msg.setObject("int", intValue); + msg.setObject("long", longValue); + msg.setObject("short", shortValue); + msg.setObject("string", stringValue); + } catch (MessageFormatException mfe) { + fail("object formats should be correct"); + } + + msg.onSend(); + msg.setContent(msg.getContent()); + + assertTrue(msg.getObject("boolean") instanceof Boolean); + assertEquals(msg.getObject("boolean"), booleanValue); + assertEquals(msg.getBoolean("boolean"), booleanValue.booleanValue()); + assertTrue(msg.getObject("byte") instanceof Byte); + assertEquals(msg.getObject("byte"), byteValue); + assertEquals(msg.getByte("byte"), byteValue.byteValue()); + assertTrue(msg.getObject("bytes") instanceof byte[]); + assertEquals(((byte[]) msg.getObject("bytes")).length, bytesValue.length); + assertEquals(msg.getBytes("bytes").length, bytesValue.length); + assertTrue(msg.getObject("char") instanceof Character); + assertEquals(msg.getObject("char"), charValue); + assertEquals(msg.getChar("char"), charValue.charValue()); + assertTrue(msg.getObject("double") instanceof Double); + assertEquals(msg.getObject("double"), doubleValue); + assertEquals(msg.getDouble("double"), doubleValue.doubleValue(), 0); + assertTrue(msg.getObject("float") instanceof Float); + assertEquals(msg.getObject("float"), floatValue); + assertEquals(msg.getFloat("float"), floatValue.floatValue(), 0); + assertTrue(msg.getObject("int") instanceof Integer); + assertEquals(msg.getObject("int"), intValue); + assertEquals(msg.getInt("int"), intValue.intValue()); + assertTrue(msg.getObject("long") instanceof Long); + assertEquals(msg.getObject("long"), longValue); + assertEquals(msg.getLong("long"), longValue.longValue()); + assertTrue(msg.getObject("short") instanceof Short); + assertEquals(msg.getObject("short"), shortValue); + assertEquals(msg.getShort("short"), shortValue.shortValue()); + assertTrue(msg.getObject("string") instanceof String); + assertEquals(msg.getObject("string"), stringValue); + assertEquals(msg.getString("string"), stringValue); + + msg.clearBody(); + try { + msg.setObject("object", new Object()); + fail("should have thrown exception"); + } catch (MessageFormatException e) { + } + + } + + public void testGetMapNames() throws JMSException { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + msg.setBoolean("boolean", true); + msg.setByte("byte", (byte) 1); + msg.setBytes("bytes1", new byte[1]); + msg.setBytes("bytes2", new byte[3], 0, 2); + msg.setChar("char", 'a'); + msg.setDouble("double", 1.5); + msg.setFloat("float", 1.5f); + msg.setInt("int", 1); + msg.setLong("long", 1); + msg.setObject("object", "stringObj"); + msg.setShort("short", (short) 1); + msg.setString("string", "string"); + + msg.onSend(); + msg.setContent(msg.getContent()); + + Enumeration mapNamesEnum = msg.getMapNames(); + ArrayList mapNamesList = Collections.list(mapNamesEnum); + + assertEquals(mapNamesList.size(), 12); + assertTrue(mapNamesList.contains("boolean")); + assertTrue(mapNamesList.contains("byte")); + assertTrue(mapNamesList.contains("bytes1")); + assertTrue(mapNamesList.contains("bytes2")); + assertTrue(mapNamesList.contains("char")); + assertTrue(mapNamesList.contains("double")); + assertTrue(mapNamesList.contains("float")); + assertTrue(mapNamesList.contains("int")); + assertTrue(mapNamesList.contains("long")); + assertTrue(mapNamesList.contains("object")); + assertTrue(mapNamesList.contains("short")); + assertTrue(mapNamesList.contains("string")); + } + + public void testItemExists() throws JMSException { + ActiveMQMapMessage mapMessage = new ActiveMQMapMessage(); + + mapMessage.setString("exists", "test"); + + mapMessage.onSend(); + mapMessage.setContent(mapMessage.getContent()); + + assertTrue(mapMessage.itemExists("exists")); + assertFalse(mapMessage.itemExists("doesntExist")); + } + + public void testClearBody() throws JMSException { + ActiveMQMapMessage mapMessage = new ActiveMQMapMessage(); + mapMessage.setString("String", "String"); + mapMessage.clearBody(); + assertFalse(mapMessage.isReadOnlyBody()); + + mapMessage.onSend(); + mapMessage.setContent(mapMessage.getContent()); + + assertNull(mapMessage.getString("String")); + mapMessage.clearBody(); + mapMessage.setString("String", "String"); + + mapMessage.onSend(); + mapMessage.setContent(mapMessage.getContent()); + + mapMessage.getString("String"); + } + + public void testReadOnlyBody() throws JMSException { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + msg.setBoolean("boolean", true); + msg.setByte("byte", (byte) 1); + msg.setBytes("bytes", new byte[1]); + msg.setBytes("bytes2", new byte[3], 0, 2); + msg.setChar("char", 'a'); + msg.setDouble("double", 1.5); + msg.setFloat("float", 1.5f); + msg.setInt("int", 1); + msg.setLong("long", 1); + msg.setObject("object", "stringObj"); + msg.setShort("short", (short) 1); + msg.setString("string", "string"); + + msg.setReadOnlyBody(true); + + try { + msg.getBoolean("boolean"); + msg.getByte("byte"); + msg.getBytes("bytes"); + msg.getChar("char"); + msg.getDouble("double"); + msg.getFloat("float"); + msg.getInt("int"); + msg.getLong("long"); + msg.getObject("object"); + msg.getShort("short"); + msg.getString("string"); + } catch (MessageNotReadableException mnre) { + fail("should be readable"); + } + try { + msg.setBoolean("boolean", true); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + msg.setByte("byte", (byte) 1); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + msg.setBytes("bytes", new byte[1]); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + msg.setBytes("bytes2", new byte[3], 0, 2); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + msg.setChar("char", 'a'); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + msg.setDouble("double", 1.5); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + msg.setFloat("float", 1.5f); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + msg.setInt("int", 1); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + msg.setLong("long", 1); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + msg.setObject("object", "stringObj"); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + msg.setShort("short", (short) 1); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + msg.setString("string", "string"); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + } + + public void testWriteOnlyBody() throws JMSException { + ActiveMQMapMessage msg = new ActiveMQMapMessage(); + msg.setReadOnlyBody(false); + + msg.setBoolean("boolean", true); + msg.setByte("byte", (byte) 1); + msg.setBytes("bytes", new byte[1]); + msg.setBytes("bytes2", new byte[3], 0, 2); + msg.setChar("char", 'a'); + msg.setDouble("double", 1.5); + msg.setFloat("float", 1.5f); + msg.setInt("int", 1); + msg.setLong("long", 1); + msg.setObject("object", "stringObj"); + msg.setShort("short", (short) 1); + msg.setString("string", "string"); + + msg.setReadOnlyBody(true); + + msg.getBoolean("boolean"); + msg.getByte("byte"); + msg.getBytes("bytes"); + msg.getChar("char"); + msg.getDouble("double"); + msg.getFloat("float"); + msg.getInt("int"); + msg.getLong("long"); + msg.getObject("object"); + msg.getShort("short"); + msg.getString("string"); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/command/ActiveMQMessageTest.java b/activemq-core/src/test/java/org/activemq/command/ActiveMQMessageTest.java new file mode 100755 index 0000000000..e47e4f8913 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/command/ActiveMQMessageTest.java @@ -0,0 +1,885 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Map; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageFormatException; +import javax.jms.MessageNotWriteableException; + +import junit.framework.TestCase; + +import org.activeio.ByteSequence; +import org.activeio.command.WireFormat; +import org.activemq.openwire.OpenWireFormat; +import org.activemq.state.CommandVisitor; + +/** + * @version $Revision$ + */ +public class ActiveMQMessageTest extends TestCase { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(ActiveMQMessageTest.class); + + private String jmsMessageID; + private String jmsCorrelationID; + private ActiveMQDestination jmsDestination; + private ActiveMQDestination jmsReplyTo; + private int jmsDeliveryMode; + private boolean jmsRedelivered; + private String jmsType; + private long jmsExpiration; + private int jmsPriority; + private long jmsTimestamp; + protected boolean readOnlyMessage; + private long[] consumerIDs; + + + public static void main(String[] args) { + } + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + this.jmsMessageID = "testid"; + this.jmsCorrelationID = "testcorrelationid"; + this.jmsDestination = new ActiveMQTopic("test.topic"); + this.jmsReplyTo = new ActiveMQTempTopic("test.replyto.topic:001"); + this.jmsDeliveryMode = Message.DEFAULT_DELIVERY_MODE; + this.jmsRedelivered = true; + this.jmsType = "test type"; + this.jmsExpiration = 100000; + this.jmsPriority = 5; + this.jmsTimestamp = System.currentTimeMillis(); + this.readOnlyMessage = false; + this.consumerIDs = new long[3]; + for (int i = 0; i < this.consumerIDs.length; i++) { + this.consumerIDs[i] = i; + } + + } + + /* + * @see TestCase#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Constructor for ActiveMQMessageTest. + * + * @param arg0 + */ + public ActiveMQMessageTest(String arg0) { + super(arg0); + } + + public void testGetDataStructureType() { + ActiveMQMessage msg = new ActiveMQMessage(); + assertEquals(msg.getDataStructureType(), CommandTypes.ACTIVEMQ_MESSAGE); + } + + public void testHashCode() throws Exception { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setJMSMessageID(this.jmsMessageID); + assertTrue(msg.hashCode() == jmsMessageID.hashCode()); + } + + public void testSetReadOnly() { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setReadOnlyProperties(true); + boolean test = false; + try { + msg.setIntProperty("test", 1); + } catch (MessageNotWriteableException me) { + test = true; + } catch (JMSException e) { + e.printStackTrace(System.err); + test = false; + } + assertTrue(test); + } + + /* + * Class to test for boolean equals(Object) + */ + public void testEqualsObject() throws Exception { + ActiveMQMessage msg1 = new ActiveMQMessage(); + ActiveMQMessage msg2 = new ActiveMQMessage(); + msg1.setJMSMessageID(this.jmsMessageID); + assertTrue(!msg1.equals(msg2)); + msg2.setJMSMessageID(this.jmsMessageID); + assertTrue(msg1.equals(msg2)); + } + + public void testShallowCopy() throws Exception { + ActiveMQMessage msg1 = new ActiveMQMessage(); + msg1.setJMSMessageID(jmsMessageID); + ActiveMQMessage msg2 = (ActiveMQMessage) msg1.copy(); + assertTrue(msg1 != msg2 && msg1.equals(msg2)); + } + + public void testCopy() throws Exception { + this.jmsMessageID = "testid"; + this.jmsCorrelationID = "testcorrelationid"; + this.jmsDestination = new ActiveMQTopic("test.topic"); + this.jmsReplyTo = new ActiveMQTempTopic("test.replyto.topic:001"); + this.jmsDeliveryMode = Message.DEFAULT_DELIVERY_MODE; + this.jmsRedelivered = true; + this.jmsType = "test type"; + this.jmsExpiration = 100000; + this.jmsPriority = 5; + this.jmsTimestamp = System.currentTimeMillis(); + this.readOnlyMessage = false; + + ActiveMQMessage msg1 = new ActiveMQMessage(); + msg1.setJMSMessageID(this.jmsMessageID); + msg1.setJMSCorrelationID(this.jmsCorrelationID); + msg1.setJMSDestination(this.jmsDestination); + msg1.setJMSReplyTo(this.jmsReplyTo); + msg1.setJMSDeliveryMode(this.jmsDeliveryMode); + msg1.setJMSRedelivered(this.jmsRedelivered); + msg1.setJMSType(this.jmsType); + msg1.setJMSExpiration(this.jmsExpiration); + msg1.setJMSPriority(this.jmsPriority); + msg1.setJMSTimestamp(this.jmsTimestamp); + msg1.setReadOnlyProperties(true); + ActiveMQMessage msg2 = new ActiveMQMessage(); + msg1.copy(msg2); + assertTrue(msg1.getJMSMessageID().equals(msg2.getJMSMessageID())); + assertTrue(msg1.getJMSCorrelationID().equals(msg2.getJMSCorrelationID())); + assertTrue(msg1.getJMSDestination().equals(msg2.getJMSDestination())); + assertTrue(msg1.getJMSReplyTo().equals(msg2.getJMSReplyTo())); + assertTrue(msg1.getJMSDeliveryMode() == msg2.getJMSDeliveryMode()); + assertTrue(msg1.getJMSRedelivered() == msg2.getJMSRedelivered()); + assertTrue(msg1.getJMSType().equals(msg2.getJMSType())); + assertTrue(msg1.getJMSExpiration() == msg2.getJMSExpiration()); + assertTrue(msg1.getJMSPriority() == msg2.getJMSPriority()); + assertTrue(msg1.getJMSTimestamp() == msg2.getJMSTimestamp()); + } + + public void testGetAndSetJMSMessageID() throws Exception { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setJMSMessageID(this.jmsMessageID); + assertEquals(msg.getJMSMessageID(), this.jmsMessageID); + } + + public void testGetAndSetJMSTimestamp() { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setJMSTimestamp(this.jmsTimestamp); + assertTrue(msg.getJMSTimestamp() == this.jmsTimestamp); + } + + public void testGetJMSCorrelationIDAsBytes() throws Exception { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setJMSCorrelationID(this.jmsCorrelationID); + byte[] testbytes = msg.getJMSCorrelationIDAsBytes(); + String str2 = new String(testbytes); + assertTrue(this.jmsCorrelationID.equals(str2)); + } + + public void testSetJMSCorrelationIDAsBytes() throws Exception { + ActiveMQMessage msg = new ActiveMQMessage(); + byte[] testbytes = this.jmsCorrelationID.getBytes(); + msg.setJMSCorrelationIDAsBytes(testbytes); + testbytes = msg.getJMSCorrelationIDAsBytes(); + String str2 = new String(testbytes); + assertTrue(this.jmsCorrelationID.equals(str2)); + } + + public void testGetAndSetJMSCorrelationID() { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setJMSCorrelationID(this.jmsCorrelationID); + assertTrue(msg.getJMSCorrelationID().equals(this.jmsCorrelationID)); + } + + public void testGetAndSetJMSReplyTo() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setJMSReplyTo(this.jmsReplyTo); + assertTrue(msg.getJMSReplyTo().equals(this.jmsReplyTo)); + } + + public void testGetAndSetJMSDestination() throws Exception { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setJMSDestination(this.jmsDestination); + assertTrue(msg.getJMSDestination().equals(this.jmsDestination)); + } + + public void testGetAndSetJMSDeliveryMode() { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setJMSDeliveryMode(this.jmsDeliveryMode); + assertTrue(msg.getJMSDeliveryMode() == this.jmsDeliveryMode); + } + + public void testGetAndSetMSRedelivered() { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setJMSRedelivered(this.jmsRedelivered); + assertTrue(msg.getJMSRedelivered() == this.jmsRedelivered); + } + + public void testGetAndSetJMSType() { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setJMSType(this.jmsType); + assertTrue(msg.getJMSType().equals(this.jmsType)); + } + + public void testGetAndSetJMSExpiration() { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setJMSExpiration(this.jmsExpiration); + assertTrue(msg.getJMSExpiration() == this.jmsExpiration); + } + + public void testGetAndSetJMSPriority() { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setJMSPriority(this.jmsPriority); + assertTrue(msg.getJMSPriority() == this.jmsPriority); + } + + public void testClearProperties() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setStringProperty("test", "test"); + msg.setContent(new ByteSequence(new byte[1],0,0)); + msg.setJMSMessageID(this.jmsMessageID); + msg.clearProperties(); + assertNull(msg.getStringProperty("test")); + assertNotNull(msg.getJMSMessageID()); + assertNotNull(msg.getContent()); + } + + public void testPropertyExists() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setStringProperty("test", "test"); + assertTrue(msg.propertyExists("test")); + } + + public void testGetBooleanProperty() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String name = "booleanProperty"; + msg.setBooleanProperty(name, true); + assertTrue(msg.getBooleanProperty(name)); + } + + public void testGetByteProperty() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String name = "byteProperty"; + msg.setByteProperty(name, (byte) 1); + assertTrue(msg.getByteProperty(name) == 1); + } + + public void testGetShortProperty() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String name = "shortProperty"; + msg.setShortProperty(name, (short) 1); + assertTrue(msg.getShortProperty(name) == 1); + } + + public void testGetIntProperty() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String name = "intProperty"; + msg.setIntProperty(name, 1); + assertTrue(msg.getIntProperty(name) == 1); + } + + public void testGetLongProperty() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String name = "longProperty"; + msg.setLongProperty(name, 1); + assertTrue(msg.getLongProperty(name) == 1); + } + + public void testGetFloatProperty() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String name = "floatProperty"; + msg.setFloatProperty(name, 1.3f); + assertTrue(msg.getFloatProperty(name) == 1.3f); + } + + public void testGetDoubleProperty() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String name = "doubleProperty"; + msg.setDoubleProperty(name, 1.3d); + assertTrue(msg.getDoubleProperty(name) == 1.3); + } + + public void testGetStringProperty() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String name = "stringProperty"; + msg.setStringProperty(name, name); + assertTrue(msg.getStringProperty(name).equals(name)); + } + + public void testGetObjectProperty() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String name = "floatProperty"; + msg.setFloatProperty(name, 1.3f); + assertTrue(msg.getObjectProperty(name) instanceof Float); + assertTrue(((Float) msg.getObjectProperty(name)).floatValue() == 1.3f); + } + + public void testGetPropertyNames() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String name = "floatProperty"; + msg.setFloatProperty(name, 1.3f); + for (Enumeration iter = msg.getPropertyNames(); iter.hasMoreElements();) { + assertTrue(iter.nextElement().equals(name)); + } + } + + public void testSetObjectProperty() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String name = "property"; + + try { + msg.setObjectProperty(name, "string"); + msg.setObjectProperty(name, Byte.valueOf("1")); + msg.setObjectProperty(name, Short.valueOf("1")); + msg.setObjectProperty(name, Integer.valueOf("1")); + msg.setObjectProperty(name, Long.valueOf("1")); + msg.setObjectProperty(name, Float.valueOf("1.1f")); + msg.setObjectProperty(name, Double.valueOf("1.1")); + msg.setObjectProperty(name, Boolean.TRUE); + msg.setObjectProperty(name, null); + } catch (MessageFormatException e) { + fail("should accept object primitives and String"); + } + try { + msg.setObjectProperty(name, new byte[5]); + fail("should accept only object primitives and String"); + } catch (MessageFormatException e) { + } + try { + msg.setObjectProperty(name, new Object()); + fail("should accept only object primitives and String"); + } catch (MessageFormatException e) { + } + } + + public void testConvertProperties() throws Exception { + org.activemq.command.Message msg = new org.activemq.command.Message() { + public org.activemq.command.Message copy() { + return null; + } + public void beforeMarshall(WireFormat wireFormat) throws IOException { + super.beforeMarshall(wireFormat); + } + public byte getDataStructureType() { + return 0; + } + public Response visit(CommandVisitor visitor) throws Throwable { + return null; + } + }; + + msg.setProperty("stringProperty", "string"); + msg.setProperty("byteProperty", Byte.valueOf("1")); + msg.setProperty("shortProperty", Short.valueOf("1")); + msg.setProperty("intProperty", Integer.valueOf("1")); + msg.setProperty("longProperty", Long.valueOf("1")); + msg.setProperty("floatProperty", Float.valueOf("1.1f")); + msg.setProperty("doubleProperty", Double.valueOf("1.1")); + msg.setProperty("booleanProperty", Boolean.TRUE); + msg.setProperty("nullProperty", null); + + msg.beforeMarshall(new OpenWireFormat()); + + Map properties = msg.getProperties(); + assertEquals(properties.get("stringProperty"),"string"); + assertEquals(((Byte)properties.get("byteProperty")).byteValue(),1); + assertEquals(((Short)properties.get("shortProperty")).shortValue(),1); + assertEquals(((Integer)properties.get("intProperty")).intValue(),1); + assertEquals(((Long)properties.get("longProperty")).longValue(),1); + assertEquals(((Float)properties.get("floatProperty")).floatValue(),1.1f,0); + assertEquals(((Double)properties.get("doubleProperty")).doubleValue(),1.1,0); + assertEquals(((Boolean)properties.get("booleanProperty")).booleanValue(),true); + assertNull(properties.get("nullProperty")); + + } + + public void testSetNullProperty() throws JMSException { + Message msg = new ActiveMQMessage(); + String name = "cheese"; + msg.setStringProperty(name, "Cheddar"); + assertEquals("Cheddar", msg.getStringProperty(name)); + + msg.setStringProperty(name, null); + assertEquals(null, msg.getStringProperty(name)); + } + + public void testSetNullPropertyName() throws JMSException { + Message msg = new ActiveMQMessage(); + + try { + msg.setStringProperty(null, "Cheese"); + fail("Should have thrown exception"); + } catch (IllegalArgumentException e) { + log.info("Worked, caught: " + e); + } + } + + public void testSetEmptyPropertyName() throws JMSException { + Message msg = new ActiveMQMessage(); + + try { + msg.setStringProperty("", "Cheese"); + fail("Should have thrown exception"); + } catch (IllegalArgumentException e) { + log.info("Worked, caught: " + e); + } + } + + public void testGetAndSetJMSXDeliveryCount() throws JMSException { + Message msg = new ActiveMQMessage(); + msg.setIntProperty("JMSXDeliveryCount", 1); + int count = msg.getIntProperty("JMSXDeliveryCount"); + assertTrue("expected delivery count = 1 - got: " + count, count == 1); + } + + public void testClearBody() throws JMSException { + ActiveMQBytesMessage message = new ActiveMQBytesMessage(); + message.clearBody(); + assertFalse(message.isReadOnlyBody()); + assertNull(message.getContent()); + } + + public void testBooleanPropertyConversion() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String propertyName = "property"; + msg.setBooleanProperty(propertyName, true); + + assertEquals(((Boolean) msg.getObjectProperty(propertyName)).booleanValue(), true); + assertTrue(msg.getBooleanProperty(propertyName)); + assertEquals(msg.getStringProperty(propertyName), "true"); + try { + msg.getByteProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getShortProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getIntProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getLongProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getFloatProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getDoubleProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + } + + public void testBytePropertyConversion() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String propertyName = "property"; + msg.setByteProperty(propertyName, (byte) 1); + + assertEquals(((Byte) msg.getObjectProperty(propertyName)).byteValue(), 1); + assertEquals(msg.getByteProperty(propertyName), 1); + assertEquals(msg.getShortProperty(propertyName), 1); + assertEquals(msg.getIntProperty(propertyName), 1); + assertEquals(msg.getLongProperty(propertyName), 1); + assertEquals(msg.getStringProperty(propertyName), "1"); + try { + msg.getBooleanProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getFloatProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getDoubleProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + } + + public void testShortPropertyConversion() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String propertyName = "property"; + msg.setShortProperty(propertyName, (short) 1); + + assertEquals(((Short) msg.getObjectProperty(propertyName)).shortValue(), 1); + assertEquals(msg.getShortProperty(propertyName), 1); + assertEquals(msg.getIntProperty(propertyName), 1); + assertEquals(msg.getLongProperty(propertyName), 1); + assertEquals(msg.getStringProperty(propertyName), "1"); + try { + msg.getBooleanProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getByteProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getFloatProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getDoubleProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + } + + public void testIntPropertyConversion() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String propertyName = "property"; + msg.setIntProperty(propertyName, (int) 1); + + assertEquals(((Integer) msg.getObjectProperty(propertyName)).intValue(), 1); + assertEquals(msg.getIntProperty(propertyName), 1); + assertEquals(msg.getLongProperty(propertyName), 1); + assertEquals(msg.getStringProperty(propertyName), "1"); + try { + msg.getBooleanProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getByteProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getShortProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getFloatProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getDoubleProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + } + + public void testLongPropertyConversion() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String propertyName = "property"; + msg.setLongProperty(propertyName, 1); + + assertEquals(((Long) msg.getObjectProperty(propertyName)).longValue(), 1); + assertEquals(msg.getLongProperty(propertyName), 1); + assertEquals(msg.getStringProperty(propertyName), "1"); + try { + msg.getBooleanProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getByteProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getShortProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getIntProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getFloatProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getDoubleProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + } + + public void testFloatPropertyConversion() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String propertyName = "property"; + msg.setFloatProperty(propertyName, (float) 1.5); + assertEquals(((Float) msg.getObjectProperty(propertyName)).floatValue(), 1.5, 0); + assertEquals(msg.getFloatProperty(propertyName), 1.5, 0); + assertEquals(msg.getDoubleProperty(propertyName), 1.5, 0); + assertEquals(msg.getStringProperty(propertyName), "1.5"); + try { + msg.getBooleanProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getByteProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getShortProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getIntProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getLongProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + } + + public void testDoublePropertyConversion() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String propertyName = "property"; + msg.setDoubleProperty(propertyName, 1.5); + assertEquals(((Double) msg.getObjectProperty(propertyName)).doubleValue(), 1.5, 0); + assertEquals(msg.getDoubleProperty(propertyName), 1.5, 0); + assertEquals(msg.getStringProperty(propertyName), "1.5"); + try { + msg.getBooleanProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getByteProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getShortProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getIntProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getLongProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getFloatProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + } + + public void testStringPropertyConversion() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String propertyName = "property"; + String stringValue = "true"; + msg.setStringProperty(propertyName, stringValue); + assertEquals(msg.getStringProperty(propertyName), stringValue); + assertEquals((String) msg.getObjectProperty(propertyName), stringValue); + assertEquals(msg.getBooleanProperty(propertyName), true); + + stringValue = "1"; + msg.setStringProperty(propertyName, stringValue); + assertEquals(msg.getByteProperty(propertyName), 1); + assertEquals(msg.getShortProperty(propertyName), 1); + assertEquals(msg.getIntProperty(propertyName), 1); + assertEquals(msg.getLongProperty(propertyName), 1); + + stringValue = "1.5"; + msg.setStringProperty(propertyName, stringValue); + assertEquals(msg.getFloatProperty(propertyName), 1.5, 0); + assertEquals(msg.getDoubleProperty(propertyName), 1.5, 0); + + stringValue = "bad"; + msg.setStringProperty(propertyName, stringValue); + try { + msg.getByteProperty(propertyName); + fail("Should have thrown exception"); + } catch (NumberFormatException e) { + } + try { + msg.getShortProperty(propertyName); + fail("Should have thrown exception"); + } catch (NumberFormatException e) { + } + try { + msg.getIntProperty(propertyName); + fail("Should have thrown exception"); + } catch (NumberFormatException e) { + } + try { + msg.getLongProperty(propertyName); + fail("Should have thrown exception"); + } catch (NumberFormatException e) { + } + try { + msg.getFloatProperty(propertyName); + fail("Should have thrown exception"); + } catch (NumberFormatException e) { + } + try { + msg.getDoubleProperty(propertyName); + fail("Should have thrown exception"); + } catch (NumberFormatException e) { + } + assertFalse(msg.getBooleanProperty(propertyName)); + } + + public void testObjectPropertyConversion() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String propertyName = "property"; + Object obj = new Object(); + try { + ((org.activemq.command.Message)msg).setProperty(propertyName, obj); //bypass object check + } catch (IOException e) { + } + try { + msg.getStringProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getBooleanProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getByteProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getShortProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getIntProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getLongProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getFloatProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + try { + msg.getDoubleProperty(propertyName); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + + } + + public void testReadOnlyProperties() throws JMSException { + ActiveMQMessage msg = new ActiveMQMessage(); + String propertyName = "property"; + msg.setReadOnlyProperties(true); + + try { + msg.setObjectProperty(propertyName, new Object()); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException e) { + } + try { + msg.setStringProperty(propertyName, "test"); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException e) { + } + try { + msg.setBooleanProperty(propertyName, true); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException e) { + } + try { + msg.setByteProperty(propertyName, (byte) 1); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException e) { + } + try { + msg.setShortProperty(propertyName, (short) 1); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException e) { + } + try { + msg.setIntProperty(propertyName, 1); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException e) { + } + try { + msg.setLongProperty(propertyName, 1); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException e) { + } + try { + msg.setFloatProperty(propertyName, (float) 1.5); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException e) { + } + try { + msg.setDoubleProperty(propertyName, 1.5); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException e) { + } + } + + public void testIsExpired() { + ActiveMQMessage msg = new ActiveMQMessage(); + msg.setJMSExpiration(System.currentTimeMillis() - 1); + assertTrue(msg.isExpired()); + msg.setJMSExpiration(System.currentTimeMillis() + 10000); + assertFalse(msg.isExpired()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/command/ActiveMQObjectMessageTest.java b/activemq-core/src/test/java/org/activemq/command/ActiveMQObjectMessageTest.java new file mode 100755 index 0000000000..cce9e63b80 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/command/ActiveMQObjectMessageTest.java @@ -0,0 +1,127 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import junit.framework.TestCase; + +import javax.jms.JMSException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; +import java.io.IOException; + +/** + * @version $Revision$ + */ +public class ActiveMQObjectMessageTest extends TestCase { + + public static void main(String[] args) { + junit.textui.TestRunner.run(ActiveMQObjectMessageTest.class); + } + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + } + + /* + * @see TestCase#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Constructor for ActiveMQObjectMessageTest. + * + * @param arg0 + */ + public ActiveMQObjectMessageTest(String arg0) { + super(arg0); + } + + public void testBytes() throws JMSException, IOException { + ActiveMQObjectMessage msg = new ActiveMQObjectMessage(); + String str = "testText"; + msg.setObject(str); + + msg = (ActiveMQObjectMessage) msg.copy(); + assertEquals(msg.getObject(), str); + + } + + public void testSetObject() throws JMSException { + ActiveMQObjectMessage msg = new ActiveMQObjectMessage(); + String str = "testText"; + msg.setObject(str); + assertTrue(msg.getObject() == str); + } + + public void testClearBody() throws JMSException { + ActiveMQObjectMessage objectMessage = new ActiveMQObjectMessage(); + try { + objectMessage.setObject("String"); + objectMessage.clearBody(); + assertFalse(objectMessage.isReadOnlyBody()); + assertNull(objectMessage.getObject()); + objectMessage.setObject("String"); + objectMessage.getObject(); + } catch (MessageNotWriteableException mnwe) { + fail("should be writeable"); + } + } + + public void testReadOnlyBody() throws JMSException { + ActiveMQObjectMessage msg = new ActiveMQObjectMessage(); + msg.setObject("test"); + msg.setReadOnlyBody(true); + try { + msg.getObject(); + } catch (MessageNotReadableException e) { + fail("should be readable"); + } + try { + msg.setObject("test"); + fail("should throw exception"); + } catch (MessageNotWriteableException e) { + } + } + + public void testWriteOnlyBody() throws JMSException { // should always be readable + ActiveMQObjectMessage msg = new ActiveMQObjectMessage(); + msg.setReadOnlyBody(false); + try { + msg.setObject("test"); + msg.getObject(); + } catch (MessageNotReadableException e) { + fail("should be readable"); + } + msg.setReadOnlyBody(true); + try { + msg.getObject(); + msg.setObject("test"); + fail("should throw exception"); + } catch (MessageNotReadableException e) { + fail("should be readable"); + } catch (MessageNotWriteableException mnwe) { + } + } + +} diff --git a/activemq-core/src/test/java/org/activemq/command/ActiveMQStreamMessageTest.java b/activemq-core/src/test/java/org/activemq/command/ActiveMQStreamMessageTest.java new file mode 100755 index 0000000000..5b8d21c44d --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/command/ActiveMQStreamMessageTest.java @@ -0,0 +1,955 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.Serializable; + +import javax.jms.JMSException; +import javax.jms.MessageFormatException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; + +import junit.framework.TestCase; + +/** + * @version $Revision$ + */ +public class ActiveMQStreamMessageTest extends TestCase { + + public static void main(String[] args) { + junit.textui.TestRunner.run(ActiveMQStreamMessageTest.class); + } + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + } + + /* + * @see TestCase#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Constructor for ActiveMQStreamMessageTest. + * + * @param arg0 + */ + public ActiveMQStreamMessageTest(String arg0) { + super(arg0); + } + + public void testGetDataStructureType() { + ActiveMQStreamMessage msg = new ActiveMQStreamMessage(); + assertEquals(msg.getDataStructureType(), CommandTypes.ACTIVEMQ_STREAM_MESSAGE); + } + + public void testReadBoolean() { + ActiveMQStreamMessage msg = new ActiveMQStreamMessage(); + try { + msg.writeBoolean(true); + msg.reset(); + assertTrue(msg.readBoolean()); + msg.reset(); + assertTrue(msg.readString().equals("true")); + msg.reset(); + try { + msg.readByte(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readShort(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readInt(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readLong(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readFloat(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readDouble(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readChar(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readBytes(new byte[1]); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testreadByte() { + ActiveMQStreamMessage msg = new ActiveMQStreamMessage(); + try { + byte test = (byte) 4; + msg.writeByte(test); + msg.reset(); + assertTrue(msg.readByte() == test); + msg.reset(); + assertTrue(msg.readShort() == test); + msg.reset(); + assertTrue(msg.readInt() == test); + msg.reset(); + assertTrue(msg.readLong() == test); + msg.reset(); + assertTrue(msg.readString().equals(new Byte(test).toString())); + msg.reset(); + try { + msg.readBoolean(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readFloat(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readDouble(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readChar(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readBytes(new byte[1]); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadShort() { + ActiveMQStreamMessage msg = new ActiveMQStreamMessage(); + try { + short test = (short) 4; + msg.writeShort(test); + msg.reset(); + assertTrue(msg.readShort() == test); + msg.reset(); + assertTrue(msg.readInt() == test); + msg.reset(); + assertTrue(msg.readLong() == test); + msg.reset(); + assertTrue(msg.readString().equals(new Short(test).toString())); + msg.reset(); + try { + msg.readBoolean(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readByte(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readFloat(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readDouble(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readChar(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readBytes(new byte[1]); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadChar() { + ActiveMQStreamMessage msg = new ActiveMQStreamMessage(); + try { + char test = 'z'; + msg.writeChar(test); + msg.reset(); + assertTrue(msg.readChar() == test); + msg.reset(); + assertTrue(msg.readString().equals(new Character(test).toString())); + msg.reset(); + try { + msg.readBoolean(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readByte(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readShort(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readInt(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readLong(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readFloat(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readDouble(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readBytes(new byte[1]); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadInt() { + ActiveMQStreamMessage msg = new ActiveMQStreamMessage(); + try { + int test = 4; + msg.writeInt(test); + msg.reset(); + assertTrue(msg.readInt() == test); + msg.reset(); + assertTrue(msg.readLong() == test); + msg.reset(); + assertTrue(msg.readString().equals(new Integer(test).toString())); + msg.reset(); + try { + msg.readBoolean(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readByte(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readShort(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readFloat(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readDouble(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readChar(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readBytes(new byte[1]); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadLong() { + ActiveMQStreamMessage msg = new ActiveMQStreamMessage(); + try { + long test = 4l; + msg.writeLong(test); + msg.reset(); + assertTrue(msg.readLong() == test); + msg.reset(); + assertTrue(msg.readString().equals(new Long(test).toString())); + msg.reset(); + try { + msg.readBoolean(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readByte(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readShort(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readInt(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readFloat(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readDouble(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readChar(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readBytes(new byte[1]); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadFloat() { + ActiveMQStreamMessage msg = new ActiveMQStreamMessage(); + try { + float test = 4.4f; + msg.writeFloat(test); + msg.reset(); + assertTrue(msg.readFloat() == test); + msg.reset(); + assertTrue(msg.readDouble() == test); + msg.reset(); + assertTrue(msg.readString().equals(new Float(test).toString())); + msg.reset(); + try { + msg.readBoolean(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readByte(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readShort(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readInt(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readLong(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readChar(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readBytes(new byte[1]); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadDouble() { + ActiveMQStreamMessage msg = new ActiveMQStreamMessage(); + try { + double test = 4.4d; + msg.writeDouble(test); + msg.reset(); + assertTrue(msg.readDouble() == test); + msg.reset(); + assertTrue(msg.readString().equals(new Double(test).toString())); + msg.reset(); + try { + msg.readBoolean(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readByte(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readShort(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readInt(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readLong(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readFloat(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readChar(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readBytes(new byte[1]); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadString() { + ActiveMQStreamMessage msg = new ActiveMQStreamMessage(); + try { + byte testByte = (byte) 2; + msg.writeString(new Byte(testByte).toString()); + msg.reset(); + assertTrue(msg.readByte() == testByte); + msg.clearBody(); + short testShort = 3; + msg.writeString(new Short(testShort).toString()); + msg.reset(); + assertTrue(msg.readShort() == testShort); + msg.clearBody(); + int testInt = 4; + msg.writeString(new Integer(testInt).toString()); + msg.reset(); + assertTrue(msg.readInt() == testInt); + msg.clearBody(); + long testLong = 6l; + msg.writeString(new Long(testLong).toString()); + msg.reset(); + assertTrue(msg.readLong() == testLong); + msg.clearBody(); + float testFloat = 6.6f; + msg.writeString(new Float(testFloat).toString()); + msg.reset(); + assertTrue(msg.readFloat() == testFloat); + msg.clearBody(); + double testDouble = 7.7d; + msg.writeString(new Double(testDouble).toString()); + msg.reset(); + assertTrue(msg.readDouble() == testDouble); + msg.clearBody(); + msg.writeString("true"); + msg.reset(); + assertTrue(msg.readBoolean()); + msg.clearBody(); + msg.writeString("a"); + msg.reset(); + try { + msg.readChar(); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + msg.clearBody(); + msg.writeString("777"); + msg.reset(); + try { + msg.readBytes(new byte[3]); + fail("Should have thrown exception"); + } catch (MessageFormatException e) { + } + + + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadBytes() { + ActiveMQStreamMessage msg = new ActiveMQStreamMessage(); + try { + byte[] test = new byte[50]; + for (int i = 0; i < test.length; i++) { + test[i] = (byte) i; + } + msg.writeBytes(test); + msg.reset(); + byte[] valid = new byte[test.length]; + msg.readBytes(valid); + for (int i = 0; i < valid.length; i++) { + assertTrue(valid[i] == test[i]); + } + msg.reset(); + try { + msg.readByte(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readShort(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readInt(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readLong(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readFloat(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readChar(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + msg.reset(); + try { + msg.readString(); + fail("Should have thrown exception"); + } catch (MessageFormatException mfe) { + } + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testReadObject() { + ActiveMQStreamMessage msg = new ActiveMQStreamMessage(); + try { + byte testByte = (byte) 2; + msg.writeByte(testByte); + msg.reset(); + assertTrue(((Byte) msg.readObject()).byteValue() == testByte); + msg.clearBody(); + + short testShort = 3; + msg.writeShort(testShort); + msg.reset(); + assertTrue(((Short) msg.readObject()).shortValue() == testShort); + msg.clearBody(); + + int testInt = 4; + msg.writeInt(testInt); + msg.reset(); + assertTrue(((Integer) msg.readObject()).intValue() == testInt); + msg.clearBody(); + + long testLong = 6l; + msg.writeLong(testLong); + msg.reset(); + assertTrue(((Long) msg.readObject()).longValue() == testLong); + msg.clearBody(); + + float testFloat = 6.6f; + msg.writeFloat(testFloat); + msg.reset(); + assertTrue(((Float) msg.readObject()).floatValue() == testFloat); + msg.clearBody(); + + double testDouble = 7.7d; + msg.writeDouble(testDouble); + msg.reset(); + assertTrue(((Double) msg.readObject()).doubleValue() == testDouble); + msg.clearBody(); + + char testChar = 'z'; + msg.writeChar(testChar); + msg.reset(); + assertTrue(((Character) msg.readObject()).charValue() == testChar); + msg.clearBody(); + + byte[] data = new byte[50]; + for (int i = 0; i < data.length; i++) { + data[i] = (byte) i; + } + msg.writeBytes(data); + msg.reset(); + byte[] valid = (byte[]) msg.readObject(); + assertTrue(valid.length == data.length); + for (int i = 0; i < valid.length; i++) { + assertTrue(valid[i] == data[i]); + } + msg.clearBody(); + msg.writeBoolean(true); + msg.reset(); + assertTrue(((Boolean) msg.readObject()).booleanValue()); + + + } catch (JMSException jmsEx) { + jmsEx.printStackTrace(); + assertTrue(false); + } + } + + public void testClearBody() throws JMSException { + ActiveMQStreamMessage streamMessage = new ActiveMQStreamMessage(); + try { + streamMessage.writeObject(new Serializable() { + private static final long serialVersionUID = -5181896809607968727L; + }); + streamMessage.clearBody(); + assertFalse(streamMessage.isReadOnlyBody()); + streamMessage.writeObject(new Serializable() { + private static final long serialVersionUID = 5074177640797561141L; + }); + streamMessage.readObject(); + fail("should throw exception"); + } catch (MessageNotReadableException mnwe) { + } catch (MessageNotWriteableException mnwe) { + fail("should be writeable"); + } + } + + public void testReset() throws JMSException { + ActiveMQStreamMessage streamMessage = new ActiveMQStreamMessage(); + try { + streamMessage.writeDouble(24.5); + streamMessage.writeLong(311); + } catch (MessageNotWriteableException mnwe) { + fail("should be writeable"); + } + streamMessage.reset(); + try { + assertTrue(streamMessage.isReadOnlyBody()); + assertEquals(streamMessage.readDouble(), 24.5, 0); + assertEquals(streamMessage.readLong(), 311); + } catch (MessageNotReadableException mnre) { + fail("should be readable"); + } + try { + streamMessage.writeInt(33); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + } + + public void testReadOnlyBody() throws JMSException { + ActiveMQStreamMessage message = new ActiveMQStreamMessage(); + try { + message.writeBoolean(true); + message.writeByte((byte) 1); + message.writeBytes(new byte[1]); + message.writeBytes(new byte[3], 0, 2); + message.writeChar('a'); + message.writeDouble(1.5); + message.writeFloat((float) 1.5); + message.writeInt(1); + message.writeLong(1); + message.writeObject("stringobj"); + message.writeShort((short) 1); + message.writeString("string"); + } catch (MessageNotWriteableException mnwe) { + fail("Should be writeable"); + } + message.reset(); + try { + message.readBoolean(); + message.readByte(); + assertEquals(1, message.readBytes(new byte[10])); + assertEquals(-1, message.readBytes(new byte[10])); + assertEquals(2, message.readBytes(new byte[10])); + assertEquals(-1, message.readBytes(new byte[10])); + message.readChar(); + message.readDouble(); + message.readFloat(); + message.readInt(); + message.readLong(); + message.readString(); + message.readShort(); + message.readString(); + } catch (MessageNotReadableException mnwe) { + fail("Should be readable"); + } + try { + message.writeBoolean(true); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeByte((byte) 1); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeBytes(new byte[1]); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeBytes(new byte[3], 0, 2); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeChar('a'); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeDouble(1.5); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeFloat((float) 1.5); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeInt(1); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeLong(1); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeObject("stringobj"); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeShort((short) 1); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + try { + message.writeString("string"); + fail("Should have thrown exception"); + } catch (MessageNotWriteableException mnwe) { + } + } + + public void testWriteOnlyBody() throws JMSException { + ActiveMQStreamMessage message = new ActiveMQStreamMessage(); + message.clearBody(); + try { + message.writeBoolean(true); + message.writeByte((byte) 1); + message.writeBytes(new byte[1]); + message.writeBytes(new byte[3], 0, 2); + message.writeChar('a'); + message.writeDouble(1.5); + message.writeFloat((float) 1.5); + message.writeInt(1); + message.writeLong(1); + message.writeObject("stringobj"); + message.writeShort((short) 1); + message.writeString("string"); + } catch (MessageNotWriteableException mnwe) { + fail("Should be writeable"); + } + try { + message.readBoolean(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException mnwe) { + } + try { + message.readByte(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readBytes(new byte[1]); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readBytes(new byte[2]); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readChar(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readDouble(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readFloat(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readInt(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readLong(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readString(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readShort(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + try { + message.readString(); + fail("Should have thrown exception"); + } catch (MessageNotReadableException e) { + } + } + + +} diff --git a/activemq-core/src/test/java/org/activemq/command/ActiveMQTextMessageTest.java b/activemq-core/src/test/java/org/activemq/command/ActiveMQTextMessageTest.java new file mode 100755 index 0000000000..ce01b3200b --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/command/ActiveMQTextMessageTest.java @@ -0,0 +1,129 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.command; + +import java.io.IOException; + +import javax.jms.JMSException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; + +import junit.framework.TestCase; +import junit.textui.TestRunner; + +import org.activeio.ByteSequence; + +/** + * @version $Revision$ + */ +public class ActiveMQTextMessageTest extends TestCase { + + public static void main(String[] args) { + TestRunner.run(ActiveMQTextMessageTest.class); + } + + public void testGetDataStructureType() { + ActiveMQTextMessage msg = new ActiveMQTextMessage(); + assertEquals(msg.getDataStructureType(), CommandTypes.ACTIVEMQ_TEXT_MESSAGE); + } + + public void testShallowCopy() throws JMSException { + ActiveMQTextMessage msg = new ActiveMQTextMessage(); + String string = "str"; + msg.setText(string); + Message copy = msg.copy(); + assertTrue(msg.getText() == ((ActiveMQTextMessage) copy).getText()); + } + + public void testSetText() { + ActiveMQTextMessage msg = new ActiveMQTextMessage(); + String str = "testText"; + try { + msg.setText(str); + assertEquals(msg.getText(), str); + } catch (JMSException e) { + e.printStackTrace(); + } + } + + public void testGetBytes() throws JMSException, IOException { + ActiveMQTextMessage msg = new ActiveMQTextMessage(); + String str = "testText"; + msg.setText(str); + msg.beforeMarshall(null); + + ByteSequence bytes = msg.getContent(); + msg = new ActiveMQTextMessage(); + msg.setContent(bytes); + + assertEquals(msg.getText(), str); + } + + public void testClearBody() throws JMSException, IOException { + ActiveMQTextMessage textMessage = new ActiveMQTextMessage(); + textMessage.setText("string"); + textMessage.clearBody(); + assertFalse(textMessage.isReadOnlyBody()); + assertNull(textMessage.getText()); + try { + textMessage.setText("String"); + textMessage.getText(); + } catch (MessageNotWriteableException mnwe) { + fail("should be writeable"); + } catch (MessageNotReadableException mnre) { + fail("should be readable"); + } + } + + public void testReadOnlyBody() throws JMSException { + ActiveMQTextMessage textMessage = new ActiveMQTextMessage(); + textMessage.setText("test"); + textMessage.setReadOnlyBody(true); + try { + textMessage.getText(); + } catch (MessageNotReadableException e) { + fail("should be readable"); + } + try { + textMessage.setText("test"); + fail("should throw exception"); + } catch (MessageNotWriteableException mnwe) { + } + } + + public void testWriteOnlyBody() throws JMSException { // should always be readable + ActiveMQTextMessage textMessage = new ActiveMQTextMessage(); + textMessage.setReadOnlyBody(false); + try { + textMessage.setText("test"); + textMessage.getText(); + } catch (MessageNotReadableException e) { + fail("should be readable"); + } + textMessage.setReadOnlyBody(true); + try { + textMessage.getText(); + textMessage.setText("test"); + fail("should throw exception"); + } catch (MessageNotReadableException e) { + fail("should be readable"); + } catch (MessageNotWriteableException mnwe) { + } + } +} diff --git a/activemq-core/src/test/java/org/activemq/command/DataStructureTestSupport.java b/activemq-core/src/test/java/org/activemq/command/DataStructureTestSupport.java new file mode 100755 index 0000000000..20a018e070 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/command/DataStructureTestSupport.java @@ -0,0 +1,166 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.command; + +import org.activeio.Packet; +import org.activeio.command.WireFormat; +import org.activemq.CombinationTestSupport; +import org.activemq.openwire.OpenWireFormat; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +public class DataStructureTestSupport extends CombinationTestSupport { + public boolean cacheEnabled; + public WireFormat wireFormat; + + public void assertBeanMarshalls(Object original) throws IOException { + Object o = marshalAndUnmarshall(original, wireFormat); + assertNotNull(o); + assertEquals(original, o); + // assertEquals(original.getClass(), o.getClass()); + // + // Method[] methods = original.getClass().getMethods(); + // for (int i = 0; i < methods.length; i++) { + // Method method = methods[i]; + // if( ( method.getName().startsWith("get") + // || method.getName().startsWith("is") + // ) + // && method.getParameterTypes().length==0 + // && method.getReturnType()!=null + // ) { + // try { + // Object expect = method.invoke(original, null); + // Object was = method.invoke(o, null); + // assertEquals(expect, was); + // } catch (IllegalArgumentException e) { + // } catch (IllegalAccessException e) { + // } catch (InvocationTargetException e) { + // } + // } + // } + } + + static public void assertEquals(Object expect, Object was) { + if (expect == null ^ was == null) + throw new AssertionFailedError("Not equals, expected: " + expect + ", was: " + was); + if (expect == null) + return; + if (expect.getClass() != was.getClass()) + throw new AssertionFailedError("Not equals, classes don't match. expected: " + expect.getClass() + ", was: " + was.getClass()); + if (expect.getClass().isArray()) { + Class componentType = expect.getClass().getComponentType(); + if (componentType.isPrimitive()) { + boolean ok = false; + if (componentType == byte.class) { + ok = Arrays.equals((byte[]) expect, (byte[]) was); + } + if (componentType == char.class) { + ok = Arrays.equals((char[]) expect, (char[]) was); + } + if (componentType == short.class) { + ok = Arrays.equals((short[]) expect, (short[]) was); + } + if (componentType == int.class) { + ok = Arrays.equals((int[]) expect, (int[]) was); + } + if (componentType == long.class) { + ok = Arrays.equals((long[]) expect, (long[]) was); + } + if (componentType == double.class) { + ok = Arrays.equals((double[]) expect, (double[]) was); + } + if (componentType == float.class) { + ok = Arrays.equals((float[]) expect, (float[]) was); + } + if (!ok) { + throw new AssertionFailedError("Arrays not equal"); + } + } + else { + Object expectArray[] = (Object[]) expect; + Object wasArray[] = (Object[]) was; + if (expectArray.length != wasArray.length) + throw new AssertionFailedError("Not equals, array lengths don't match. expected: " + expectArray.length + ", was: " + wasArray.length); + for (int i = 0; i < wasArray.length; i++) { + assertEquals(expectArray[i], wasArray[i]); + } + + } + } + else if (expect instanceof Command) { + assertEquals(expect.getClass(), was.getClass()); + Method[] methods = expect.getClass().getMethods(); + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + if ((method.getName().startsWith("get") || method.getName().startsWith("is")) && method.getParameterTypes().length == 0 + && method.getReturnType() != null) { + + // Check to see if there is a setter for the method. + try { + if (method.getName().startsWith("get")) { + expect.getClass().getMethod(method.getName().replaceFirst("get", "set"), new Class[] { method.getReturnType() }); + } + else { + expect.getClass().getMethod(method.getName().replaceFirst("is", "set"), new Class[] { method.getReturnType() }); + } + } + catch (Throwable ignore) { + continue; + } + + try { + assertEquals(method.invoke(expect, null), method.invoke(was, null)); + } + catch (IllegalArgumentException e) { + } + catch (IllegalAccessException e) { + } + catch (InvocationTargetException e) { + } + } + } + } + else { + TestCase.assertEquals(expect, was); + } + } + + protected void setUp() throws Exception { + wireFormat = createWireFormat(); + super.setUp(); + } + + protected WireFormat createWireFormat() { + OpenWireFormat answer = new OpenWireFormat(); + answer.setCacheEnabled(cacheEnabled); + return answer; + } + + protected Object marshalAndUnmarshall(Object original, WireFormat wireFormat) throws IOException { + Packet packet = wireFormat.marshal(original); + return wireFormat.unmarshal(packet); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/command/MessageSendTest.java b/activemq-core/src/test/java/org/activemq/command/MessageSendTest.java new file mode 100755 index 0000000000..5ddd4cb6a8 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/command/MessageSendTest.java @@ -0,0 +1,77 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.command; + +import org.activeio.ByteSequence; + +import java.io.IOException; + +import junit.framework.Test; + +public class MessageSendTest extends DataStructureTestSupport { + + public static Test suite() { + return suite(MessageSendTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public void initCombosForTestMessageSendMarshaling() { + addCombinationValues("cacheEnabled", new Object[] { Boolean.TRUE, Boolean.FALSE }); + } + + public void testMessageSendMarshaling() throws IOException { + ActiveMQMessage message = new ActiveMQMessage(); + message.setCommandId((short) 1); + message.setDestination(new ActiveMQQueue("queue")); + message.setGroupID("group"); + message.setGroupSequence(4); + message.setCorrelationId("correlation"); + message.setMessageId(new MessageId("c1:1:1", 1)); + + assertBeanMarshalls(message); + assertBeanMarshalls(message); + + } + + public void xtestPerformance() throws IOException { + ActiveMQMessage message = new ActiveMQMessage(); + message.setProducerId(new ProducerId(new SessionId(new ConnectionId(new ConnectionId("test")), 1), 1)); + message.setMessageId(new MessageId(message.getProducerId(), 1)); + message.setCommandId((short) 1); + message.setGroupID("group"); + message.setGroupSequence(4); + message.setCorrelationId("correlation"); + message.setContent(new ByteSequence(new byte[1024], 0, 1024)); + message.setTimestamp(System.currentTimeMillis()); + message.setDestination(new ActiveMQQueue("TEST")); + + int p = 1000000; + + long start = System.currentTimeMillis(); + for (int i = 0; i < p; i++) { + marshalAndUnmarshall(message, wireFormat); + } + long end = System.currentTimeMillis(); + + System.out.println("marshaled/unmarshaled: " + p + " msgs at " + (p * 1000f / (end - start)) + " msgs/sec"); + } +} diff --git a/activemq-core/src/test/java/org/activemq/command/MessageTest.java b/activemq-core/src/test/java/org/activemq/command/MessageTest.java new file mode 100755 index 0000000000..0862effa27 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/command/MessageTest.java @@ -0,0 +1,93 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.command; + +import org.activeio.command.WireFormat; +import org.activemq.openwire.OpenWireFormat; + +import java.io.IOException; + +import junit.framework.Test; +import junit.textui.TestRunner; + +public class MessageTest extends DataStructureTestSupport { + + public boolean cacheEnabled; + + public static Test suite() { + return suite(MessageTest.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } + + public void initCombosForTestActiveMQMessageMarshaling() { + addCombinationValues("cacheEnabled", new Object[] { Boolean.TRUE, Boolean.FALSE }); + } + + public void testActiveMQMessageMarshaling() throws IOException { + ActiveMQMessage message = new ActiveMQMessage(); + message.setCommandId((short) 1); + message.setOriginalDestination(new ActiveMQQueue("queue")); + message.setGroupID("group"); + message.setGroupSequence(4); + message.setCorrelationId("correlation"); + message.setMessageId(new MessageId("c1:1:1", 1)); + assertBeanMarshalls(message); + } + + public void testActiveMQMessageMarshalingBigMessageId() throws IOException { + ActiveMQMessage message = new ActiveMQMessage(); + message.setCommandId((short) 1); + message.setOriginalDestination(new ActiveMQQueue("queue")); + message.setGroupID("group"); + message.setGroupSequence(4); + message.setCorrelationId("correlation"); + message.setMessageId(new MessageId("c1:1:1", Short.MAX_VALUE)); + assertBeanMarshalls(message); + } + + public void testActiveMQMessageMarshalingBiggerMessageId() throws IOException { + ActiveMQMessage message = new ActiveMQMessage(); + message.setCommandId((short) 1); + message.setOriginalDestination(new ActiveMQQueue("queue")); + message.setGroupID("group"); + message.setGroupSequence(4); + message.setCorrelationId("correlation"); + message.setMessageId(new MessageId("c1:1:1", Integer.MAX_VALUE)); + assertBeanMarshalls(message); + } + + public void testActiveMQMessageMarshalingBiggestMessageId() throws IOException { + ActiveMQMessage message = new ActiveMQMessage(); + message.setCommandId((short) 1); + message.setOriginalDestination(new ActiveMQQueue("queue")); + message.setGroupID("group"); + message.setGroupSequence(4); + message.setCorrelationId("correlation"); + message.setMessageId(new MessageId("c1:1:1", Long.MAX_VALUE)); + assertBeanMarshalls(message); + } + + public void testMessageIdMarshaling() throws IOException { + assertBeanMarshalls(new MessageId("c1:1:1", 1)); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/demo/SimpleConsumer.java b/activemq-core/src/test/java/org/activemq/demo/SimpleConsumer.java new file mode 100755 index 0000000000..daf223a52f --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/demo/SimpleConsumer.java @@ -0,0 +1,141 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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. +* +**/ + +/** + * The SimpleQueueReceiver class consists only of a main method, + * which fetches one or more messages from a queue using + * synchronous message delivery. Run this program in conjunction + * with SimpleQueueSender. Specify a queue name on the command + * line when you run the program. + */ +package org.activemq.demo; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +/** + * A simple polymorphic JMS consumer which can work with Queues or Topics + * which uses JNDI to lookup the JMS connection factory and destination + * + * @version $Revision: 1.2 $ + */ +public class SimpleConsumer { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(SimpleConsumer.class); + + /** + * @param args the queue used by the example + */ + public static void main(String[] args) { + String destinationName = null; + Context jndiContext = null; + ConnectionFactory connectionFactory = null; + Connection connection = null; + Session session = null; + Destination destination = null; + MessageConsumer consumer = null; + + /* + * Read destination name from command line and display it. + */ + if (args.length != 1) { + log.info("Usage: java SimpleConsumer "); + System.exit(1); + } + destinationName = args[0]; + log.info("Destination name is " + destinationName); + + /* + * Create a JNDI API InitialContext object + */ + try { + jndiContext = new InitialContext(); + } + catch (NamingException e) { + log.info("Could not create JNDI API " + + "context: " + e.toString()); + System.exit(1); + } + + /* + * Look up connection factory and destination. + */ + try { + connectionFactory = (ConnectionFactory) + jndiContext.lookup("ConnectionFactory"); + destination = (Destination) jndiContext.lookup(destinationName); + } + catch (NamingException e) { + log.info("JNDI API lookup failed: " + + e.toString()); + System.exit(1); + } + + /* + * Create connection. + * Create session from connection; false means session is + * not transacted. + * Create receiver, then start message delivery. + * Receive all text messages from destination until + * a non-text message is received indicating end of + * message stream. + * Close connection. + */ + try { + connection = connectionFactory.createConnection(); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + consumer = session.createConsumer(destination); + connection.start(); + while (true) { + Message m = consumer.receive(1); + if (m != null) { + if (m instanceof TextMessage) { + TextMessage message = (TextMessage) m; + log.info("Reading message: " + message.getText()); + } + else { + break; + } + } + } + } + catch (JMSException e) { + log.info("Exception occurred: " + e); + } + finally { + if (connection != null) { + try { + connection.close(); + } + catch (JMSException e) { + } + } + } + } +} diff --git a/activemq-core/src/test/java/org/activemq/demo/SimpleProducer.java b/activemq-core/src/test/java/org/activemq/demo/SimpleProducer.java new file mode 100755 index 0000000000..7562b1a1af --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/demo/SimpleProducer.java @@ -0,0 +1,143 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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. +* +**/ + +/** + * The SimpleQueueSender class consists only of a main method, + * which sends several messages to a queue. + * + * Run this program in conjunction with SimpleQueueReceiver. + * Specify a queue name on the command line when you run the + * program. By default, the program sends one message. Specify + * a number after the queue name to send that number of messages. + */ +package org.activemq.demo; + +// START SNIPPET: demo + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +/** + * A simple polymorphic JMS producer which can work with Queues or Topics + * which uses JNDI to lookup the JMS connection factory and destination + * + * @version $Revision: 1.2 $ + */ +public class SimpleProducer { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(SimpleProducer.class); + + /** + * @param args the destination name to send to and optionally, the number of messages to send + */ + public static void main(String[] args) { + Context jndiContext = null; + ConnectionFactory connectionFactory = null; + Connection connection = null; + Session session = null; + Destination destination = null; + MessageProducer producer = null; + String destinationName = null; + final int NUM_MSGS; + + if ((args.length < 1) || (args.length > 2)) { + log.info("Usage: java SimpleProducer []"); + System.exit(1); + } + destinationName = args[0]; + log.info("Destination name is " + destinationName); + if (args.length == 2) { + NUM_MSGS = (new Integer(args[1])).intValue(); + } + else { + NUM_MSGS = 1; + } + + /* + * Create a JNDI API InitialContext object + */ + try { + jndiContext = new InitialContext(); + } + catch (NamingException e) { + log.info("Could not create JNDI API context: " + e.toString()); + System.exit(1); + } + + /* + * Look up connection factory and destination. + */ + try { + connectionFactory = (ConnectionFactory) jndiContext.lookup("ConnectionFactory"); + destination = (Destination) jndiContext.lookup(destinationName); + } + catch (NamingException e) { + log.info("JNDI API lookup failed: " + e); + System.exit(1); + } + + /* + * Create connection. + * Create session from connection; false means session is not transacted. + * Create sender and text message. + * Send messages, varying text slightly. + * Send end-of-messages message. + * Finally, close connection. + */ + try { + connection = connectionFactory.createConnection(); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + producer = session.createProducer(destination); + TextMessage message = session.createTextMessage(); + for (int i = 0; i < NUM_MSGS; i++) { + message.setText("This is message " + (i + 1)); + log.info("Sending message: " + message.getText()); + producer.send(message); + } + + /* + * Send a non-text control message indicating end of messages. + */ + producer.send(session.createMessage()); + } + catch (JMSException e) { + log.info("Exception occurred: " + e); + } + finally { + if (connection != null) { + try { + connection.close(); + } + catch (JMSException e) { + } + } + } + } +} + +// END SNIPPET: demo diff --git a/activemq-core/src/test/java/org/activemq/demo/SimpleQueueReceiver.java b/activemq-core/src/test/java/org/activemq/demo/SimpleQueueReceiver.java new file mode 100755 index 0000000000..4c08ce5745 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/demo/SimpleQueueReceiver.java @@ -0,0 +1,147 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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. +* +**/ + +/** + * The SimpleQueueReceiver class consists only of a main method, + * which fetches one or more messages from a queue using + * synchronous message delivery. Run this program in conjunction + * with SimpleQueueSender. Specify a queue name on the command + * line when you run the program. + */ +package org.activemq.demo; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueConnectionFactory; +import javax.jms.QueueReceiver; +import javax.jms.QueueSession; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +public class SimpleQueueReceiver { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(SimpleQueueReceiver.class); + + /** + * Main method. + * + * @param args the queue used by the example + */ + public static void main(String[] args) { + String queueName = null; + Context jndiContext = null; + QueueConnectionFactory queueConnectionFactory = null; + QueueConnection queueConnection = null; + QueueSession queueSession = null; + Queue queue = null; + QueueReceiver queueReceiver = null; + TextMessage message = null; + + /* + * Read queue name from command line and display it. + */ + if (args.length != 1) { + log.info("Usage: java " + + "SimpleQueueReceiver "); + System.exit(1); + } + queueName = args[0]; + log.info("Queue name is " + queueName); + + /* + * Create a JNDI API InitialContext object if none exists + * yet. + */ + try { + jndiContext = new InitialContext(); + } + catch (NamingException e) { + log.info("Could not create JNDI API " + + "context: " + e.toString()); + System.exit(1); + } + + /* + * Look up connection factory and queue. If either does + * not exist, exit. + */ + try { + queueConnectionFactory = (QueueConnectionFactory) + jndiContext.lookup("QueueConnectionFactory"); + queue = (Queue) jndiContext.lookup(queueName); + } + catch (NamingException e) { + log.info("JNDI API lookup failed: " + + e.toString()); + System.exit(1); + } + + /* + * Create connection. + * Create session from connection; false means session is + * not transacted. + * Create receiver, then start message delivery. + * Receive all text messages from queue until + * a non-text message is received indicating end of + * message stream. + * Close connection. + */ + try { + queueConnection = + queueConnectionFactory.createQueueConnection(); + queueSession = + queueConnection.createQueueSession(false, + Session.AUTO_ACKNOWLEDGE); + queueReceiver = queueSession.createReceiver(queue); + queueConnection.start(); + while (true) { + Message m = queueReceiver.receive(1); + if (m != null) { + if (m instanceof TextMessage) { + message = (TextMessage) m; + log.info("Reading message: " + + message.getText()); + } + else { + break; + } + } + } + } + catch (JMSException e) { + log.info("Exception occurred: " + + e.toString()); + } + finally { + if (queueConnection != null) { + try { + queueConnection.close(); + } + catch (JMSException e) { + } + } + } + } +} diff --git a/activemq-core/src/test/java/org/activemq/demo/SimpleQueueSender.java b/activemq-core/src/test/java/org/activemq/demo/SimpleQueueSender.java new file mode 100755 index 0000000000..50b545c755 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/demo/SimpleQueueSender.java @@ -0,0 +1,150 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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. +* +**/ + +/** + * The SimpleQueueSender class consists only of a main method, + * which sends several messages to a queue. + * + * Run this program in conjunction with SimpleQueueReceiver. + * Specify a queue name on the command line when you run the + * program. By default, the program sends one message. Specify + * a number after the queue name to send that number of messages. + */ +package org.activemq.demo; + +// START SNIPPET: demo + + +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueConnectionFactory; +import javax.jms.QueueSender; +import javax.jms.QueueSession; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +public class SimpleQueueSender { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(SimpleQueueSender.class); + + /** + * Main method. + * + * @param args the queue used by the example and, + * optionally, the number of messages to send + */ + public static void main(String[] args) { + String queueName = null; + Context jndiContext = null; + QueueConnectionFactory queueConnectionFactory = null; + QueueConnection queueConnection = null; + QueueSession queueSession = null; + Queue queue = null; + QueueSender queueSender = null; + TextMessage message = null; + final int NUM_MSGS; + + if ((args.length < 1) || (args.length > 2)) { + log.info("Usage: java SimpleQueueSender " + + " []"); + System.exit(1); + } + queueName = args[0]; + log.info("Queue name is " + queueName); + if (args.length == 2) { + NUM_MSGS = (new Integer(args[1])).intValue(); + } + else { + NUM_MSGS = 1; + } + + /* + * Create a JNDI API InitialContext object if none exists + * yet. + */ + try { + jndiContext = new InitialContext(); + } + catch (NamingException e) { + log.info("Could not create JNDI API context: " + e.toString()); + System.exit(1); + } + + /* + * Look up connection factory and queue. If either does + * not exist, exit. + */ + try { + queueConnectionFactory = (QueueConnectionFactory) jndiContext.lookup("QueueConnectionFactory"); + queue = (Queue) jndiContext.lookup(queueName); + } + catch (NamingException e) { + log.info("JNDI API lookup failed: " + e); + System.exit(1); + } + + /* + * Create connection. + * Create session from connection; false means session is + * not transacted. + * Create sender and text message. + * Send messages, varying text slightly. + * Send end-of-messages message. + * Finally, close connection. + */ + try { + queueConnection = queueConnectionFactory.createQueueConnection(); + queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + queueSender = queueSession.createSender(queue); + message = queueSession.createTextMessage(); + for (int i = 0; i < NUM_MSGS; i++) { + message.setText("This is message " + (i + 1)); + log.info("Sending message: " + + message.getText()); + queueSender.send(message); + } + + /* + * Send a non-text control message indicating end of + * messages. + */ + queueSender.send(queueSession.createMessage()); + } + catch (JMSException e) { + log.info("Exception occurred: " + + e.toString()); + } + finally { + if (queueConnection != null) { + try { + queueConnection.close(); + } + catch (JMSException e) { + } + } + } + } +} + +// END SNIPPET: demo diff --git a/activemq-core/src/test/java/org/activemq/filter/BadDummyPolicyConfigTest.java b/activemq-core/src/test/java/org/activemq/filter/BadDummyPolicyConfigTest.java new file mode 100644 index 0000000000..4db6258769 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/filter/BadDummyPolicyConfigTest.java @@ -0,0 +1,60 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.filter; + +import junit.framework.TestCase; + +/** + * + * @version $Revision: 1.1 $ + */ +public class BadDummyPolicyConfigTest extends TestCase { + + protected DummyPolicy policy = new DummyPolicy(); + + public void testNoDestinationSpecified() throws Exception { + DummyPolicyEntry entry = new DummyPolicyEntry(); + entry.setDescription("cheese"); + + assertFailsToSetEntries(entry); + } + + public void testNoValueSpecified() throws Exception { + DummyPolicyEntry entry = new DummyPolicyEntry(); + entry.setTopic("FOO.BAR"); + + assertFailsToSetEntries(entry); + } + + public void testValidEntry() throws Exception { + DummyPolicyEntry entry = new DummyPolicyEntry(); + entry.setDescription("cheese"); + entry.setTopic("FOO.BAR"); + + entry.afterPropertiesSet(); + } + + protected void assertFailsToSetEntries(DummyPolicyEntry entry) throws Exception { + try { + entry.afterPropertiesSet(); + } + catch (IllegalArgumentException e) { + System.out.println("Worked! Caught expected exception: " + e); + } + } +} diff --git a/activemq-core/src/test/java/org/activemq/filter/DestinationMapTest.java b/activemq-core/src/test/java/org/activemq/filter/DestinationMapTest.java new file mode 100755 index 0000000000..cd43a8222b --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/filter/DestinationMapTest.java @@ -0,0 +1,344 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import junit.framework.TestCase; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQTopic; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class DestinationMapTest extends TestCase { + protected DestinationMap map = new DestinationMap(); + + protected ActiveMQDestination d1 = createDestination("TEST.D1"); + protected ActiveMQDestination d2 = createDestination("TEST.BAR.D2"); + protected ActiveMQDestination d3 = createDestination("TEST.BAR.D3"); + protected ActiveMQDestination compositeDestination1 = createDestination("TEST.D1,TEST.BAR.D2"); + protected ActiveMQDestination compositeDestination2 = createDestination("TEST.D1,TEST.BAR.D3"); + + protected Object v1 = "value1"; + protected Object v2 = "value2"; + protected Object v3 = "value3"; + protected Object v4 = "value4"; + protected Object v5 = "value5"; + protected Object v6 = "value6"; + + + public void testCompositeDestinations() throws Exception { + + ActiveMQDestination d1 = createDestination("TEST.BAR.D2"); + ActiveMQDestination d2 = createDestination("TEST.BAR.D3"); + map.put(d1,d1); + map.put(d2,d2); + map.get(createDestination("TEST.BAR.D2,TEST.BAR.D3")); + + } + public void testSimpleDestinations() throws Exception { + map.put(d1, v1); + map.put(d2, v2); + map.put(d3, v3); + + assertMapValue(d1, v1); + assertMapValue(d2, v2); + assertMapValue(d3, v3); + } + + public void testSimpleDestinationsWithMultipleValues() throws Exception { + map.put(d1, v1); + map.put(d2, v2); + map.put(d2, v3); + + assertMapValue(d1, v1); + assertMapValue("TEST.BAR.D2", v2, v3); + assertMapValue(d3, null); + } + + public void testSimpleAndCompositeDestinations() throws Exception { + map.put(d1, v1); + map.put(compositeDestination1, v2); + map.put(compositeDestination2, v3); + + assertMapValue("TEST.D1", v1, v2, v3); + assertMapValue(d2, v2); + assertMapValue(d3, v3); + assertMapValue(compositeDestination1.toString(), v1, v2, v3); + assertMapValue(compositeDestination2.toString(), v1, v2, v3); + + map.remove(compositeDestination1, v2); + map.remove(compositeDestination2, v3); + + assertMapValue("TEST.D1", v1); + } + + public void testLookupOneStepWildcardDestinations() throws Exception { + map.put(d1, v1); + map.put(d2, v2); + map.put(d3, v3); + + assertMapValue("TEST.D1", v1); + assertMapValue("TEST.*", v1); + assertMapValue("*.D1", v1); + assertMapValue("*.*", v1); + + assertMapValue("TEST.BAR.D2", v2); + assertMapValue("TEST.*.D2", v2); + assertMapValue("*.BAR.D2", v2); + assertMapValue("*.*.D2", v2); + + assertMapValue("TEST.BAR.D3", v3); + assertMapValue("TEST.*.D3", v3); + assertMapValue("*.BAR.D3", v3); + assertMapValue("*.*.D3", v3); + + assertMapValue("TEST.BAR.D4", null); + + assertMapValue("TEST.BAR.*", v2, v3); + } + + public void testLookupMultiStepWildcardDestinations() throws Exception { + map.put(d1, v1); + map.put(d2, v2); + map.put(d3, v3); + + List allValues = Arrays.asList(new Object[]{v1, v2, v3}); + + assertMapValue(">", allValues); + assertMapValue("TEST.>", allValues); + assertMapValue("*.>", allValues); + + assertMapValue("FOO.>", null); + } + + + public void testStoreWildcardWithOneStepPath() throws Exception { + put("TEST.*", v1); + put("TEST.D1", v2); + put("TEST.BAR.*", v2); + put("TEST.BAR.D3", v3); + + assertMapValue("FOO", null); + assertMapValue("TEST.FOO", v1); + assertMapValue("TEST.D1", v1, v2); + + assertMapValue("TEST.FOO.FOO", null); + assertMapValue("TEST.BAR.FOO", v2); + assertMapValue("TEST.BAR.D3", v2, v3); + + assertMapValue("TEST.*", v1, v2); + assertMapValue("*.D1", v1, v2); + assertMapValue("*.*", v1, v2); + assertMapValue("TEST.*.*", v2, v3); + assertMapValue("TEST.BAR.*", v2, v3); + assertMapValue("*.*.*", v2, v3); + assertMapValue("*.BAR.*", v2, v3); + assertMapValue("*.BAR.D3", v2, v3); + assertMapValue("*.*.D3", v2, v3); + } + + public void testStoreWildcardInMiddleOfPath() throws Exception { + put("TEST.*", v1); + put("TEST.D1", v2); + put("TEST.BAR.*", v2); + put("TEST.XYZ.D3", v3); + put("TEST.XYZ.D4", v4); + put("TEST.BAR.D3", v5); + put("TEST.*.D2", v6); + + + assertMapValue("TEST.*.D3", v2, v3, v5); + assertMapValue("TEST.*.D4", v2, v4); + + assertMapValue("TEST.*", v1, v2); + assertMapValue("TEST.*.*", v2, v3, v4, v5, v6); + assertMapValue("*.*.D3", v2, v3, v5); + assertMapValue("TEST.BAR.*", v2, v5, v6); + + assertMapValue("TEST.BAR.D2", v2, v6); + assertMapValue("TEST.*.D2", v2, v6); + assertMapValue("TEST.BAR.*", v2, v5, v6); + } + + public void testSimpleAddRemove() throws Exception { + put("TEST.D1", v2); + + assertEquals("Root child count", 1, map.getRootChildCount()); + + assertMapValue("TEST.D1", v2); + + remove("TEST.D1", v2); + + assertEquals("Root child count", 0, map.getRootChildCount()); + assertMapValue("TEST.D1", null); + } + + public void testStoreAndLookupAllWildcards() throws Exception { + loadSample2(); + + assertSample2(); + + // lets remove everything and add it back + remove("TEST.FOO", v1); + + assertMapValue("TEST.FOO", v2, v3, v4); + assertMapValue("TEST.*", v2, v3, v4, v6); + assertMapValue("*.*", v2, v3, v4, v6); + + remove("TEST.XYZ", v6); + + assertMapValue("TEST.*", v2, v3, v4); + assertMapValue("*.*", v2, v3, v4); + + remove("TEST.*", v2); + + assertMapValue("TEST.*", v3, v4); + assertMapValue("*.*", v3, v4); + + remove(">", v4); + + assertMapValue("TEST.*", v3); + assertMapValue("*.*", v3); + + remove("TEST.>", v3); + remove("TEST.FOO.BAR", v5); + + assertMapValue("FOO", null); + assertMapValue("TEST.FOO", null); + assertMapValue("TEST.D1", null); + + assertMapValue("TEST.FOO.FOO", null); + assertMapValue("TEST.BAR.FOO", null); + assertMapValue("TEST.FOO.BAR", null); + assertMapValue("TEST.BAR.D3", null); + + assertMapValue("TEST.*", null); + assertMapValue("*.*", null); + assertMapValue("*.D1", null); + assertMapValue("TEST.*.*", null); + assertMapValue("TEST.BAR.*", null); + + loadSample2(); + + assertSample2(); + + remove(">", v4); + remove("TEST.*", v2); + + assertMapValue("FOO", null); + assertMapValue("TEST.FOO", v1, v3); + assertMapValue("TEST.D1", v3); + + assertMapValue("TEST.FOO.FOO", v3); + assertMapValue("TEST.BAR.FOO", v3); + assertMapValue("TEST.FOO.BAR", v3, v5); + assertMapValue("TEST.BAR.D3", v3); + + assertMapValue("TEST.*", v1, v3, v6); + assertMapValue("*.*", v1, v3, v6); + assertMapValue("*.D1", v3); + assertMapValue("TEST.*.*", v3, v5); + assertMapValue("TEST.BAR.*", v3); + } + + protected void loadSample2() { + put("TEST.FOO", v1); + put("TEST.*", v2); + put("TEST.>", v3); + put(">", v4); + put("TEST.FOO.BAR", v5); + put("TEST.XYZ", v6); + } + + protected void assertSample2() { + assertMapValue("FOO", v4); + assertMapValue("TEST.FOO", v1, v2, v3, v4); + assertMapValue("TEST.D1", v2, v3, v4); + + assertMapValue("TEST.FOO.FOO", v3, v4); + assertMapValue("TEST.BAR.FOO", v3, v4); + assertMapValue("TEST.FOO.BAR", v3, v4, v5); + assertMapValue("TEST.BAR.D3", v3, v4); + + assertMapValue("TEST.*", v1, v2, v3, v4, v6); + assertMapValue("*.*", v1, v2, v3, v4, v6); + assertMapValue("*.D1", v2, v3, v4); + assertMapValue("TEST.*.*", v3, v4, v5); + assertMapValue("TEST.BAR.*", v3, v4); + } + + + protected void put(String name, Object value) { + map.put(createDestination(name), value); + } + + protected void remove(String name, Object value) { + ActiveMQDestination destination = createDestination(name); + map.remove(destination, value); + } + + + protected void assertMapValue(String destinationName, Object expected) { + ActiveMQDestination destination = createDestination(destinationName); + assertMapValue(destination, expected); + } + + protected void assertMapValue(String destinationName, Object expected1, Object expected2) { + assertMapValue(destinationName, Arrays.asList(new Object[]{expected1, expected2})); + } + + protected void assertMapValue(String destinationName, Object expected1, Object expected2, Object expected3) { + assertMapValue(destinationName, Arrays.asList(new Object[]{expected1, expected2, expected3})); + } + + protected void assertMapValue(String destinationName, Object expected1, Object expected2, Object expected3, Object expected4) { + assertMapValue(destinationName, Arrays.asList(new Object[]{expected1, expected2, expected3, expected4})); + } + + protected void assertMapValue(String destinationName, Object expected1, Object expected2, Object expected3, Object expected4, Object expected5) { + assertMapValue(destinationName, Arrays.asList(new Object[]{expected1, expected2, expected3, expected4, expected5})); + } + + protected void assertMapValue(ActiveMQDestination destination, Object expected) { + List expectedList = null; + if (expected == null) { + expectedList = Collections.EMPTY_LIST; + } + else if (expected instanceof List) { + expectedList = (List) expected; + } + else { + expectedList = new ArrayList(); + expectedList.add(expected); + } + Collections.sort(expectedList); + Set actualSet = map.get(destination); + List actual = new ArrayList(actualSet); + Collections.sort(actual); + assertEquals("map value for destinationName: " + destination, expectedList, actual); + } + + protected ActiveMQDestination createDestination(String name) { + return new ActiveMQTopic(name); + } +} diff --git a/activemq-core/src/test/java/org/activemq/filter/DestinationPathTest.java b/activemq-core/src/test/java/org/activemq/filter/DestinationPathTest.java new file mode 100755 index 0000000000..09f0cbac07 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/filter/DestinationPathTest.java @@ -0,0 +1,46 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.filter; + +import junit.framework.TestCase; + +public class DestinationPathTest extends TestCase { + + public void testPathParse() { + assertParse("FOO", new String[]{"FOO"}); + assertParse("FOO.BAR", new String[]{"FOO", "BAR"}); + assertParse("FOO.*", new String[]{"FOO", "*"}); + assertParse("FOO.>", new String[]{"FOO", ">"}); + assertParse("FOO.BAR.XYZ", new String[]{"FOO", "BAR", "XYZ"}); + assertParse("FOO.BAR.", new String[]{"FOO", "BAR", ""}); + } + + protected void assertParse(String subject, String[] expected) { + String[] path = DestinationPath.getDestinationPaths(subject); + assertArrayEqual(subject, expected, path); + } + + protected void assertArrayEqual(String message, Object[] expected, Object[] actual) { + assertEquals(message + ". Array length", expected.length, actual.length); + for (int i = 0; i < expected.length; i++) { + assertEquals(message + ". element: " + i, expected[i], actual[i]); + } + } +} diff --git a/activemq-core/src/test/java/org/activemq/filter/DummyPolicy.java b/activemq-core/src/test/java/org/activemq/filter/DummyPolicy.java new file mode 100644 index 0000000000..94a4bc779d --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/filter/DummyPolicy.java @@ -0,0 +1,37 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.filter; + +import java.util.List; + +/** + * Represents a destination based policy + * + * @version $Revision: 1.1 $ + */ +public class DummyPolicy extends DestinationMap { + + protected Class getEntryClass() { + return DummyPolicyEntry.class; + } + + public void setEntries(List entries) { + super.setEntries(entries); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/filter/DummyPolicyEntry.java b/activemq-core/src/test/java/org/activemq/filter/DummyPolicyEntry.java new file mode 100644 index 0000000000..5339a3bed1 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/filter/DummyPolicyEntry.java @@ -0,0 +1,44 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.filter; + +/** + * + * @version $Revision: 1.1 $ + */ +public class DummyPolicyEntry extends DestinationMapEntry { + + private String description; + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Object getValue() { + return description; + } + + protected String getValuePropertyName() { + return "description"; + } + +} diff --git a/activemq-core/src/test/java/org/activemq/filter/DummyPolicyTest.java b/activemq-core/src/test/java/org/activemq/filter/DummyPolicyTest.java new file mode 100644 index 0000000000..912669265e --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/filter/DummyPolicyTest.java @@ -0,0 +1,46 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.filter; + +import org.activemq.SpringTestSupport; +import org.activemq.command.ActiveMQTopic; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import java.util.Set; + +/** + * + * @version $Revision: 1.1 $ + */ +public class DummyPolicyTest extends SpringTestSupport { + + public void testPolicy() throws Exception { + DummyPolicy policy = (DummyPolicy) getBean("policy"); + + Set set = policy.get(new ActiveMQTopic("FOO.BAR")); + + assertSetEquals("FOO.BAR set", new Object[] { "Edam", "Cheddar" }, set); + } + + protected AbstractApplicationContext createApplicationContext() { + // TODO Auto-generated method stub + return new ClassPathXmlApplicationContext("org/activemq/filter/dummyPolicy.xml"); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/jndi/ActiveMQInitialContextFactoryTest.java b/activemq-core/src/test/java/org/activemq/jndi/ActiveMQInitialContextFactoryTest.java new file mode 100755 index 0000000000..f2734caef4 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/jndi/ActiveMQInitialContextFactoryTest.java @@ -0,0 +1,77 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.jndi; + +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; + + +import javax.naming.*; + +/** + * @version $Revision: 1.4 $ + */ +public class ActiveMQInitialContextFactoryTest extends JNDITestSupport { + + public void testConnectionFactoriesArePresent() throws NamingException { + String lookupName = getConnectionFactoryLookupName(); + assertConnectionFactoryPresent(lookupName); + } + + public void testDestinationsArePresent() throws NamingException { + + //Retrieving destinations context is not yet implemented on the broker. For this test, a jndi file properties will be used. + + InitialContext context = new InitialContext(); + + //make sure contest is not null + assertTrue("Created context", context != null); + + Object topicDestination = context.lookup("MyTopic"); + + // check if MyTopic is an ActiveMQTopic + assertTrue("Should have found a topic but found: " + topicDestination, topicDestination instanceof ActiveMQTopic); + + Object queueDestination = context.lookup("MyQueue"); + + // check if MyQueue is an ActiveMQueue + assertTrue("Should have found a queue but found: " + queueDestination,queueDestination instanceof ActiveMQQueue); + + } + + public void testDynamicallyGrowing() throws Exception { + Object answer = context.lookup("dynamicQueues/FOO.BAR"); + assertTrue("Should have found a queue but found: " + answer, answer instanceof ActiveMQQueue); + + ActiveMQQueue queue = (ActiveMQQueue) answer; + assertEquals("queue name", "FOO.BAR", queue.getPhysicalName()); + + answer = context.lookup("dynamicTopics/A.B.C"); + assertTrue("Should have found a topic but found: " + answer, answer instanceof ActiveMQTopic); + + ActiveMQTopic topic = (ActiveMQTopic) answer; + assertEquals("topic name", "A.B.C", topic.getPhysicalName()); + + } + + + protected String getConnectionFactoryLookupName() { + return "ConnectionFactory"; + } +} diff --git a/activemq-core/src/test/java/org/activemq/jndi/CustomConnectionFactoryNameTest.java b/activemq-core/src/test/java/org/activemq/jndi/CustomConnectionFactoryNameTest.java new file mode 100755 index 0000000000..0d7cf169cf --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/jndi/CustomConnectionFactoryNameTest.java @@ -0,0 +1,55 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.jndi; + +import javax.naming.NamingException; + +import org.activemq.ActiveMQConnectionFactory; + +/** + * Test case for AMQ-141 + * + * @version $Revision: 1.2 $ + */ +public class CustomConnectionFactoryNameTest extends ActiveMQInitialContextFactoryTest { + + public void testConnectionFactoriesArePresent() throws NamingException { + super.testConnectionFactoriesArePresent(); + assertConnectionFactoryPresent("jms/Connection"); + assertConnectionFactoryPresent("jms/DURABLE_SUB_CONNECTION_FACTORY"); + } + + public void testConnectionFactoriesAreConfigured() throws NamingException { + super.testConnectionFactoriesArePresent(); + ActiveMQConnectionFactory factory1 = (ActiveMQConnectionFactory) context.lookup("jms/Connection"); + assertNull(factory1.getClientID()); + ActiveMQConnectionFactory factory2 = (ActiveMQConnectionFactory) context.lookup("jms/DURABLE_SUB_CONNECTION_FACTORY"); + assertEquals("testclient", factory2.getClientID()); + } + + protected String getConnectionFactoryLookupName() { + return "myConnectionFactory"; + } + + protected void configureEnvironment() { + super.configureEnvironment(); + environment.put("connectionFactoryNames", " myConnectionFactory, jms/Connection, jms/DURABLE_SUB_CONNECTION_FACTORY"); + environment.put("connection.jms/DURABLE_SUB_CONNECTION_FACTORY.clientID", "testclient"); + } +} diff --git a/activemq-core/src/test/java/org/activemq/jndi/DestinationNameWithSlashTest.java b/activemq-core/src/test/java/org/activemq/jndi/DestinationNameWithSlashTest.java new file mode 100755 index 0000000000..3d35bb094c --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/jndi/DestinationNameWithSlashTest.java @@ -0,0 +1,37 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.jndi; + + +/** + * Test case for AMQ-140 + * + * @version $Revision: 1.2 $ + */ +public class DestinationNameWithSlashTest extends JNDITestSupport { + public void testNameWithSlash() throws Exception { + assertDestinationExists("jms/Queue"); + + } + + protected void configureEnvironment() { + super.configureEnvironment(); + environment.put("queue.jms/Queue", "example.myqueue"); + } +} diff --git a/activemq-core/src/test/java/org/activemq/jndi/InitialContextTest.java b/activemq-core/src/test/java/org/activemq/jndi/InitialContextTest.java new file mode 100755 index 0000000000..edd65e4b5b --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/jndi/InitialContextTest.java @@ -0,0 +1,63 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.jndi; + +import junit.framework.TestCase; +import org.activemq.ActiveMQConnectionFactory; + +import javax.naming.InitialContext; +import javax.naming.Context; +import java.util.Properties; + +/** + * @version $Revision: 1.3 $ + */ +public class InitialContextTest extends TestCase { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(InitialContextTest.class); + + public void testInitialContext() throws Exception { + InitialContext context = new InitialContext(); + assertTrue("Created context", context != null); + + ActiveMQConnectionFactory connectionFactory = (ActiveMQConnectionFactory) context.lookup("ConnectionFactory"); + + assertTrue("Should have created a ConnectionFactory", connectionFactory != null); + + log.info("Created with brokerURL: " + connectionFactory.getBrokerURL()); + + } + + public void testUsingStandardJNDIKeys() throws Exception { + Properties properties = new Properties(); + properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.activemq.jndi.ActiveMQInitialContextFactory"); + String expected = "tcp://localhost:65432"; + properties.put(Context.PROVIDER_URL, expected); + + InitialContext context = new InitialContext(properties); + assertTrue("Created context", context != null); + + ActiveMQConnectionFactory connectionFactory = (ActiveMQConnectionFactory) context.lookup("ConnectionFactory"); + + assertTrue("Should have created a ConnectionFactory", connectionFactory != null); + + assertEquals("the brokerURL should match", expected, connectionFactory.getBrokerURL()); + } +} diff --git a/activemq-core/src/test/java/org/activemq/jndi/JNDITestSupport.java b/activemq-core/src/test/java/org/activemq/jndi/JNDITestSupport.java new file mode 100755 index 0000000000..efd31be3ed --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/jndi/JNDITestSupport.java @@ -0,0 +1,103 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.jndi; + +import junit.framework.TestCase; + +import javax.naming.NamingException; +import javax.naming.Binding; +import javax.naming.Context; +import javax.naming.NamingEnumeration; +import javax.naming.spi.InitialContextFactory; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import java.util.Hashtable; + +import org.activemq.ActiveMQConnectionFactory; + +/** + * @version $Revision: 1.3 $ + */ +public abstract class JNDITestSupport extends TestCase { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JNDITestSupport.class); + + protected Hashtable environment = new Hashtable(); + protected Context context; + + protected void assertConnectionFactoryPresent(String lookupName) throws NamingException { + Object connectionFactory = context.lookup(lookupName); + + assertTrue("Should have created a ConnectionFactory for key: " + lookupName + + " but got: " + connectionFactory, connectionFactory instanceof ConnectionFactory); + } + + protected void assertBinding(Binding binding) throws NamingException { + Object object = binding.getObject(); + assertTrue("Should have got a child context but got: " + object, object instanceof Context); + + Context childContext = (Context) object; + NamingEnumeration iter = childContext.listBindings(""); + while (iter.hasMore()) { + Binding destinationBinding = (Binding) iter.next(); + log.info("Found destination: " + destinationBinding.getName()); + Object destination = destinationBinding.getObject(); + assertTrue("Should have a Destination but got: " + destination, destination instanceof Destination); + } + } + + protected void setUp() throws Exception { + super.setUp(); + + configureEnvironment(); + + InitialContextFactory factory = new ActiveMQInitialContextFactory(); + context = factory.getInitialContext(environment); + assertTrue("No context created", context != null); + } + + /** + * Stops all existing ActiveMQConnectionFactory in Context. + * + * @throws javax.naming.NamingException + */ + protected void tearDown() throws NamingException, JMSException { + NamingEnumeration iter = context.listBindings(""); + while (iter.hasMore()) { + Binding binding = (Binding) iter.next(); + Object connFactory = binding.getObject(); + if (connFactory instanceof ActiveMQConnectionFactory) { + // ((ActiveMQConnectionFactory) connFactory).stop(); + } + } + } + + protected void configureEnvironment() { + environment.put("useEmbeddedBroker", "true"); + environment.put("brokerURL", "vm://localhost"); + } + + protected void assertDestinationExists(String name) throws NamingException { + Object object = context.lookup(name); + assertTrue("Should have received a Destination for name: " + name + " but instead found: " + object, + object instanceof Destination); + } +} diff --git a/activemq-core/src/test/java/org/activemq/management/BoundaryStatisticTest.java b/activemq-core/src/test/java/org/activemq/management/BoundaryStatisticTest.java new file mode 100755 index 0000000000..0833dc15f4 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/management/BoundaryStatisticTest.java @@ -0,0 +1,39 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +public class BoundaryStatisticTest extends StatisticTestSupport { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(BoundaryStatisticTest.class); + + /** + * Use case for BoundaryStatisticImpl class. + * @throws Exception + */ + public void testStatistic() throws Exception { + BoundaryStatisticImpl stat = new BoundaryStatisticImpl("myBoundaryStat", "seconds", "myBoundaryStatDesc", 1000, 2000); + assertStatistic(stat, "myBoundaryStat", "seconds", "myBoundaryStatDesc"); + + assertEquals(1000, stat.getLowerBound()); + assertEquals(2000, stat.getUpperBound()); + + log.info("Stat is: " + stat); + } +} diff --git a/activemq-core/src/test/java/org/activemq/management/BoundedRangeStatisticTest.java b/activemq-core/src/test/java/org/activemq/management/BoundedRangeStatisticTest.java new file mode 100755 index 0000000000..eab0acef6e --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/management/BoundedRangeStatisticTest.java @@ -0,0 +1,39 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + + +/** + * @version $Revision: 1.2 $ + */ +public class BoundedRangeStatisticTest extends RangeStatisticTest { + + /** + * Use case for BoundedRangeStatisticImpl class. + * @throws Exception + */ + public void testStatistic() throws Exception { + BoundedRangeStatisticImpl stat = new BoundedRangeStatisticImpl("myRange", "millis", "myDescription", 10, 3000); + assertStatistic(stat, "myRange", "millis", "myDescription"); + assertEquals(10, stat.getLowerBound()); + assertEquals(3000, stat.getUpperBound()); + + assertRangeStatistic(stat); + } +} diff --git a/activemq-core/src/test/java/org/activemq/management/CountStatisticTest.java b/activemq-core/src/test/java/org/activemq/management/CountStatisticTest.java new file mode 100755 index 0000000000..7d5510eb6a --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/management/CountStatisticTest.java @@ -0,0 +1,57 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +public class CountStatisticTest extends StatisticTestSupport { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(CountStatisticTest.class); + + /** + * Use case for CountStatisticImple class. + * @throws Exception + */ + public void testStatistic() throws Exception { + CountStatisticImpl stat = new CountStatisticImpl("myCounter", "seconds", "myDescription"); + assertStatistic(stat, "myCounter", "seconds", "myDescription"); + + assertEquals(0, stat.getCount()); + + stat.increment(); + assertEquals(1, stat.getCount()); + + stat.increment(); + assertEquals(2, stat.getCount()); + + stat.decrement(); + assertEquals(1, stat.getCount()); + + Thread.sleep(500); + + stat.increment(); + + assertLastTimeNotStartTime(stat); + + log.info("Counter is: " + stat); + + stat.reset(); + + assertEquals(0, stat.getCount()); + } +} diff --git a/activemq-core/src/test/java/org/activemq/management/RangeStatisticTest.java b/activemq-core/src/test/java/org/activemq/management/RangeStatisticTest.java new file mode 100755 index 0000000000..e2c244c66d --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/management/RangeStatisticTest.java @@ -0,0 +1,79 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +public class RangeStatisticTest extends StatisticTestSupport { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(RangeStatisticTest.class); + + /** + * Use case for RangeStatisticImpl class. + * @throws Exception + */ + public void testStatistic() throws Exception { + RangeStatisticImpl stat = new RangeStatisticImpl("myRange", "millis", "myDescription"); + assertStatistic(stat, "myRange", "millis", "myDescription"); + + assertRangeStatistic(stat); + } + + protected void assertRangeStatistic(RangeStatisticImpl stat) throws InterruptedException { + assertEquals(0, stat.getCurrent()); + assertEquals(0, stat.getLowWaterMark()); + assertEquals(0, stat.getHighWaterMark()); + + stat.setCurrent(100); + assertEquals(100, stat.getCurrent()); + assertEquals(100, stat.getLowWaterMark()); + assertEquals(100, stat.getHighWaterMark()); + + stat.setCurrent(50); + assertEquals(50, stat.getCurrent()); + assertEquals(50, stat.getLowWaterMark()); + assertEquals(100, stat.getHighWaterMark()); + + stat.setCurrent(200); + assertEquals(200, stat.getCurrent()); + assertEquals(50, stat.getLowWaterMark()); + assertEquals(200, stat.getHighWaterMark()); + + Thread.sleep(500); + + stat.setCurrent(10); + assertEquals(10, stat.getCurrent()); + assertEquals(10, stat.getLowWaterMark()); + assertEquals(200, stat.getHighWaterMark()); + + assertLastTimeNotStartTime(stat); + + log.info("Stat is: " + stat); + + stat.reset(); + + assertEquals(0, stat.getCurrent()); + assertEquals(0, stat.getLowWaterMark()); + assertEquals(0, stat.getHighWaterMark()); + + stat.setCurrent(100); + assertEquals(100, stat.getCurrent()); + assertEquals(100, stat.getLowWaterMark()); + assertEquals(100, stat.getHighWaterMark()); + } +} diff --git a/activemq-core/src/test/java/org/activemq/management/StatisticTestSupport.java b/activemq-core/src/test/java/org/activemq/management/StatisticTestSupport.java new file mode 100755 index 0000000000..2bce0d4956 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/management/StatisticTestSupport.java @@ -0,0 +1,45 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +import junit.framework.TestCase; + +public abstract class StatisticTestSupport extends TestCase { + + /** + * assert method used by the management related classes for its usecase. + * @param counter + * @param name + * @param unit + * @param description + */ + protected void assertStatistic(StatisticImpl counter, String name, String unit, String description) { + assertEquals(name, counter.getName()); + assertEquals(unit, counter.getUnit()); + assertEquals(description, counter.getDescription()); + } + + /** + * assert method to determine last time vs the start time. + * @param counter + */ + protected void assertLastTimeNotStartTime(StatisticImpl counter) { + assertTrue("Should not have start time the same as last sample time. Start time: " + counter.getStartTime() + " lastTime: " + counter.getLastSampleTime(), counter.getStartTime() != counter.getLastSampleTime()); + } +} diff --git a/activemq-core/src/test/java/org/activemq/management/TimeStatisticTest.java b/activemq-core/src/test/java/org/activemq/management/TimeStatisticTest.java new file mode 100755 index 0000000000..8f8ae96c14 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/management/TimeStatisticTest.java @@ -0,0 +1,76 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.management; + +public class TimeStatisticTest extends StatisticTestSupport { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(TimeStatisticTest.class); + + /** + * Use case for TimeStatisticImpl class. + * @throws Exception + */ + public void testStatistic() throws Exception { + TimeStatisticImpl stat = new TimeStatisticImpl("myTimer", "millis", "myDescription"); + assertStatistic(stat, "myTimer", "millis", "myDescription"); + + assertEquals(0, stat.getCount()); + + stat.addTime(100); + assertEquals(1, stat.getCount()); + assertEquals(100, stat.getMinTime()); + assertEquals(100, stat.getMaxTime()); + + stat.addTime(403); + assertEquals(2, stat.getCount()); + assertEquals(100, stat.getMinTime()); + assertEquals(403, stat.getMaxTime()); + + stat.addTime(50); + assertEquals(3, stat.getCount()); + assertEquals(50, stat.getMinTime()); + assertEquals(403, stat.getMaxTime()); + + + assertEquals(553, stat.getTotalTime()); + + Thread.sleep(500); + + stat.addTime(10); + + assertLastTimeNotStartTime(stat); + + log.info("Stat is: " + stat); + + stat.reset(); + + assertEquals(0, stat.getCount()); + assertEquals(0, stat.getMinTime()); + assertEquals(0, stat.getMaxTime()); + assertEquals(0, stat.getTotalTime()); + + stat.addTime(100); + assertEquals(1, stat.getCount()); + assertEquals(100, stat.getMinTime()); + assertEquals(100, stat.getMaxTime()); + assertEquals(100, stat.getTotalTime()); + + } +} diff --git a/activemq-core/src/test/java/org/activemq/memory/buffer/DummyMessage.java b/activemq-core/src/test/java/org/activemq/memory/buffer/DummyMessage.java new file mode 100644 index 0000000000..af79a6f6ec --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/memory/buffer/DummyMessage.java @@ -0,0 +1,44 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.memory.buffer; + +import org.activemq.command.ActiveMQMessage; + +/** + * A message implementation which is useful for testing as we can spoof its size + * + * @version $Revision: 1.1 $ + */ +public class DummyMessage extends ActiveMQMessage { + + private int size; + + public DummyMessage(int size) { + this.size = size; + } + + public int getSize() { + return size; + } + + public String toString() { + return "DummyMessage[id=" + getMessageId() + " size=" + size + "]"; + } + + +} diff --git a/activemq-core/src/test/java/org/activemq/memory/buffer/MemoryBufferTestSupport.java b/activemq-core/src/test/java/org/activemq/memory/buffer/MemoryBufferTestSupport.java new file mode 100644 index 0000000000..155f26206d --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/memory/buffer/MemoryBufferTestSupport.java @@ -0,0 +1,66 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.memory.buffer; + +import org.activemq.command.ActiveMQMessage; +import org.activemq.memory.buffer.MessageBuffer; +import org.activemq.memory.buffer.MessageQueue; + +import junit.framework.TestCase; + +/** + * + * @version $Revision: 1.1 $ + */ +public abstract class MemoryBufferTestSupport extends TestCase { + + protected abstract MessageBuffer createMessageBuffer(); + + protected MessageBuffer buffer = createMessageBuffer(); + protected MessageQueue qA = buffer.createMessageQueue(); + protected MessageQueue qB = buffer.createMessageQueue(); + protected MessageQueue qC = buffer.createMessageQueue(); + protected int messageCount; + + protected void setUp() throws Exception { + buffer = createMessageBuffer(); + qA = buffer.createMessageQueue(); + qB = buffer.createMessageQueue(); + qC = buffer.createMessageQueue(); + } + + protected void dump() { + System.out.println("Dumping current state"); + dumpQueue(qA, "A"); + dumpQueue(qB, "B"); + dumpQueue(qC, "C"); + System.out.println(); + } + + protected void dumpQueue(MessageQueue queue, String name) { + System.out.println(" " + name + " = " + queue.getList()); + } + + protected ActiveMQMessage createMessage(int size) throws Exception { + DummyMessage answer = new DummyMessage(size); + answer.setIntProperty("counter", ++messageCount); + answer.setJMSMessageID("" + messageCount); + return answer; + } + +} diff --git a/activemq-core/src/test/java/org/activemq/memory/buffer/OrderBasedMemoryBufferTest.java b/activemq-core/src/test/java/org/activemq/memory/buffer/OrderBasedMemoryBufferTest.java new file mode 100644 index 0000000000..445295fbe6 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/memory/buffer/OrderBasedMemoryBufferTest.java @@ -0,0 +1,74 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.memory.buffer; + +import org.activemq.memory.buffer.MessageBuffer; +import org.activemq.memory.buffer.OrderBasedMessageBuffer; + +/** + * + * @version $Revision: 1.1 $ + */ +public class OrderBasedMemoryBufferTest extends MemoryBufferTestSupport { + + public void testSizeWorks() throws Exception { + qA.add(createMessage(10)); + qB.add(createMessage(10)); + qB.add(createMessage(10)); + qC.add(createMessage(10)); + + dump(); + + assertEquals("buffer size", 40, buffer.getSize()); + assertEquals("qA", 10, qA.getSize()); + assertEquals("qB", 20, qB.getSize()); + assertEquals("qC", 10, qC.getSize()); + + qC.add(createMessage(10)); + + dump(); + + assertEquals("buffer size", 40, buffer.getSize()); + assertEquals("qA", 0, qA.getSize()); + assertEquals("qB", 20, qB.getSize()); + assertEquals("qC", 20, qC.getSize()); + + qB.add(createMessage(10)); + + dump(); + + assertEquals("buffer size", 40, buffer.getSize()); + assertEquals("qA", 0, qA.getSize()); + assertEquals("qB", 20, qB.getSize()); + assertEquals("qC", 20, qC.getSize()); + + qA.add(createMessage(10)); + + dump(); + + assertEquals("buffer size", 40, buffer.getSize()); + assertEquals("qA", 10, qA.getSize()); + assertEquals("qB", 10, qB.getSize()); + assertEquals("qC", 20, qC.getSize()); + } + + + protected MessageBuffer createMessageBuffer() { + return new OrderBasedMessageBuffer(40); + } +} diff --git a/activemq-core/src/test/java/org/activemq/memory/buffer/SizeBasedMessageBufferTest.java b/activemq-core/src/test/java/org/activemq/memory/buffer/SizeBasedMessageBufferTest.java new file mode 100644 index 0000000000..f0bce5d747 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/memory/buffer/SizeBasedMessageBufferTest.java @@ -0,0 +1,57 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.memory.buffer; + +import org.activemq.memory.buffer.MessageBuffer; +import org.activemq.memory.buffer.SizeBasedMessageBuffer; + +/** + * + * @version $Revision: 1.1 $ + */ +public class SizeBasedMessageBufferTest extends MemoryBufferTestSupport { + + public void testSizeWorks() throws Exception { + qA.add(createMessage(10)); + qB.add(createMessage(10)); + qB.add(createMessage(10)); + qC.add(createMessage(10)); + + dump(); + + assertEquals("buffer size", 40, buffer.getSize()); + assertEquals("qA", 10, qA.getSize()); + assertEquals("qB", 20, qB.getSize()); + assertEquals("qC", 10, qC.getSize()); + + // now lets force an eviction + qC.add(createMessage(10)); + + dump(); + + assertEquals("buffer size", 40, buffer.getSize()); + assertEquals("qA", 10, qA.getSize()); + assertEquals("qB", 10, qB.getSize()); + assertEquals("qC", 20, qC.getSize()); + } + + + protected MessageBuffer createMessageBuffer() { + return new SizeBasedMessageBuffer(40); + } +} diff --git a/activemq-core/src/test/java/org/activemq/network/DemandForwardingBridgeTest.java b/activemq-core/src/test/java/org/activemq/network/DemandForwardingBridgeTest.java new file mode 100755 index 0000000000..158adbcc0e --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/network/DemandForwardingBridgeTest.java @@ -0,0 +1,156 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.network; + +import javax.jms.DeliveryMode; + +import org.activemq.broker.StubConnection; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.ProducerInfo; +import org.activemq.command.SessionInfo; + +import junit.framework.Test; + + +public class DemandForwardingBridgeTest extends NetworkTestSupport { + + public ActiveMQDestination destination; + public byte destinationType; + public int deliveryMode; + private DemandForwardingBridge bridge; + + public void initCombosForTestSendThenAddConsumer() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + } ); + } + public void testSendThenAddConsumer() throws Throwable { + + // Start a producer on local broker + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo); + + destination = createDestinationInfo(connection1, connectionInfo1, destinationType); + + // Start a consumer on a remote broker + StubConnection connection2 = createRemoteConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + + // Send the message to the local broker. + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + + // Verify that the message stayed on the local broker. + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + connection1.send(consumerInfo1); + Message m = receiveMessage(connection1); + assertNotNull(m); + // Close consumer to cause the message to rollback. + connection1.send(consumerInfo1.createRemoveCommand()); + + // Now create remote consumer that should cause message to move to this remote consumer. + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + connection2.send(consumerInfo2); + + // Make sure the message was delivered via the remote. + m = receiveMessage(connection2); + assertNotNull(m); + } + + public void initCombosForTestAddConsumerThenSend() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + } ); + } + public void testAddConsumerThenSend() throws Throwable { + + // Start a producer on local broker + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo); + + destination = createDestinationInfo(connection1, connectionInfo1, destinationType); + + // Start a consumer on a remote broker + StubConnection connection2 = createRemoteConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo2, destination); + connection2.send(consumerInfo); + + // Send the message to the local boker. + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + + // Make sure the message was delivered via the remote. + Message m = receiveMessage(connection2); + assertNotNull(m); + } + + protected void setUp() throws Exception { + super.setUp(); + bridge = new DemandForwardingBridge(createTransport(), createRemoteTransport()); + bridge.setClientId("local-remote-bridge"); + bridge.setDispatchAsync(false); + bridge.start(); + + // PATCH: Give demand forwarding bridge a chance to finish setting up + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + + protected void tearDown() throws Exception { + bridge.stop(); + super.tearDown(); + } + + public static Test suite() { + return suite(DemandForwardingBridgeTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/network/ForwardingBridgeTest.java b/activemq-core/src/test/java/org/activemq/network/ForwardingBridgeTest.java new file mode 100755 index 0000000000..65bc35e9ff --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/network/ForwardingBridgeTest.java @@ -0,0 +1,108 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.network; + +import javax.jms.DeliveryMode; + +import org.activemq.broker.StubConnection; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.ProducerInfo; +import org.activemq.command.SessionInfo; + +import junit.framework.Test; + + +public class ForwardingBridgeTest extends NetworkTestSupport { + + public ActiveMQDestination destination; + public byte destinationType; + public int deliveryMode; + private ForwardingBridge bridge; + + public void initCombosForTestAddConsumerThenSend() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT)} ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.QUEUE_TYPE), + new Byte(ActiveMQDestination.TOPIC_TYPE), + } ); + } + public void testAddConsumerThenSend() throws Throwable { + + // Start a producer on local broker + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo); + + destination = createDestinationInfo(connection1, connectionInfo1, destinationType); + + // Start a consumer on a remote broker + StubConnection connection2 = createRemoteConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo2, destination); + connection2.send(consumerInfo); + + // Send the message to the local boker. + connection1.send(createMessage(producerInfo, destination, deliveryMode)); + + // Make sure the message was delivered via the remote. + Message m = receiveMessage(connection2); + assertNotNull(m); + } + + protected void setUp() throws Exception { + super.setUp(); + bridge = new ForwardingBridge(createTransport(), createRemoteTransport()); + bridge.setClientId("local-remote-bridge"); + bridge.setDispatchAsync(false); + bridge.start(); + + // PATCH: Give forwarding bridge a chance to finish setting up + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + + protected void tearDown() throws Exception { + bridge.stop(); + super.tearDown(); + } + + public static Test suite() { + return suite(ForwardingBridgeTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/network/NetworkTestSupport.java b/activemq-core/src/test/java/org/activemq/network/NetworkTestSupport.java new file mode 100755 index 0000000000..1aa48c2398 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/network/NetworkTestSupport.java @@ -0,0 +1,172 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.network; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Iterator; + +import org.activemq.broker.BrokerRegistry; +import org.activemq.broker.BrokerService; +import org.activemq.broker.BrokerTestSupport; +import org.activemq.broker.StubConnection; +import org.activemq.broker.TransportConnector; +import org.activemq.memory.UsageManager; +import org.activemq.store.PersistenceAdapter; +import org.activemq.store.memory.MemoryPersistenceAdapter; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; + +public class NetworkTestSupport extends BrokerTestSupport { + + protected ArrayList connections = new ArrayList(); + + protected TransportConnector connector; + + protected PersistenceAdapter remotePersistenceAdapter; + protected BrokerService remoteBroker; + protected UsageManager remoteMemoryManager; + protected TransportConnector remoteConnector; + + + protected void setUp() throws Exception { + + super.setUp(); + connector = createConnector(); + connector.start(); + + remotePersistenceAdapter = createRemotePersistenceAdapter(true); + remotePersistenceAdapter.start(); + remoteBroker = createRemoteBroker(remotePersistenceAdapter); + remoteBroker.start(); + BrokerRegistry.getInstance().bind("remotehost", remoteBroker); + remoteConnector = createRemoteConnector(); + remoteConnector.start(); + } + + /** + * @return + * @throws Exception + * @throws IOException + * @throws URISyntaxException + */ + protected TransportConnector createRemoteConnector() throws Exception, IOException, URISyntaxException { + return new TransportConnector(remoteBroker.getBroker(), + TransportFactory.bind(broker.getBrokerName(), + new URI(getRemoteURI()))); + } + + /** + * @param brokerId + * @return + * @throws Exception + * @throws IOException + * @throws URISyntaxException + */ + protected TransportConnector createConnector() throws Exception, IOException, URISyntaxException { + return new TransportConnector(broker.getBroker(), + TransportFactory.bind(broker.getBrokerName(), new URI(getLocalURI()))); + } + + protected String getRemoteURI() { + return "vm://remotehost"; + } + + protected String getLocalURI() { + return "vm://localhost"; + } + + protected PersistenceAdapter createRemotePersistenceAdapter(boolean clean) throws Exception { + if( remotePersistenceAdapter == null || clean ) { + remotePersistenceAdapter = new MemoryPersistenceAdapter(); + } + return remotePersistenceAdapter; + } + + protected BrokerService createRemoteBroker(PersistenceAdapter persistenceAdapter) throws Exception { + BrokerService answer = new BrokerService(); + answer.setPersistenceAdapter(persistenceAdapter); + return answer; + } + + protected StubConnection createConnection() throws Exception { + Transport transport = TransportFactory.connect(connector.getServer().getConnectURI()); + StubConnection connection = new StubConnection(transport); + connections.add(connection); + return connection; + } + + protected StubConnection createRemoteConnection() throws Exception { + Transport transport = TransportFactory.connect(remoteConnector.getServer().getConnectURI()); + StubConnection connection = new StubConnection(transport); + connections.add(connection); + return connection; + } + + protected Transport createTransport() throws Exception { + Transport transport = TransportFactory.connect(connector.getServer().getConnectURI()); + return transport; + } + + protected Transport createRemoteTransport() throws Exception { + Transport transport = TransportFactory.connect(remoteConnector.getServer().getConnectURI()); + return transport; + } + + /** + * Simulates a broker restart. The memory based persistence adapter is + * reused so that it does not "loose" it's "persistent" messages. + * @throws Exception + */ + protected void restartRemoteBroker() throws Exception { + + BrokerRegistry.getInstance().unbind("remotehost"); + remoteConnector.stop(); + + remoteBroker.stop(); + remotePersistenceAdapter.stop(); + remotePersistenceAdapter = createRemotePersistenceAdapter(false); + remotePersistenceAdapter.start(); + remoteBroker = createRemoteBroker(remotePersistenceAdapter); + remoteBroker.start(); + String brokerId = remoteBroker.getBrokerName(); + remoteConnector = new TransportConnector(broker.getBroker(),TransportFactory.bind(brokerId,new URI(getRemoteURI()))); + remoteConnector.start(); + BrokerRegistry.getInstance().bind("remotehost", remoteBroker); + } + + protected void tearDown() throws Exception { + for (Iterator iter = connections.iterator(); iter.hasNext();) { + StubConnection connection = (StubConnection) iter.next(); + connection.stop(); + iter.remove(); + } + + BrokerRegistry.getInstance().unbind("remotehost"); + remoteConnector.stop(); + connector.stop(); + + remoteBroker.stop(); + remotePersistenceAdapter.stop(); + super.tearDown(); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/network/jms/QueueBridgeTest.java b/activemq-core/src/test/java/org/activemq/network/jms/QueueBridgeTest.java new file mode 100755 index 0000000000..7b28bdf4e6 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/network/jms/QueueBridgeTest.java @@ -0,0 +1,103 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) RAJD Consultancy Ltd +* +* Licensed 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.activemq.network.jms; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Iterator; +import javax.jms.*; +import junit.framework.TestCase; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.broker.BrokerRegistry; +import org.activemq.broker.BrokerService; +import org.activemq.broker.BrokerTestSupport; +import org.activemq.broker.StubConnection; +import org.activemq.broker.TransportConnector; +import org.activemq.broker.region.QueueRegion; +import org.activemq.memory.UsageManager; +import org.activemq.store.PersistenceAdapter; +import org.activemq.store.memory.MemoryPersistenceAdapter; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class QueueBridgeTest extends TestCase implements MessageListener { + + protected static final int MESSAGE_COUNT = 10; + protected AbstractApplicationContext context; + protected QueueConnection localConnection; + protected QueueConnection remoteConnection; + protected QueueRequestor requestor; + protected QueueSession requestServerSession; + protected MessageConsumer requestServerConsumer; + protected MessageProducer requestServerProducer; + + protected void setUp() throws Exception { + + super.setUp(); + context = new ClassPathXmlApplicationContext("org/activemq/network/jms/queue-config.xml"); + ActiveMQConnectionFactory fac = (ActiveMQConnectionFactory) context.getBean("localFactory"); + localConnection = fac.createQueueConnection(); + localConnection.start(); + requestServerSession = localConnection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE); + Queue theQueue = requestServerSession.createQueue(getClass().getName()); + requestServerConsumer = requestServerSession.createConsumer(theQueue); + requestServerConsumer.setMessageListener(this); + requestServerProducer = requestServerSession.createProducer(null); + + fac = (ActiveMQConnectionFactory) context.getBean("remoteFactory"); + remoteConnection = fac.createQueueConnection(); + remoteConnection.start(); + QueueSession session = remoteConnection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE); + requestor = new QueueRequestor(session,theQueue); + } + + + protected void tearDown() throws Exception { + localConnection.close(); + super.tearDown(); + } + + public void testQueueRequestorOverBridge() throws JMSException{ + for (int i =0;i < MESSAGE_COUNT; i++){ + TextMessage msg = requestServerSession.createTextMessage("test msg: " +i); + TextMessage result = (TextMessage) requestor.request(msg); + assertNotNull(result); + System.out.println(result.getText()); + } + } + + public void onMessage(Message msg){ + try{ + TextMessage textMsg=(TextMessage) msg; + String payload="REPLY: "+textMsg.getText(); + Destination replyTo; + replyTo=msg.getJMSReplyTo(); + textMsg.clearBody(); + textMsg.setText(payload); + requestServerProducer.send(replyTo,textMsg); + }catch(JMSException e){ + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +} diff --git a/activemq-core/src/test/java/org/activemq/network/jms/TopicBridgeSpringTest.java b/activemq-core/src/test/java/org/activemq/network/jms/TopicBridgeSpringTest.java new file mode 100755 index 0000000000..34318584a5 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/network/jms/TopicBridgeSpringTest.java @@ -0,0 +1,106 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) RAJD Consultancy Ltd +* +* Licensed 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.activemq.network.jms; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicConnection; +import javax.jms.TopicRequestor; +import javax.jms.TopicSession; +import junit.framework.TestCase; +import org.activemq.ActiveMQConnectionFactory; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class TopicBridgeSpringTest extends TestCase implements MessageListener { + + protected static final int MESSAGE_COUNT = 10; + protected AbstractApplicationContext context; + protected TopicConnection localConnection; + protected TopicConnection remoteConnection; + protected TopicRequestor requestor; + protected TopicSession requestServerSession; + protected MessageConsumer requestServerConsumer; + protected MessageProducer requestServerProducer; + + protected void setUp() throws Exception { + + super.setUp(); + context = createApplicationContext(); + ActiveMQConnectionFactory fac = (ActiveMQConnectionFactory) context.getBean("localFactory"); + localConnection = fac.createTopicConnection(); + localConnection.start(); + requestServerSession = localConnection.createTopicSession(false,Session.AUTO_ACKNOWLEDGE); + Topic theTopic = requestServerSession.createTopic(getClass().getName()); + requestServerConsumer = requestServerSession.createConsumer(theTopic); + requestServerConsumer.setMessageListener(this); + requestServerProducer = requestServerSession.createProducer(null); + + fac = (ActiveMQConnectionFactory) context.getBean("remoteFactory"); + remoteConnection = fac.createTopicConnection(); + remoteConnection.start(); + TopicSession session = remoteConnection.createTopicSession(false,Session.AUTO_ACKNOWLEDGE); + requestor = new TopicRequestor(session,theTopic); + } + + + protected AbstractApplicationContext createApplicationContext() { + return new ClassPathXmlApplicationContext("org/activemq/network/jms/topic-spring.xml"); + } + + + protected void tearDown() throws Exception { + localConnection.close(); + super.tearDown(); + } + + public void testTopicRequestorOverBridge() throws JMSException{ + for (int i =0;i < MESSAGE_COUNT; i++){ + TextMessage msg = requestServerSession.createTextMessage("test msg: " +i); + System.out.println("Making request: " + msg); + TextMessage result = (TextMessage) requestor.request(msg); + assertNotNull(result); + System.out.println("Received result: " + result.getText()); + } + } + + public void onMessage(Message msg){ + try{ + TextMessage textMsg=(TextMessage) msg; + String payload="REPLY: "+textMsg.getText(); + Destination replyTo; + replyTo=msg.getJMSReplyTo(); + textMsg.clearBody(); + textMsg.setText(payload); + System.out.println("Sending response: " + textMsg); + requestServerProducer.send(replyTo,textMsg); + }catch(JMSException e){ + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +} diff --git a/activemq-core/src/test/java/org/activemq/network/jms/TopicBridgeXBeanTest.java b/activemq-core/src/test/java/org/activemq/network/jms/TopicBridgeXBeanTest.java new file mode 100644 index 0000000000..fd76ca868a --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/network/jms/TopicBridgeXBeanTest.java @@ -0,0 +1,33 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.network.jms; + +import org.springframework.context.support.AbstractApplicationContext; +import org.xbean.spring.context.ClassPathXmlApplicationContext; + +/** + * + * @version $Revision: 1.1 $ + */ +public class TopicBridgeXBeanTest extends TopicBridgeSpringTest { + + protected AbstractApplicationContext createApplicationContext() { + return new ClassPathXmlApplicationContext("org/activemq/network/jms/topic-config.xml"); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/openwire/BrokerInfoData.java b/activemq-core/src/test/java/org/activemq/openwire/BrokerInfoData.java new file mode 100644 index 0000000000..324b94d16c --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/openwire/BrokerInfoData.java @@ -0,0 +1,19 @@ +package org.activemq.openwire; + +import org.activemq.command.BrokerId; +import org.activemq.command.BrokerInfo; + +public class BrokerInfoData extends DataFileGenerator { + + protected Object createObject() { + BrokerInfo rc = new BrokerInfo(); + rc.setResponseRequired(false); + rc.setBrokerName("localhost"); + rc.setBrokerURL("tcp://localhost:61616"); + rc.setBrokerId(new BrokerId("ID:1289012830123")); + rc.setCommandId((short) 12); + rc.setResponseRequired(false); + return rc; + } + +} diff --git a/activemq-core/src/test/java/org/activemq/openwire/DataFileGenerator.java b/activemq-core/src/test/java/org/activemq/openwire/DataFileGenerator.java new file mode 100644 index 0000000000..a95d61f14a --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/openwire/DataFileGenerator.java @@ -0,0 +1,139 @@ +package org.activemq.openwire; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; + +import junit.framework.Assert; + +abstract public class DataFileGenerator extends Assert { + + static final File moduleBaseDir; + static final File controlDir; + static final File classFileDir; + + static { + File basedir=null; + try { + URL resource = DataFileGenerator.class.getResource("DataFileGenerator.class"); + URI baseURI = new URI(resource.toString()).resolve("../../../../.."); + basedir = new File(baseURI).getCanonicalFile(); + } catch (Exception e) { + throw new RuntimeException(e); + } + moduleBaseDir = basedir; + controlDir = new File(moduleBaseDir, "src/test/resources/openwire-control"); + classFileDir = new File(moduleBaseDir, "src/test/java/org/activemq/openwire"); + } + + public static void main(String[] args) throws Exception { + generateControlFiles(); + } + + /** + * @param srcdir + * @return + * @throws ClassNotFoundException + * @throws InstantiationException + * @throws IllegalAccessException + */ + public static ArrayList getAllDataFileGenerators() throws Exception{ + System.out.println("Looking for generators in : "+classFileDir); + ArrayList l = new ArrayList(); + File[] files = classFileDir.listFiles(); + for (int i = 0; files!=null && i < files.length; i++) { + File file = files[i]; + if( file.getName().endsWith("Data.java") ) { + String cn = file.getName(); + cn = cn.substring(0, cn.length() - ".java".length()); + Class clazz = DataFileGenerator.class.getClassLoader().loadClass("org.activemq.openwire."+cn); + l.add((DataFileGenerator) clazz.newInstance()); + } + } + return l; + } + + private static void generateControlFiles() throws Exception { + ArrayList generators = getAllDataFileGenerators(); + for (Iterator iter = generators.iterator(); iter.hasNext();) { + DataFileGenerator object = (DataFileGenerator) iter.next(); + try { + System.out.println("Processing: "+object.getClass()); + object.generateControlFile(); + } catch (Exception e) { + System.out.println("Error while processing: "+object.getClass()); + e.printStackTrace(System.out); + } + } + } + + public void generateControlFile() throws Exception { + controlDir.mkdirs(); + File dataFile = new File(controlDir, getClass().getName()+".bin"); + + OpenWireFormat wf = new OpenWireFormat(); + wf.setCacheEnabled(false); + wf.setStackTraceEnabled(false); + wf.setVersion(1); + + FileOutputStream os = new FileOutputStream(dataFile); + DataOutputStream ds = new DataOutputStream(os); + wf.marshal(createObject(), ds); + ds.close(); + } + + public InputStream generateInputStream() throws Exception { + OpenWireFormat wf = new OpenWireFormat(); + wf.setCacheEnabled(false); + wf.setStackTraceEnabled(false); + wf.setVersion(1); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + DataOutputStream ds = new DataOutputStream(os); + wf.marshal(createObject(), ds); + ds.close(); + + return new ByteArrayInputStream(os.toByteArray()); + } + + public static void assertAllControlFileAreEqual() throws Exception { + ArrayList generators = getAllDataFileGenerators(); + for (Iterator iter = generators.iterator(); iter.hasNext();) { + DataFileGenerator object = (DataFileGenerator) iter.next(); + System.out.println("Processing: "+object.getClass()); + object.assertControlFileIsEqual(); + } + } + + public void assertControlFileIsEqual() throws Exception { + File dataFile = new File(controlDir, getClass().getName()+".bin"); + FileInputStream is1 = new FileInputStream(dataFile); + int pos=0; + try { + InputStream is2 = generateInputStream(); + int a = is1.read(); + int b = is2.read(); + pos++; + assertEquals("Data does not match control file: "+dataFile+" at byte position "+pos,a,b); + while(a >= 0 && b>= 0) { + a = is1.read(); + b = is2.read(); + pos++; + assertEquals(a,b); + } + is2.close(); + } finally { + is1.close(); + } + } + + abstract protected Object createObject(); +} diff --git a/activemq-core/src/test/java/org/activemq/openwire/ItStillMarshallsTheSameTest.java b/activemq-core/src/test/java/org/activemq/openwire/ItStillMarshallsTheSameTest.java new file mode 100644 index 0000000000..d45325dc68 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/openwire/ItStillMarshallsTheSameTest.java @@ -0,0 +1,11 @@ +package org.activemq.openwire; + +import junit.framework.TestCase; + +public class ItStillMarshallsTheSameTest extends TestCase { + + public void testAll() throws Exception { + BrokerInfoData.assertAllControlFileAreEqual(); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/openwire/WireFormatInfoData.java b/activemq-core/src/test/java/org/activemq/openwire/WireFormatInfoData.java new file mode 100644 index 0000000000..ab83935a00 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/openwire/WireFormatInfoData.java @@ -0,0 +1,14 @@ +package org.activemq.openwire; + +import org.activemq.command.WireFormatInfo; + +public class WireFormatInfoData extends DataFileGenerator { + + protected Object createObject() { + WireFormatInfo rc = new WireFormatInfo(); + rc.setResponseRequired(false); + rc.setVersion(1); + return rc; + } + +} diff --git a/activemq-core/src/test/java/org/activemq/perf/PerfConsumer.java b/activemq-core/src/test/java/org/activemq/perf/PerfConsumer.java new file mode 100644 index 0000000000..d392bc5af2 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/perf/PerfConsumer.java @@ -0,0 +1,63 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.perf; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Session; +import javax.jms.Topic; +/** + * @version $Revision: 1.3 $ + */ +public class PerfConsumer implements MessageListener{ + protected Connection connection; + protected MessageConsumer consumer; + protected PerfRate rate=new PerfRate(); + public PerfConsumer(ConnectionFactory fac,Destination dest,String consumerName) throws JMSException{ + connection=fac.createConnection(); + Session s=connection.createSession(false,Session.AUTO_ACKNOWLEDGE); + if(dest instanceof Topic&&consumerName!=null&&consumerName.length()>0){ + consumer=s.createDurableSubscriber((Topic) dest,consumerName); + }else{ + consumer=s.createConsumer(dest); + } + consumer.setMessageListener(this); + } + public PerfConsumer(ConnectionFactory fac,Destination dest) throws JMSException{ + this(fac,dest,null); + } + public void start() throws JMSException{ + connection.start(); + rate.getRate(); + } + public void stop() throws JMSException{ + connection.stop(); + } + public void shutDown() throws JMSException{ + connection.close(); + } + public PerfRate getRate(){ + return rate; + } + public void onMessage(Message msg){ + rate.increment(); + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/perf/PerfProducer.java b/activemq-core/src/test/java/org/activemq/perf/PerfProducer.java new file mode 100644 index 0000000000..3750fcc0ad --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/perf/PerfProducer.java @@ -0,0 +1,58 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.perf; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.Session; +/** + * @version $Revision: 1.3 $ + */ +public class PerfProducer{ + protected Connection connection; + protected MessageProducer producer; + protected PerfRate rate=new PerfRate(); + public PerfProducer(ConnectionFactory fac,Destination dest) throws JMSException{ + connection=fac.createConnection(); + Session s=connection.createSession(false,Session.AUTO_ACKNOWLEDGE); + producer=s.createProducer(dest); + } + public void setDeliveryMode(int mode) throws JMSException{ + producer.setDeliveryMode(mode); + } + public void start() throws JMSException{ + connection.start(); + rate.getRate(); + } + public void stop() throws JMSException{ + connection.stop(); + } + public void shutDown() throws JMSException{ + connection.close(); + } + public void sendMessage(Message msg) throws JMSException{ + producer.send(msg); + rate.increment(); + } + public PerfRate getRate(){ + return rate; + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/perf/PerfRate.java b/activemq-core/src/test/java/org/activemq/perf/PerfRate.java new file mode 100644 index 0000000000..1989186c77 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/perf/PerfRate.java @@ -0,0 +1,58 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.perf; + +/** + * @version $Revision: 1.3 $ + */ +public class PerfRate{ + protected int totalCount; + protected int count; + protected long startTime=System.currentTimeMillis(); + /** + * @return Returns the count. + */ + public int getCount(){ + return totalCount; + } + public void increment(){ + totalCount++; + count++; + } + public void start(){ + count=0; + startTime=System.currentTimeMillis(); + } + public int getRate(){ + long endTime=System.currentTimeMillis(); + long totalTime=endTime-startTime; + int result=(int) ((count*1000)/totalTime); + return result; + } + /** + * @return Returns the totalCount. + */ + public int getTotalCount(){ + return totalCount; + } + /** + * @param totalCount + * The totalCount to set. + */ + public void setTotalCount(int totalCount){ + this.totalCount=totalCount; + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/perf/SimpleDurableTopicTest.java b/activemq-core/src/test/java/org/activemq/perf/SimpleDurableTopicTest.java new file mode 100644 index 0000000000..27c72a1b00 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/perf/SimpleDurableTopicTest.java @@ -0,0 +1,35 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.perf; + +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +/** + * @version $Revision: 1.3 $ + */ +public class SimpleDurableTopicTest extends SimpleTopicTest{ + protected PerfProducer createProducer(ConnectionFactory fac,Destination dest,int number) throws JMSException{ + PerfProducer pp=new PerfProducer(fac,dest); + pp.setDeliveryMode(DeliveryMode.PERSISTENT); + return pp; + } + + protected PerfConsumer createConsumer(ConnectionFactory fac,Destination dest,int number) throws JMSException{ + return new PerfConsumer(fac,dest,"subs:"+number); + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/perf/SimpleNonPersistentQueueTest.java b/activemq-core/src/test/java/org/activemq/perf/SimpleNonPersistentQueueTest.java new file mode 100644 index 0000000000..12f0074a1b --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/perf/SimpleNonPersistentQueueTest.java @@ -0,0 +1,32 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.perf; + +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +/** + * @version $Revision: 1.3 $ + */ +public class SimpleNonPersistentQueueTest extends SimpleQueueTest{ + + protected PerfProducer createProducer(ConnectionFactory fac,Destination dest,int number) throws JMSException{ + PerfProducer pp=new PerfProducer(fac,dest); + pp.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + return pp; + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/perf/SimpleNonPersistentTopicTest.java b/activemq-core/src/test/java/org/activemq/perf/SimpleNonPersistentTopicTest.java new file mode 100644 index 0000000000..6c97f22d78 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/perf/SimpleNonPersistentTopicTest.java @@ -0,0 +1,32 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.perf; + +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +/** + * @version $Revision: 1.3 $ + */ +public class SimpleNonPersistentTopicTest extends SimpleTopicTest{ + + protected PerfProducer createProducer(ConnectionFactory fac,Destination dest,int number) throws JMSException{ + PerfProducer pp = new PerfProducer(fac,dest); + pp.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + return pp; + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/perf/SimpleQueueTest.java b/activemq-core/src/test/java/org/activemq/perf/SimpleQueueTest.java new file mode 100644 index 0000000000..c17b2df00d --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/perf/SimpleQueueTest.java @@ -0,0 +1,30 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.perf; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Session; +/** + * @version $Revision: 1.3 $ + */ +public class SimpleQueueTest extends SimpleTopicTest{ + + protected Destination createDestination(Session s,String destinationName) throws JMSException{ + return s.createQueue(destinationName); + } + +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/perf/SimpleTopicTest.java b/activemq-core/src/test/java/org/activemq/perf/SimpleTopicTest.java new file mode 100644 index 0000000000..5db0547da1 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/perf/SimpleTopicTest.java @@ -0,0 +1,171 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) Simula Labs Inc. + * + * Licensed 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.activemq.perf; + +import javax.jms.BytesMessage; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Session; +import junit.framework.TestCase; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.broker.BrokerService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +/** + * @version $Revision: 1.3 $ + */ +public class SimpleTopicTest extends TestCase{ + private static final Log log=LogFactory.getLog(SimpleTopicTest.class); + protected BrokerService broker; + protected String bindAddress="tcp://localhost:61616"; + protected PerfProducer[] producers; + protected PerfConsumer[] consumers; + protected String DESTINATION_NAME=getClass().toString(); + protected int NUMBER_OF_CONSUMERS=1; + protected int NUMBER_OF_PRODUCERS=1; + protected BytesMessage payload; + protected int PAYLOAD_SIZE=1024; + protected int MESSAGE_COUNT=1000000; + protected byte[] array=null; + + /** + * Sets up a test where the producer and consumer have their own connection. + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception{ + if(broker==null){ + broker=createBroker(); + } + array=new byte[PAYLOAD_SIZE]; + for(int i=0;iActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.proxy; + +import javax.jms.DeliveryMode; + +import org.activemq.broker.StubConnection; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.ProducerInfo; +import org.activemq.command.SessionInfo; + +import junit.framework.Test; + + +public class ProxyConnectorTest extends ProxyTestSupport { + + public static Test suite() { + return suite(ProxyConnectorTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public ActiveMQDestination destination; + public byte destinationType; + public int deliveryMode; + + public void initCombosForTestSendAndConsume() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) + } ); + addCombinationValues( "destinationType", new Object[]{ + new Byte(ActiveMQDestination.TOPIC_TYPE), + } ); + } + public void testSendAndConsume() throws Throwable { + + // Start a producer on local broker using the proxy + StubConnection connection1 = createProxyConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ProducerInfo producerInfo = createProducerInfo(sessionInfo1); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.send(producerInfo); + + destination = createDestinationInfo(connection1, connectionInfo1, destinationType); + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + connection1.send(consumerInfo1); + + // Start a consumer on a remote broker using a proxy connection. + StubConnection connection2 = createRemoteProxyConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + connection2.send(consumerInfo2); + + // Give broker enough time to receive and register the consumer info + // Either that or make consumer retroactive + try { + Thread.sleep(1000); + } catch (Exception e) { + e.printStackTrace(); + } + + // Send the message to the local broker. + connection1.request(createMessage(producerInfo, destination, deliveryMode)); + + // Verify that the message Was sent to the remote broker and the local broker. + Message m; + m = receiveMessage(connection1); + assertNotNull(m); + assertNoMessagesLeft(connection1); + + m = receiveMessage(connection2); + assertNotNull(m); + assertNoMessagesLeft(connection2); + + } + + +} diff --git a/activemq-core/src/test/java/org/activemq/proxy/ProxyTestSupport.java b/activemq-core/src/test/java/org/activemq/proxy/ProxyTestSupport.java new file mode 100755 index 0000000000..d54e77ecb9 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/proxy/ProxyTestSupport.java @@ -0,0 +1,136 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.proxy; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Iterator; + +import org.activemq.broker.BrokerService; +import org.activemq.broker.BrokerTestSupport; +import org.activemq.broker.StubConnection; +import org.activemq.broker.TransportConnector; +import org.activemq.memory.UsageManager; +import org.activemq.store.PersistenceAdapter; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; + +public class ProxyTestSupport extends BrokerTestSupport { + + protected ArrayList connections = new ArrayList(); + + protected TransportConnector connector; + + protected PersistenceAdapter remotePersistenceAdapter; + protected BrokerService remoteBroker; + protected UsageManager remoteMemoryManager; + protected TransportConnector remoteConnector; + private ProxyConnector proxyConnector; + private ProxyConnector remoteProxyConnector; + + protected BrokerService createBroker() throws Exception { + BrokerService service = new BrokerService(); + service.setBrokerName("broker1"); + service.setPersistent(false); + + connector = service.addConnector(getLocalURI()); + proxyConnector=new ProxyConnector(); + proxyConnector.setBind(new URI(getLocalProxyURI())); + proxyConnector.setRemote(new URI("fanout:static://"+getRemoteURI())); + service.addProxyConnector(proxyConnector); + + return service; + } + + protected BrokerService createRemoteBroker() throws Exception { + BrokerService service = new BrokerService(); + service.setBrokerName("broker2"); + service.setPersistent(false); + + remoteConnector = service.addConnector(getRemoteURI()); + remoteProxyConnector = new ProxyConnector(); + remoteProxyConnector.setBind(new URI(getRemoteProxyURI())); + remoteProxyConnector.setRemote(new URI("fanout:static://"+getLocalURI())); + service.addProxyConnector(remoteProxyConnector); + + return service; + } + + + protected void setUp() throws Exception { + super.setUp(); + remoteBroker = createRemoteBroker(); + remoteBroker.start(); + } + + protected void tearDown() throws Exception { + for (Iterator iter = connections.iterator(); iter.hasNext();) { + StubConnection connection = (StubConnection) iter.next(); + connection.stop(); + iter.remove(); + } + remoteBroker.stop(); + super.tearDown(); + } + + protected String getRemoteURI() { + return "tcp://localhost:7001"; + } + + protected String getLocalURI() { + return "tcp://localhost:6001"; + } + + protected String getRemoteProxyURI() { + return "tcp://localhost:7002"; + } + + protected String getLocalProxyURI() { + return "tcp://localhost:6002"; + } + + protected StubConnection createConnection() throws Exception { + Transport transport = TransportFactory.connect(connector.getServer().getConnectURI()); + StubConnection connection = new StubConnection(transport); + connections.add(connection); + return connection; + } + + protected StubConnection createRemoteConnection() throws Exception { + Transport transport = TransportFactory.connect(remoteConnector.getServer().getConnectURI()); + StubConnection connection = new StubConnection(transport); + connections.add(connection); + return connection; + } + + protected StubConnection createProxyConnection() throws Exception { + Transport transport = TransportFactory.connect(proxyConnector.getServer().getConnectURI()); + StubConnection connection = new StubConnection(transport); + connections.add(connection); + return connection; + } + + protected StubConnection createRemoteProxyConnection() throws Exception { + Transport transport = TransportFactory.connect(remoteProxyConnector.getServer().getConnectURI()); + StubConnection connection = new StubConnection(transport); + connections.add(connection); + return connection; + } + +} diff --git a/activemq-core/src/test/java/org/activemq/security/SimpleSecurityBrokerSystemTest.java b/activemq-core/src/test/java/org/activemq/security/SimpleSecurityBrokerSystemTest.java new file mode 100644 index 0000000000..623d656ee8 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/security/SimpleSecurityBrokerSystemTest.java @@ -0,0 +1,361 @@ +package org.activemq.security; + +import java.io.IOException; +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import junit.framework.Test; + +import org.activemq.JmsTestSupport; +import org.activemq.broker.Broker; +import org.activemq.broker.BrokerService; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.activemq.filter.DestinationMap; +import org.activemq.jaas.GroupPrincipal; + +/** + * Tests that the broker allows/fails access to destinations based on the + * security policy installed on the broker. + * + * @version $Revision$ + */ +public class SimpleSecurityBrokerSystemTest extends JmsTestSupport { + + static final GroupPrincipal guests = new GroupPrincipal("guests"); + static final GroupPrincipal users = new GroupPrincipal("users"); + static final GroupPrincipal admins = new GroupPrincipal("admins"); + + public ActiveMQDestination destination; + public SecurityFactory authorizationFactory; + public SecurityFactory authenticationFactory; + + interface SecurityFactory { + public Broker create(Broker broker); + } + + class SimpleAuthorizationFactory implements SecurityFactory { + public Broker create(Broker broker) { + + DestinationMap readAccess = new DestinationMap(); + readAccess.put(new ActiveMQQueue(">"), admins); + readAccess.put(new ActiveMQQueue("USERS.>"), users); + readAccess.put(new ActiveMQQueue("GUEST.>"), guests); + readAccess.put(new ActiveMQTopic(">"), admins); + readAccess.put(new ActiveMQTopic("USERS.>"), users); + readAccess.put(new ActiveMQTopic("GUEST.>"), guests); + + DestinationMap writeAccess = new DestinationMap(); + writeAccess.put(new ActiveMQQueue(">"), admins); + writeAccess.put(new ActiveMQQueue("USERS.>"), users); + writeAccess.put(new ActiveMQQueue("GUEST.>"), users); + writeAccess.put(new ActiveMQQueue("GUEST.>"), guests); + writeAccess.put(new ActiveMQTopic(">"), admins); + writeAccess.put(new ActiveMQTopic("USERS.>"), users); + writeAccess.put(new ActiveMQTopic("GUEST.>"), users); + writeAccess.put(new ActiveMQTopic("GUEST.>"), guests); + + readAccess.put(new ActiveMQTopic("ActiveMQ.Advisory.>"), guests); + readAccess.put(new ActiveMQTopic("ActiveMQ.Advisory.>"), users); + writeAccess.put(new ActiveMQTopic("ActiveMQ.Advisory.>"), guests); + writeAccess.put(new ActiveMQTopic("ActiveMQ.Advisory.>"), users); + + DestinationMap adminAccess = new DestinationMap(); + adminAccess.put(new ActiveMQQueue(">"), admins); + adminAccess.put(new ActiveMQQueue(">"), users); + adminAccess.put(new ActiveMQQueue(">"), guests); + + return new SimpleAuthorizationBroker(broker, writeAccess, readAccess, adminAccess); + } + public String toString() { + return "SimpleAuthorizationBroker"; + } + } + + class SimpleAuthenticationFactory implements SecurityFactory { + public Broker create(Broker broker) { + + HashMap u = new HashMap(); + u.put("system", "manager"); + u.put("user", "password"); + u.put("guest", "password"); + + HashMap groups = new HashMap(); + groups.put("system", new HashSet(Arrays.asList(new Object[]{admins, users}))); + groups.put("user", new HashSet(Arrays.asList(new Object[]{users}))); + groups.put("guest", new HashSet(Arrays.asList(new Object[]{guests}))); + + return new SimpleAuthenticationBroker(broker, u, groups); + } + public String toString() { + return "SimpleAuthenticationBroker"; + } + } + + static { + String path = System.getProperty("java.security.auth.login.config"); + if( path == null ) { + URL resource = SimpleSecurityBrokerSystemTest.class.getResource("login.config"); + if( resource!=null ) { + path = resource.getFile(); + System.setProperty("java.security.auth.login.config", path); + } + } + System.out.println("Path to login config: "+path); + } + + class JaasAuthenticationFactory implements SecurityFactory { + public Broker create(Broker broker) { + return new JaasAuthenticationBroker(broker, "activemq-test-domain"); + } + public String toString() { + return "JassAuthenticationBroker"; + } + } + + public static Test suite() { + return suite(SimpleSecurityBrokerSystemTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public void initCombos() { + addCombinationValues("authorizationFactory", new Object[] { + new SimpleAuthorizationFactory(), + }); + addCombinationValues("authenticationFactory", new Object[] { + new SimpleAuthenticationFactory(), + new JaasAuthenticationFactory(), + }); + } + + protected BrokerService createBroker() throws Exception { + BrokerService broker = new BrokerService() { + protected Broker addInterceptors(Broker broker) throws IOException { + broker = super.addInterceptors(broker); + broker = authorizationFactory.create(broker); + broker = authenticationFactory.create(broker); + return broker; + } + }; + broker.setPersistent(false); + return broker; + } + + public void initCombosForTestUserReceiveFails() { + addCombinationValues("userName", new Object[] {"user"}); + addCombinationValues("password", new Object[] {"password"}); + addCombinationValues("destination", new Object[] { + new ActiveMQQueue("TEST"), + new ActiveMQTopic("TEST"), + new ActiveMQQueue("GUEST.BAR"), + new ActiveMQTopic("GUEST.BAR"), + }); + } + public void testUserReceiveFails() throws JMSException { + doReceive(true); + } + + + public void initCombosForTestInvalidAuthentication() { + addCombinationValues("userName", new Object[] {"user"}); + addCombinationValues("password", new Object[] {"password"}); + } + public void testInvalidAuthentication() throws JMSException { + try { + // No user id + Connection c= factory.createConnection(); + connections.add(c); + c.start(); + fail("Expected exception."); + } catch (JMSException e) { + } + + try { + // Bad password + Connection c = factory.createConnection("user", "krap"); + connections.add(c); + c.start(); + fail("Expected exception."); + } catch (JMSException e) { + } + + try { + // Bad userid + Connection c = factory.createConnection("userkrap", null); + connections.add(c); + c.start(); + fail("Expected exception."); + } catch (JMSException e) { + } + } + + public void initCombosForTestUserReceiveSucceeds() { + addCombinationValues("userName", new Object[] {"user"}); + addCombinationValues("password", new Object[] {"password"}); + addCombinationValues("destination", new Object[] { + new ActiveMQQueue("USERS.FOO"), + new ActiveMQTopic("USERS.FOO"), + }); + } + public void testUserReceiveSucceeds() throws JMSException { + doReceive(false); + } + + + public void initCombosForTestGuestReceiveSucceeds() { + addCombinationValues("userName", new Object[] {"guest"}); + addCombinationValues("password", new Object[] {"password"}); + addCombinationValues("destination", new Object[] { + new ActiveMQQueue("GUEST.BAR"), + new ActiveMQTopic("GUEST.BAR"), + }); + } + public void testGuestReceiveSucceeds() throws JMSException { + doReceive(false); + } + + public void initCombosForTestGuestReceiveFails() { + addCombinationValues("userName", new Object[] {"guest"}); + addCombinationValues("password", new Object[] {"password"}); + addCombinationValues("destination", new Object[] { + new ActiveMQQueue("TEST"), + new ActiveMQTopic("TEST"), + new ActiveMQQueue("USERS.FOO"), + new ActiveMQTopic("USERS.FOO"), + }); + } + public void testGuestReceiveFails() throws JMSException { + doReceive(true); + } + + + public void initCombosForTestUserSendSucceeds() { + addCombinationValues("userName", new Object[] {"user"}); + addCombinationValues("password", new Object[] {"password"}); + addCombinationValues("destination", new Object[] { + new ActiveMQQueue("USERS.FOO"), + new ActiveMQQueue("GUEST.BAR"), + new ActiveMQTopic("USERS.FOO"), + new ActiveMQTopic("GUEST.BAR"), + }); + } + public void testUserSendSucceeds() throws JMSException { + doSend(false); + } + + public void initCombosForTestUserSendFails() { + addCombinationValues("userName", new Object[] {"user"}); + addCombinationValues("password", new Object[] {"password"}); + addCombinationValues("destination", new Object[] { + new ActiveMQQueue("TEST"), + new ActiveMQTopic("TEST"), + }); + } + public void testUserSendFails() throws JMSException { + doSend(true); + } + + + public void initCombosForTestGuestSendFails() { + addCombinationValues("userName", new Object[] {"guest"}); + addCombinationValues("password", new Object[] {"password"}); + addCombinationValues("destination", new Object[] { + new ActiveMQQueue("TEST"), + new ActiveMQTopic("TEST"), + new ActiveMQQueue("USERS.FOO"), + new ActiveMQTopic("USERS.FOO"), + }); + } + public void testGuestSendFails() throws JMSException { + doSend(true); + } + + public void initCombosForTestGuestSendSucceeds() { + addCombinationValues("userName", new Object[] {"guest"}); + addCombinationValues("password", new Object[] {"password"}); + addCombinationValues("destination", new Object[] { + new ActiveMQQueue("GUEST.BAR"), + new ActiveMQTopic("GUEST.BAR"), + }); + } + public void testGuestSendSucceeds() throws JMSException { + doSend(false); + } + + /** + * @throws JMSException + */ + public void doSend(boolean fail) throws JMSException { + + Connection adminConnection = factory.createConnection("system", "manager"); + connections.add(adminConnection); + + adminConnection.start(); + Session adminSession = adminConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageConsumer consumer = adminSession.createConsumer(destination); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + try { + sendMessages(session, destination, 1); + } catch (JMSException e) { + // If test is expected to fail, the cause must only be a SecurityException + // otherwise rethrow the exception + if (!fail || !(e.getCause() instanceof SecurityException)) { + throw e; + } + } + + Message m = consumer.receive(1000); + if(fail) + assertNull(m); + else { + assertNotNull(m); + assertEquals("0", ((TextMessage)m).getText()); + assertNull(consumer.receiveNoWait()); + } + + } + + /** + * @throws JMSException + */ + public void doReceive(boolean fail) throws JMSException { + + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageConsumer consumer=null; + try { + consumer = session.createConsumer(destination); + if( fail ) + fail("Expected failure due to security constraint."); + } catch (JMSException e) { + if (fail && e.getCause() instanceof SecurityException) + return; + throw e; + } + + Connection adminConnection = factory.createConnection("system", "manager"); + connections.add(adminConnection); + Session adminSession = adminConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + sendMessages(adminSession, destination, 1); + + Message m = consumer.receive(1000); + assertNotNull(m); + assertEquals("0", ((TextMessage)m).getText()); + assertNull(consumer.receiveNoWait()); + + } +} diff --git a/activemq-core/src/test/java/org/activemq/selector/SelectorParserTest.java b/activemq-core/src/test/java/org/activemq/selector/SelectorParserTest.java new file mode 100755 index 0000000000..fa253cd7f3 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/selector/SelectorParserTest.java @@ -0,0 +1,74 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.selector; + +import junit.framework.TestCase; + +import org.activemq.filter.BooleanExpression; +import org.activemq.filter.ComparisonExpression; +import org.activemq.filter.Expression; +import org.activemq.filter.LogicExpression; +import org.activemq.filter.PropertyExpression; +import org.activemq.filter.XPathExpression; + +/** + * @version $Revision: 1.2 $ + */ +public class SelectorParserTest extends TestCase { + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(SelectorParserTest.class); + + public void testParseXPath() throws Exception { + BooleanExpression filter = parse("XPATH '//title[@lang=''eng'']'"); + assertTrue("Created XPath expression", filter instanceof XPathExpression); + log.info("Expression: "+filter); + } + + public void testParseWithParensAround() throws Exception { + String[] values = {"x = 1 and y = 2", "(x = 1) and (y = 2)", "((x = 1) and (y = 2))"}; + + for (int i = 0; i < values.length; i++) { + String value = values[i]; + log.info("Parsing: " + value); + + BooleanExpression andExpression = parse(value); + assertTrue("Created LogicExpression expression", andExpression instanceof LogicExpression); + LogicExpression logicExpression = (LogicExpression) andExpression; + Expression left = logicExpression.getLeft(); + Expression right = logicExpression.getRight(); + + assertTrue("Left is a binary filter", left instanceof ComparisonExpression); + assertTrue("Right is a binary filter", right instanceof ComparisonExpression); + ComparisonExpression leftCompare = (ComparisonExpression) left; + ComparisonExpression rightCompare = (ComparisonExpression) right; + assertPropertyExpression("left", leftCompare.getLeft(), "x"); + assertPropertyExpression("right", rightCompare.getLeft(), "y"); + } + } + + protected void assertPropertyExpression(String message, Expression expression, String expected) { + assertTrue(message + ". Must be PropertyExpression", expression instanceof PropertyExpression); + PropertyExpression propExp = (PropertyExpression) expression; + assertEquals(message + ". Property name", expected, propExp.getName()); + } + + protected BooleanExpression parse(String text) throws Exception { + return new SelectorParser().parse(text); + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/selector/SelectorTest.java b/activemq-core/src/test/java/org/activemq/selector/SelectorTest.java new file mode 100755 index 0000000000..8da08aa213 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/selector/SelectorTest.java @@ -0,0 +1,336 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.selector; + +import javax.jms.InvalidSelectorException; +import javax.jms.JMSException; +import javax.jms.Message; + +import junit.framework.TestCase; + +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ActiveMQTextMessage; +import org.activemq.command.ActiveMQTopic; +import org.activemq.filter.BooleanExpression; +import org.activemq.filter.MessageEvaluationContext; + +/** + * @version $Revision: 1.7 $ + */ +public class SelectorTest extends TestCase { + + public void testBooleanSelector() throws Exception { + Message message = createMessage(); + + assertSelector(message, "(trueProp OR falseProp) AND trueProp", true); + assertSelector(message, "(trueProp OR falseProp) AND falseProp", false); + + } + + public void testXPathSelectors() throws Exception { + ActiveMQTextMessage message = new ActiveMQTextMessage(); + + message.setJMSType("xml"); + message.setText(""); + + assertSelector(message, "XPATH 'root/a'", true); + assertSelector(message, "XPATH '//root/b'", true); + assertSelector(message, "XPATH 'root/c'", false); + + assertSelector(message, "XPATH '//root/*[@key=''second'']'", true); + assertSelector(message, "XPATH '//root/*[@key=''third'']'", false); + + assertSelector(message, "XPATH '//root/a[@key=''first'']'", true); + assertSelector(message, "XPATH '//root/a[@key=''second'']'", false); + } + + public void testJMSPropertySelectors() throws Exception { + Message message = createMessage(); + message.setJMSType("selector-test"); + message.setJMSMessageID("id:test:1:1:1:1"); + + assertSelector(message, "JMSType = 'selector-test'", true); + assertSelector(message, "JMSType = 'crap'", false); + + assertSelector(message, "JMSMessageID = 'id:test:1:1:1:1'", true); + assertSelector(message, "JMSMessageID = 'id:not-test:1:1:1:1'", false); + } + + public void testBasicSelectors() throws Exception { + Message message = createMessage(); + + assertSelector(message, "name = 'James'", true); + assertSelector(message, "rank > 100", true); + assertSelector(message, "rank >= 123", true); + assertSelector(message, "rank >= 124", false); + } + + public void testAndSelectors() throws Exception { + Message message = createMessage(); + + assertSelector(message, "name = 'James' and rank < 200", true); + assertSelector(message, "name = 'James' and rank > 200", false); + assertSelector(message, "name = 'Foo' and rank < 200", false); + assertSelector(message, "unknown = 'Foo' and anotherUnknown < 200", false); + } + + public void testOrSelectors() throws Exception { + Message message = createMessage(); + + assertSelector(message, "name = 'James' or rank < 200", true); + assertSelector(message, "name = 'James' or rank > 200", true); + assertSelector(message, "name = 'Foo' or rank < 200", true); + assertSelector(message, "name = 'Foo' or rank > 200", false); + assertSelector(message, "unknown = 'Foo' or anotherUnknown < 200", false); + } + + public void testPlus() throws Exception { + Message message = createMessage(); + + assertSelector(message, "rank + 2 = 125", true); + assertSelector(message, "(rank + 2) = 125", true); + assertSelector(message, "125 = (rank + 2)", true); + assertSelector(message, "rank + version = 125", true); + assertSelector(message, "rank + 2 < 124", false); + assertSelector(message, "name + '!' = 'James!'", true); + } + + public void testMinus() throws Exception { + Message message = createMessage(); + + assertSelector(message, "rank - 2 = 121", true); + assertSelector(message, "rank - version = 121", true); + assertSelector(message, "rank - 2 > 122", false); + } + + public void testMultiply() throws Exception { + Message message = createMessage(); + + assertSelector(message, "rank * 2 = 246", true); + assertSelector(message, "rank * version = 246", true); + assertSelector(message, "rank * 2 < 130", false); + } + + public void testDivide() throws Exception { + Message message = createMessage(); + + assertSelector(message, "rank / version = 61.5", true); + assertSelector(message, "rank / 3 > 100.0", false); + assertSelector(message, "rank / 3 > 100", false); + assertSelector(message, "version / 2 = 1", true); + + } + + public void testBetween() throws Exception { + Message message = createMessage(); + + assertSelector(message, "rank between 100 and 150", true); + assertSelector(message, "rank between 10 and 120", false); + } + + public void testIn() throws Exception { + Message message = createMessage(); + + assertSelector(message, "name in ('James', 'Bob', 'Gromit')", true); + assertSelector(message, "name in ('Bob', 'James', 'Gromit')", true); + assertSelector(message, "name in ('Gromit', 'Bob', 'James')", true); + + assertSelector(message, "name in ('Gromit', 'Bob', 'Cheddar')", false); + assertSelector(message, "name not in ('Gromit', 'Bob', 'Cheddar')", true); + } + + public void testIsNull() throws Exception { + Message message = createMessage(); + + assertSelector(message, "dummy is null", true); + assertSelector(message, "dummy is not null", false); + assertSelector(message, "name is not null", true); + assertSelector(message, "name is null", false); + } + + + public void testLike() throws Exception { + Message message = createMessage(); + message.setStringProperty("modelClassId", "com.whatever.something.foo.bar"); + message.setStringProperty("modelInstanceId", "170"); + message.setStringProperty("modelRequestError", "abc"); + message.setStringProperty("modelCorrelatedClientId", "whatever"); + + assertSelector(message, "modelClassId LIKE 'com.whatever.something.%' AND modelInstanceId = '170' AND (modelRequestError IS NULL OR modelCorrelatedClientId = 'whatever')", true); + + message.setStringProperty("modelCorrelatedClientId", "shouldFailNow"); + + assertSelector(message, "modelClassId LIKE 'com.whatever.something.%' AND modelInstanceId = '170' AND (modelRequestError IS NULL OR modelCorrelatedClientId = 'whatever')", false); + + message = createMessage(); + message.setStringProperty("modelClassId", "com.whatever.something.foo.bar"); + message.setStringProperty("modelInstanceId", "170"); + message.setStringProperty("modelCorrelatedClientId", "shouldNotMatch"); + + assertSelector(message, "modelClassId LIKE 'com.whatever.something.%' AND modelInstanceId = '170' AND (modelRequestError IS NULL OR modelCorrelatedClientId = 'whatever')", true); + } + + /** + * Test cases from Mats Henricson + */ + public void testMatsHenricsonUseCases() throws Exception { + Message message = createMessage(); + assertSelector(message, "SessionserverId=1870414179", false); + + message.setLongProperty("SessionserverId", 1870414179); + assertSelector(message, "SessionserverId=1870414179", true); + + message.setLongProperty("SessionserverId", 1234); + assertSelector(message, "SessionserverId=1870414179", false); + + + assertSelector(message, "Command NOT IN ('MirrorLobbyRequest', 'MirrorLobbyReply')", false); + + message.setStringProperty("Command", "Cheese"); + assertSelector(message, "Command NOT IN ('MirrorLobbyRequest', 'MirrorLobbyReply')", true); + + message.setStringProperty("Command", "MirrorLobbyRequest"); + assertSelector(message, "Command NOT IN ('MirrorLobbyRequest', 'MirrorLobbyReply')", false); + message.setStringProperty("Command", "MirrorLobbyReply"); + assertSelector(message, "Command NOT IN ('MirrorLobbyRequest', 'MirrorLobbyReply')", false); + } + + public void testFloatComparisons() throws Exception { + Message message = createMessage(); + + // JMS 1.1 Section 3.8.1.1 : Approximate literals use the Java floating-point literal syntax. + // We will use the java varible x to demo valid floating point syntaxs. + double x; + + // test decimals like x.x + x = 1.0; + x = -1.1; + x = 1.0E1; + x = 1.1E1; + x = -1.1E1; + assertSelector(message, "1.0 < 1.1", true); + assertSelector(message, "-1.1 < 1.0", true); + assertSelector(message, "1.0E1 < 1.1E1", true); + assertSelector(message, "-1.1E1 < 1.0E1", true); + + // test decimals like x. + x = 1.; + x = 1.E1; + assertSelector(message, "1. < 1.1", true); + assertSelector(message, "-1.1 < 1.", true); + assertSelector(message, "1.E1 < 1.1E1", true); + assertSelector(message, "-1.1E1 < 1.E1", true); + + // test decimals like .x + x = .5; + x = -.5; + x = .5E1; + assertSelector(message, ".1 < .5", true); + assertSelector(message, "-.5 < .1", true); + assertSelector(message, ".1E1 < .5E1", true); + assertSelector(message, "-.5E1 < .1E1", true); + + // test exponents + x = 4E10; + x = -4E10; + x = 5E+10; + x = 5E-10; + assertSelector(message, "4E10 < 5E10", true); + assertSelector(message, "5E8 < 5E10", true); + assertSelector(message, "-4E10 < 2E10", true); + assertSelector(message, "-5E8 < 2E2", true); + assertSelector(message, "4E+10 < 5E+10", true); + assertSelector(message, "4E-10 < 5E-10", true); + } + + public void testStringQuoteParsing() throws Exception { + Message message = createMessage(); + assertSelector(message, "quote = '''In God We Trust'''", true); + } + + public void testLikeComparisons() throws Exception { + Message message = createMessage(); + + assertSelector(message, "quote LIKE '''In G_d We Trust'''", true); + assertSelector(message, "quote LIKE '''In Gd_ We Trust'''", false); + assertSelector(message, "quote NOT LIKE '''In G_d We Trust'''", false); + assertSelector(message, "quote NOT LIKE '''In Gd_ We Trust'''", true); + + assertSelector(message, "foo LIKE '%oo'", true); + assertSelector(message, "foo LIKE '%ar'", false); + assertSelector(message, "foo NOT LIKE '%oo'", false); + assertSelector(message, "foo NOT LIKE '%ar'", true); + + assertSelector(message, "foo LIKE '!_%' ESCAPE '!'", true); + assertSelector(message, "quote LIKE '!_%' ESCAPE '!'", false); + assertSelector(message, "foo NOT LIKE '!_%' ESCAPE '!'", false); + assertSelector(message, "quote NOT LIKE '!_%' ESCAPE '!'", true); + + assertSelector(message, "punctuation LIKE '!#$&()*+,-./:;<=>?@[\\]^`{|}~'", true); + } + + public void testInvalidSelector() throws Exception { + Message message = createMessage(); + assertInvalidSelector(message, "3+5"); + assertInvalidSelector(message, "True AND 3+5"); + assertInvalidSelector(message, "=TEST 'test'"); + } + + protected Message createMessage() throws JMSException { + Message message = createMessage("FOO.BAR"); + message.setJMSType("selector-test"); + message.setJMSMessageID("connection:1:1:1:1"); + message.setObjectProperty("name", "James"); + message.setObjectProperty("location", "London"); + message.setIntProperty("rank", 123); + message.setIntProperty("version", 2); + message.setStringProperty("quote", "'In God We Trust'"); + message.setStringProperty("foo", "_foo"); + message.setStringProperty("punctuation", "!#$&()*+,-./:;<=>?@[\\]^`{|}~"); + message.setBooleanProperty("trueProp", true); + message.setBooleanProperty("falseProp", false); + + return message; + } + + + protected void assertInvalidSelector(Message message, String text) throws JMSException { + try { + new SelectorParser().parse(text); + fail("Created a valid selector"); + } + catch (InvalidSelectorException e) { + } + } + + protected void assertSelector(Message message, String text, boolean expected) throws JMSException { + BooleanExpression selector = new SelectorParser().parse(text); + assertTrue("Created a valid selector", selector != null); + MessageEvaluationContext context = new MessageEvaluationContext(); + context.setMessageReference((org.activemq.command.Message)message); + boolean value = selector.matches(context); + assertEquals("Selector for: " + text, expected, value); + } + + protected Message createMessage(String subject) throws JMSException { + ActiveMQMessage message = new ActiveMQMessage(); + message.setJMSDestination(new ActiveMQTopic(subject)); + return message; + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/simple/Consumer.java b/activemq-core/src/test/java/org/activemq/simple/Consumer.java new file mode 100755 index 0000000000..581c36781c --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/simple/Consumer.java @@ -0,0 +1,73 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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. +* +**/ + +/** + * The SimpleQueueSender class consists only of a main method, + * which sends several messages to a queue. + * + * Run this program in conjunction with SimpleQueueReceiver. + * Specify a queue name on the command line when you run the + * program. By default, the program sends one message. Specify + * a number after the queue name to send that number of messages. + */ +package org.activemq.simple; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Session; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.command.ActiveMQQueue; + +public class Consumer { + + public static void main(String[] args) throws JMSException, InterruptedException { + + String url = "tcp://localhost:61616"; + if( args.length>0 ) { + url = args[0]; + } + + ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url); + Destination destination = new ActiveMQQueue("TEST.QUEUE"); + + Connection connection = connectionFactory.createConnection(); + connection.start(); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageConsumer consumer = session.createConsumer(destination); + + for( ;; ) { + System.out.println("Waiting for message."); + Message message = consumer.receive(); + if( message == null ) { + break; + } + System.out.println("Got message: " + message); + } + + connection.close(); + } +} + +// END SNIPPET: demo diff --git a/activemq-core/src/test/java/org/activemq/simple/Producer.java b/activemq-core/src/test/java/org/activemq/simple/Producer.java new file mode 100755 index 0000000000..6966e28b78 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/simple/Producer.java @@ -0,0 +1,72 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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. +* +**/ + +/** + * The SimpleQueueSender class consists only of a main method, + * which sends several messages to a queue. + * + * Run this program in conjunction with SimpleQueueReceiver. + * Specify a queue name on the command line when you run the + * program. By default, the program sends one message. Specify + * a number after the queue name to send that number of messages. + */ +package org.activemq.simple; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.command.ActiveMQQueue; + +public class Producer { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(Producer.class); + + public static void main(String[] args) throws JMSException, InterruptedException { + + String url = "peer://localhost1/groupA?persistent=false"; + if( args.length>0 ) { + url = args[0]; + } + + ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url); + Destination destination = new ActiveMQQueue("TEST.QUEUE"); + + Connection connection = connectionFactory.createConnection(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = session.createProducer(destination); + TextMessage message = session.createTextMessage(); + for (int i = 0; i < 1000; i++) { + message.setText("This is message " + (i + 1)); + log.info("Sending message: " + message.getText()); + producer.send(message); + Thread.sleep(1000); + } + connection.close(); + + } +} + +// END SNIPPET: demo diff --git a/activemq-core/src/test/java/org/activemq/spring/ConsumerBean.java b/activemq-core/src/test/java/org/activemq/spring/ConsumerBean.java new file mode 100755 index 0000000000..15031af09c --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/spring/ConsumerBean.java @@ -0,0 +1,135 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.spring; + +import javax.jms.Message; +import javax.jms.MessageListener; +import java.util.ArrayList; +import java.util.List; + +public class ConsumerBean implements MessageListener { + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(ConsumerBean.class); + + private List messages = new ArrayList(); + private Object semaphore; + + /** + * Constructor. + */ + public ConsumerBean() { + this(new Object()); + } + + /** + * Constructor, initialized semaphore object. + * @param semaphore + */ + public ConsumerBean(Object semaphore) { + this.semaphore = semaphore; + } + + /** + * @return all the messages on the list so far, clearing the buffer + */ + public synchronized List flushMessages() { + List answer = new ArrayList(messages); + messages.clear(); + return answer; + } + + /** + * Method implemented from MessageListener interface. + * @param message + */ + public synchronized void onMessage(Message message) { + messages.add(message); + synchronized (semaphore) { + semaphore.notifyAll(); + } + } + + /** + * Use to wait for a single message to arrive. + */ + public void waitForMessageToArrive() { + log.info("Waiting for message to arrive"); + + long start = System.currentTimeMillis(); + + try { + if (hasReceivedMessage()) { + synchronized (semaphore) { + semaphore.wait(4000); + } + } + } + catch (InterruptedException e) { + log.info("Caught: " + e); + } + long end = System.currentTimeMillis() - start; + + log.info("End of wait for " + end + " millis"); + } + + /** + * Used to wait for a message to arrive given a particular message count. + * @param messageCount + */ + public void waitForMessagesToArrive(int messageCount) { + log.info("Waiting for message to arrive"); + + long start = System.currentTimeMillis(); + + for (int i = 0; i < 10; i++) { + try { + if (hasReceivedMessages(messageCount)) { + break; + } + synchronized (semaphore) { + semaphore.wait(1000); + } + } + catch (InterruptedException e) { + log.info("Caught: " + e); + } + } + long end = System.currentTimeMillis() - start; + + log.info("End of wait for " + end + " millis"); + } + + /** + * Identifies if the message is empty. + * @return + */ + protected boolean hasReceivedMessage() { + return messages.isEmpty(); + } + + /** + * Identifies if the message count has reached the total size of message. + * @param messageCount + * @return + */ + protected synchronized boolean hasReceivedMessages(int messageCount) { + return messages.size() >= messageCount; + } +} diff --git a/activemq-core/src/test/java/org/activemq/spring/SpringConsumer.java b/activemq-core/src/test/java/org/activemq/spring/SpringConsumer.java new file mode 100755 index 0000000000..6ec8f45ed1 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/spring/SpringConsumer.java @@ -0,0 +1,117 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.spring; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.jms.core.JmsTemplate; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Session; + +public class SpringConsumer extends ConsumerBean implements MessageListener { + private static final Log log = LogFactory.getLog(SpringConsumer.class); + + private JmsTemplate template; + private String myId = "foo"; + private Destination destination; + private Connection connection; + private Session session; + private MessageConsumer consumer; + + public void start() throws JMSException { + String selector = "next = '" + myId + "'"; + + try { + ConnectionFactory factory = template.getConnectionFactory(); + connection = factory.createConnection(); + + // we might be a reusable connection in spring + // so lets only set the client ID once if its not set + synchronized (connection) { + if (connection.getClientID() == null) { + connection.setClientID(myId); + } + } + + connection.start(); + + session = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE); + consumer = session.createConsumer(destination, selector, false); + consumer.setMessageListener(this); + } + catch (JMSException ex) { + log.error("", ex); + throw ex; + } + } + + + public void stop() throws JMSException { + if( consumer!=null ) + consumer.close(); + if( session!=null ) + session.close(); + if( connection!=null ) + connection.close(); + } + + public void onMessage(Message message) { + super.onMessage(message); + try { + message.acknowledge(); + } + catch (JMSException e) { + log.error("Failed to acknowledge: " + e, e); + } + } + + // Properties + //------------------------------------------------------------------------- + public Destination getDestination() { + return destination; + } + + public void setDestination(Destination destination) { + this.destination = destination; + } + + public String getMyId() { + return myId; + } + + public void setMyId(String myId) { + this.myId = myId; + } + + public JmsTemplate getTemplate() { + return template; + } + + public void setTemplate(JmsTemplate template) { + this.template = template; + } +} diff --git a/activemq-core/src/test/java/org/activemq/spring/SpringProducer.java b/activemq-core/src/test/java/org/activemq/spring/SpringProducer.java new file mode 100755 index 0000000000..c7bb98ede1 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/spring/SpringProducer.java @@ -0,0 +1,84 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.spring; + +import org.springframework.jms.core.JmsTemplate; +import org.springframework.jms.core.MessageCreator; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; +import javax.jms.TextMessage; + +public class SpringProducer { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(SpringProducer.class); + + private JmsTemplate template; + private Destination destination; + private int messageCount = 10; + + + public void start() throws JMSException { + for (int i = 0; i < messageCount; i++) { + final String text = "Text for message: " + i; + template.send(destination, new MessageCreator() { + public Message createMessage(Session session) throws JMSException { + log.info("Sending message: " + text); + TextMessage message = session.createTextMessage(text); + message.setStringProperty("next", "foo"); + return message; + } + }); + } + } + + public void stop() throws JMSException { + } + + // Properties + //------------------------------------------------------------------------- + + public JmsTemplate getTemplate() { + return template; + } + + public void setTemplate(JmsTemplate template) { + this.template = template; + } + + public int getMessageCount() { + return messageCount; + } + + public void setMessageCount(int messageCount) { + this.messageCount = messageCount; + } + + public Destination getDestination() { + return destination; + } + + public void setDestination(Destination destination) { + this.destination = destination; + } +} diff --git a/activemq-core/src/test/java/org/activemq/spring/SpringTest.java b/activemq-core/src/test/java/org/activemq/spring/SpringTest.java new file mode 100755 index 0000000000..525b7710f2 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/spring/SpringTest.java @@ -0,0 +1,138 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.spring; + +import java.util.Iterator; +import java.util.List; + +import junit.framework.TestCase; + +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class SpringTest extends TestCase { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(SpringTest.class); + + protected AbstractApplicationContext context; + protected SpringConsumer consumer; + protected SpringProducer producer; + + /** + * Uses ActiveMQConnectionFactory to create the connection context. + * Configuration file is /resources/spring.xml + * + * @throws Exception + */ + public void testSenderWithSpringXml() throws Exception { + String config = "spring.xml"; + assertSenderConfig(config); + } + + /** + * Spring configured test that uses ActiveMQConnectionFactory for + * connection context and ActiveMQQueue for destination. Configuration + * file is /resources/spring-queue.xml. + * + * @throws Exception + */ + public void testSenderWithSpringXmlAndQueue() throws Exception { + String config = "spring-queue.xml"; + assertSenderConfig(config); + } + + /** + * Spring configured test that uses JNDI. Configuration file is + * /resources/spring-jndi.xml. + * + * @throws Exception + */ + public void testSenderWithSpringXmlUsingJNDI() throws Exception { + String config = "spring-jndi.xml"; + assertSenderConfig(config); + } + + /** + * Spring configured test where in the connection context is set to use + * an embedded broker. Configuration file is /resources/spring-embedded.xml + * and /resources/activemq.xml. + * + * @throws Exception + */ + public void testSenderWithSpringXmlEmbeddedBrokerConfiguredViaXml() throws Exception { + String config = "spring-embedded.xml"; + assertSenderConfig(config); + } + + /** + * assert method that is used by all the test method to send and receive messages + * based on each spring configuration. + * + * @param config + * @throws Exception + */ + protected void assertSenderConfig(String config) throws Exception { + context = new ClassPathXmlApplicationContext(config); + + consumer = (SpringConsumer) context.getBean("consumer"); + assertTrue("Found a valid consumer", consumer != null); + + consumer.start(); + + producer = (SpringProducer) context.getBean("producer"); + assertTrue("Found a valid producer", producer != null); + + consumer.flushMessages(); + producer.start(); + + // lets sleep a little to give the JMS time to dispatch stuff + consumer.waitForMessagesToArrive(producer.getMessageCount()); + + // now lets check that the consumer has received some messages + List messages = consumer.flushMessages(); + log.info("Consumer has received messages...."); + for (Iterator iter = messages.iterator(); iter.hasNext();) { + Object message = iter.next(); + log.info("Received: " + message); + } + + assertEquals("Message count", producer.getMessageCount(), messages.size()); + } + + /** + * Clean up method. + * + * @throws Exception + */ + protected void tearDown() throws Exception { + if (consumer != null) { + consumer.stop(); + } + if (producer != null) { + producer.stop(); + } + + if (context != null) { + context.destroy(); + } + } + +} diff --git a/activemq-core/src/test/java/org/activemq/streams/JMSInputStreamTest.java b/activemq-core/src/test/java/org/activemq/streams/JMSInputStreamTest.java new file mode 100755 index 0000000000..435755c638 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/streams/JMSInputStreamTest.java @@ -0,0 +1,127 @@ +/* + * Created on Feb 21, 2005 + * + * To change the template for this generated file go to + * Window - Preferences - Java - Code Generation - Code and Comments + */ + +package org.activemq.streams; +import java.io.DataInputStream; +import java.io.DataOutputStream; + +import javax.jms.Destination; + +import junit.framework.Test; + +import org.activemq.ActiveMQConnection; +import org.activemq.JmsTestSupport; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; + +/** + * JMSInputStreamTest + */ +public class JMSInputStreamTest extends JmsTestSupport { + + protected DataOutputStream out; + protected DataInputStream in; + private ActiveMQConnection connection2; + + public Destination destination; + + public static Test suite() { + return suite(JMSInputStreamTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public void initCombos() { + addCombinationValues("destination", new Object[] { + new ActiveMQQueue("TEST.QUEUE"), + new ActiveMQTopic("TEST.TOPIC") }); + } + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + connection2 = (ActiveMQConnection) factory.createConnection(userName, password); + connections.add(connection2); + out = new DataOutputStream(connection.createOutputStream(destination)); + in = new DataInputStream(connection2.createInputStream(destination)); + } + + /* + * @see TestCase#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testStreams() throws Exception { + out.writeInt(4); + out.flush(); + assertTrue(in.readInt() == 4); + out.writeFloat(2.3f); + out.flush(); + assertTrue(in.readFloat() == 2.3f); + String str = "this is a test string"; + out.writeUTF(str); + out.flush(); + assertTrue(in.readUTF().equals(str)); + for (int i = 0;i < 100;i++) { + out.writeLong(i); + } + out.flush(); + for (int i = 0;i < 100;i++) { + assertTrue(in.readLong() == i); + } + } + + public void testLarge() throws Exception { + final int TEST_DATA = 23; + final int DATA_LENGTH = 4096; + final int COUNT = 1024; + byte[] data = new byte[DATA_LENGTH]; + for (int i = 0;i < data.length;i++) { + data[i] = TEST_DATA; + } + final AtomicBoolean complete = new AtomicBoolean(false); + Thread runner = new Thread(new Runnable() { + public void run() { + try { + for (int x = 0;x < COUNT;x++) { + byte[] b = new byte[2048]; + in.readFully(b); + for (int i = 0;i < b.length;i++) { + assertTrue(b[i] == TEST_DATA); + } + } + complete.set(true); + synchronized(complete){ + complete.notify(); + } + } + catch (Exception ex) { + ex.printStackTrace(); + } + } + }); + runner.start(); + for (int i = 0;i < COUNT;i++) { + out.write(data); + } + out.flush(); + synchronized (complete) { + if (!complete.get()) { + complete.wait(30000); + } + } + assertTrue(complete.get()); + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/test/JmsResourceProvider.java b/activemq-core/src/test/java/org/activemq/test/JmsResourceProvider.java new file mode 100755 index 0000000000..6bb95d1bbe --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/test/JmsResourceProvider.java @@ -0,0 +1,258 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.test; + +import javax.jms.Connection; +import javax.jms.ConnectionConsumer; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.ServerSessionPool; +import javax.jms.Session; +import javax.jms.DeliveryMode; +import javax.jms.Topic; + +import org.activemq.ActiveMQConnectionFactory; + +/** + * @version $Revision: 1.4 $ + */ +public class JmsResourceProvider { + + private String serverUri = "vm://localhost?broker.persistent=false"; + private boolean transacted = false; + private int ackMode = Session.AUTO_ACKNOWLEDGE; + private boolean isTopic; + private int deliveryMode=DeliveryMode.PERSISTENT; + private String durableName = "DummyName"; + private String clientID = getClass().getName(); + + /** + * Creates a connection factory. + * + * @see org.activemq.test.JmsResourceProvider#createConnectionFactory() + */ + public ConnectionFactory createConnectionFactory() throws Exception { + return new ActiveMQConnectionFactory(serverUri); + } + + /** + * Creates a connection. + * + * @see org.activemq.test.JmsResourceProvider#createConnection(javax.jms.ConnectionFactory) + */ + public Connection createConnection(ConnectionFactory cf) throws JMSException { + Connection connection = cf.createConnection(); + if (getClientID()!=null) { + connection.setClientID(getClientID()); + } + return connection; + } + + /** + * @see org.activemq.test.JmsResourceProvider#createSession(javax.jms.Connection) + */ + public Session createSession(Connection conn) throws JMSException { + return conn.createSession(transacted, ackMode); + } + + /** + * @see org.activemq.test.JmsResourceProvider#createConsumer(javax.jms.Session, + * javax.jms.Destination) + */ + public MessageConsumer createConsumer(Session session, + Destination destination) throws JMSException { + if (isDurableSubscriber()) { + return session.createDurableSubscriber((Topic) destination, durableName); + } + return session.createConsumer(destination); + } + + /** + * Creates a connection for a consumer. + * + * @param ssp - ServerSessionPool + * @return ConnectionConsumer + */ + public ConnectionConsumer createConnectionConsumer(Connection connection, Destination destination, ServerSessionPool ssp) throws JMSException { + return connection.createConnectionConsumer(destination,null,ssp,1); + } + + /** + * Creates a producer. + * + * @see org.activemq.test.JmsResourceProvider#createProducer(javax.jms.Session, + * javax.jms.Destination) + */ + public MessageProducer createProducer(Session session, + Destination destination) throws JMSException { + MessageProducer producer = session.createProducer(destination); + producer.setDeliveryMode(deliveryMode); + return producer; + } + + /** + * Creates a destination, which can either a topic or a queue. + * + * @see org.activemq.test.JmsResourceProvider#createDestination(javax.jms.Session, + * java.lang.String) + */ + public Destination createDestination(Session session, String name) + throws JMSException { + if( isTopic ) + return session.createTopic("TOPIC."+name); + else + return session.createQueue("QUEUE."+name); + } + + /** + * Returns true if the subscriber is durable. + * + * @return isDurableSubscriber + */ + public boolean isDurableSubscriber() { + return isTopic && durableName!=null; + } + + /** + * Returns the acknowledgement mode. + * + * @return Returns the ackMode. + */ + public int getAckMode() { + return ackMode; + } + + /** + * Sets the acnknowledgement mode. + * + * @param ackMode The ackMode to set. + */ + public void setAckMode(int ackMode) { + this.ackMode = ackMode; + } + + /** + * Returns true if the destination is a topic, false if the destination is a queue. + * + * @return Returns the isTopic. + */ + public boolean isTopic() { + return isTopic; + } + + /** + * @param isTopic The isTopic to set. + */ + public void setTopic(boolean isTopic) { + this.isTopic = isTopic; + } + + /** + * Returns the server URI. + * + * @return Returns the serverUri. + */ + public String getServerUri() { + return serverUri; + } + + /** + * Sets the server URI. + * + * @param serverUri - the server URI to set. + */ + public void setServerUri(String serverUri) { + this.serverUri = serverUri; + } + + /** + * Return true if the session is transacted. + * + * @return Returns the transacted. + */ + public boolean isTransacted() { + return transacted; + } + + /** + * Sets the session to be transacted. + * + * @param transacted + */ + public void setTransacted(boolean transacted) { + this.transacted = transacted; + } + + /** + * Returns the delivery mode. + * + * @return deliveryMode + */ + public int getDeliveryMode() { + return deliveryMode; + } + + /** + * Sets the delivery mode. + * + * @param deliveryMode + */ + public void setDeliveryMode(int deliveryMode) { + this.deliveryMode = deliveryMode; + } + + /** + * Returns the client id. + * + * @return clientID + */ + public String getClientID() { + return clientID; + } + + /** + * Sets the client id. + * + * @param clientID + */ + public void setClientID(String clientID) { + this.clientID = clientID; + } + + /** + * Returns the durable name of the provider. + * + * @return durableName + */ + public String getDurableName() { + return durableName; + } + + /** + * Sets the durable name of the provider. + * + * @param durableName + */ + public void setDurableName(String durableName) { + this.durableName = durableName; + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/test/JmsSendReceiveTestSupport.java b/activemq-core/src/test/java/org/activemq/test/JmsSendReceiveTestSupport.java new file mode 100755 index 0000000000..d159ecf014 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/test/JmsSendReceiveTestSupport.java @@ -0,0 +1,206 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.test; + +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +/** + * @version $Revision: 1.2 $ + */ +public class JmsSendReceiveTestSupport extends TestSupport implements MessageListener { + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsSendReceiveTestSupport.class); + + protected int messageCount = 100; + protected String[] data; + protected Session session; + protected Session consumeSession; + protected MessageConsumer consumer; + protected MessageProducer producer; + protected Destination consumerDestination; + protected Destination producerDestination; + protected List messages = createConcurrentList(); + protected boolean topic = true; + protected boolean durable = false; + protected int deliveryMode = DeliveryMode.PERSISTENT; + protected final Object lock = new Object(); + protected boolean verbose = false; + protected boolean useSeparateSession = false; + + /* + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + String temp = System.getProperty("messageCount"); + + if (temp != null) { + int i = Integer.parseInt(temp); + if (i > 0) { + messageCount = i; + } + } + + log.info("Message count for test case is: " + messageCount); + data = new String[messageCount]; + + for (int i = 0; i < messageCount; i++) { + data[i] = "Text for message: " + i + " at " + new Date(); + } + } + + + + /** + * Test if all the messages sent are being received. + * + * @throws Exception + */ + public void testSendReceive() throws Exception { + messages.clear(); + + for (int i = 0; i < data.length; i++) { + Message message = session.createTextMessage(data[i]); + + if (verbose) { + log.info("About to send a message: " + message + " with text: " + data[i]); + } + + producer.send(producerDestination, message); + } + + assertMessagesAreReceived(); + log.info("" + data.length + " messages(s) received, closing down connections"); + } + + /** + * Waits to receive the messages and performs the test if all messages have been received and + * are in sequential order. + * + * @throws JMSException + */ + protected void assertMessagesAreReceived() throws JMSException { + waitForMessagesToBeDelivered(); + assertMessagesReceivedAreValid(messages); + } + + /** + * Tests if the messages have all been received and are in sequential order. + * + * @param receivedMessages + * @throws JMSException + */ + protected void assertMessagesReceivedAreValid(List receivedMessages) throws JMSException { + List copyOfMessages = Arrays.asList(receivedMessages.toArray()); + int counter = 0; + + if (data.length != copyOfMessages.size()) { + for (Iterator iter = copyOfMessages.iterator(); iter.hasNext();) { + TextMessage message = (TextMessage) iter.next(); + log.info("<== " + counter++ + " = " + message); + } + } + + assertEquals("Not enough messages received", data.length, receivedMessages.size()); + + for (int i = 0; i < data.length; i++) { + TextMessage received = (TextMessage) receivedMessages.get(i); + String text = received.getText(); + + if (verbose) { + log.info("Received Text: " + text); + } + + assertEquals("Message: " + i, data[i], text); + } + } + + /** + * Waits for the messages to be delivered or when the wait time has been reached. + */ + protected void waitForMessagesToBeDelivered() { + long maxWaitTime = 30000; + long waitTime = maxWaitTime; + long start = (maxWaitTime <= 0) ? 0 : System.currentTimeMillis(); + + synchronized (lock) { + while (messages.size() < data.length && waitTime >= 0) { + try { + lock.wait(200); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + + waitTime = maxWaitTime - (System.currentTimeMillis() - start); + } + } + } + + + /** + * @see javax.jms.MessageListener#onMessage(javax.jms.Message) + */ + public synchronized void onMessage(Message message) { + consumeMessage(message, messages); + } + + /** + * Consumes a received message. + * + * @param message - a newly received message. + * @param messageList - list containing the received messages. + */ + protected void consumeMessage(Message message, List messageList) { + if (verbose) { + log.info("Received message: " + message); + } + + messageList.add(message); + + if (messageList.size() >= data.length) { + synchronized (lock) { + lock.notifyAll(); + } + } + } + + /** + * Creates a synchronized list. + * + * @return a synchronized view of the specified list. + */ + protected List createConcurrentList() { + return Collections.synchronizedList(new ArrayList()); + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/test/JmsTopicSendReceiveTest.java b/activemq-core/src/test/java/org/activemq/test/JmsTopicSendReceiveTest.java new file mode 100755 index 0000000000..3d2330e158 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/test/JmsTopicSendReceiveTest.java @@ -0,0 +1,117 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.test; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.Session; +import javax.jms.Topic; + +/** + * @version $Revision: 1.2 $ + */ +public class JmsTopicSendReceiveTest extends JmsSendReceiveTestSupport { + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsTopicSendReceiveTest.class); + + protected Connection connection; + + protected void setUp() throws Exception { + super.setUp(); + + connectionFactory = createConnectionFactory(); + connection = createConnection(); + if (durable) { + connection.setClientID(getClass().getName()); + } + + log.info("Created connection: " + connection); + + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + consumeSession = createConsumerSession(); + + log.info("Created session: " + session); + log.info("Created consumeSession: " + consumeSession); + producer = session.createProducer(null); + producer.setDeliveryMode(deliveryMode); + + log.info("Created producer: " + producer + " delivery mode = " + + (deliveryMode == DeliveryMode.PERSISTENT ? "PERSISTENT" : "NON_PERSISTENT")); + + if (topic) { + consumerDestination = session.createTopic(getConsumerSubject()); + producerDestination = session.createTopic(getProducerSubject()); + } + else { + consumerDestination = session.createQueue(getConsumerSubject()); + producerDestination = session.createQueue(getProducerSubject()); + } + + log.info("Created consumer destination: " + consumerDestination + " of type: " + consumerDestination.getClass()); + log.info("Created producer destination: " + producerDestination + " of type: " + producerDestination.getClass()); + consumer = createConsumer(); + consumer.setMessageListener(this); + connection.start(); + + log.info("Created connection: " + connection); + } + + protected void tearDown() throws Exception { + log.info("Dumping stats..."); + //TODO + //connectionFactory.getFactoryStats().dump(new IndentPrinter()); + + log.info("Closing down connection"); + + /** TODO we should be able to shut down properly */ + session.close(); + connection.close(); + } + + /** + * Creates a session. + * + * @return session + * @throws JMSException + */ + protected Session createConsumerSession() throws JMSException { + if (useSeparateSession) { + return connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + } + else { + return session; + } + } + + /** + * Creates a durable suscriber or a consumer. + * + * @return MessageConsumer - durable suscriber or consumer. + * @throws JMSException + */ + protected MessageConsumer createConsumer() throws JMSException { + if (durable) { + log.info("Creating durable consumer"); + return consumeSession.createDurableSubscriber((Topic) consumerDestination, getName()); + } + return consumeSession.createConsumer(consumerDestination); + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/test/JmsTopicSendReceiveWithEmbeddedBrokerAndUserIDTest.java b/activemq-core/src/test/java/org/activemq/test/JmsTopicSendReceiveWithEmbeddedBrokerAndUserIDTest.java new file mode 100644 index 0000000000..b058d7b750 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/test/JmsTopicSendReceiveWithEmbeddedBrokerAndUserIDTest.java @@ -0,0 +1,62 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.test; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.broker.BrokerService; + +import javax.jms.JMSException; +import javax.jms.Message; + +import java.util.Iterator; +import java.util.List; + +/** + * + * @version $Revision: 1.1 $ + */ +public class JmsTopicSendReceiveWithEmbeddedBrokerAndUserIDTest extends + JmsTopicSendReceiveWithTwoConnectionsAndEmbeddedBrokerTest { + + protected String userName = "James"; + + protected ActiveMQConnectionFactory createConnectionFactory() throws Exception { + ActiveMQConnectionFactory answer = super.createConnectionFactory(); + answer.setUserName(userName); + return answer; + } + + protected void configureBroker(BrokerService answer) throws Exception { + answer.setPopulateJMSXUserID(true); + super.configureBroker(answer); + } + + protected void assertMessagesReceivedAreValid(List receivedMessages) throws JMSException { + super.assertMessagesReceivedAreValid(receivedMessages); + + // lets assert that the user ID is set + for (Iterator iter = receivedMessages.iterator(); iter.hasNext();) { + Message message = (Message) iter.next(); + String userID = message.getStringProperty("JMSXUserID"); + + System.out.println("Received message with userID: " + userID); + + assertEquals("JMSXUserID header", userName, userID); + } + } +} diff --git a/activemq-core/src/test/java/org/activemq/test/JmsTopicSendReceiveWithTwoConnectionsAndEmbeddedBrokerTest.java b/activemq-core/src/test/java/org/activemq/test/JmsTopicSendReceiveWithTwoConnectionsAndEmbeddedBrokerTest.java new file mode 100644 index 0000000000..33e21c3032 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/test/JmsTopicSendReceiveWithTwoConnectionsAndEmbeddedBrokerTest.java @@ -0,0 +1,71 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.test; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.broker.BrokerService; + +/** + * @version $Revision: 1.3 $ + */ +public class JmsTopicSendReceiveWithTwoConnectionsAndEmbeddedBrokerTest extends JmsTopicSendReceiveWithTwoConnectionsTest { + + protected BrokerService broker; + protected String bindAddress = "tcp://localhost:61616"; + + /** + * Sets up a test where the producer and consumer have their own connection. + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + if (broker == null) { + broker = createBroker(); + } + super.setUp(); + } + + protected void tearDown() throws Exception { + super.tearDown(); + + if (broker != null) { + broker.stop(); + } + } + + /** + * Factory method to create a new broker + * + * @throws Exception + */ + protected BrokerService createBroker() throws Exception { + BrokerService answer = new BrokerService(); + configureBroker(answer); + answer.start(); + return answer; + } + + protected void configureBroker(BrokerService answer) throws Exception { + answer.addConnector(bindAddress); + } + + protected ActiveMQConnectionFactory createConnectionFactory() throws Exception { + return new ActiveMQConnectionFactory(bindAddress); + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/test/JmsTopicSendReceiveWithTwoConnectionsTest.java b/activemq-core/src/test/java/org/activemq/test/JmsTopicSendReceiveWithTwoConnectionsTest.java new file mode 100755 index 0000000000..059f6cc9f0 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/test/JmsTopicSendReceiveWithTwoConnectionsTest.java @@ -0,0 +1,126 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.test; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Session; + +import org.activemq.ActiveMQConnectionFactory; + +/** + * @version $Revision: 1.3 $ + */ +public class JmsTopicSendReceiveWithTwoConnectionsTest extends JmsSendReceiveTestSupport { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(JmsTopicSendReceiveWithTwoConnectionsTest.class); + + protected Connection sendConnection; + protected Connection receiveConnection; + protected Session receiveSession; + + /** + * Sets up a test where the producer and consumer have their own connection. + * + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + + connectionFactory = createConnectionFactory(); + + sendConnection = createSendConnection(); + sendConnection.start(); + + receiveConnection = createReceiveConnection(); + receiveConnection.start(); + + log.info("Created sendConnection: " + sendConnection); + log.info("Created receiveConnection: " + receiveConnection); + + session = sendConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + receiveSession = receiveConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + log.info("Created sendSession: " + session); + log.info("Created receiveSession: " + receiveSession); + + producer = session.createProducer(null); + producer.setDeliveryMode(deliveryMode); + + log.info("Created producer: " + producer + " delivery mode = " + + (deliveryMode == DeliveryMode.PERSISTENT ? "PERSISTENT" : "NON_PERSISTENT")); + + if (topic) { + consumerDestination = session.createTopic(getConsumerSubject()); + producerDestination = session.createTopic(getProducerSubject()); + } + else { + consumerDestination = session.createQueue(getConsumerSubject()); + producerDestination = session.createQueue(getProducerSubject()); + } + + log.info("Created consumer destination: " + consumerDestination + " of type: " + consumerDestination.getClass()); + log.info("Created producer destination: " + producerDestination + " of type: " + producerDestination.getClass()); + + consumer = receiveSession.createConsumer(consumerDestination); + consumer.setMessageListener(this); + + log.info("Started connections"); + } + + /* + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception { + session.close(); + receiveSession.close(); + sendConnection.close(); + receiveConnection.close(); + } + + /** + * Creates a connection. + * + * @return Connection + * @throws Exception + */ + protected Connection createReceiveConnection() throws Exception { + return createConnection(); + } + + /** + * Creates a connection. + * + * @return Connection + * @throws Exception + */ + protected Connection createSendConnection() throws Exception { + return createConnection(); + } + + /** + * Creates an ActiveMQConnectionFactory. + * + * @see org.activemq.test.TestSupport#createConnectionFactory() + */ + protected ActiveMQConnectionFactory createConnectionFactory() throws Exception { + return new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false"); + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/test/TestSupport.java b/activemq-core/src/test/java/org/activemq/test/TestSupport.java new file mode 100755 index 0000000000..f0f133e5bc --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/test/TestSupport.java @@ -0,0 +1,230 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.test; + +import junit.framework.TestCase; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.TextMessage; + + +/** + * Useful base class for unit test cases + * + * @version $Revision: 1.4 $ + */ +public class TestSupport extends TestCase { + protected Log log = LogFactory.getLog(getClass()); + protected ActiveMQConnectionFactory connectionFactory; + protected boolean topic = true; + + public TestSupport() { + super(); + } + + public TestSupport(String name) { + super(name); + } + + /** + * Creates an ActiveMQMessage. + * + * @return ActiveMQMessage + */ + protected ActiveMQMessage createMessage() { + return new ActiveMQMessage(); + } + + /** + * Creates a destination. + * + * @param subject - topic or queue name. + * @return Destination - either an ActiveMQTopic or ActiveMQQUeue. + */ + protected Destination createDestination(String subject) { + if (topic) { + return new ActiveMQTopic(subject); + } + else { + return new ActiveMQQueue(subject); + } + } + + /** + * Tests if firstSet and secondSet are equal. + * + * @param messsage - string to be displayed when the assertion fails. + * @param firstSet[] - set of messages to be compared with its counterpart in the secondset. + * @param secondSet[] - set of messages to be compared with its counterpart in the firstset. + * @throws JMSException + */ + protected void assertTextMessagesEqual(Message[] firstSet, Message[] secondSet) throws JMSException { + assertTextMessagesEqual("", firstSet, secondSet); + } + + /** + * Tests if firstSet and secondSet are equal. + * + * @param messsage - string to be displayed when the assertion fails. + * @param firstSet[] - set of messages to be compared with its counterpart in the secondset. + * @param secondSet[] - set of messages to be compared with its counterpart in the firstset. + */ + protected void assertTextMessagesEqual(String messsage, Message[] firstSet, Message[] secondSet) throws JMSException { + assertEquals("Message count does not match: " + messsage, firstSet.length, secondSet.length); + + for (int i = 0; i < secondSet.length; i++) { + TextMessage m1 = (TextMessage) firstSet[i]; + TextMessage m2 = (TextMessage) secondSet[i]; + assertTextMessageEqual("Message " + (i + 1) + " did not match : ", m1,m2); + } + } + + /** + * Tests if m1 and m2 are equal. + * + * @param m1 - message to be compared with m2. + * @param m2 - message to be compared with m1. + * @throws JMSException + */ + protected void assertEquals(TextMessage m1, TextMessage m2) throws JMSException { + assertEquals("", m1, m2); + } + + /** + * Tests if m1 and m2 are equal. + * + * @param message - string to be displayed when the assertion fails. + * @param m1 - message to be compared with m2. + * @param m2 - message to be compared with m1. + */ + protected void assertTextMessageEqual(String message, TextMessage m1, TextMessage m2) throws JMSException { + assertFalse(message + ": expected {" + m1 + "}, but was {" + m2 + "}", m1 == null ^ m2 == null); + + if( m1 == null ) { + return; + } + + assertEquals(message, m1.getText(), m2.getText()); + } + + /** + * Tests if m1 and m2 are equal. + * + * @param m1 - message to be compared with m2. + * @param m2 - message to be compared with m1. + * @throws JMSException + */ + protected void assertEquals(Message m1, Message m2) throws JMSException { + assertEquals("", m1, m2); + } + + /** + * Tests if m1 and m2 are equal. + * + * @param message - error message. + * @param m1 - message to be compared with m2. + * @param m2 -- message to be compared with m1. + */ + protected void assertEquals(String message, Message m1, Message m2) throws JMSException { + assertFalse(message + ": expected {" + m1 + "}, but was {" + m2 + "}", m1 == null ^ m2 == null); + + if( m1 == null ){ + return; + } + + assertTrue(message + ": expected {" + m1 + "}, but was {" + m2 + "}", m1.getClass()==m2.getClass()); + + if( m1 instanceof TextMessage ) { + assertTextMessageEqual(message, (TextMessage)m1, (TextMessage)m2); + } else { + assertEquals(message, m1, m2); + } + } + + /** + * Creates an ActiveMQConnectionFactory. + * + * @return ActiveMQConnectionFactory + * @throws Exception + */ + protected ActiveMQConnectionFactory createConnectionFactory() throws Exception { + return new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false"); + } + + /** + * Factory method to create a new connection. + * + * @return connection + * @throws Exception + */ + protected Connection createConnection() throws Exception { + return getConnectionFactory().createConnection(); + } + + /** + * Creates an ActiveMQ connection factory. + * + * @return connectionFactory + * @throws Exception + */ + public ActiveMQConnectionFactory getConnectionFactory() throws Exception { + if (connectionFactory == null) { + connectionFactory = createConnectionFactory(); + assertTrue("Should have created a connection factory!", connectionFactory != null); + } + + return connectionFactory; + } + + /** + * Returns the consumer subject. + * + * @return String + */ + protected String getConsumerSubject() { + return getSubject(); + } + + /** + * Returns the producer subject. + * + * @return String + */ + protected String getProducerSubject() { + return getSubject(); + } + + /** + * Returns the subject. + * + * @return String + */ + protected String getSubject() { + return getClass().getName() + "." + getName(); + } +} diff --git a/activemq-core/src/test/java/org/activemq/test/retroactive/RetroactiveConsumerTestWithDestinationBasedBufferTest.java b/activemq-core/src/test/java/org/activemq/test/retroactive/RetroactiveConsumerTestWithDestinationBasedBufferTest.java new file mode 100644 index 0000000000..528a6387ff --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/test/retroactive/RetroactiveConsumerTestWithDestinationBasedBufferTest.java @@ -0,0 +1,28 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.test.retroactive; + +/** + * + * @version $Revision: 1.1 $ + */ +public class RetroactiveConsumerTestWithDestinationBasedBufferTest extends RetroactiveConsumerTestWithSimpleMessageListTest { + protected String getBrokerXml() { + return "org/activemq/test/retroactive/activemq-fixed-destination-buffer.xml"; + } +} diff --git a/activemq-core/src/test/java/org/activemq/test/retroactive/RetroactiveConsumerTestWithSimpleMessageListTest.java b/activemq-core/src/test/java/org/activemq/test/retroactive/RetroactiveConsumerTestWithSimpleMessageListTest.java new file mode 100644 index 0000000000..7daca60575 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/test/retroactive/RetroactiveConsumerTestWithSimpleMessageListTest.java @@ -0,0 +1,107 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.test.retroactive; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.EmbeddedBrokerTestSupport; +import org.activemq.broker.BrokerService; +import org.activemq.util.MessageList; +import org.activemq.xbean.BrokerFactoryBean; +import org.springframework.core.io.ClassPathResource; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import java.util.Date; + +/** + * + * @version $Revision: 1.1 $ + */ +public class RetroactiveConsumerTestWithSimpleMessageListTest extends EmbeddedBrokerTestSupport { + protected int messageCount = 20; + protected Connection connection; + protected Session session; + + public void testSendThenConsume() throws Exception { + + // lets some messages + connection = createConnection(); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = session.createProducer(destination); + for (int i = 0; i < messageCount; i++) { + TextMessage message = session.createTextMessage("Message: " + i + " sent at: " + new Date()); + producer.send(message); + } + producer.close(); + session.close(); + connection.close(); + + connection = createConnection(); + connection.start(); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + MessageConsumer consumer = session.createConsumer(destination); + MessageList listener = new MessageList(); + consumer.setMessageListener(listener); + listener.waitForMessagesToArrive(messageCount); + listener.assertMessagesReceived(messageCount); + + } + + protected void setUp() throws Exception { + useTopic = true; + bindAddress = "vm://localhost"; + super.setUp(); + } + + + protected void tearDown() throws Exception { + if (session != null) { + session.close(); + session = null; + } + if (connection != null) { + connection.close(); + } + super.tearDown(); + } + + protected ConnectionFactory createConnectionFactory() throws Exception { + ActiveMQConnectionFactory answer = new ActiveMQConnectionFactory(bindAddress); + answer.setUseRetroactiveConsumer(true); + return answer; + } + + protected BrokerService createBroker() throws Exception { + String uri = getBrokerXml(); + System.out.println("Loading broker configuration from the classpath with URI: " + uri); + BrokerFactoryBean factory = new BrokerFactoryBean(new ClassPathResource(uri)); + factory.afterPropertiesSet(); + return factory.getBroker(); + } + + protected String getBrokerXml() { + return "org/activemq/test/retroactive/activemq-fixed-buffer.xml"; + } + +} diff --git a/activemq-core/src/test/java/org/activemq/test/retroactive/RetroactiveConsumerTestWithTimePolicyTest.java b/activemq-core/src/test/java/org/activemq/test/retroactive/RetroactiveConsumerTestWithTimePolicyTest.java new file mode 100644 index 0000000000..865a9fbc1a --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/test/retroactive/RetroactiveConsumerTestWithTimePolicyTest.java @@ -0,0 +1,28 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.test.retroactive; + +/** + * + * @version $Revision: 1.1 $ + */ +public class RetroactiveConsumerTestWithTimePolicyTest extends RetroactiveConsumerTestWithSimpleMessageListTest { + protected String getBrokerXml() { + return "org/activemq/test/retroactive/activemq-timed-policy.xml"; + } +} diff --git a/activemq-core/src/test/java/org/activemq/thread/TaskRunnerTest.java b/activemq-core/src/test/java/org/activemq/thread/TaskRunnerTest.java new file mode 100755 index 0000000000..4db1050ddb --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/thread/TaskRunnerTest.java @@ -0,0 +1,105 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.thread; + +import junit.framework.TestCase; +import edu.emory.mathcs.backport.java.util.concurrent.BrokenBarrierException; +import edu.emory.mathcs.backport.java.util.concurrent.CyclicBarrier; +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +public class TaskRunnerTest extends TestCase { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(TaskRunnerTest.class); + + /** + * Simulate multiple threads queuing work for the + * TaskRunner. The Task Runner dequeues the + * work. + * + * @throws InterruptedException + * @throws BrokenBarrierException + */ + public void testWakeup() throws InterruptedException, BrokenBarrierException { + + final AtomicInteger iterations = new AtomicInteger(0); + final AtomicInteger counter = new AtomicInteger(0); + final AtomicInteger queue = new AtomicInteger(0); + final CountDownLatch doneCountDownLatch = new CountDownLatch(1); + final int ENQUEUE_COUNT = 100000; + + TaskRunnerFactory factory = new TaskRunnerFactory(); + final TaskRunner runner = factory.createTaskRunner(new Task() { + public boolean iterate() { + if( queue.get()==0 ) { + return false; + } else { + while(queue.get()>0) { + queue.decrementAndGet(); + counter.incrementAndGet(); + } + iterations.incrementAndGet(); + if (counter.get()==ENQUEUE_COUNT) + doneCountDownLatch.countDown(); + return true; + } + } + }); + + long start = System.currentTimeMillis(); + final int WORKER_COUNT=5; + final CyclicBarrier barrier = new CyclicBarrier(WORKER_COUNT+1); + for( int i=0; i< WORKER_COUNT; i++ ) { + new Thread() { + public void run() { + try { + barrier.await(); + for( int i=0; i < ENQUEUE_COUNT/WORKER_COUNT; i++ ) { + queue.incrementAndGet(); + runner.wakeup(); + yield(); + } + } + catch (BrokenBarrierException e) { + } + catch (InterruptedException e) { + } + } + }.start(); + } + barrier.await(); + + boolean b = doneCountDownLatch.await(30, TimeUnit.SECONDS); + long end = System.currentTimeMillis(); + log.info("Iterations: "+iterations.get()); + log.info("counter: "+counter.get()); + log.info("Dequeues/s: "+(1000.0*ENQUEUE_COUNT/(end-start))); + log.info("duration: "+((end-start)/1000.0)); + assertTrue(b); + } + + + + public static void main(String[] args) { + junit.textui.TestRunner.run(TaskRunnerTest.class); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/transport/TransportBrokerTestSupport.java b/activemq-core/src/test/java/org/activemq/transport/TransportBrokerTestSupport.java new file mode 100755 index 0000000000..3f9257d43f --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/transport/TransportBrokerTestSupport.java @@ -0,0 +1,78 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Iterator; + +import org.activemq.broker.BrokerService; +import org.activemq.broker.BrokerTest; +import org.activemq.broker.StubConnection; +import org.activemq.broker.TransportConnector; + +abstract public class TransportBrokerTestSupport extends BrokerTest { + + private TransportConnector connector; + ArrayList connections = new ArrayList(); + + protected void setUp() throws Exception { + super.setUp(); + } + + protected BrokerService createBroker() throws Exception { + BrokerService service = super.createBroker(); + connector = service.addConnector(getBindLocation()); + return service; + } + + protected abstract String getBindLocation(); + + protected void tearDown() throws Exception { + for (Iterator iter = connections.iterator(); iter.hasNext();) { + StubConnection connection = (StubConnection) iter.next(); + connection.stop(); + iter.remove(); + } + connector.stop(); + super.tearDown(); + } + + protected URI getBindURI() throws URISyntaxException { + return new URI(getBindLocation()); + } + + protected StubConnection createConnection() throws Exception { + URI bindURI = getBindURI(); + + // Note: on platforms like OS X we cannot bind to the actual hostname, so we + // instead use the orignal host name (typically localhost) to bind to + + URI actualURI = connector.getServer().getConnectURI(); + URI connectURI = new URI(actualURI.getScheme(), actualURI.getUserInfo(), bindURI.getHost(), actualURI.getPort(), actualURI.getPath(), actualURI + .getQuery(), actualURI.getFragment()); + + Transport transport = TransportFactory.connect(connectURI); + StubConnection connection = new StubConnection(transport); + connections.add(connection); + return connection; + } + +} diff --git a/activemq-core/src/test/java/org/activemq/transport/activeio/NIOActiveIOTransportBrokerTest.java b/activemq-core/src/test/java/org/activemq/transport/activeio/NIOActiveIOTransportBrokerTest.java new file mode 100644 index 0000000000..b548208c04 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/transport/activeio/NIOActiveIOTransportBrokerTest.java @@ -0,0 +1,45 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.activeio; + +import org.activemq.transport.TransportBrokerTestSupport; + +import junit.framework.Test; + +public class NIOActiveIOTransportBrokerTest extends TransportBrokerTestSupport { + + protected String getBindLocation() { + return "nio://localhost:0?wireFormat.tcpNoDelayEnabled=true&wireFormat.cacheEnabled=true"; + } + + protected void setUp() throws Exception { + MAX_WAIT=2000; + super.setUp(); + } + + public static Test suite() { + return suite(NIOActiveIOTransportBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + +} diff --git a/activemq-core/src/test/java/org/activemq/transport/activeio/TCPActiveIOTransportBrokerTest.java b/activemq-core/src/test/java/org/activemq/transport/activeio/TCPActiveIOTransportBrokerTest.java new file mode 100755 index 0000000000..27bc731217 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/transport/activeio/TCPActiveIOTransportBrokerTest.java @@ -0,0 +1,45 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.activeio; + +import org.activemq.transport.TransportBrokerTestSupport; + +import junit.framework.Test; + +public class TCPActiveIOTransportBrokerTest extends TransportBrokerTestSupport { + + protected String getBindLocation() { + return "tcp://localhost:0?wireFormat.tcpNoDelayEnabled=true&wireFormat.cacheEnabled=true"; + } + + protected void setUp() throws Exception { + MAX_WAIT=2000; + super.setUp(); + } + + public static Test suite() { + return suite(TCPActiveIOTransportBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + +} diff --git a/activemq-core/src/test/java/org/activemq/transport/discovery/DiscoveryTransportBrokerTest.java b/activemq-core/src/test/java/org/activemq/transport/discovery/DiscoveryTransportBrokerTest.java new file mode 100755 index 0000000000..c4f958a95e --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/transport/discovery/DiscoveryTransportBrokerTest.java @@ -0,0 +1,150 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.discovery; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import javax.jms.DeliveryMode; + +import junit.framework.Test; + +import org.activemq.broker.StubConnection; +import org.activemq.broker.TransportConnector; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.ProducerInfo; +import org.activemq.command.SessionInfo; +import org.activemq.network.NetworkTestSupport; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.failover.FailoverTransport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class DiscoveryTransportBrokerTest extends NetworkTestSupport { + + static final private Log log = LogFactory.getLog(DiscoveryTransportBrokerTest.class); + + public void testPublisherFailsOver() throws Throwable { + ActiveMQDestination destination = new ActiveMQQueue("TEST"); + int deliveryMode = DeliveryMode.NON_PERSISTENT; + + // Start a normal consumer on the local broker + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.request(consumerInfo1); + + // Start a normal consumer on a remote broker + StubConnection connection2 = createRemoteConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.request(consumerInfo2); + + // Start a failover publisher. + StubConnection connection3 = createFailoverConnection(); + ConnectionInfo connectionInfo3 = createConnectionInfo(); + SessionInfo sessionInfo3 = createSessionInfo(connectionInfo3); + ProducerInfo producerInfo3 = createProducerInfo(sessionInfo3); + connection3.send(connectionInfo3); + connection3.send(sessionInfo3); + connection3.send(producerInfo3); + + // Send the message using the fail over publisher. + connection3.request(createMessage(producerInfo3, destination, deliveryMode)); + + // The message will be sent to one of the brokers. + FailoverTransport ft = (FailoverTransport) connection3.getTransport().narrow(FailoverTransport.class); + + // See which broker we were connected to. + StubConnection connectionA; + StubConnection connectionB; + TransportConnector serverA; + if( connector.getServer().getConnectURI().equals(ft.getConnectedTransportURI() ) ) { + connectionA=connection1; + connectionB=connection2; + serverA = connector; + } else { + connectionA=connection2; + connectionB=connection1; + serverA = remoteConnector; + } + + assertNotNull(receiveMessage(connectionA)); + assertNoMessagesLeft(connectionB); + + // Dispose the server so that it fails over to the other server. + log.info("Disconnecting active server"); + serverA.stop(); + + log.info("Sending request that should failover"); + connection3.request(createMessage(producerInfo3, destination, deliveryMode)); + + assertNotNull(receiveMessage(connectionB)); + assertNoMessagesLeft(connectionA); + + } + + protected String getLocalURI() { + return "tcp://localhost:0?wireFormat.tcpNoDelayEnabled=true"; + } + + protected String getRemoteURI() { + return "tcp://localhost:0?wireFormat.tcpNoDelayEnabled=true"; + } + + protected TransportConnector createConnector() throws Exception, IOException, URISyntaxException { + TransportConnector x = super.createConnector(); + x.setDiscoveryUri(new URI("multicast://default")); + return x; + } + + protected TransportConnector createRemoteConnector() throws Exception, IOException, URISyntaxException { + TransportConnector x = super.createRemoteConnector(); + x.setDiscoveryUri(new URI("multicast://default")); + return x; + } + + protected StubConnection createFailoverConnection() throws Exception { + URI failoverURI = new URI("discovery:multicast://default"); + Transport transport = TransportFactory.connect(failoverURI); + StubConnection connection = new StubConnection(transport); + connections.add(connection); + return connection; + } + + public static Test suite() { + return suite(DiscoveryTransportBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/transport/failover/BadConnectionTest.java b/activemq-core/src/test/java/org/activemq/transport/failover/BadConnectionTest.java new file mode 100644 index 0000000000..a48c75332c --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/transport/failover/BadConnectionTest.java @@ -0,0 +1,63 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.failover; + +import org.activemq.command.ActiveMQMessage; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; + +import java.io.IOException; +import java.net.URI; + +import junit.framework.TestCase; + +/** + * + * @version $Revision: 1.1 $ + */ +public class BadConnectionTest extends TestCase { + + protected Transport transport; + + public void testConnectingToUnavailableServer() throws Exception { + try { + transport.asyncRequest(new ActiveMQMessage()); + fail("This should never succeed"); + } + catch (IOException e) { + System.out.println("Caught expected exception: " + e); + e.printStackTrace(); + } + } + protected Transport createTransport() throws Exception { + return TransportFactory.connect(new URI("failover://(tcp://doesNotExist:1234)?useExponentialBackOff=false&maxReconnectAttempts=3&initialReconnectDelay=100")); + } + + protected void setUp() throws Exception { + transport = createTransport(); + } + + protected void tearDown() throws Exception { + if (transport != null) { + transport.stop(); + } + } + + + +} diff --git a/activemq-core/src/test/java/org/activemq/transport/failover/FailoverTransportBrokerTest.java b/activemq-core/src/test/java/org/activemq/transport/failover/FailoverTransportBrokerTest.java new file mode 100755 index 0000000000..82d200e543 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/transport/failover/FailoverTransportBrokerTest.java @@ -0,0 +1,147 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.failover; + +import java.net.URI; + +import javax.jms.DeliveryMode; + +import junit.framework.Test; + +import org.activemq.broker.StubConnection; +import org.activemq.broker.TransportConnector; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.ProducerInfo; +import org.activemq.command.SessionInfo; +import org.activemq.network.NetworkTestSupport; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; + +public class FailoverTransportBrokerTest extends NetworkTestSupport { + + private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory + .getLog(FailoverTransportBrokerTest.class); + + public ActiveMQDestination destination; + public int deliveryMode; + + public void initCombosForTestPublisherFailsOver() { + addCombinationValues( "deliveryMode", new Object[]{ + new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) + } ); + addCombinationValues( "destination", new Object[]{ + new ActiveMQQueue("TEST"), + new ActiveMQTopic("TEST"), + } ); + } + public void testPublisherFailsOver() throws Throwable { + + // Start a normal consumer on the local broker + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.request(consumerInfo1); + + // Start a normal consumer on a remote broker + StubConnection connection2 = createRemoteConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.request(consumerInfo2); + + // Start a failover publisher. + log.info("Starting the failover connection."); + StubConnection connection3 = createFailoverConnection(); + ConnectionInfo connectionInfo3 = createConnectionInfo(); + SessionInfo sessionInfo3 = createSessionInfo(connectionInfo3); + ProducerInfo producerInfo3 = createProducerInfo(sessionInfo3); + connection3.send(connectionInfo3); + connection3.send(sessionInfo3); + connection3.send(producerInfo3); + + // Send the message using the fail over publisher. + connection3.request(createMessage(producerInfo3, destination, deliveryMode)); + + // The message will be sent to one of the brokers. + FailoverTransport ft = (FailoverTransport) connection3.getTransport().narrow(FailoverTransport.class); + + // See which broker we were connected to. + StubConnection connectionA; + StubConnection connectionB; + TransportConnector serverA; + if( connector.getServer().getConnectURI().equals(ft.getConnectedTransportURI() ) ) { + connectionA=connection1; + connectionB=connection2; + serverA = connector; + } else { + connectionA=connection2; + connectionB=connection1; + serverA = remoteConnector; + } + + assertNotNull(receiveMessage(connectionA)); + assertNoMessagesLeft(connectionB); + + // Dispose the server so that it fails over to the other server. + log.info("Disconnecting the active connection"); + serverA.stop(); + + connection3.request(createMessage(producerInfo3, destination, deliveryMode)); + + assertNotNull(receiveMessage(connectionB)); + assertNoMessagesLeft(connectionA); + + } + + protected String getLocalURI() { + return "tcp://localhost:0?wireFormat.tcpNoDelayEnabled=true"; + } + + protected String getRemoteURI() { + return "tcp://localhost:0?wireFormat.tcpNoDelayEnabled=true"; + } + + protected StubConnection createFailoverConnection() throws Exception { + URI failoverURI = new URI("failover://"+connector.getServer().getConnectURI()+","+remoteConnector.getServer().getConnectURI()+""); + Transport transport = TransportFactory.connect(failoverURI); + StubConnection connection = new StubConnection(transport); + connections.add(connection); + return connection; + } + + + public static Test suite() { + return suite(FailoverTransportBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/transport/fanout/FanoutTransportBrokerTest.java b/activemq-core/src/test/java/org/activemq/transport/fanout/FanoutTransportBrokerTest.java new file mode 100755 index 0000000000..212d85c873 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/transport/fanout/FanoutTransportBrokerTest.java @@ -0,0 +1,211 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.fanout; + +import java.io.IOException; +import java.net.URI; + +import javax.jms.DeliveryMode; + +import junit.framework.Test; + +import org.activemq.broker.StubConnection; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.Command; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.ProducerInfo; +import org.activemq.command.SessionInfo; +import org.activemq.network.NetworkTestSupport; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportFilter; +import org.activemq.transport.mock.MockTransport; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +public class FanoutTransportBrokerTest extends NetworkTestSupport { + + private static final Log log = LogFactory.getLog(FanoutTransportBrokerTest.class); + + public ActiveMQDestination destination; + public int deliveryMode; + + private String remoteURI = "tcp://localhost:0?wireFormat.tcpNoDelayEnabled=true"; + + public static Test suite() { + return suite(FanoutTransportBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public void initCombosForTestPublisherFansout() { + addCombinationValues("deliveryMode", new Object[] { new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) }); + addCombinationValues("destination", new Object[] { new ActiveMQQueue("TEST"), new ActiveMQTopic("TEST"), }); + } + + public void xtestPublisherFansout() throws Throwable { + + // Start a normal consumer on the local broker + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.request(consumerInfo1); + + // Start a normal consumer on a remote broker + StubConnection connection2 = createRemoteConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.request(consumerInfo2); + + // Start a fanout publisher. + log.info("Starting the fanout connection."); + StubConnection connection3 = createFanoutConnection(); + ConnectionInfo connectionInfo3 = createConnectionInfo(); + SessionInfo sessionInfo3 = createSessionInfo(connectionInfo3); + ProducerInfo producerInfo3 = createProducerInfo(sessionInfo3); + connection3.send(connectionInfo3); + connection3.send(sessionInfo3); + connection3.send(producerInfo3); + + // Send the message using the fail over publisher. + connection3.request(createMessage(producerInfo3, destination, deliveryMode)); + + assertNotNull(receiveMessage(connection1)); + assertNoMessagesLeft(connection1); + + assertNotNull(receiveMessage(connection2)); + assertNoMessagesLeft(connection2); + + } + + + public void initCombosForTestPublisherWaitsForServerToBeUp() { + addCombinationValues("deliveryMode", new Object[] { new Integer(DeliveryMode.NON_PERSISTENT), + new Integer(DeliveryMode.PERSISTENT) }); + addCombinationValues("destination", new Object[] { new ActiveMQQueue("TEST"), new ActiveMQTopic("TEST"), }); + } + public void testPublisherWaitsForServerToBeUp() throws Throwable { + + // Start a normal consumer on the local broker + StubConnection connection1 = createConnection(); + ConnectionInfo connectionInfo1 = createConnectionInfo(); + SessionInfo sessionInfo1 = createSessionInfo(connectionInfo1); + ConsumerInfo consumerInfo1 = createConsumerInfo(sessionInfo1, destination); + connection1.send(connectionInfo1); + connection1.send(sessionInfo1); + connection1.request(consumerInfo1); + + // Start a normal consumer on a remote broker + StubConnection connection2 = createRemoteConnection(); + ConnectionInfo connectionInfo2 = createConnectionInfo(); + SessionInfo sessionInfo2 = createSessionInfo(connectionInfo2); + ConsumerInfo consumerInfo2 = createConsumerInfo(sessionInfo2, destination); + connection2.send(connectionInfo2); + connection2.send(sessionInfo2); + connection2.request(consumerInfo2); + + // Start a fanout publisher. + log.info("Starting the fanout connection."); + final StubConnection connection3 = createFanoutConnection(); + ConnectionInfo connectionInfo3 = createConnectionInfo(); + SessionInfo sessionInfo3 = createSessionInfo(connectionInfo3); + final ProducerInfo producerInfo3 = createProducerInfo(sessionInfo3); + connection3.send(connectionInfo3); + connection3.send(sessionInfo3); + connection3.send(producerInfo3); + + // Send the message using the fail over publisher. + connection3.request(createMessage(producerInfo3, destination, deliveryMode)); + + assertNotNull(receiveMessage(connection1)); + assertNoMessagesLeft(connection1); + + assertNotNull(receiveMessage(connection2)); + assertNoMessagesLeft(connection2); + + final CountDownLatch publishDone = new CountDownLatch(1); + + // The MockTransport is on the remote connection. + // Slip in a new transport filter after the MockTransport + MockTransport mt = (MockTransport) connection3.getTransport().narrow(MockTransport.class); + mt.install(new TransportFilter(mt.getNext()) { + public void oneway(Command command) throws IOException { + System.out.println("Dropping: "+command); + // just eat it! to simulate a recent failure. + } + }); + + // Send a message (async) as this will block + new Thread() { + public void run() { + // Send the message using the fail over publisher. + try { + connection3.request(createMessage(producerInfo3, destination, deliveryMode)); + } catch (Throwable e) { + e.printStackTrace(); + } + publishDone.countDown(); + } + }.start(); + + // Assert that we block: + assertFalse( publishDone.await(3, TimeUnit.SECONDS) ); + + // Restart the remote server. State should be re-played and the publish should continue. + remoteURI = remoteConnector.getServer().getConnectURI().toString(); + restartRemoteBroker(); + + // This should reconnect, and resend + assertTrue( publishDone.await(10, TimeUnit.SECONDS) ); + + } + + protected String getLocalURI() { + return "tcp://localhost:0?wireFormat.tcpNoDelayEnabled=true"; + } + + protected String getRemoteURI() { + return remoteURI; + } + + protected StubConnection createFanoutConnection() throws Exception { + URI fanoutURI = new URI("fanout://static://(" + connector.getServer().getConnectURI() + "," + + "mock://"+remoteConnector.getServer().getConnectURI() + ")"); + Transport transport = TransportFactory.connect(fanoutURI); + StubConnection connection = new StubConnection(transport); + connections.add(connection); + return connection; + } + +} diff --git a/activemq-core/src/test/java/org/activemq/transport/peer/PeerTransportTest.java b/activemq-core/src/test/java/org/activemq/transport/peer/PeerTransportTest.java new file mode 100755 index 0000000000..8b4dbb0b9c --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/transport/peer/PeerTransportTest.java @@ -0,0 +1,129 @@ +/** + * + * Copyright 2005 Simula Labs, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.peer; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTextMessage; +import org.activemq.command.ActiveMQTopic; +import org.activemq.util.MessageList; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import junit.framework.TestCase; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class PeerTransportTest extends TestCase { + protected Log log = LogFactory.getLog(getClass()); + protected Destination destination; + protected boolean topic = true; + protected static int MESSAGE_COUNT = 50; + protected static int NUMBER_IN_CLUSTER = 3; + protected int deliveryMode = DeliveryMode.NON_PERSISTENT; + protected MessageProducer[] producers; + protected Connection[] connections; + protected MessageList messageList = new MessageList(); + + protected void setUp() throws Exception { + messageList.setVerbose(true); + + connections = new Connection[NUMBER_IN_CLUSTER]; + producers = new MessageProducer[NUMBER_IN_CLUSTER]; + Destination destination = createDestination(); + + String root = System.getProperty("activemq.store.dir"); + + for (int i = 0;i < NUMBER_IN_CLUSTER;i++) { + System.setProperty("activemq.store.dir", root + "_broker_" + i); + connections[i] = createConnection(); + connections[i].setClientID("ClusterTest" + i); + connections[i].start(); + Session session = connections[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + producers[i] = session.createProducer(destination); + producers[i].setDeliveryMode(deliveryMode); + MessageConsumer consumer = createMessageConsumer(session, destination); + consumer.setMessageListener(messageList); + } + System.out.println("Sleeping to ensure cluster is fully connected"); + Thread.sleep(10000); + System.out.println("Finished sleeping"); + } + + protected void tearDown() throws Exception { + if (connections != null) { + for (int i = 0;i < connections.length;i++) { + connections[i].close(); + } + } + } + + protected MessageConsumer createMessageConsumer(Session session, Destination destination) throws JMSException { + return session.createConsumer(destination); + } + + protected int expectedReceiveCount() { + return MESSAGE_COUNT * NUMBER_IN_CLUSTER * NUMBER_IN_CLUSTER; + } + + protected Connection createConnection() throws JMSException { + System.err.println("creating connection ...."); + ActiveMQConnectionFactory fac = new ActiveMQConnectionFactory("peer://" + getClass().getName()); + return fac.createConnection(); + } + + protected Destination createDestination() { + return createDestination(getClass().getName()); + } + + protected Destination createDestination(String name) { + if (topic) { + return new ActiveMQTopic(name); + } + else { + return new ActiveMQQueue(name); + } + } + + + /** + * @throws Exception + */ + public void testSendReceive() throws Exception { + for (int i = 0;i < MESSAGE_COUNT;i++) { + for (int x = 0;x < producers.length;x++) { + TextMessage textMessage = new ActiveMQTextMessage(); + textMessage.setText("MSG-NO: " + i + " in cluster: " + x); + producers[x].send(textMessage); + // System.out.println("SENT MSG: " + textMessage); + } + } + + messageList.assertMessagesReceived(expectedReceiveCount()); + } +} \ No newline at end of file diff --git a/activemq-core/src/test/java/org/activemq/transport/stomp/StompWireFormatTest.java b/activemq-core/src/test/java/org/activemq/transport/stomp/StompWireFormatTest.java new file mode 100644 index 0000000000..244015bcdd --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/transport/stomp/StompWireFormatTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2005 Your Corporation. All Rights Reserved. + */ +package org.activemq.transport.stomp; + +import org.activemq.broker.BrokerService; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.Response; +import org.activemq.command.SessionInfo; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; + +import junit.framework.TestCase; + +public class StompWireFormatTest extends TestCase { + private StompWireFormat wire; + + public void setUp() throws Exception { + wire = new StompWireFormat(); + } + + public void testDummy() throws Exception { + } + + public void TODO_testValidConnectHandshake() throws Exception { + String connect_frame = "CONNECT\n" + "login: brianm\n" + "passcode: wombats\n" + "\n" + Stomp.NULL; + DataInputStream din = new DataInputStream(new ByteArrayInputStream(connect_frame.getBytes())); + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + DataOutputStream dout = new DataOutputStream(bout); + + wire.registerTransportStreams(dout, din); + wire.initiateServerSideProtocol(); + + ConnectionInfo ci = (ConnectionInfo) wire.readCommand(din); + assertNotNull(ci); + assertTrue(ci.isResponseRequired()); + + Response cr = new Response(); + cr.setCorrelationId(ci.getCommandId()); + wire.writeCommand(cr, dout); + + SessionInfo si = (SessionInfo) wire.readCommand(null); + assertNotNull(si); + assertTrue(si.isResponseRequired()); + + Response sr = new Response(); + sr.setCorrelationId(si.getCommandId()); + wire.writeCommand(sr, dout); + + String response = new String(bout.toByteArray()); + assertTrue(response.startsWith("CONNECTED")); + } + + public void _testFakeServer() throws Exception { + final BrokerService container = new BrokerService(); + new Thread(new Runnable() { + public void run() { + try { + container.addConnector("stomp://localhost:61613"); + container.start(); + } + catch (Exception e) { + System.err.println("ARGH: caught: " + e); + e.printStackTrace(); + } + } + }).start(); + System.err.println("started container"); + System.err.println("okay, go play"); + + System.err.println(System.in.read()); + } +} diff --git a/activemq-core/src/test/java/org/activemq/transport/tcp/TcpTransportBrokerTest.java b/activemq-core/src/test/java/org/activemq/transport/tcp/TcpTransportBrokerTest.java new file mode 100755 index 0000000000..866b6628c1 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/transport/tcp/TcpTransportBrokerTest.java @@ -0,0 +1,45 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.tcp; + +import org.activemq.transport.TransportBrokerTestSupport; + +import junit.framework.Test; + +public class TcpTransportBrokerTest extends TransportBrokerTestSupport { + + protected String getBindLocation() { + return "tcp://localhost:0"; + } + + protected void setUp() throws Exception { + MAX_WAIT=2000; + super.setUp(); + } + + public static Test suite() { + return suite(TcpTransportBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + +} diff --git a/activemq-core/src/test/java/org/activemq/transport/vm/VMTransportBrokerTest.java b/activemq-core/src/test/java/org/activemq/transport/vm/VMTransportBrokerTest.java new file mode 100755 index 0000000000..40b3555b33 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/transport/vm/VMTransportBrokerTest.java @@ -0,0 +1,40 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.vm; + +import org.activemq.transport.TransportBrokerTestSupport; + +import junit.framework.Test; + +public class VMTransportBrokerTest extends TransportBrokerTestSupport { + + protected String getBindLocation() { + return "vm://localhost"; + } + + public static Test suite() { + return suite(VMTransportBrokerTest.class); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + +} diff --git a/activemq-core/src/test/java/org/activemq/transport/vm/VMTransportEmbeddedBrokerTest.java b/activemq-core/src/test/java/org/activemq/transport/vm/VMTransportEmbeddedBrokerTest.java new file mode 100755 index 0000000000..049a97c8d0 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/transport/vm/VMTransportEmbeddedBrokerTest.java @@ -0,0 +1,102 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.transport.vm; + +import java.net.URI; +import java.net.URISyntaxException; + +import javax.jms.DeliveryMode; + +import org.activemq.broker.BrokerRegistry; +import org.activemq.broker.BrokerTestSupport; +import org.activemq.broker.StubConnection; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.ConsumerInfo; +import org.activemq.command.Message; +import org.activemq.command.ProducerInfo; +import org.activemq.command.SessionInfo; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.util.IOExceptionSupport; + +/** + * Used to see if the VM transport starts an embedded broker on demand. + * + * @version $Revision$ + */ +public class VMTransportEmbeddedBrokerTest extends BrokerTestSupport { + + public static void main(String[] args) { + junit.textui.TestRunner.run(VMTransportEmbeddedBrokerTest.class); + } + + public void testConsumerPrefetchAtOne() throws Throwable { + + // Make sure the broker is created due to the connection being started. + assertNull(BrokerRegistry.getInstance().lookup("localhost")); + StubConnection connection = createConnection(); + assertNotNull(BrokerRegistry.getInstance().lookup("localhost")); + + // Start a producer and consumer + ConnectionInfo connectionInfo = createConnectionInfo(); + SessionInfo sessionInfo = createSessionInfo(connectionInfo); + ProducerInfo producerInfo = createProducerInfo(sessionInfo); + connection.send(connectionInfo); + connection.send(sessionInfo); + connection.send(producerInfo); + + ActiveMQQueue destination = new ActiveMQQueue("TEST"); + + ConsumerInfo consumerInfo = createConsumerInfo(sessionInfo, destination); + consumerInfo.setPrefetchSize(1); + connection.send(consumerInfo); + + // Send 2 messages to the broker. + connection.send(createMessage(producerInfo, destination, DeliveryMode.NON_PERSISTENT)); + connection.send(createMessage(producerInfo, destination, DeliveryMode.NON_PERSISTENT)); + + // Make sure only 1 message was delivered. + Message m = receiveMessage(connection); + assertNotNull(m); + assertNoMessagesLeft(connection); + + // Make sure the broker is shutdown when the connection is stopped. + assertNotNull(BrokerRegistry.getInstance().lookup("localhost")); + connection.stop(); + assertNull(BrokerRegistry.getInstance().lookup("localhost")); + } + + protected void setUp() throws Exception { + // Don't call super since it manually starts up a broker. + } + protected void tearDown() throws Exception { + // Don't call super since it manually tears down a broker. + } + protected StubConnection createConnection() throws Exception { + try { + Transport transport = TransportFactory.connect(new URI("vm://localhost?broker.persistent=false")); + StubConnection connection = new StubConnection(transport); + return connection; + } catch (URISyntaxException e) { + throw IOExceptionSupport.create(e); + } + } + +} diff --git a/activemq-core/src/test/java/org/activemq/util/ReflectionSupportTest.java b/activemq-core/src/test/java/org/activemq/util/ReflectionSupportTest.java new file mode 100755 index 0000000000..46bfee2f5e --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/util/ReflectionSupportTest.java @@ -0,0 +1,44 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; + +import junit.framework.TestCase; + +public class ReflectionSupportTest extends TestCase { + + public void testSetProperties() throws URISyntaxException { + SimplePojo pojo = new SimplePojo(); + HashMap map = new HashMap(); + map.put("age", "27"); + map.put("name", "Hiram"); + map.put("enabled", "true"); + map.put("uri", "test://value"); + + IntrospectionSupport.setProperties(pojo, map); + + assertEquals(27, pojo.getAge()); + assertEquals("Hiram", pojo.getName()); + assertEquals(true, pojo.isEnabled()); + assertEquals(new URI("test://value"), pojo.getUri()); + } +} diff --git a/activemq-core/src/test/java/org/activemq/util/SimplePojo.java b/activemq-core/src/test/java/org/activemq/util/SimplePojo.java new file mode 100755 index 0000000000..df7f3851a3 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/util/SimplePojo.java @@ -0,0 +1,55 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.util; + +import java.net.URI; + +public class SimplePojo { + + String name; + int age; + boolean enabled; + URI uri; + + public int getAge() { + return age; + } + public void setAge(int age) { + this.age = age; + } + public boolean isEnabled() { + return enabled; + } + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public URI getUri() { + return uri; + } + public void setUri(URI uri) { + this.uri = uri; + } + +} diff --git a/activemq-core/src/test/java/org/activemq/util/URISupportTest.java b/activemq-core/src/test/java/org/activemq/util/URISupportTest.java new file mode 100644 index 0000000000..7acda7bba6 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/util/URISupportTest.java @@ -0,0 +1,85 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.util; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; + +import org.activemq.util.URISupport.CompositeData; + +import junit.framework.TestCase; + +/** + * + * @version $Revision: 1.1 $ + */ +public class URISupportTest extends TestCase { + + public void testEmptyCompositePath() throws Exception { + CompositeData data = URISupport.parseComposite(new URI("broker:()/localhost?persistent=false")); + assertEquals(0, data.getComponents().length); + } + + public void testCompositePath() throws Exception { + CompositeData data = URISupport.parseComposite(new URI("test:(path)/path")); + assertEquals("path", data.getPath()); + data = URISupport.parseComposite(new URI("test:path")); + assertNull(data.getPath()); + } + + public void testSimpleComposite() throws Exception { + CompositeData data = URISupport.parseComposite(new URI("test:part1")); + assertEquals(1, data.getComponents().length); + } + + public void testComposite() throws Exception { + CompositeData data = URISupport.parseComposite(new URI("test:(part1://host,part2://(sub1://part,sube2:part))")); + assertEquals(2, data.getComponents().length); + } + + public void testParsingURI() throws Exception { + URI source = new URI("tcp://localhost:61626/foo/bar?cheese=Edam&x=123"); + + Map map = URISupport.parseParamters(source); + + assertEquals("Size: " + map, 2, map.size()); + assertMapKey(map, "cheese", "Edam"); + assertMapKey(map, "x", "123"); + + URI result = URISupport.removeQuery(source); + + assertEquals("result", new URI("tcp://localhost:61626/foo/bar"), result); + } + + protected void assertMapKey(Map map, String key, Object expected) { + assertEquals("Map key: " + key, map.get(key), expected); + } + + public void testParsingCompositeURI() throws URISyntaxException { + URISupport.parseComposite(new URI("broker://(tcp://localhost:61616)?name=foo")); + } + + public void testCheckParenthesis() throws Exception { + String str = "fred:(((ddd))"; + assertFalse(URISupport.checkParenthesis(str)); + str += ")"; + assertTrue(URISupport.checkParenthesis(str)); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/xbean/XBeanConfigTest.java b/activemq-core/src/test/java/org/activemq/xbean/XBeanConfigTest.java new file mode 100644 index 0000000000..616ef11830 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/xbean/XBeanConfigTest.java @@ -0,0 +1,114 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.xbean; + +import org.activemq.broker.Broker; +import org.activemq.broker.BrokerService; +import org.activemq.broker.ConnectionContext; +import org.activemq.broker.region.Topic; +import org.activemq.broker.region.policy.DispatchPolicy; +import org.activemq.broker.region.policy.LastImageSubscriptionRecoveryPolicy; +import org.activemq.broker.region.policy.RoundRobinDispatchPolicy; +import org.activemq.broker.region.policy.StrictOrderDispatchPolicy; +import org.activemq.broker.region.policy.SubscriptionRecoveryPolicy; +import org.activemq.broker.region.policy.TimedSubscriptionRecoveryPolicy; +import org.activemq.command.ActiveMQTopic; +import org.activemq.command.ConnectionId; +import org.activemq.command.ConnectionInfo; +import org.springframework.core.io.ClassPathResource; + +import junit.framework.TestCase; + +/** + * + * @version $Revision: 1.1 $ + */ +public class XBeanConfigTest extends TestCase { + + protected BrokerService brokerService; + protected Broker broker; + protected ConnectionContext context; + protected ConnectionInfo info; + + public void testBrokerConfiguredCorrectly() throws Throwable { + + + Topic topic = (Topic) broker.addDestination(context, new ActiveMQTopic("FOO.BAR")); + DispatchPolicy dispatchPolicy = topic.getDispatchPolicy(); + assertTrue("dispatchPolicy should be RoundRobinDispatchPolicy: " + dispatchPolicy, dispatchPolicy instanceof RoundRobinDispatchPolicy); + + SubscriptionRecoveryPolicy subscriptionRecoveryPolicy = topic.getSubscriptionRecoveryPolicy(); + assertTrue("subscriptionRecoveryPolicy should be LastImageSubscriptionRecoveryPolicy: " + subscriptionRecoveryPolicy, + subscriptionRecoveryPolicy instanceof LastImageSubscriptionRecoveryPolicy); + + System.out.println("destination: " + topic); + System.out.println("dispatchPolicy: " + dispatchPolicy); + System.out.println("subscriptionRecoveryPolicy: " + subscriptionRecoveryPolicy); + + topic = (Topic) broker.addDestination(context, new ActiveMQTopic("ORDERS.BOOKS")); + dispatchPolicy = topic.getDispatchPolicy(); + assertTrue("dispatchPolicy should be StrictOrderDispatchPolicy: " + dispatchPolicy, dispatchPolicy instanceof StrictOrderDispatchPolicy); + + subscriptionRecoveryPolicy = topic.getSubscriptionRecoveryPolicy(); + assertTrue("subscriptionRecoveryPolicy should be TimedSubscriptionRecoveryPolicy: " + subscriptionRecoveryPolicy, + subscriptionRecoveryPolicy instanceof TimedSubscriptionRecoveryPolicy); + TimedSubscriptionRecoveryPolicy timedSubcriptionPolicy = (TimedSubscriptionRecoveryPolicy) subscriptionRecoveryPolicy; + assertEquals("getRecoverDuration()", 60000, timedSubcriptionPolicy.getRecoverDuration()); + + System.out.println("destination: " + topic); + System.out.println("dispatchPolicy: " + dispatchPolicy); + System.out.println("subscriptionRecoveryPolicy: " + subscriptionRecoveryPolicy); + } + + protected void setUp() throws Exception { + brokerService = createBroker(); + broker = brokerService.getBroker(); + + brokerService.start(); + + context = new ConnectionContext(); + context.setBroker(broker); + info = new ConnectionInfo(); + info.setClientId("James"); + info.setUserName("James"); + info.setConnectionId(new ConnectionId("1234")); + + try { + broker.addConnection(context, info); + } + catch (Throwable e) { + e.printStackTrace(); + fail(e.getMessage()); + } + + assertNotNull("No broker created!"); + } + + protected void tearDown() throws Exception { + if (brokerService != null) { + brokerService.stop(); + } + } + + protected BrokerService createBroker() throws Exception { + BrokerFactoryBean factory = new BrokerFactoryBean(new ClassPathResource("org/activemq/xbean/activemq-policy.xml")); + factory.afterPropertiesSet(); + return factory.getBroker(); + } + +} diff --git a/activemq-core/src/test/java/org/activemq/xbean/XBeanXmlTest.java b/activemq-core/src/test/java/org/activemq/xbean/XBeanXmlTest.java new file mode 100755 index 0000000000..ec8b726410 --- /dev/null +++ b/activemq-core/src/test/java/org/activemq/xbean/XBeanXmlTest.java @@ -0,0 +1,29 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.xbean; + +import org.activemq.broker.SpringTest; + +public class XBeanXmlTest extends SpringTest { + + public void testSenderWithSpringXml() throws Exception { + assertSenderConfig("org/activemq/xbean/spring.xml"); + } +} diff --git a/activemq-core/src/test/resources/activemq.xml b/activemq-core/src/test/resources/activemq.xml new file mode 100755 index 0000000000..a70d065d3f --- /dev/null +++ b/activemq-core/src/test/resources/activemq.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/src/test/resources/jndi.properties b/activemq-core/src/test/resources/jndi.properties new file mode 100755 index 0000000000..7a1aebd6a7 --- /dev/null +++ b/activemq-core/src/test/resources/jndi.properties @@ -0,0 +1,28 @@ +# START SNIPPET: jndi + +java.naming.factory.initial = org.activemq.jndi.ActiveMQInitialContextFactory + +# use the following property to configure the default connector +java.naming.provider.url = vm://localhost + +# use the following property to embed a broker inside this JVM +#useEmbeddedBroker = true + +# use the following property to specify a class path resource or URL +# used to configure an embedded broker using the XML configuration file +#brokerXmlConfig = file:src/conf/sample-conf/default.xml + +# use the following property to specify the JNDI name the connection factory +# should appear as. +#connectionFactoryNames = connectionFactory, queueConnectionFactory, topicConnectionFactry + +# register some queues in JNDI using the form +# queue.[jndiName] = [physicalName] +queue.MyQueue = example.MyQueue + + +# register some topics in JNDI using the form +# topic.[jndiName] = [physicalName] +topic.MyTopic = example.MyTopic + +# END SNIPPET: jndi diff --git a/activemq-core/src/test/resources/log4j.properties b/activemq-core/src/test/resources/log4j.properties new file mode 100755 index 0000000000..9bd5d778b4 --- /dev/null +++ b/activemq-core/src/test/resources/log4j.properties @@ -0,0 +1,18 @@ +# +# The logging properties used during tests.. +# +log4j.rootLogger=INFO, out + +log4j.logger.org.activemq.spring=WARN + +# CONSOLE appender not used by default +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n + +# File appender +log4j.appender.out=org.apache.log4j.FileAppender +log4j.appender.out.layout=org.apache.log4j.PatternLayout +log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +log4j.appender.out.file=target/test-reports/activemq-test.log +log4j.appender.out.append=true diff --git a/activemq-core/src/test/resources/login.config b/activemq-core/src/test/resources/login.config new file mode 100644 index 0000000000..42513ed407 --- /dev/null +++ b/activemq-core/src/test/resources/login.config @@ -0,0 +1,6 @@ +activemq-test-domain { + org.activemq.jaas.PropertiesLoginModule required + debug=true + org.activemq.jaas.properties.user="org/activemq/security/users.properties" + org.activemq.jaas.properties.group="org/activemq/security/groups.properties"; +}; \ No newline at end of file diff --git a/activemq-core/src/test/resources/openwire-control/org.activemq.openwire.BrokerInfoData.bin b/activemq-core/src/test/resources/openwire-control/org.activemq.openwire.BrokerInfoData.bin new file mode 100644 index 0000000000..9e034f89c5 Binary files /dev/null and b/activemq-core/src/test/resources/openwire-control/org.activemq.openwire.BrokerInfoData.bin differ diff --git a/activemq-core/src/test/resources/openwire-control/org.activemq.openwire.WireFormatInfoData.bin b/activemq-core/src/test/resources/openwire-control/org.activemq.openwire.WireFormatInfoData.bin new file mode 100644 index 0000000000..88dd693d97 Binary files /dev/null and b/activemq-core/src/test/resources/openwire-control/org.activemq.openwire.WireFormatInfoData.bin differ diff --git a/activemq-core/src/test/resources/org/activemq/broker/spring.xml b/activemq-core/src/test/resources/org/activemq/broker/spring.xml new file mode 100644 index 0000000000..491a0eff22 --- /dev/null +++ b/activemq-core/src/test/resources/org/activemq/broker/spring.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + tcp://localhost:61616 + tcp://localhost:61636 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10 + + + + + + + + + + + + + + + + + + org.activemq.spring.Test.spring.topic + + + + + + diff --git a/activemq-core/src/test/resources/org/activemq/broker/store/loadtester.xml b/activemq-core/src/test/resources/org/activemq/broker/store/loadtester.xml new file mode 100755 index 0000000000..8c4136fecd --- /dev/null +++ b/activemq-core/src/test/resources/org/activemq/broker/store/loadtester.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/src/test/resources/org/activemq/filter/dummyPolicy.xml b/activemq-core/src/test/resources/org/activemq/filter/dummyPolicy.xml new file mode 100644 index 0000000000..f9a850154e --- /dev/null +++ b/activemq-core/src/test/resources/org/activemq/filter/dummyPolicy.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/src/test/resources/org/activemq/network/jms/queue-config.xml b/activemq-core/src/test/resources/org/activemq/network/jms/queue-config.xml new file mode 100644 index 0000000000..319e0bcece --- /dev/null +++ b/activemq-core/src/test/resources/org/activemq/network/jms/queue-config.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + tcp://localhost:61666 + + + + + + + + + + + + + + + + + tcp://localhost:61234 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/src/test/resources/org/activemq/network/jms/topic-config.xml b/activemq-core/src/test/resources/org/activemq/network/jms/topic-config.xml new file mode 100644 index 0000000000..f0daf7bcac --- /dev/null +++ b/activemq-core/src/test/resources/org/activemq/network/jms/topic-config.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + tcp://localhost:61666 + + + + + + + + + + + + + + + + + tcp://localhost:61234 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/src/test/resources/org/activemq/network/jms/topic-spring.xml b/activemq-core/src/test/resources/org/activemq/network/jms/topic-spring.xml new file mode 100644 index 0000000000..cb6f818cf2 --- /dev/null +++ b/activemq-core/src/test/resources/org/activemq/network/jms/topic-spring.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + tcp://localhost:61666 + + + + + + + + + + + + + + + + + tcp://localhost:61234 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/src/test/resources/org/activemq/security/groups.properties b/activemq-core/src/test/resources/org/activemq/security/groups.properties new file mode 100644 index 0000000000..7a42bdd44c --- /dev/null +++ b/activemq-core/src/test/resources/org/activemq/security/groups.properties @@ -0,0 +1,3 @@ +admins=system +users=system,user +guests=guest \ No newline at end of file diff --git a/activemq-core/src/test/resources/org/activemq/security/users.properties b/activemq-core/src/test/resources/org/activemq/security/users.properties new file mode 100644 index 0000000000..9aca502644 --- /dev/null +++ b/activemq-core/src/test/resources/org/activemq/security/users.properties @@ -0,0 +1,3 @@ +system=manager +user=password +guest=password \ No newline at end of file diff --git a/activemq-core/src/test/resources/org/activemq/test/retroactive/activemq-fixed-buffer.xml b/activemq-core/src/test/resources/org/activemq/test/retroactive/activemq-fixed-buffer.xml new file mode 100644 index 0000000000..9a0ab79935 --- /dev/null +++ b/activemq-core/src/test/resources/org/activemq/test/retroactive/activemq-fixed-buffer.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/src/test/resources/org/activemq/test/retroactive/activemq-fixed-destination-buffer.xml b/activemq-core/src/test/resources/org/activemq/test/retroactive/activemq-fixed-destination-buffer.xml new file mode 100644 index 0000000000..09f23efc85 --- /dev/null +++ b/activemq-core/src/test/resources/org/activemq/test/retroactive/activemq-fixed-destination-buffer.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/src/test/resources/org/activemq/test/retroactive/activemq-timed-policy.xml b/activemq-core/src/test/resources/org/activemq/test/retroactive/activemq-timed-policy.xml new file mode 100644 index 0000000000..a55298bd03 --- /dev/null +++ b/activemq-core/src/test/resources/org/activemq/test/retroactive/activemq-timed-policy.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/src/test/resources/org/activemq/xbean/activemq-policy.xml b/activemq-core/src/test/resources/org/activemq/xbean/activemq-policy.xml new file mode 100644 index 0000000000..d0026a9e24 --- /dev/null +++ b/activemq-core/src/test/resources/org/activemq/xbean/activemq-policy.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/src/test/resources/org/activemq/xbean/activemq.xml b/activemq-core/src/test/resources/org/activemq/xbean/activemq.xml new file mode 100644 index 0000000000..f9ab6e4c57 --- /dev/null +++ b/activemq-core/src/test/resources/org/activemq/xbean/activemq.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/src/test/resources/org/activemq/xbean/spring.xml b/activemq-core/src/test/resources/org/activemq/xbean/spring.xml new file mode 100644 index 0000000000..e00083f999 --- /dev/null +++ b/activemq-core/src/test/resources/org/activemq/xbean/spring.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-core/src/test/resources/spring-embedded.xml b/activemq-core/src/test/resources/spring-embedded.xml new file mode 100755 index 0000000000..a817a45f93 --- /dev/null +++ b/activemq-core/src/test/resources/spring-embedded.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + 10 + + + + + + + + + + + + + + + + + + org.activemq.spring.Test.spring.embedded + + + + + diff --git a/activemq-core/src/test/resources/spring-jndi.xml b/activemq-core/src/test/resources/spring-jndi.xml new file mode 100755 index 0000000000..e3860fb401 --- /dev/null +++ b/activemq-core/src/test/resources/spring-jndi.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + org.activemq.jndi.ActiveMQInitialContextFactory + true + + + example.Spring.MyTopic + + + + + + + + + + + ConnectionFactory + + + + + + + + + + MyTopic + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10 + + + + + + + + + + + + + + + + + + example.Spring.MyTopic + + + + + diff --git a/activemq-core/src/test/resources/spring-queue.xml b/activemq-core/src/test/resources/spring-queue.xml new file mode 100755 index 0000000000..bbe8e5f7f2 --- /dev/null +++ b/activemq-core/src/test/resources/spring-queue.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + 10 + + + + + + + + + + + + + + + + + + + org.activemq.spring.Test.spring.queue + + + + + + diff --git a/activemq-core/src/test/resources/spring.xml b/activemq-core/src/test/resources/spring.xml new file mode 100755 index 0000000000..670e30f321 --- /dev/null +++ b/activemq-core/src/test/resources/spring.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10 + + + + + + + + + + + + + + + + + + org.activemq.spring.Test.spring.topic + + + + + + diff --git a/activemq-gbean-management/project.properties b/activemq-gbean-management/project.properties new file mode 100755 index 0000000000..ac1bf0f121 --- /dev/null +++ b/activemq-gbean-management/project.properties @@ -0,0 +1,9 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=jar + +maven.repo.remote=\ +http://www.ibiblio.org/maven,\ +http://dist.codehaus.org,\ +http://cvs.apache.org/repository diff --git a/activemq-gbean-management/project.xml b/activemq-gbean-management/project.xml new file mode 100755 index 0000000000..ad0b286095 --- /dev/null +++ b/activemq-gbean-management/project.xml @@ -0,0 +1,31 @@ + + + + 3 + ${basedir}/../../etc/project.xml + + ActiveMQ :: GBean Interfaces + activemq-gbean-management + Geronimo / GBean management support + ActiveMQ management interfaces used for integration into Apache Geronimo + + org.activemq.gbean + + + Geronimo / GBean management support + org.activemq.gbean + + + + + + + + + geronimo + geronimo-management + ${geronimo_management_version} + + + + diff --git a/activemq-gbean-management/src/java/org/activemq/gbean/ActiveMQBroker.java b/activemq-gbean-management/src/java/org/activemq/gbean/ActiveMQBroker.java new file mode 100644 index 0000000000..237fa53672 --- /dev/null +++ b/activemq-gbean-management/src/java/org/activemq/gbean/ActiveMQBroker.java @@ -0,0 +1,31 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.gbean; + +import org.apache.geronimo.management.geronimo.JMSBroker; + +/** + * The mangement interface for the ActiveMQ broker GBean. + * This is separate from ActiveMQContainer because that interface has hard + * links to code in activemq-core, yet we still want to be able to + * distinguish ActiveMQ brokers from non-ActiveMQ JMS brokers. + * + * @version $Revision: 1.0$ + */ +public interface ActiveMQBroker extends JMSBroker { +} diff --git a/activemq-gbean-management/src/java/org/activemq/gbean/ActiveMQConnector.java b/activemq-gbean-management/src/java/org/activemq/gbean/ActiveMQConnector.java new file mode 100644 index 0000000000..44c4c4f03f --- /dev/null +++ b/activemq-gbean-management/src/java/org/activemq/gbean/ActiveMQConnector.java @@ -0,0 +1,35 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.gbean; + +import org.apache.geronimo.management.geronimo.JMSConnector; + +/** + * The GBean interface for the ActiveMQ network connector GBean + * + * @version $Revision: 1.0$ + */ +public interface ActiveMQConnector extends JMSConnector { + public final static String CONNECTOR_J2EE_TYPE = "JMSConnector"; + + // Additional stuff you can add to an ActiveMQ connector URI + public String getPath(); + public void setPath(String path); + public String getQuery(); + public void setQuery(String query); +} diff --git a/activemq-gbean-management/src/java/org/activemq/gbean/ActiveMQManager.java b/activemq-gbean-management/src/java/org/activemq/gbean/ActiveMQManager.java new file mode 100644 index 0000000000..8563cfd0ca --- /dev/null +++ b/activemq-gbean-management/src/java/org/activemq/gbean/ActiveMQManager.java @@ -0,0 +1,29 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.gbean; + +import org.apache.geronimo.management.geronimo.JMSManager; + +/** + * The GBean interface for the ActiveMQ management GBean. This defines the + * features that should be available to the management interface at runtime. + * + * @version $Revision: 1.0$ + */ +public interface ActiveMQManager extends JMSManager { +} diff --git a/activemq-gbean-management/src/java/org/activemq/gbean/package.html b/activemq-gbean-management/src/java/org/activemq/gbean/package.html new file mode 100755 index 0000000000..f7d94b0e17 --- /dev/null +++ b/activemq-gbean-management/src/java/org/activemq/gbean/package.html @@ -0,0 +1,12 @@ + + + + + +

+ The management API for ActiveMQ, when run in a + container like Geronimo +

+ + + diff --git a/activemq-jaas/login.config b/activemq-jaas/login.config new file mode 100644 index 0000000000..cf0afa294f --- /dev/null +++ b/activemq-jaas/login.config @@ -0,0 +1,6 @@ +testLogin { + org.activemq.jaas.PropertiesLoginModule required + debug=true + org.activemq.jaas.properties.user="src/test/resources/users.properties" + org.activemq.jaas.properties.group="src/test/resources/groups.properties"; +}; \ No newline at end of file diff --git a/activemq-jaas/maven.xml b/activemq-jaas/maven.xml new file mode 100644 index 0000000000..bfe82dfbd6 --- /dev/null +++ b/activemq-jaas/maven.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/activemq-jaas/pom.xml b/activemq-jaas/pom.xml new file mode 100644 index 0000000000..7837a4513f --- /dev/null +++ b/activemq-jaas/pom.xml @@ -0,0 +1,109 @@ + + 4.0.0 + + + activemq + activemq + 4.0-SNAPSHOT + + + activemq-jaas + jar + ActiveMQ JAAS Login Modules + + + + + activeio + activeio + + + + + backport-util-concurrent + backport-util-concurrent + + + + commons-logging + commons-logging + + + + log4j + log4j + + + + org.apache.derby + derby + + + + axion + axion + + + + + directory + apacheds-core + 0.9.2 + + + directory + apacheds-shared + 0.9.2 + + + directory-shared + apache-ldapber-provider + 0.9.2 + + + directory-shared + ldap-common + 0.9.2 + + + + directory-asn1 + asn1-codec + 0.3.2 + + + directory-asn1 + asn1-ber + 0.3.2 + + + directory-asn1 + asn1-der + 0.3.2 + + + + directory-shared + kerberos-common + 0.5 + + + + directory-network + mina + 0.7.3 + + + directory-protocols + kerberos-protocol + 0.5 + + + directory-protocols + ldap-protocol + 0.9.2 + + + + + diff --git a/activemq-jaas/project.properties b/activemq-jaas/project.properties new file mode 100644 index 0000000000..5d0a89cc3f --- /dev/null +++ b/activemq-jaas/project.properties @@ -0,0 +1,12 @@ +maven.repo.remote=\ +http://dist.codehaus.org,\ +http://www.ibiblio.org/maven,\ +http://cvs.apache.org/repository + +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=jar +maven.eclipse.classpath.include=src/test/resources + +maven.junit.jvmargs=-Djava.security.auth.login.config=src/test/resources/login.config diff --git a/activemq-jaas/project.xml b/activemq-jaas/project.xml new file mode 100644 index 0000000000..ca7e07cff5 --- /dev/null +++ b/activemq-jaas/project.xml @@ -0,0 +1,256 @@ + + + + + + 3 + activemq-jaas + activemq + ${basedir}/../etc/project.xml + ActiveMQ :: JAAS + activemq-jaas + ActiveMQ is an open source message broker and JMS 1.1 provider + ActiveMQ JAAS Login Modules + org.activemq.jaas + + + Utilities + org.activemq.jaas.* + + + + + + + + + + activeio + activeio + ${activeio_version} + + + + + org.apache.derby + derby + ${derby_version} + + + + + axion + axion + ${axion_version} + + + + jdbm + jdbm + ${jdbm_version} + http://jdbm.sourceforge.net + + + + directory + apacheds-core + ${apacheds_version} + + + + directory + apacheds-shared + ${apacheds_version} + + + + directory-asn1 + asn1-codec + ${asn1_version} + + + + directory-shared + apache-ldapber-provider + ${apacheds_version} + + + + directory-asn1 + asn1-ber + ${asn1_version} + + + + directory-asn1 + asn1-der + ${asn1_version} + + + + jdbm + jdbm + ${jdbm_version} + http://jdbm.sourceforge.net + + + + regexp + regexp + ${regexp_version} + http://jakarta.apache.org/regexp/index.html + + + + oro + oro + ${oro_version} + http://jakarta.apache.org/oro + + + + commons-lang + commons-lang + ${commons_lang_version} + http://jakarta.apache.org/commons/lang/api + + + + commons-collections + commons-collections + ${commons_collections_version} + http://jakarta.apache.org/commons/collections + + + + commons-io + commons-io + ${commons_io_version} + http://jakarta.apache.org/commons/io + + + + commons-primitives + commons-primitives + ${commons_primitives_version} + http://jakarta.apache.org/commons/primitives + + + + directory-shared + ldap-common + ${apacheds_version} + + + + directory-shared + kerberos-common + ${kerberos_common_version} + + + + directory-network + mina + ${mina_version} + + + + directory-protocols + kerberos-protocol + ${kerberos_protocols_version} + + + + directory-protocols + ldap-protocol + ${ldap_protocols_version} + + + + antlr + antlr + ${antlr_version} + + + + org.slf4j + slf4j-simple + ${slf4j_version} + + + + commons-logging + commons-logging + ${commons_logging_version} + http://jakarta.apache.org/commons/logging + + + + + + dev@activemq.codehaus.org + src/main/java + src/test/java + + + + src/test/resources + + **/*.properties + **/*.xml + + false + + + + **/*Test.* + + + + + + + src/main/resources + + **/* + + false + + + + + + + Alan D. Cabrera + maguro + adc@toolazydogs.com + + + + + maven-developer-activity-plugin + + + + scm:cvs:pserver:jlim@protique.exist.com:/cvs/activemq:activemq-4 + scm:cvs:pserver:jlim@protique.exist.com:/cvs/activemq:activemq-4 + http://activemq.org + + + diff --git a/activemq-jaas/src/main/java/org/activemq/jaas/GroupPrincipal.java b/activemq-jaas/src/main/java/org/activemq/jaas/GroupPrincipal.java new file mode 100644 index 0000000000..46ee389b37 --- /dev/null +++ b/activemq-jaas/src/main/java/org/activemq/jaas/GroupPrincipal.java @@ -0,0 +1,61 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.jaas; + +import java.security.Principal; + + +/** + * @version $Rev: $ $Date: $ + */ +public class GroupPrincipal implements Principal { + + private final String name; + private transient int hash; + + public GroupPrincipal(String name) { + if (name == null) throw new IllegalArgumentException("name cannot be null"); + this.name = name; + } + + public String getName() { + return name; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final GroupPrincipal that = (GroupPrincipal) o; + + if (!name.equals(that.name)) return false; + + return true; + } + + public int hashCode() { + if (hash == 0) { + hash = name.hashCode(); + } + return hash; + } + + public String toString() { + return name; + } +} diff --git a/activemq-jaas/src/main/java/org/activemq/jaas/LDAPLoginModule.java b/activemq-jaas/src/main/java/org/activemq/jaas/LDAPLoginModule.java new file mode 100644 index 0000000000..a02aaae51d --- /dev/null +++ b/activemq-jaas/src/main/java/org/activemq/jaas/LDAPLoginModule.java @@ -0,0 +1,398 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.jaas; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import javax.naming.AuthenticationException; +import javax.naming.CommunicationException; +import javax.naming.Context; +import javax.naming.Name; +import javax.naming.NameParser; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.LoginException; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.spi.LoginModule; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * @version $Rev: $ $Date: $ + */ +public class LDAPLoginModule implements LoginModule { + + private static Log log = LogFactory.getLog(LDAPLoginModule.class); + + private Subject subject; + private CallbackHandler handler; + + private static final String INITIAL_CONTEXT_FACTORY = "initialContextFactory"; + private static final String CONNECTION_URL = "connectionURL"; + private static final String CONNECTION_USERNAME = "connectionUsername"; + private static final String CONNECTION_PASSWORD = "connectionPassword"; + private static final String CONNECTION_PROTOCOL = "connectionProtocol"; + private static final String AUTHENTICATION = "authentication"; + private static final String USER_BASE = "userBase"; + private static final String USER_SEARCH_MATCHING = "userSearchMatching"; + private static final String USER_SEARCH_SUBTREE = "userSearchSubtree"; + private static final String ROLE_BASE = "roleBase"; + private static final String ROLE_NAME = "roleName"; + private static final String ROLE_SEARCH_MATCHING = "roleSearchMatching"; + private static final String ROLE_SEARCH_SUBTREE = "roleSearchSubtree"; + private static final String USER_ROLE_NAME = "userRoleName"; + + private String initialContextFactory; + private String connectionURL; + private String connectionUsername; + private String connectionPassword; + private String connectionProtocol; + private String authentication; + private String userBase; + private String roleBase; + private String roleName; + private String userRoleName; + + private String username; + + protected DirContext context = null; + + private MessageFormat userSearchMatchingFormat; + private MessageFormat roleSearchMatchingFormat; + + private boolean userSearchSubtreeBool = false; + private boolean roleSearchSubtreeBool = false; + + Set groups = new HashSet(); + + public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { + this.subject = subject; + this.handler = callbackHandler; + initialContextFactory = (String) options.get(INITIAL_CONTEXT_FACTORY); + connectionURL = (String) options.get(CONNECTION_URL); + connectionUsername = (String) options.get(CONNECTION_USERNAME); + connectionPassword = (String) options.get(CONNECTION_PASSWORD); + connectionProtocol = (String) options.get(CONNECTION_PROTOCOL); + authentication = (String) options.get(AUTHENTICATION); + userBase = (String) options.get(USER_BASE); + String userSearchMatching = (String) options.get(USER_SEARCH_MATCHING); + String userSearchSubtree = (String) options.get(USER_SEARCH_SUBTREE); + roleBase = (String) options.get(ROLE_BASE); + roleName = (String) options.get(ROLE_NAME); + String roleSearchMatching = (String) options.get(ROLE_SEARCH_MATCHING); + String roleSearchSubtree = (String) options.get(ROLE_SEARCH_SUBTREE); + userRoleName = (String) options.get(USER_ROLE_NAME); + userSearchMatchingFormat = new MessageFormat(userSearchMatching); + roleSearchMatchingFormat = new MessageFormat(roleSearchMatching); + userSearchSubtreeBool = new Boolean(userSearchSubtree).booleanValue(); + roleSearchSubtreeBool = new Boolean(roleSearchSubtree).booleanValue(); + } + + public boolean login() throws LoginException { + Callback[] callbacks = new Callback[2]; + + callbacks[0] = new NameCallback("User name"); + callbacks[1] = new PasswordCallback("Password", false); + try { + handler.handle(callbacks); + } catch (IOException ioe) { + throw (LoginException) new LoginException().initCause(ioe); + } catch (UnsupportedCallbackException uce) { + throw (LoginException) new LoginException().initCause(uce); + } + username = ((NameCallback) callbacks[0]).getName(); + String password = new String(((PasswordCallback) callbacks[1]).getPassword()); + + if (username == null || "".equals(username) || password == null || "".equals(password)) { + return false; + } + + try { + boolean result = authenticate(username, password); + if(!result) { + throw new FailedLoginException(); + } else { + return true; + } + } catch (Exception e) { + throw (LoginException) new LoginException("LDAP Error").initCause(e); + } + } + + public boolean logout() throws LoginException { + username = null; + return true; + } + + public boolean commit() throws LoginException { + Set principals = subject.getPrincipals(); + principals.add(new UserPrincipal(username)); + Iterator iter = groups.iterator(); + while (iter.hasNext()) { + principals.add(iter.next()); + } + return true; + } + + public boolean abort() throws LoginException { + username = null; + return true; + } + + protected void close(DirContext context) { + try { + context.close(); + } catch (Exception e) { + log.error(e); + } + } + + protected boolean authenticate(String username, String password) throws Exception { + + DirContext context = null; + context = open(); + + try { + + String filter = userSearchMatchingFormat.format(new String[]{username}); + SearchControls constraints = new SearchControls(); + if (userSearchSubtreeBool) { + constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); + } else { + constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE); + } + + //setup attributes + ArrayList list = new ArrayList(); + if (userRoleName != null) { + list.add(userRoleName); + } + String[] attribs = new String[list.size()]; + list.toArray(attribs); + constraints.setReturningAttributes(attribs); + + + NamingEnumeration results = context.search(userBase, filter, constraints); + + if (results == null || !results.hasMore()) { + return false; + } + + SearchResult result = (SearchResult) results.next(); + + if (results.hasMore()) { + //ignore for now + } + NameParser parser = context.getNameParser(""); + Name contextName = parser.parse(context.getNameInNamespace()); + Name baseName = parser.parse(userBase); + Name entryName = parser.parse(result.getName()); + Name name = contextName.addAll(baseName); + name = name.addAll(entryName); + String dn = name.toString(); + + Attributes attrs = result.getAttributes(); + if (attrs == null) { + return false; + } + ArrayList roles = null; + if (userRoleName != null) { + roles = addAttributeValues(userRoleName, attrs, roles); + } + + //check the credentials by binding to server + if (bindUser(context, dn, password)) { + //if authenticated add more roles + roles = getRoles(context, dn, username, roles); + for (int i = 0; i < roles.size(); i++) { + groups.add(new GroupPrincipal((String) roles.get(i))); + } + } else { + return false; + } + } catch (CommunicationException e) { + + } catch (NamingException e) { + if (context != null) { + close(context); + } + return false; + } + + + return true; + } + + protected ArrayList getRoles(DirContext context, String dn, String username, ArrayList currentRoles) throws NamingException { + ArrayList list = currentRoles; + if (list == null) { + list = new ArrayList(); + } + if (roleName == null || "".equals(roleName)) { + return list; + } + String filter = roleSearchMatchingFormat.format(new String[]{doRFC2254Encoding(dn), username}); + + SearchControls constraints = new SearchControls(); + if (roleSearchSubtreeBool) { + constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); + } else { + constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE); + } + NamingEnumeration results = + context.search(roleBase, filter, constraints); + while (results.hasMore()) { + SearchResult result = (SearchResult) results.next(); + Attributes attrs = result.getAttributes(); + if (attrs == null) { + continue; + } + list = addAttributeValues(roleName, attrs, list); + } + return list; + + } + + + protected String doRFC2254Encoding(String inputString) { + StringBuffer buf = new StringBuffer(inputString.length()); + for (int i = 0; i < inputString.length(); i++) { + char c = inputString.charAt(i); + switch (c) { + case '\\': + buf.append("\\5c"); + break; + case '*': + buf.append("\\2a"); + break; + case '(': + buf.append("\\28"); + break; + case ')': + buf.append("\\29"); + break; + case '\0': + buf.append("\\00"); + break; + default: + buf.append(c); + break; + } + } + return buf.toString(); + } + + protected boolean bindUser(DirContext context, String dn, String password) throws NamingException { + boolean isValid = false; + + context.addToEnvironment(Context.SECURITY_PRINCIPAL, dn); + context.addToEnvironment(Context.SECURITY_CREDENTIALS, password); + try { + context.getAttributes("", null); + isValid = true; + } catch (AuthenticationException e) { + isValid = false; + log.debug("Authentication failed for dn=" + dn); + } + + if (connectionUsername != null) { + context.addToEnvironment(Context.SECURITY_PRINCIPAL, + connectionUsername); + } else { + context.removeFromEnvironment(Context.SECURITY_PRINCIPAL); + } + + if (connectionPassword != null) { + context.addToEnvironment(Context.SECURITY_CREDENTIALS, + connectionPassword); + } else { + context.removeFromEnvironment(Context.SECURITY_CREDENTIALS); + } + + return isValid; + } + + private ArrayList addAttributeValues(String attrId, Attributes attrs, ArrayList values) + throws NamingException + { + + if (attrId == null || attrs == null) { + return values; + } + if (values == null) { + values = new ArrayList(); + } + Attribute attr = attrs.get(attrId); + if (attr == null) { + return (values); + } + NamingEnumeration e = attr.getAll(); + while (e.hasMore()) { + String value = (String) e.next(); + values.add(value); + } + return values; + } + + protected DirContext open() throws NamingException { + if (context != null) { + return context; + } + + try { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory); + if (connectionUsername != null || !"".equals(connectionUsername)) { + env.put(Context.SECURITY_PRINCIPAL, connectionUsername); + } + if (connectionPassword != null || !"".equals(connectionPassword)) { + env.put(Context.SECURITY_CREDENTIALS, connectionPassword); + } + env.put(Context.SECURITY_PROTOCOL, connectionProtocol); + env.put(Context.PROVIDER_URL, connectionURL); + env.put(Context.SECURITY_AUTHENTICATION, authentication); + context = new InitialDirContext(env); + + } catch (NamingException e) { + log.error(e); + throw e; + } + return context; + } + +} diff --git a/activemq-jaas/src/main/java/org/activemq/jaas/PropertiesLoginModule.java b/activemq-jaas/src/main/java/org/activemq/jaas/PropertiesLoginModule.java new file mode 100644 index 0000000000..0237341b9c --- /dev/null +++ b/activemq-jaas/src/main/java/org/activemq/jaas/PropertiesLoginModule.java @@ -0,0 +1,173 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.jaas; + +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.LoginException; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.spi.LoginModule; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * @version $Rev: $ $Date: $ + */ +public class PropertiesLoginModule implements LoginModule { + + private final String USER_FILE = "org.activemq.jaas.properties.user"; + private final String GROUP_FILE = "org.activemq.jaas.properties.group"; + + private static final Log log = LogFactory.getLog(PropertiesLoginModule.class); + + private Subject subject; + private CallbackHandler callbackHandler; + + private boolean debug; + private String usersFile; + private String groupsFile; + private Properties users = new Properties(); + private Properties groups = new Properties(); + private String user; + private Set principals = new HashSet(); + private File baseDir; + + public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { + this.subject = subject; + this.callbackHandler = callbackHandler; + + if( System.getProperty("java.security.auth.login.config")!=null ) { + baseDir=new File(System.getProperty("java.security.auth.login.config")).getParentFile(); + } else { + baseDir = new File("."); + } + System.out.println("setting based dir="+baseDir); + + debug = "true".equalsIgnoreCase((String) options.get("debug")); + usersFile = (String) options.get(USER_FILE)+""; + groupsFile = (String) options.get(GROUP_FILE)+""; + + if (debug) { + log.debug("Initialized debug=" + debug + " usersFile=" + usersFile + " groupsFile=" + groupsFile+" basedir="+baseDir); + } + } + + public boolean login() throws LoginException { + File f = new File(baseDir,usersFile); + try { + users.load(new java.io.FileInputStream(f)); + } catch (IOException ioe) { + throw new LoginException("Unable to load user properties file " + f); + } + f = new File(baseDir, groupsFile); + try { + groups.load(new java.io.FileInputStream(f)); + } catch (IOException ioe) { + throw new LoginException("Unable to load group properties file " + f); + } + + Callback[] callbacks = new Callback[2]; + + callbacks[0] = new NameCallback("Username: "); + callbacks[1] = new PasswordCallback("Password: ", false); + try { + callbackHandler.handle(callbacks); + } catch (IOException ioe) { + throw new LoginException(ioe.getMessage()); + } catch (UnsupportedCallbackException uce) { + throw new LoginException(uce.getMessage() + " not available to obtain information from user"); + } + user = ((NameCallback) callbacks[0]).getName(); + char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword(); + if (tmpPassword == null) tmpPassword = new char[0]; + + String password = users.getProperty(user); + + if (password == null) throw new FailedLoginException("User does exist"); + if (!password.equals(new String(tmpPassword))) throw new FailedLoginException("Password does not match"); + + users.clear(); + + if (debug) { + log.debug("login " + user); + } + return true; + } + + public boolean commit() throws LoginException { + principals.add(new UserPrincipal(user)); + + for (Enumeration enumeration = groups.keys(); enumeration.hasMoreElements();) { + String name = (String) enumeration.nextElement(); + String[] userList = ((String) groups.getProperty(name) + "").split(","); + for (int i = 0; i < userList.length; i++) { + if (user.equals(userList[i])) { + principals.add(new GroupPrincipal(name)); + break; + } + } + } + + subject.getPrincipals().addAll(principals); + + clear(); + + if (debug) { + log.debug("commit"); + } + return true; + } + + public boolean abort() throws LoginException { + clear(); + + if (debug) { + log.debug("abort"); + } + return true; + } + + public boolean logout() throws LoginException { + subject.getPrincipals().removeAll(principals); + principals.clear(); + + if (debug) { + log.debug("logout"); + } + return true; + } + + private void clear() { + groups.clear(); + user = null; + } +} diff --git a/activemq-jaas/src/main/java/org/activemq/jaas/UserPrincipal.java b/activemq-jaas/src/main/java/org/activemq/jaas/UserPrincipal.java new file mode 100644 index 0000000000..21817a1194 --- /dev/null +++ b/activemq-jaas/src/main/java/org/activemq/jaas/UserPrincipal.java @@ -0,0 +1,61 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.jaas; + +import java.security.Principal; + + +/** + * @version $Rev: $ $Date: $ + */ +public class UserPrincipal implements Principal { + + private final String name; + private transient int hash; + + public UserPrincipal(String name) { + if (name == null) throw new IllegalArgumentException("name cannot be null"); + this.name = name; + } + + public String getName() { + return name; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final UserPrincipal that = (UserPrincipal) o; + + if (!name.equals(that.name)) return false; + + return true; + } + + public int hashCode() { + if (hash == 0) { + hash = name.hashCode(); + } + return hash; + } + + public String toString() { + return name; + } +} diff --git a/activemq-jaas/src/test/java/org/activemq/jaas/GroupPrincipalTest.java b/activemq-jaas/src/test/java/org/activemq/jaas/GroupPrincipalTest.java new file mode 100644 index 0000000000..2f2e441f8f --- /dev/null +++ b/activemq-jaas/src/test/java/org/activemq/jaas/GroupPrincipalTest.java @@ -0,0 +1,60 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.jaas; + +import junit.framework.TestCase; + + +/** + * @version $Rev: $ $Date: $ + */ +public class GroupPrincipalTest extends TestCase { + + public void testArguments() { + GroupPrincipal principal = new GroupPrincipal("FOO"); + + assertEquals("FOO", principal.getName()); + + try { + new GroupPrincipal(null); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException ingore) { + + } + } + + public void testHash() { + GroupPrincipal p1 = new GroupPrincipal("FOO"); + GroupPrincipal p2 = new GroupPrincipal("FOO"); + + assertEquals(p1.hashCode(), p1.hashCode()); + assertEquals(p1.hashCode(), p2.hashCode()); + } + + public void testEquals() { + GroupPrincipal p1 = new GroupPrincipal("FOO"); + GroupPrincipal p2 = new GroupPrincipal("FOO"); + GroupPrincipal p3 = new GroupPrincipal("BAR"); + + assertTrue(p1.equals(p1)); + assertTrue(p1.equals(p2)); + assertFalse(p1.equals(null)); + assertFalse(p1.equals("FOO")); + assertFalse(p1.equals(p3)); + } +} diff --git a/activemq-jaas/src/test/java/org/activemq/jaas/LDAPLoginModuleTest.java b/activemq-jaas/src/test/java/org/activemq/jaas/LDAPLoginModuleTest.java new file mode 100644 index 0000000000..a3eb30da04 --- /dev/null +++ b/activemq-jaas/src/test/java/org/activemq/jaas/LDAPLoginModuleTest.java @@ -0,0 +1,139 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.jaas; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Properties; +import javax.naming.Context; +import javax.naming.NameClassPair; +import javax.naming.NamingEnumeration; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import junit.framework.TestCase; +import org.apache.ldap.server.configuration.ShutdownConfiguration; +import org.apache.ldap.server.jndi.CoreContextFactory; + +import org.activemq.jaas.ldap.MutableServerStartupConfiguration; +import org.activemq.jaas.ldap.ServerContextFactory; + + +/** + * @version $Rev: $ $Date: $ + */ +public class LDAPLoginModuleTest extends TestCase { + + private static final String PRINCIPAL = "uid=admin,ou=system"; + private static final String CREDENTIALS = "secret"; + + public void testNothing() { + } + + public void testRunning() throws Exception { + + Hashtable env = new Hashtable(); + env.put(Context.PROVIDER_URL, "ldap://localhost:9389"); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.SECURITY_AUTHENTICATION, "simple"); + env.put(Context.SECURITY_PRINCIPAL, PRINCIPAL); + env.put(Context.SECURITY_CREDENTIALS, CREDENTIALS); + DirContext ctx = new InitialDirContext(env); + + // Perform search using URL + // NamingEnumeration answer = ctx.search( + // "ldap://localhost:389/ou=system", "(uid=admin)", null); + HashSet set = new HashSet(); + + NamingEnumeration list = ctx.list("ou=system"); + + while (list.hasMore()) { + NameClassPair ncp = (NameClassPair) list.next(); + set.add(ncp.getName()); + } + + assertTrue(set.contains("uid=admin")); + assertTrue(set.contains("ou=users")); + assertTrue(set.contains("ou=groups")); + assertTrue(set.contains("ou=configuration")); + assertTrue(set.contains("prefNodeName=sysPrefRoot")); + + } + + public void XtestLogin() throws LoginException { + LoginContext context = new LoginContext("LDAPLogin", new CallbackHandler() { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } + }); + context.login(); + context.logout(); + } + + public void setUp() throws Exception { + MutableServerStartupConfiguration startup = new MutableServerStartupConfiguration(); + // put some mandatory JNDI properties here + startup.setWorkingDirectory(new File("target/ldap")); + startup.setAllowAnonymousAccess(true); + startup.setLdapPort(9389); + startup.setEnableNetworking(true); + startup.setHost(InetAddress.getByName("localhost")); + + Properties env = new Properties(); + env.putAll(startup.toJndiEnvironment()); + env.put(Context.INITIAL_CONTEXT_FACTORY, ServerContextFactory.class.getName()); + env.put(Context.PROVIDER_URL, "ou=system"); + env.put(Context.SECURITY_AUTHENTICATION, "simple"); + env.put(Context.SECURITY_PRINCIPAL, PRINCIPAL); + env.put(Context.SECURITY_CREDENTIALS, CREDENTIALS); + + //Fire it up + new InitialDirContext(env); + } + + public void tearDown() throws Exception { + Properties env = new Properties(); + env.putAll(new ShutdownConfiguration().toJndiEnvironment()); + env.put(Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName()); + env.put(Context.PROVIDER_URL, "ou=system"); + env.put(Context.SECURITY_AUTHENTICATION, "simple"); + env.put(Context.SECURITY_PRINCIPAL, PRINCIPAL); + env.put(Context.SECURITY_CREDENTIALS, CREDENTIALS); + + //Shut it down + new InitialDirContext(env); + } +} diff --git a/activemq-jaas/src/test/java/org/activemq/jaas/PropertiesLoginModuleTest.java b/activemq-jaas/src/test/java/org/activemq/jaas/PropertiesLoginModuleTest.java new file mode 100644 index 0000000000..223050c4f7 --- /dev/null +++ b/activemq-jaas/src/test/java/org/activemq/jaas/PropertiesLoginModuleTest.java @@ -0,0 +1,109 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.jaas; + +import java.io.IOException; +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import junit.framework.TestCase; + + +/** + * @version $Rev: $ $Date: $ + */ +public class PropertiesLoginModuleTest extends TestCase { + + public void testLogin() throws LoginException { + LoginContext context = new LoginContext("PropertiesLogin", new CallbackHandler() { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } + }); + context.login(); + + Subject subject = context.getSubject(); + + assertEquals("Should have three principals", 3, subject.getPrincipals().size()); + assertEquals("Should have one user principal", 1, subject.getPrincipals(UserPrincipal.class).size()); + assertEquals("Should have two group principals", 2, subject.getPrincipals(GroupPrincipal.class).size()); + + context.logout(); + + assertEquals("Should have zero principals", 0, subject.getPrincipals().size()); + } + + public void testBadUseridLogin() throws Exception { + LoginContext context = new LoginContext("PropertiesLogin", new CallbackHandler() { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("BAD"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } + }); + try { + context.login(); + fail("Should have thrown a FailedLoginException"); + } catch (FailedLoginException doNothing) { + } + + } + + public void testBadPWLogin() throws Exception { + LoginContext context = new LoginContext("PropertiesLogin", new CallbackHandler() { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("BAD".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + } + }); + try { + context.login(); + fail("Should have thrown a FailedLoginException"); + } catch (FailedLoginException doNothing) { + } + + } +} diff --git a/activemq-jaas/src/test/java/org/activemq/jaas/UserPrincipalTest.java b/activemq-jaas/src/test/java/org/activemq/jaas/UserPrincipalTest.java new file mode 100644 index 0000000000..afc74d37cb --- /dev/null +++ b/activemq-jaas/src/test/java/org/activemq/jaas/UserPrincipalTest.java @@ -0,0 +1,60 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.jaas; + +import junit.framework.TestCase; + + +/** + * @version $Rev: $ $Date: $ + */ +public class UserPrincipalTest extends TestCase { + + public void testArguments() { + UserPrincipal principal = new UserPrincipal("FOO"); + + assertEquals("FOO", principal.getName()); + + try { + new UserPrincipal(null); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException ingore) { + + } + } + + public void testHash() { + UserPrincipal p1 = new UserPrincipal("FOO"); + UserPrincipal p2 = new UserPrincipal("FOO"); + + assertEquals(p1.hashCode(), p1.hashCode()); + assertEquals(p1.hashCode(), p2.hashCode()); + } + + public void testEquals() { + UserPrincipal p1 = new UserPrincipal("FOO"); + UserPrincipal p2 = new UserPrincipal("FOO"); + UserPrincipal p3 = new UserPrincipal("BAR"); + + assertTrue(p1.equals(p1)); + assertTrue(p1.equals(p2)); + assertFalse(p1.equals(null)); + assertFalse(p1.equals("FOO")); + assertFalse(p1.equals(p3)); + } +} diff --git a/activemq-jaas/src/test/java/org/activemq/jaas/ldap/MutableServerStartupConfiguration.java b/activemq-jaas/src/test/java/org/activemq/jaas/ldap/MutableServerStartupConfiguration.java new file mode 100644 index 0000000000..2081fff0bd --- /dev/null +++ b/activemq-jaas/src/test/java/org/activemq/jaas/ldap/MutableServerStartupConfiguration.java @@ -0,0 +1,87 @@ +/** + * + * Copyright 2005 The Apache Software Foundation + * + * Licensed 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.activemq.jaas.ldap; + +import java.io.File; +import java.net.InetAddress; +import java.util.List; +import java.util.Set; + +import org.apache.mina.registry.ServiceRegistry; + +/** + * A mutable version of {@link ServerStartupConfiguration}. + * + * @version $Rev: 233391 $ $Date: 2005-08-18 16:38:47 -0600 (Thu, 18 Aug 2005) $ + */ +public class MutableServerStartupConfiguration extends + ServerStartupConfiguration +{ + private static final long serialVersionUID = 515104910980600099L; + + public MutableServerStartupConfiguration() { + super(); + } + + public void setAllowAnonymousAccess(boolean arg0) { + super.setAllowAnonymousAccess(arg0); + } + + public void setAuthenticatorConfigurations(Set arg0) { + super.setAuthenticatorConfigurations(arg0); + } + + public void setBootstrapSchemas(Set arg0) { + super.setBootstrapSchemas(arg0); + } + + public void setContextPartitionConfigurations(Set arg0) { + super.setContextPartitionConfigurations(arg0); + } + + public void setInterceptorConfigurations(List arg0) { + super.setInterceptorConfigurations(arg0); + } + + public void setTestEntries(List arg0) { + super.setTestEntries(arg0); + } + + public void setWorkingDirectory(File arg0) { + super.setWorkingDirectory(arg0); + } + + public void setEnableKerberos(boolean enableKerberos) { + super.setEnableKerberos(enableKerberos); + } + + public void setHost(InetAddress host) { + super.setHost(host); + } + + public void setLdapPort(int ldapPort) { + super.setLdapPort(ldapPort); + } + + public void setLdapsPort(int ldapsPort) { + super.setLdapsPort(ldapsPort); + } + + public void setMinaServiceRegistry(ServiceRegistry minaServiceRegistry) { + super.setMinaServiceRegistry(minaServiceRegistry); + } +} \ No newline at end of file diff --git a/activemq-jaas/src/test/java/org/activemq/jaas/ldap/ServerContextFactory.java b/activemq-jaas/src/test/java/org/activemq/jaas/ldap/ServerContextFactory.java new file mode 100644 index 0000000000..265764e5c2 --- /dev/null +++ b/activemq-jaas/src/test/java/org/activemq/jaas/ldap/ServerContextFactory.java @@ -0,0 +1,217 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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. + */ +/** + * + * Copyright 2005 The Apache Software Foundation + * + * Licensed 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.activemq.jaas.ldap; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Properties; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.ldap.Control; +import javax.naming.ldap.InitialLdapContext; +import javax.naming.ldap.LdapContext; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.kerberos.protocol.KerberosProtocolProvider; +import org.apache.kerberos.sam.SamSubsystem; +import org.apache.kerberos.service.KdcConfiguration; +import org.apache.kerberos.store.JndiPrincipalStoreImpl; +import org.apache.kerberos.store.PrincipalStore; +import org.apache.ldap.common.exception.LdapConfigurationException; +import org.apache.ldap.common.name.LdapName; +import org.apache.ldap.common.util.NamespaceTools; +import org.apache.ldap.common.util.PropertiesUtils; +import org.apache.ldap.server.jndi.ContextFactoryService; +import org.apache.ldap.server.jndi.CoreContextFactory; +import org.apache.ldap.server.protocol.LdapProtocolProvider; +import org.apache.mina.common.TransportType; +import org.apache.mina.registry.Service; +import org.apache.mina.registry.ServiceRegistry; + + +/** + * Adds additional bootstrapping for server socket listeners when firing + * up the server. + * + * @version $Rev: 233391 $ $Date: 2005-08-18 16:38:47 -0600 (Thu, 18 Aug 2005) $ + * @see javax.naming.spi.InitialContextFactory + */ +public class ServerContextFactory extends CoreContextFactory { + private static final Log log = LogFactory.getLog(ServerContextFactory.class); + private static Service ldapService; + private static Service kerberosService; + private static ServiceRegistry minaRegistry; + + protected ServiceRegistry getMinaRegistry() { + return minaRegistry; + } + + public void afterShutdown(ContextFactoryService service) { + if (minaRegistry != null) { + if (ldapService != null) { + minaRegistry.unbind(ldapService); + if (log.isInfoEnabled()) { + log.info("Unbind of LDAP Service complete: " + ldapService); + } + ldapService = null; + } + + if (kerberosService != null) { + minaRegistry.unbind(kerberosService); + if (log.isInfoEnabled()) { + log.info("Unbind of KRB5 Service complete: " + kerberosService); + } + kerberosService = null; + } + } + } + + public void afterStartup(ContextFactoryService service) throws NamingException { + ServerStartupConfiguration cfg = + (ServerStartupConfiguration) service.getConfiguration().getStartupConfiguration(); + Hashtable env = service.getConfiguration().getEnvironment(); + + if (cfg.isEnableNetworking()) { + setupRegistry(cfg); + startLdapProtocol(cfg, env); + + if (cfg.isEnableKerberos()) { + startKerberosProtocol(env); + } + } + } + + /** + * Starts up the MINA registry so various protocol providers can be started. + */ + private void setupRegistry(ServerStartupConfiguration cfg) { + minaRegistry = cfg.getMinaServiceRegistry(); + } + + + /** + * Starts the Kerberos protocol provider which is backed by the LDAP store. + * + * @throws NamingException if there are problems starting up the Kerberos provider + */ + private void startKerberosProtocol(Hashtable env) throws NamingException { + /* + * Looks like KdcConfiguration takes properties and we use Hashtable for JNDI + * so I'm copying over the String based properties into a new Properties obj. + */ + Properties props = new Properties(); + Iterator list = env.keySet().iterator(); + while (list.hasNext()) { + String key = (String) list.next(); + + if (env.get(key) instanceof String) { + props.setProperty(key, (String) env.get(key)); + } + } + + // construct the configuration, get the port, create the service, and prepare kdc objects + KdcConfiguration config = new KdcConfiguration(props); + int port = PropertiesUtils.get(env, KdcConfiguration.KERBEROS_PORT_KEY, KdcConfiguration.DEFAULT_KERBEROS_PORT); + Service service = new Service("kerberos", TransportType.DATAGRAM, new InetSocketAddress(port)); + LdapContext ctx = getBaseRealmContext(config, env); + PrincipalStore store = new JndiPrincipalStoreImpl(ctx, new LdapName("ou=Users")); + SamSubsystem.getInstance().setUserContext((DirContext) ctx, "ou=Users"); + + try { + minaRegistry.bind(service, new KerberosProtocolProvider(config, store)); + kerberosService = service; + if (log.isInfoEnabled()) { + log.info("Successful bind of KRB5 Service completed: " + kerberosService); + } + } + catch (IOException e) { + log.error("Could not start the kerberos service on port " + + KdcConfiguration.DEFAULT_KERBEROS_PORT, e); + } + } + + + /** + * Maps a Kerberos Realm name to a position within the DIT. The primary realm of + * the KDC will use this area for configuration and for storing user entries. + * + * @param config the KDC's configuration + * @param env the JNDI environment properties + * @return the base context for the primary realm of the KDC + * @throws NamingException + */ + private LdapContext getBaseRealmContext(KdcConfiguration config, Hashtable env) throws NamingException { + Hashtable cloned = (Hashtable) env.clone(); + String dn = NamespaceTools.inferLdapName(config.getPrimaryRealm()); + cloned.put(Context.PROVIDER_URL, dn); + + if (log.isInfoEnabled()) { + log.info("Getting initial context for realm base at " + dn + " for " + config.getPrimaryRealm()); + } + + return new InitialLdapContext(cloned, new Control[]{}); + } + + + /** + * Starts up the LDAP protocol provider to service LDAP requests + * + * @throws NamingException if there are problems starting the LDAP provider + */ + private void startLdapProtocol(ServerStartupConfiguration cfg, Hashtable env) throws NamingException { + int port = cfg.getLdapPort(); + InetAddress host = cfg.getHost(); + Service service = new Service("ldap", TransportType.SOCKET, new InetSocketAddress(host, port)); + + try { + minaRegistry.bind(service, new LdapProtocolProvider((Hashtable) env.clone())); + ldapService = service; + if (log.isInfoEnabled()) { + log.info("Successful bind of LDAP Service completed: " + ldapService); + } + } + catch (IOException e) { + String msg = "Failed to bind the LDAP protocol service to the service registry: " + service; + LdapConfigurationException lce = new LdapConfigurationException(msg); + lce.setRootCause(e); + log.error(msg, e); + throw lce; + } + } +} \ No newline at end of file diff --git a/activemq-jaas/src/test/java/org/activemq/jaas/ldap/ServerStartupConfiguration.java b/activemq-jaas/src/test/java/org/activemq/jaas/ldap/ServerStartupConfiguration.java new file mode 100644 index 0000000000..08c0e9c17c --- /dev/null +++ b/activemq-jaas/src/test/java/org/activemq/jaas/ldap/ServerStartupConfiguration.java @@ -0,0 +1,126 @@ +/** + * + * Copyright 2005 The Apache Software Foundation + * + * Licensed 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.activemq.jaas.ldap; + +import java.net.InetAddress; + +import org.apache.ldap.server.configuration.ConfigurationException; +import org.apache.ldap.server.configuration.StartupConfiguration; +import org.apache.mina.registry.ServiceRegistry; +import org.apache.mina.registry.SimpleServiceRegistry; + +/** + * A {@link StartupConfiguration} that starts up ApacheDS with network layer support. + * + * @version $Rev: 233391 $ $Date: 2005-08-18 16:38:47 -0600 (Thu, 18 Aug 2005) $ + */ +public class ServerStartupConfiguration extends StartupConfiguration { + private static final long serialVersionUID = -7138616822614155454L; + + private boolean enableNetworking = true; + private ServiceRegistry minaServiceRegistry = new SimpleServiceRegistry(); + private int ldapPort = 389; + private int ldapsPort = 636; + private InetAddress host = null; + private boolean enableKerberos; + + protected ServerStartupConfiguration() { + } + + protected InetAddress getHost() { + return host; + } + + protected void setHost(InetAddress host) { + this.host = host; + } + + /** + * Returns true if networking (LDAP, LDAPS, and Kerberos) is enabled. + */ + public boolean isEnableNetworking() { + return enableNetworking; + } + + /** + * Sets whether to enable networking (LDAP, LDAPS, and Kerberos) or not. + */ + public void setEnableNetworking(boolean enableNetworking) { + this.enableNetworking = enableNetworking; + } + + /** + * Returns true if Kerberos support is enabled. + */ + public boolean isEnableKerberos() { + return enableKerberos; + } + + /** + * Sets whether to enable Kerberos support or not. + */ + protected void setEnableKerberos(boolean enableKerberos) { + this.enableKerberos = enableKerberos; + } + + /** + * Returns LDAP TCP/IP port number to listen to. + */ + public int getLdapPort() { + return ldapPort; + } + + /** + * Sets LDAP TCP/IP port number to listen to. + */ + protected void setLdapPort(int ldapPort) { + this.ldapPort = ldapPort; + } + + /** + * Returns LDAPS TCP/IP port number to listen to. + */ + public int getLdapsPort() { + return ldapsPort; + } + + /** + * Sets LDAPS TCP/IP port number to listen to. + */ + protected void setLdapsPort(int ldapsPort) { + this.ldapsPort = ldapsPort; + } + + /** + * Returns MINA + * {@link ServiceRegistry} that will be used by ApacheDS. + */ + public ServiceRegistry getMinaServiceRegistry() { + return minaServiceRegistry; + } + + /** + * Sets MINA + * {@link ServiceRegistry} that will be used by ApacheDS. + */ + protected void setMinaServiceRegistry(ServiceRegistry minaServiceRegistry) { + if (minaServiceRegistry == null) { + throw new ConfigurationException("MinaServiceRegistry cannot be null"); + } + this.minaServiceRegistry = minaServiceRegistry; + } +} \ No newline at end of file diff --git a/activemq-jaas/src/test/resources/groups.properties b/activemq-jaas/src/test/resources/groups.properties new file mode 100644 index 0000000000..5e4ed2b1c0 --- /dev/null +++ b/activemq-jaas/src/test/resources/groups.properties @@ -0,0 +1,3 @@ +programmers=first +accounting=second +employees=first,second \ No newline at end of file diff --git a/activemq-jaas/src/test/resources/login.config b/activemq-jaas/src/test/resources/login.config new file mode 100644 index 0000000000..5c5d849329 --- /dev/null +++ b/activemq-jaas/src/test/resources/login.config @@ -0,0 +1,19 @@ +PropertiesLogin { + org.activemq.jaas.PropertiesLoginModule required + debug=true + org.activemq.jaas.properties.user="users.properties" + org.activemq.jaas.properties.group="groups.properties"; +}; + +LDAPLogin { + org.activemq.jaas.LDAPLoginModule required + debug=true + initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory + connectionURL="ldap://localhost:9389" + connectionUsername="uid=admin,ou=system" + connectionPassword=secret + connectionProtocol=s + authentication=simple + ; +}; + diff --git a/activemq-jaas/src/test/resources/users.properties b/activemq-jaas/src/test/resources/users.properties new file mode 100644 index 0000000000..9ee2b36829 --- /dev/null +++ b/activemq-jaas/src/test/resources/users.properties @@ -0,0 +1,2 @@ +first=secret +second=password \ No newline at end of file diff --git a/activemq-optional/.cvsignore b/activemq-optional/.cvsignore new file mode 100755 index 0000000000..2273bd4815 --- /dev/null +++ b/activemq-optional/.cvsignore @@ -0,0 +1,9 @@ +target +.classpath +.project +*.iws +*.ipr +*.iml +build.properties + +bin diff --git a/activemq-optional/maven.xml b/activemq-optional/maven.xml new file mode 100755 index 0000000000..3589c7986d --- /dev/null +++ b/activemq-optional/maven.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-optional/project.properties b/activemq-optional/project.properties new file mode 100755 index 0000000000..f9e42ed3f3 --- /dev/null +++ b/activemq-optional/project.properties @@ -0,0 +1,5 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=jar +maven.eclipse.classpath.include=${basedir}/src/conf diff --git a/activemq-optional/project.xml b/activemq-optional/project.xml new file mode 100755 index 0000000000..983f949ca8 --- /dev/null +++ b/activemq-optional/project.xml @@ -0,0 +1,214 @@ + + + + 3 + ${basedir}/../etc/project.xml + + ActiveMQ :: Optional + activemq-optional + ActiveMQ Optional Tools + ActiveMQ Optional Tools + + org.activemq + + + HTTP Message Transport + org.activemq.transport.http + + + Web Service and Apache Axis support + org.activemq.axis + + + + + + + + + + + + activemq + activemq-core + ${pom.currentVersion} + + true + true + + + + + jgroups + jgroups + ${jgroups_version} + + + + activeio + activeio + ${activeio_version} + + + + + geronimo + geronimo-kernel + ${geronimo_kernel_version} + + + geronimo + geronimo-system + ${geronimo_system_version} + + + geronimo-spec + geronimo-spec-j2ee-connector + ${geronimo_spec_j2ee_connector_version} + + + + + springframework + spring + ${spring_version} + http://www.springframework.org + + true + + + + commons-pool + commons-pool + ${commons_pool_version} + + true + + + + commons-collections + commons-collections + ${commons_collections_version} + + true + + + + + + jotm + jotm + 1.5.3 + + + jotm+carol + jotm-carol + 1.5.3 + + + jotm+jrmp+stubs + jotm-jrmp-stubs + 1.5.3 + + + + + aopalliance + aopalliance + 1.0 + + true + + + + + + axis + axis + ${axis_version} + + + + commons-httpclient + commons-httpclient + ${commons_httpclient_version} + + + + + jetty + servlet-api + ${servlet_api_version} + + + jetty + jetty + ${jetty_version} + + + + xstream + xstream + ${xstream_version} + + true + + + + + xmlpull + xmlpull + ${xmlpull_version} + + true + + + + activemq + jmdns + ${jmdns_version} + + + + + activemq + activemq-core-test + ${pom.currentVersion} + + + + + + dev@activemq.codehaus.org + src/main/java + src/test/java + + + + + src/test/resources + + **/*.properties + **/*.xml + + + + + **/*Test.* + + + + + + + + src/main/resources + + **/* + + + + + + + diff --git a/activemq-optional/src/conf/META-INF/services/org.apache.axis.components.jms.JMSVendorAdapter b/activemq-optional/src/conf/META-INF/services/org.apache.axis.components.jms.JMSVendorAdapter new file mode 100755 index 0000000000..c22d6d641b --- /dev/null +++ b/activemq-optional/src/conf/META-INF/services/org.apache.axis.components.jms.JMSVendorAdapter @@ -0,0 +1 @@ +org.activemq.axis.ActiveMQVendorAdapter diff --git a/activemq-optional/src/conf/META-INF/services/org/activemq/transport/http b/activemq-optional/src/conf/META-INF/services/org/activemq/transport/http new file mode 100755 index 0000000000..45f959d66a --- /dev/null +++ b/activemq-optional/src/conf/META-INF/services/org/activemq/transport/http @@ -0,0 +1 @@ +class=org.activemq.transport.http.HttpTransportFactory \ No newline at end of file diff --git a/activemq-optional/src/conf/META-INF/services/org/activemq/transport/jgroups b/activemq-optional/src/conf/META-INF/services/org/activemq/transport/jgroups new file mode 100755 index 0000000000..f9e88cd77b --- /dev/null +++ b/activemq-optional/src/conf/META-INF/services/org/activemq/transport/jgroups @@ -0,0 +1 @@ +class=org.activemq.transport.jgroups.JGroupsTransportChannelFactory \ No newline at end of file diff --git a/activemq-optional/src/conf/META-INF/services/org/activemq/transport/jrms b/activemq-optional/src/conf/META-INF/services/org/activemq/transport/jrms new file mode 100755 index 0000000000..b76ca70877 --- /dev/null +++ b/activemq-optional/src/conf/META-INF/services/org/activemq/transport/jrms @@ -0,0 +1 @@ +class=org.activemq.transport.jrms.JRMSTransportChannelFactory \ No newline at end of file diff --git a/activemq-optional/src/conf/META-INF/services/org/activemq/transport/ssl b/activemq-optional/src/conf/META-INF/services/org/activemq/transport/ssl new file mode 100755 index 0000000000..97a86ab354 --- /dev/null +++ b/activemq-optional/src/conf/META-INF/services/org/activemq/transport/ssl @@ -0,0 +1 @@ +class=org.activemq.transport.ssl.SslTransportChannelFactory \ No newline at end of file diff --git a/activemq-optional/src/conf/META-INF/services/org/activemq/transport/zeroconf b/activemq-optional/src/conf/META-INF/services/org/activemq/transport/zeroconf new file mode 100755 index 0000000000..91144c08fb --- /dev/null +++ b/activemq-optional/src/conf/META-INF/services/org/activemq/transport/zeroconf @@ -0,0 +1 @@ +class=org.activemq.transport.zeroconf.ZeroconfTransportChannelFactory \ No newline at end of file diff --git a/activemq-optional/src/conf/META-INF/services/org/activemq/wireformat/xstream b/activemq-optional/src/conf/META-INF/services/org/activemq/wireformat/xstream new file mode 100755 index 0000000000..3bc2f4657e --- /dev/null +++ b/activemq-optional/src/conf/META-INF/services/org/activemq/wireformat/xstream @@ -0,0 +1 @@ +class=org.activemq.transport.xstream.XStreamWireFormatFactory \ No newline at end of file diff --git a/activemq-optional/src/main/java/org/activemq/axis/ActiveMQVendorAdapter.java b/activemq-optional/src/main/java/org/activemq/axis/ActiveMQVendorAdapter.java new file mode 100755 index 0000000000..2fae9e26e5 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/axis/ActiveMQVendorAdapter.java @@ -0,0 +1,122 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.axis; + +import org.apache.axis.components.jms.BeanVendorAdapter; +import org.apache.axis.transport.jms.JMSURLHelper; +import org.activemq.ActiveMQConnectionFactory; + +import javax.jms.ConnectionFactory; +import javax.jms.QueueConnectionFactory; +import javax.jms.TopicConnectionFactory; +import java.util.HashMap; + +/** + * An adapter for using ActiveMQ inside Apache Axis + * + * @version $Revision$ + */ +public class ActiveMQVendorAdapter extends BeanVendorAdapter { + + protected final static String QCF_CLASS = ActiveMQConnectionFactory.class.getName(); + protected final static String TCF_CLASS = QCF_CLASS; + + + /** + * The URL to connect to the broker + */ + public final static String BROKER_URL = "brokerURL"; + + /** + * Specifies the default user name + */ + public final static String DEFAULT_USERNAME = "defaultUser"; + + /** + * Specifies the default password + */ + public final static String DEFAULT_PASSWORD = "defaultPassword"; + + /** + * Specifies whether the broker is embedded + */ + public final static String EMBEDDED_BROKER = "embeddedBroker"; + + + public QueueConnectionFactory getQueueConnectionFactory(HashMap properties) + throws Exception { + properties = (HashMap) properties.clone(); + properties.put(CONNECTION_FACTORY_CLASS, QCF_CLASS); + return super.getQueueConnectionFactory(properties); + } + + public TopicConnectionFactory getTopicConnectionFactory(HashMap properties) + throws Exception { + properties = (HashMap) properties.clone(); + properties.put(CONNECTION_FACTORY_CLASS, TCF_CLASS); + return super.getTopicConnectionFactory(properties); + } + + + public void addVendorConnectionFactoryProperties(JMSURLHelper jmsUrl, HashMap properties) { + if (jmsUrl.getPropertyValue(BROKER_URL) != null) { + properties.put(BROKER_URL, jmsUrl.getPropertyValue(BROKER_URL)); + } + + if (jmsUrl.getPropertyValue(DEFAULT_USERNAME) != null) { + properties.put(DEFAULT_USERNAME, jmsUrl.getPropertyValue(DEFAULT_USERNAME)); + } + if (jmsUrl.getPropertyValue(DEFAULT_PASSWORD) != null) { + properties.put(DEFAULT_PASSWORD, jmsUrl.getPropertyValue(DEFAULT_PASSWORD)); + } + if (jmsUrl.getPropertyValue(EMBEDDED_BROKER) != null) { + properties.put(EMBEDDED_BROKER, jmsUrl.getPropertyValue(EMBEDDED_BROKER)); + } + } + + public boolean isMatchingConnectionFactory(ConnectionFactory connectionFactory, JMSURLHelper jmsURL, HashMap properties) { + String brokerURL = null; + boolean embeddedBroker = false; + + if (connectionFactory instanceof ActiveMQConnectionFactory) { + ActiveMQConnectionFactory amqConnectionFactory = + (ActiveMQConnectionFactory) connectionFactory; + + // get existing queue connection factory properties + brokerURL = amqConnectionFactory.getBrokerURL(); + embeddedBroker = amqConnectionFactory.isUseEmbeddedBroker(); + } + + // compare broker url + String propertyBrokerURL = (String) properties.get(BROKER_URL); + if (!brokerURL.equals(propertyBrokerURL)) { + return false; + } + + // compare load balancing flag + String tmpEmbeddedBroker = (String) properties.get(EMBEDDED_BROKER); + boolean propertyEmbeddedBroker = false; + if (tmpEmbeddedBroker != null) { + propertyEmbeddedBroker = Boolean.valueOf(tmpEmbeddedBroker).booleanValue(); + } + if (embeddedBroker != propertyEmbeddedBroker) { + return false; + } + return true; + } +} diff --git a/activemq-optional/src/main/java/org/activemq/axis/package.html b/activemq-optional/src/main/java/org/activemq/axis/package.html new file mode 100755 index 0000000000..c5df0e731d --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/axis/package.html @@ -0,0 +1,10 @@ + + + + + + Support classes for integration with Apache Axis + for web service integration + + + diff --git a/activemq-optional/src/main/java/org/activemq/benchmark/BenchmarkSupport.java b/activemq-optional/src/main/java/org/activemq/benchmark/BenchmarkSupport.java new file mode 100755 index 0000000000..ecf63113dd --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/benchmark/BenchmarkSupport.java @@ -0,0 +1,243 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.benchmark; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.util.IdGenerator; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Session; + +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; + +/** + * Abstract base class for some simple benchmark tools + * + * @author James Strachan + * @version $Revision$ + */ +public class BenchmarkSupport { + + protected int connectionCount = 1; + protected int batch = 1000; + protected Destination destination; + protected boolean embeddedBroker = false; + private boolean topic = true; + private boolean durable = false; + + private ActiveMQConnectionFactory factory; + private String url; + protected String[] subjects; + private long time = System.currentTimeMillis(); + private int counter; + private List resources = new ArrayList(); + private NumberFormat formatter = NumberFormat.getInstance(); + private AtomicInteger connectionCounter = new AtomicInteger(0); + private IdGenerator idGenerator = new IdGenerator(); + + public BenchmarkSupport() { + } + + public void start() { + System.out.println("Using: " + connectionCount + " connection(s)"); + subjects = new String[connectionCount]; + for (int i = 0; i < connectionCount; i++) { + subjects[i] = "BENCHMARK.FEED" + i; + } + if (useTimerLoop()) { + Thread timer = new Thread() { + public void run() { + timerLoop(); + } + }; + timer.start(); + } + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public boolean isTopic() { + return topic; + } + + public void setTopic(boolean topic) { + this.topic = topic; + } + + public ActiveMQConnectionFactory getFactory() { + return factory; + } + + public void setFactory(ActiveMQConnectionFactory factory) { + this.factory = factory; + } + + public void setSubject(String subject) { + connectionCount = 1; + subjects = new String[]{subject}; + } + + public boolean isDurable() { + return durable; + } + + public void setDurable(boolean durable) { + this.durable = durable; + } + + public boolean isEmbeddedBroker() { + return embeddedBroker; + } + + public void setEmbeddedBroker(boolean embeddedBroker) { + this.embeddedBroker = embeddedBroker; + } + + public int getConnectionCount() { + return connectionCount; + } + + public void setConnectionCount(int connectionCount) { + this.connectionCount = connectionCount; + } + + protected Session createSession() throws JMSException { + if (factory == null) { + factory = createFactory(); + } + Connection connection = factory.createConnection(); + int value = connectionCounter.incrementAndGet(); + System.out.println("Created connection: " + value + " = " + connection); + if (durable) { + connection.setClientID(idGenerator.generateId()); + } + addResource(connection); + connection.start(); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + addResource(session); + return session; + } + + protected ActiveMQConnectionFactory createFactory() { + ActiveMQConnectionFactory answer = new ActiveMQConnectionFactory(getUrl()); + if (embeddedBroker) { + answer.setUseEmbeddedBroker(true); + } + return answer; + } + + protected synchronized void count(int count) { + counter += count; + /* + if (counter > batch) { + counter = 0; + long current = System.currentTimeMillis(); + double end = current - time; + end /= 1000; + time = current; + + System.out.println("Processed " + batch + " messages in " + end + " (secs)"); + } + */ + } + + protected synchronized int resetCount() { + int answer = counter; + counter = 0; + return answer; + } + + + protected void timerLoop() { + int times = 0; + int total = 0; + int dumpVmStatsFrequency = 10; + Runtime runtime = Runtime.getRuntime(); + + while (true) { + try { + Thread.sleep(1000); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + int processed = resetCount(); + double average = 0; + if (processed > 0) { + total += processed; + times++; + } + if (times > 0) { + average = total / times; + } + + long oldtime = time; + time = System.currentTimeMillis(); + + double diff = time - oldtime; + + System.out.println(getClass().getName() + " Processed: " + processed + " messages this second. Average: " + average); + + if ((times % dumpVmStatsFrequency) == 0 && times != 0) { + System.out.println("Used memory: " + asMemoryString(runtime.totalMemory() - runtime.freeMemory()) + + " Free memory: " + asMemoryString(runtime.freeMemory()) + + " Total memory: " + asMemoryString(runtime.totalMemory()) + + " Max memory: " + asMemoryString(runtime.maxMemory())); + } + + } + } + + protected String asMemoryString(long value) { + return formatter.format(value / 1024) + " K"; + } + + protected boolean useTimerLoop() { + return true; + } + + protected Destination createDestination(Session session, String subject) throws JMSException { + if (topic) { + return session.createTopic(subject); + } + else { + return session.createQueue(subject); + } + } + + protected void addResource(Object resource) { + resources.add(resource); + } + + protected static boolean parseBoolean(String text) { + return text.equalsIgnoreCase("true"); + } +} diff --git a/activemq-optional/src/main/java/org/activemq/benchmark/Consumer.java b/activemq-optional/src/main/java/org/activemq/benchmark/Consumer.java new file mode 100755 index 0000000000..51e40f449f --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/benchmark/Consumer.java @@ -0,0 +1,112 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.benchmark; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; + +/** + * @author James Strachan + * @version $Revision$ + */ +public class Consumer extends BenchmarkSupport implements MessageListener { + + public static void main(String[] args) { + Consumer tool = new Consumer(); + if (args.length > 0) { + tool.setUrl(args[0]); + } + if (args.length > 1) { + tool.setTopic(parseBoolean(args[1])); + } + if (args.length > 2) { + tool.setSubject(args[2]); + } + if (args.length > 3) { + tool.setDurable(parseBoolean(args[3])); + } + if (args.length > 4) { + tool.setConnectionCount(Integer.parseInt(args[4])); + } + + try { + tool.run(); + } + catch (Exception e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + + public Consumer() { + } + + public void run() throws JMSException { + start(); + subscribe(); + } + + protected void subscribe() throws JMSException { + for (int i = 0; i < subjects.length; i++) { + subscribe(subjects[i]); + } + } + + protected void subscribe(String subject) throws JMSException { + Session session = createSession(); + + Destination destination = createDestination(session, subject); + + System.out.println("Consuming on : " + destination + " of type: " + destination.getClass().getName()); + + MessageConsumer consumer = null; + if (isDurable() && isTopic()) { + consumer = session.createDurableSubscriber((Topic) destination, getClass().getName()); + } + else { + consumer = session.createConsumer(destination); + } + consumer.setMessageListener(this); + addResource(consumer); + } + + public void onMessage(Message message) { + try { + TextMessage textMessage = (TextMessage) message; + + // lets force the content to be deserialized + String text = textMessage.getText(); + count(1); + + // lets count the messages + + //message.acknowledge(); + } + catch (JMSException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +} diff --git a/activemq-optional/src/main/java/org/activemq/benchmark/Producer.java b/activemq-optional/src/main/java/org/activemq/benchmark/Producer.java new file mode 100755 index 0000000000..37c81f3e23 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/benchmark/Producer.java @@ -0,0 +1,183 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.benchmark; + +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.Session; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +/** + * @author James Strachan + * @version $Revision$ + */ +public class Producer extends BenchmarkSupport { + + int loops = -1; + int loopSize = 1000; + private int messageSize = 1000; + + public static void main(String[] args) { + Producer tool = new Producer(); + if (args.length > 0) { + tool.setUrl(args[0]); + } + if (args.length > 1) { + tool.setTopic(parseBoolean(args[1])); + } + if (args.length > 2) { + tool.setSubject(args[2]); + } + if (args.length > 3) { + tool.setDurable(parseBoolean(args[3])); + } + if (args.length > 4) { + tool.setMessageSize(Integer.parseInt(args[4])); + } + if (args.length > 5) { + tool.setConnectionCount(Integer.parseInt(args[5])); + } + try { + tool.run(); + } + catch (Exception e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + + public Producer() { + } + + public void run() throws Exception { + start(); + publish(); + } + + // Properties + //------------------------------------------------------------------------- + public int getMessageSize() { + return messageSize; + } + + public void setMessageSize(int messageSize) { + this.messageSize = messageSize; + } + + public int getLoopSize() { + return loopSize; + } + + public void setLoopSize(int loopSize) { + this.loopSize = loopSize; + } + + // Implementation methods + //------------------------------------------------------------------------- + + protected void publish() throws Exception { + final String text = getMessage(); + + System.out.println("Publishing to: " + subjects.length + " subject(s)"); + + for (int i = 0; i < subjects.length; i++) { + final String subject = subjects[i]; + Thread thread = new Thread() { + public void run() { + try { + publish(text, subject); + } + catch (JMSException e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + }; + thread.start(); + } + + } + + protected String getMessage() { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < messageSize; i++) { + char ch = 'X'; + buffer.append(ch); + } + return buffer.toString(); + } + + protected void publish(String text, String subject) throws JMSException { + Session session = createSession(); + + Destination destination = createDestination(session, subject); + + MessageProducer publisher = session.createProducer(destination); + if (isDurable()) { + publisher.setDeliveryMode(DeliveryMode.PERSISTENT); + } + else { + publisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + } + + System.out.println("Starting publisher on : " + destination + " of type: " + destination.getClass().getName()); + System.out.println("Message length: " + text.length()); + + if (loops <= 0) { + while (true) { + publishLoop(session, publisher, text); + } + } + else { + for (int i = 0; i < loops; i++) { + publishLoop(session, publisher, text); + } + } + } + + protected void publishLoop(Session session, MessageProducer publisher, String text) throws JMSException { + for (int i = 0; i < loopSize; i++) { + Message message = session.createTextMessage(text); + + publisher.send(message); + count(1); + } + } + + protected String loadFile(String file) throws IOException { + System.out.println("Loading file: " + file); + + StringBuffer buffer = new StringBuffer(); + BufferedReader in = new BufferedReader(new FileReader(file)); + while (true) { + String line = in.readLine(); + if (line == null) { + break; + } + buffer.append(line); + buffer.append(File.separator); + } + return buffer.toString(); + } +} diff --git a/activemq-optional/src/main/java/org/activemq/benchmark/ProducerConsumer.java b/activemq-optional/src/main/java/org/activemq/benchmark/ProducerConsumer.java new file mode 100755 index 0000000000..1b5374f903 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/benchmark/ProducerConsumer.java @@ -0,0 +1,83 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.benchmark; + + +/** + * @author James Strachan + * @version $Revision$ + */ +public class ProducerConsumer extends Producer { + + private Consumer consumer = new Consumer(); + + public static void main(String[] args) { + ProducerConsumer tool = new ProducerConsumer(); + if (args.length > 0) { + tool.setUrl(args[0]); + } + if (args.length > 1) { + tool.setTopic(parseBoolean(args[1])); + } + if (args.length > 2) { + tool.setSubject(args[2]); + } + if (args.length > 3) { + tool.setDurable(Boolean.getBoolean(args[3])); + } + if (args.length > 4) { + tool.setConnectionCount(Integer.parseInt(args[4])); + } + try { + tool.run(); + } + catch (Exception e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + + public ProducerConsumer() { + } + + public void run() throws Exception { + consumer.start(); + consumer.subscribe(); + start(); + publish(); + } + + public void setTopic(boolean topic) { + super.setTopic(topic); + consumer.setTopic(topic); + } + + public void setSubject(String subject) { + super.setSubject(subject); + consumer.setSubject(subject); + } + + public void setUrl(String url) { + super.setUrl(url); + consumer.setUrl(url); + } + + protected boolean useTimerLoop() { + return false; + } +} diff --git a/activemq-optional/src/main/java/org/activemq/tool/AcidTestTool.java b/activemq-optional/src/main/java/org/activemq/tool/AcidTestTool.java new file mode 100755 index 0000000000..7328200711 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/tool/AcidTestTool.java @@ -0,0 +1,365 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.tool; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.command.ActiveMQQueue; + +import javax.jms.BytesMessage; +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +import junit.framework.TestCase; + +/** + * @version $Revision$ + */ +public class AcidTestTool extends TestCase { + + private Random random = new Random(); + private byte data[]; + private int workerCount = 10; + private PrintWriter statWriter; + + // Worker configuration. + protected int recordSize = 1024; + protected int batchSize = 5; + protected int workerThinkTime = 500; + AtomicBoolean ignoreJMSErrors = new AtomicBoolean(false); + + protected Destination target; + private ActiveMQConnectionFactory factory; + private Connection connection; + + AtomicInteger publishedBatches = new AtomicInteger(0); + AtomicInteger consumedBatches = new AtomicInteger(0); + + List errors = Collections.synchronizedList(new ArrayList()); + + private interface Worker extends Runnable { + public boolean waitForExit(long i) throws InterruptedException; + } + + private final class ProducerWorker implements Worker { + + Session session; + private MessageProducer producer; + private BytesMessage message; + CountDownLatch doneLatch = new CountDownLatch(1); + private final String workerId; + + ProducerWorker(Session session, String workerId) throws JMSException { + this.session = session; + this.workerId = workerId; + producer = session.createProducer(target); + producer.setDeliveryMode(DeliveryMode.PERSISTENT); + message = session.createBytesMessage(); + message.setStringProperty("workerId", workerId); + message.writeBytes(data); + } + + public void run() { + try { + for( int batchId=0; true; batchId++ ) { +// System.out.println("Sending batch: "+workerId+" "+batchId); + for( int msgId=0; msgId < batchSize; msgId++ ) { + // Sleep some random amount of time less than workerThinkTime + try { + Thread.sleep(random.nextInt(workerThinkTime)); + } catch (InterruptedException e1) { + return; + } + + message.setIntProperty("batch-id",batchId); + message.setIntProperty("msg-id",msgId); + + + producer.send(message); + } + session.commit(); + publishedBatches.incrementAndGet(); +// System.out.println("Commited send batch: "+workerId+" "+batchId); + } + } catch (JMSException e) { + if( !ignoreJMSErrors.get() ) { + e.printStackTrace(); + errors.add(e); + } + return; + } catch (Throwable e) { + e.printStackTrace(); + errors.add(e); + return; + } finally { + System.out.println("Producer exiting."); + doneLatch.countDown(); + } + } + + public boolean waitForExit(long i) throws InterruptedException { + return doneLatch.await(i, TimeUnit.MILLISECONDS); + } + } + + private final class ConsumerWorker implements Worker { + + Session session; + private MessageConsumer consumer; + private final long timeout; + CountDownLatch doneLatch = new CountDownLatch(1); + private final String workerId; + + ConsumerWorker(Session session, String workerId, long timeout) throws JMSException { + this.session = session; + this.workerId = workerId; + this.timeout = timeout; + consumer = session.createConsumer(target,"workerId='"+workerId+"'"); + } + + public void run() { + + try { + int batchId=0; + while( true ) { + for( int msgId=0; msgId < batchSize; msgId++ ) { + + // Sleep some random amount of time less than workerThinkTime + try { + Thread.sleep(random.nextInt(workerThinkTime)); + } catch (InterruptedException e1) { + return; + } + + Message message = consumer.receive(timeout); + if( msgId > 0 ) { + assertNotNull(message); + assertEquals(message.getIntProperty("batch-id"), batchId); + assertEquals(message.getIntProperty("msg-id"), msgId); + } else { + if( message==null ) { + System.out.println("At end of batch an don't have a next batch to process. done."); + return; + } + assertEquals(msgId, message.getIntProperty("msg-id") ); + batchId = message.getIntProperty("batch-id"); +// System.out.println("Receiving batch: "+workerId+" "+batchId); + } + + } + session.commit(); + consumedBatches.incrementAndGet(); +// System.out.println("Commited receive batch: "+workerId+" "+batchId); + } + } catch (JMSException e) { + if( !ignoreJMSErrors.get() ) { + e.printStackTrace(); + errors.add(e); + } + return; + } catch (Throwable e) { + e.printStackTrace(); + errors.add(e); + return; + } finally { + System.out.println("Consumer exiting."); + doneLatch.countDown(); + } + } + + public boolean waitForExit(long i) throws InterruptedException { + return doneLatch.await(i, TimeUnit.MILLISECONDS); + } + } + + /** + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + factory = new ActiveMQConnectionFactory("tcp://localhost:61616"); + this.target = new ActiveMQQueue(getClass().getName()); + } + + protected void tearDown() throws Exception { + if( connection!=null ) { + try { connection.close(); } catch (Throwable ignore) {} + connection = null; + } + } + + /** + * @throws InterruptedException + * @throws JMSException + * @throws JMSException + * + */ + private void reconnect() throws InterruptedException, JMSException { + if( connection!=null ) { + try { connection.close(); } catch (Throwable ignore) {} + connection = null; + } + + long reconnectDelay=1000; + JMSException lastError=null; + + while( connection == null) { + if( reconnectDelay > 1000*10 ) { + reconnectDelay = 1000*10; + } + try { + connection = factory.createConnection(); + connection.start(); + } catch (JMSException e) { + lastError = e; + Thread.sleep(reconnectDelay); + reconnectDelay*=2; + } + } + } + + /** + * @throws Throwable + * @throws IOException + * + */ + public void testAcidTransactions() throws Throwable { + + System.out.println("Client threads write records using: Record Size: " + recordSize + ", Batch Size: " + + batchSize + ", Worker Think Time: " + workerThinkTime); + + // Create the record and fill it with some values. + data = new byte[recordSize]; + for (int i = 0; i < data.length; i++) { + data[i] = (byte) i; + } + + System.out.println("=============================================="); + System.out.println("===> Start the server now."); + System.out.println("=============================================="); + reconnect(); + + System.out.println("Starting " + workerCount + " Workers..."); + ArrayList workers = new ArrayList(); + for( int i=0; i< workerCount; i++ ){ + String workerId = "worker-"+i; + + Worker w = new ConsumerWorker(connection.createSession(true,Session.SESSION_TRANSACTED), workerId, 1000*5); + workers.add(w); + new Thread(w,"Consumer:"+workerId).start(); + + w = new ProducerWorker(connection.createSession(true,Session.SESSION_TRANSACTED), workerId); + workers.add(w); + new Thread(w,"Producer:"+workerId).start(); + } + + System.out.println("Waiting for "+(workerCount*10)+" batches to be delivered."); + + // + // Wait for about 5 batches of messages per worker to be consumed before restart. + // + while( publishedBatches.get() < workerCount*5) { + System.out.println("Stats: Produced Batches: "+this.publishedBatches.get()+", Consumed Batches: "+this.consumedBatches.get()); + Thread.sleep(1000); + } + + System.out.println("=============================================="); + System.out.println("===> Server is under load now. Kill it!"); + System.out.println("=============================================="); + ignoreJMSErrors.set(true); + + // Wait for all the workers to finish. + System.out.println("Waiting for all workers to exit due to server shutdown."); + for (Iterator iter = workers.iterator(); iter.hasNext();) { + Worker worker = (Worker) iter.next(); + while( !worker.waitForExit(1000) ) { + System.out.println("=============================================="); + System.out.println("===> Server is under load now. Kill it!"); + System.out.println("=============================================="); + System.out.println("Stats: Produced Batches: "+this.publishedBatches.get()+", Consumed Batches: "+this.consumedBatches.get()); + } + } + workers.clear(); + + // No errors should have occured so far. + if( errors.size()>0 ) + throw (Throwable) errors.get(0); + + System.out.println("=============================================="); + System.out.println("===> Start the server now."); + System.out.println("=============================================="); + reconnect(); + + System.out.println("Restarted."); + + // Validate the all transactions were commited as a uow. Looking for partial commits. + for( int i=0; i< workerCount; i++ ){ + String workerId = "worker-"+i; + Worker w = new ConsumerWorker(connection.createSession(true,Session.SESSION_TRANSACTED), workerId, 5*1000); + workers.add(w); + new Thread(w, "Consumer:"+workerId).start(); + } + + System.out.println("Waiting for restarted consumers to finish consuming all messages.."); + for (Iterator iter = workers.iterator(); iter.hasNext();) { + Worker worker = (Worker) iter.next(); + while( !worker.waitForExit(1000*5) ) { + System.out.println("Waiting for restarted consumers to finish consuming all messages.."); + System.out.println("Stats: Produced Batches: "+this.publishedBatches.get()+", Consumed Batches: "+this.consumedBatches.get()); + } + } + workers.clear(); + + System.out.println("Workers finished.."); + System.out.println("Stats: Produced Batches: "+this.publishedBatches.get()+", Consumed Batches: "+this.consumedBatches.get()); + + if( errors.size()>0 ) + throw (Throwable) errors.get(0); + + } + + public static void main(String[] args) { + try { + AcidTestTool tool = new AcidTestTool(); + tool.setUp(); + tool.testAcidTransactions(); + tool.tearDown(); + } catch (Throwable e) { + System.out.println("Test Failed: "+e.getMessage()); + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/activemq-optional/src/main/java/org/activemq/tool/ConsumerTool.java b/activemq-optional/src/main/java/org/activemq/tool/ConsumerTool.java new file mode 100755 index 0000000000..38cbf73a31 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/tool/ConsumerTool.java @@ -0,0 +1,141 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.tool; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; +import java.io.IOException; + +/** + * A simple tool for consuming messages + * + * @version $Revision$ + */ +public class ConsumerTool extends ToolSupport implements MessageListener { + + protected int count = 0; + protected int dumpCount = 10; + protected boolean verbose = true; + protected int maxiumMessages = 0; + private boolean pauseBeforeShutdown; + + + public static void main(String[] args) { + ConsumerTool tool = new ConsumerTool(); + if (args.length > 0) { + tool.url = args[0]; + } + if (args.length > 1) { + tool.topic = args[1].equalsIgnoreCase("true"); + } + if (args.length > 2) { + tool.subject = args[2]; + } + if (args.length > 3) { + tool.durable = args[3].equalsIgnoreCase("true"); + } + if (args.length > 4) { + tool.maxiumMessages = Integer.parseInt(args[4]); + } + tool.run(); + } + + public void run() { + try { + System.out.println("Connecting to URL: " + url); + System.out.println("Consuming " + (topic ? "topic" : "queue") + ": " + subject); + System.out.println("Using " + (durable ? "durable" : "non-durable") + " subscription"); + + Connection connection = createConnection(); + Session session = createSession(connection); + MessageConsumer consumer = null; + if (durable && topic) { + consumer = session.createDurableSubscriber((Topic) destination, consumerName); + } + else { + consumer = session.createConsumer(destination); + } + if (maxiumMessages <= 0) { + consumer.setMessageListener(this); + } + connection.start(); + + if (maxiumMessages > 0) { + consumeMessagesAndClose(connection, session, consumer); + } + } + catch (Exception e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + + public void onMessage(Message message) { + try { + if (message instanceof TextMessage) { + TextMessage txtMsg = (TextMessage) message; + if (verbose) { + + String msg = txtMsg.getText(); + if( msg.length() > 50 ) + msg = msg.substring(0, 50)+"..."; + + System.out.println("Received: " + msg); + } + } + else { + if (verbose) { + System.out.println("Received: " + message); + } + } + /* + if (++count % dumpCount == 0) { + dumpStats(connection); + } + */ + } + catch (JMSException e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + + + protected void consumeMessagesAndClose(Connection connection, Session session, MessageConsumer consumer) throws JMSException, IOException { + System.out.println("We are about to wait until we consume: " + maxiumMessages + " message(s) then we will shutdown"); + + for (int i = 0; i < maxiumMessages; i++) { + Message message = consumer.receive(); + onMessage(message); + } + System.out.println("Closing connection"); + consumer.close(); + session.close(); + connection.close(); + if (pauseBeforeShutdown) { + System.out.println("Press return to shut down"); + System.in.read(); + } + } +} \ No newline at end of file diff --git a/activemq-optional/src/main/java/org/activemq/tool/JndiProducerTool.java b/activemq-optional/src/main/java/org/activemq/tool/JndiProducerTool.java new file mode 100755 index 0000000000..c202c810c7 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/tool/JndiProducerTool.java @@ -0,0 +1,44 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.tool; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Queue; +import javax.naming.InitialContext; + +/** + * @version $Revision$ + */ +public class JndiProducerTool extends ProducerTool { + + public static void main(String[] args) { + runTool(args, new JndiProducerTool()); + } + + protected Connection createConnection() throws Exception { + InitialContext jndiContext = new InitialContext(); + + ConnectionFactory queueConnectionFactory = (ConnectionFactory) jndiContext.lookup("ConnectionFactory"); + Connection connection = queueConnectionFactory.createConnection(); + destination = (Queue) jndiContext.lookup(subject); + return connection; + + } + +} diff --git a/activemq-optional/src/main/java/org/activemq/tool/ProducerTool.java b/activemq-optional/src/main/java/org/activemq/tool/ProducerTool.java new file mode 100755 index 0000000000..99e792c3fd --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/tool/ProducerTool.java @@ -0,0 +1,134 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.tool; + +import java.util.Date; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +/** + * A simple tool for publishing messages + * + * @version $Revision$ + */ +public class ProducerTool extends ToolSupport { + + protected int messageCount = 10; + protected long sleepTime = 0L; + protected boolean verbose = true; + protected int messageSize = 255; + + public static void main(String[] args) { + runTool(args, new ProducerTool()); + } + + protected static void runTool(String[] args, ProducerTool tool) { + if (args.length > 0) { + tool.url = args[0]; + } + if (args.length > 1) { + tool.topic = args[1].equalsIgnoreCase("true"); + } + if (args.length > 2) { + tool.subject = args[2]; + } + if (args.length > 3) { + tool.durable = args[3].equalsIgnoreCase("true"); + } + if (args.length > 4) { + tool.messageCount = Integer.parseInt(args[4]); + } + if (args.length > 5) { + tool.messageSize = Integer.parseInt(args[5]); + } + tool.run(); + } + + public void run() { + try { + System.out.println("Connecting to URL: " + url); + System.out.println("Publishing a Message with size "+messageSize+" to " + (topic ? "topic" : "queue") + ": " + subject); + System.out.println("Using " + (durable ? "durable" : "non-durable") + " publishing"); + + Connection connection = createConnection(); + Session session = createSession(connection); + MessageProducer producer = createProducer(session); + //connection.start(); + + sendLoop(session, producer); + + System.out.println("Done."); + close(connection, session); + } + catch (Exception e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + + protected MessageProducer createProducer(Session session) throws JMSException { + MessageProducer producer = session.createProducer(destination); + if (durable) { + producer.setDeliveryMode(DeliveryMode.PERSISTENT); + } + else { + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + } + return producer; + } + + protected void sendLoop(Session session, MessageProducer producer) throws Exception { + + for (int i = 0; i < messageCount; i++) { + + + TextMessage message = session.createTextMessage(createMessageText(i)); + + if (verbose) { + String msg = message.getText(); + if( msg.length() > 50 ) + msg = msg.substring(0, 50)+"..."; + System.out.println("Sending message: " + msg); + } + + producer.send(message); + Thread.sleep(sleepTime); + } + producer.send(session.createMessage()); + } + + /** + * @param i + * @return + */ + private String createMessageText(int index) { + StringBuffer buffer = new StringBuffer(messageSize); + buffer.append("Message: " + index + " sent at: " + new Date()); + if( buffer.length() > messageSize ) { + return buffer.substring(0, messageSize); + } + for( int i=buffer.length(); i < messageSize; i++) + buffer.append(' '); + return buffer.toString(); + } +} \ No newline at end of file diff --git a/activemq-optional/src/main/java/org/activemq/tool/ToolSupport.java b/activemq-optional/src/main/java/org/activemq/tool/ToolSupport.java new file mode 100755 index 0000000000..e5136434f6 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/tool/ToolSupport.java @@ -0,0 +1,85 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.tool; + +import org.activemq.ActiveMQConnection; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.util.IndentPrinter; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Session; + +/** + * Abstract base class useful for implementation inheritence + * + * @version $Revision$ + */ +public class ToolSupport { + + + protected Destination destination; + protected String subject = "TOOL.DEFAULT"; + protected boolean topic = true; + protected String user = ActiveMQConnection.DEFAULT_USER; + protected String pwd = ActiveMQConnection.DEFAULT_PASSWORD; + protected String url = ActiveMQConnection.DEFAULT_BROKER_URL; + protected boolean transacted = false; + protected boolean durable = false; + protected String clientID = getClass().getName(); + protected int ackMode = Session.AUTO_ACKNOWLEDGE; + protected String consumerName = "James"; + + + protected Session createSession(Connection connection) throws Exception { + if (durable) { + connection.setClientID(clientID); + } + Session session = connection.createSession(transacted, ackMode); + if (topic) { + destination = session.createTopic(subject); + } + else { + destination = session.createQueue(subject); + } + return session; + } + + protected Connection createConnection() throws JMSException, Exception { + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user, pwd, url); + return connectionFactory.createConnection(); + } + + protected void close(Connection connection, Session session) throws JMSException { + // lets dump the stats + dumpStats(connection); + + if (session != null) { + session.close(); + } + if (connection != null) { + connection.close(); + } + } + + protected void dumpStats(Connection connection) { + ActiveMQConnection c = (ActiveMQConnection) connection; + c.getConnectionStats().dump(new IndentPrinter()); + } +} \ No newline at end of file diff --git a/activemq-optional/src/main/java/org/activemq/tool/WebServer.java b/activemq-optional/src/main/java/org/activemq/tool/WebServer.java new file mode 100755 index 0000000000..ed7589a482 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/tool/WebServer.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.tool; +import org.mortbay.jetty.Connector; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.bio.SocketConnector; +import org.mortbay.jetty.webapp.WebAppContext; + +/** + * @version $Revision$ + */ +public class WebServer { + public static final int PORT = 8080; + //public static final String WEBAPP_DIR = "target/activemq"; + public static final String WEBAPP_DIR = "src/webapp"; + public static final String WEBAPP_CTX = "/"; + + public static void main(String[] args) throws Exception { + Server server = new Server(); + Connector context = new SocketConnector(); + context.setServer(server); + context.setPort(PORT); + + String webappDir = WEBAPP_DIR; + if( args.length > 0 ) { + webappDir = args[0]; + } + + WebAppContext webapp = new WebAppContext(); + webapp.setServer(server); + webapp.setContextPath(WEBAPP_CTX); + webapp.setResourceBase(webappDir); + + server.setHandler(webapp); + + server.setConnectors(new Connector[]{context}); + server.start(); + + } +} diff --git a/activemq-optional/src/main/java/org/activemq/transport/http/BlockingQueueTransport.java b/activemq-optional/src/main/java/org/activemq/transport/http/BlockingQueueTransport.java new file mode 100755 index 0000000000..50fa0f5ff4 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/http/BlockingQueueTransport.java @@ -0,0 +1,57 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.http; + +import edu.emory.mathcs.backport.java.util.Queue; +import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue; + +import org.activemq.command.Command; +import org.activemq.transport.TransportSupport; + +import javax.jms.JMSException; + +import java.io.IOException; + +/** + * A server side HTTP based TransportChannel which processes incoming packets + * and adds outgoing packets onto a {@link Queue} so that they can be dispatched + * by the HTTP GET requests from the client. + * + * @version $Revision$ + */ +public class BlockingQueueTransport extends TransportSupport { + private BlockingQueue queue; + + public BlockingQueueTransport(BlockingQueue channel) { + this.queue = channel; + } + + public BlockingQueue getQueue() { + return queue; + } + + public void start() throws JMSException { + } + + public void oneway(Command command) throws IOException { + queue.add(command); + } + + public void stop() throws Exception { + } +} diff --git a/activemq-optional/src/main/java/org/activemq/transport/http/HttpClientTransport.java b/activemq-optional/src/main/java/org/activemq/transport/http/HttpClientTransport.java new file mode 100755 index 0000000000..a7b30678e5 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/http/HttpClientTransport.java @@ -0,0 +1,177 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.http; + +import org.activemq.command.Command; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.Response; +import org.activemq.transport.FutureResponse; +import org.activemq.transport.util.TextWireFormat; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.ServiceStopper; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.DataInputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; + +/** + * A HTTP {@link org.activemq.transport.TransportChannel} which uses the commons-httpclient + * library + * + * @version $Revision$ + */ +public class HttpClientTransport extends HttpTransportSupport { + private static final Log log = LogFactory.getLog(HttpClientTransport.class); + + private HttpClient sendHttpClient; + private HttpClient receiveHttpClient; + private String clientID; + private String sessionID; + + + public HttpClientTransport(TextWireFormat wireFormat, URI remoteUrl) { + super(wireFormat, remoteUrl); + } + + public FutureResponse asyncRequest(Command command) throws IOException { + return null; + } + + public void oneway(Command command) throws IOException { + if (command.getDataStructureType()==ConnectionInfo.DATA_STRUCTURE_TYPE) + clientID=((ConnectionInfo)command).getClientId(); + + PostMethod httpMethod = new PostMethod(getRemoteUrl().toString()); + configureMethod(httpMethod); + httpMethod.setRequestBody(getTextWireFormat().toString(command)); + try { + HttpClient client = getSendHttpClient(); + int answer = client.executeMethod(httpMethod); + if (answer != HttpStatus.SC_OK) { + throw new IOException("Failed to post command: " + command + " as response was: " + answer); + } + checkSession(httpMethod); + } + catch (IOException e) { + throw IOExceptionSupport.create("Could not post command: " + command + " due to: " + e, e); + } + } + + public Response request(Command command) throws IOException { + return null; + } + + public void run() { + log.trace("HTTP GET consumer thread starting: " + this); + HttpClient httpClient = getReceiveHttpClient(); + URI remoteUrl = getRemoteUrl(); + while (!isClosed()) { + + GetMethod httpMethod = new GetMethod(remoteUrl.toString()); + configureMethod(httpMethod); + + try { + int answer = httpClient.executeMethod(httpMethod); + if (answer != HttpStatus.SC_OK) { + if (answer == HttpStatus.SC_REQUEST_TIMEOUT) { + log.info("GET timed out"); + } + else { + log.warn("Failed to perform GET on: " + remoteUrl + " as response was: " + answer); + } + } + else { + checkSession(httpMethod); + Command command = getTextWireFormat().readCommand(new DataInputStream(httpMethod.getResponseBodyAsStream())); + if (command == null) { + log.warn("Received null command from url: " + remoteUrl); + } + else { + doConsume(command); + } + } + } + catch (IOException e) { + log.warn("Failed to perform GET on: " + remoteUrl + " due to: " + e, e); + } + } + } + + + // Properties + // ------------------------------------------------------------------------- + public HttpClient getSendHttpClient() { + if (sendHttpClient == null) { + sendHttpClient = createHttpClient(); + } + return sendHttpClient; + } + + public void setSendHttpClient(HttpClient sendHttpClient) { + this.sendHttpClient = sendHttpClient; + } + + public HttpClient getReceiveHttpClient() { + if (receiveHttpClient == null) { + receiveHttpClient = createHttpClient(); + } + return receiveHttpClient; + } + + public void setReceiveHttpClient(HttpClient receiveHttpClient) { + this.receiveHttpClient = receiveHttpClient; + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected void doStop(ServiceStopper stopper) throws Exception { + // TODO + } + + protected HttpClient createHttpClient() { + return new HttpClient(); + } + + protected void configureMethod(HttpMethod method) { + if (sessionID!=null) { + method.addRequestHeader("Cookie", "JSESSIONID="+sessionID); + } + else if (clientID != null) { + method.setRequestHeader("clientID", clientID); + } + } + + protected void checkSession(HttpMethod client) { + String set_cookie=client.getRequestHeader("Set-Cookie").getValue(); + + if (set_cookie!=null && set_cookie.startsWith("JSESSIONID=")) { + String[] bits=set_cookie.split("[=;]"); + sessionID=bits[1]; + } + } + +} diff --git a/activemq-optional/src/main/java/org/activemq/transport/http/HttpEmbeddedTunnelServlet.java b/activemq-optional/src/main/java/org/activemq/transport/http/HttpEmbeddedTunnelServlet.java new file mode 100755 index 0000000000..9e1ad7526a --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/http/HttpEmbeddedTunnelServlet.java @@ -0,0 +1,77 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.http; + +import org.activemq.broker.BrokerService; +import org.activemq.transport.TransportAcceptListener; + +import javax.servlet.ServletException; + +import java.net.URI; + +/** + * This servlet embeds an ActiveMQ broker inside a servlet engine which is ideal + * for deploying ActiveMQ inside a WAR and using this servlet as a HTTP tunnel. + * + * @version $Revision$ + */ +public class HttpEmbeddedTunnelServlet extends HttpTunnelServlet { + private static final long serialVersionUID = -3705734740251302361L; + + private BrokerService broker; + private HttpTransportServer transportConnector; + + public synchronized void init() throws ServletException { + // lets initialize the ActiveMQ broker + try { + if (broker == null) { + broker = createBroker(); + } + broker.start(); + } + catch (Exception e) { + throw new ServletException("Failed to start embedded broker: " + e, e); + } + // now lets register the listener + TransportAcceptListener listener = transportConnector.getAcceptListener(); + getServletContext().setAttribute("transportChannelListener", listener); + super.init(); + } + + /** + * Factory method to create a new broker + * + * @throws Exception + */ + protected BrokerService createBroker() throws Exception { + BrokerService answer = new BrokerService(); + String url = getConnectorURL(); + transportConnector = new HttpTransportServer(new URI(url)); + answer.addConnector(transportConnector); + + String brokerURL = getServletContext().getInitParameter("org.activemq.brokerURL"); + if (brokerURL != null) { + log("Listening for internal communication on: " + brokerURL); + } + return answer; + } + + protected String getConnectorURL() { + return "http://localhost/" + getServletContext().getServletContextName(); + } +} diff --git a/activemq-optional/src/main/java/org/activemq/transport/http/HttpTransport.java b/activemq-optional/src/main/java/org/activemq/transport/http/HttpTransport.java new file mode 100755 index 0000000000..d24545917b --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/http/HttpTransport.java @@ -0,0 +1,203 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.http; + +import org.activemq.command.Command; +import org.activemq.command.ConnectionInfo; +import org.activemq.transport.util.TextWireFormat; +import org.activemq.util.Callback; +import org.activemq.util.IOExceptionSupport; +import org.activemq.util.ServiceStopper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; + +/** + * @version $Revision$ + */ +public class HttpTransport extends HttpTransportSupport { + private static final Log log = LogFactory.getLog(HttpTransport.class); + private HttpURLConnection sendConnection; + private HttpURLConnection receiveConnection; + private URL url; + private String clientID; + private String sessionID; + + public HttpTransport(TextWireFormat wireFormat, URI remoteUrl) throws MalformedURLException { + super(wireFormat, remoteUrl); + url = new URL(remoteUrl.toString()); + } + + public void oneway(Command command) throws IOException { + try { + if (command.getDataStructureType()==ConnectionInfo.DATA_STRUCTURE_TYPE) + clientID=((ConnectionInfo)command).getClientId(); + + HttpURLConnection connection = getSendConnection(); + String text = getTextWireFormat().toString(command); + Writer writer = new OutputStreamWriter(connection.getOutputStream()); + writer.write(text); + writer.flush(); + int answer = connection.getResponseCode(); + if (answer != HttpURLConnection.HTTP_OK) { + throw new IOException("Failed to post command: " + command + " as response was: " + answer); + } + checkSession(connection); + + } + catch (IOException e) { + throw IOExceptionSupport.create("Could not post command: " + command + " due to: " + e, e); + } + } + + public void run() { + log.trace("HTTP GET consumer thread starting for transport: " + this); + URI remoteUrl = getRemoteUrl(); + while (!isClosed()) { + try { + HttpURLConnection connection = getReceiveConnection(); + int answer = connection.getResponseCode(); + if (answer != HttpURLConnection.HTTP_OK) { + if (answer == HttpURLConnection.HTTP_CLIENT_TIMEOUT) { + log.trace("GET timed out"); + } + else { + log.warn("Failed to perform GET on: " + remoteUrl + " as response was: " + answer); + } + } + else { + checkSession(connection); + Command command = getTextWireFormat().readCommand(new DataInputStream(connection.getInputStream())); + + if (command == null) { + log.warn("Received null packet from url: " + remoteUrl); + } + else { + doConsume(command); + } + } + } + catch (Exception e) { + if (!isClosed()) { + log.warn("Failed to perform GET on: " + remoteUrl + " due to: " + e, e); + } + else { + log.trace("Caught error after closed: " + e, e); + } + } + } + } + + + + // Implementation methods + // ------------------------------------------------------------------------- + protected HttpURLConnection createSendConnection() throws IOException { + HttpURLConnection conn = (HttpURLConnection) getRemoteURL().openConnection(); + conn.setDoOutput(true); + conn.setRequestMethod("POST"); + configureConnection(conn); + conn.connect(); + return conn; + } + + protected HttpURLConnection createReceiveConnection() throws IOException { + HttpURLConnection conn = (HttpURLConnection) getRemoteURL().openConnection(); + conn.setDoOutput(false); + conn.setDoInput(true); + conn.setRequestMethod("GET"); + configureConnection(conn); + conn.connect(); + return conn; + } + + protected void checkSession(HttpURLConnection connection) + { + String set_cookie=connection.getHeaderField("Set-Cookie"); + if (set_cookie!=null && set_cookie.startsWith("JSESSIONID=")) + { + String[] bits=set_cookie.split("[=;]"); + sessionID=bits[1]; + } + } + + protected void configureConnection(HttpURLConnection connection) { + if (sessionID !=null) { + connection.addRequestProperty("Cookie", "JSESSIONID="+sessionID); + } + else if (clientID != null) { + connection.setRequestProperty("clientID", clientID); + } + } + + protected URL getRemoteURL() { + return url; + } + + protected HttpURLConnection getSendConnection() throws IOException { + setSendConnection(createSendConnection()); + return sendConnection; + } + + protected HttpURLConnection getReceiveConnection() throws IOException { + setReceiveConnection(createReceiveConnection()); + return receiveConnection; + } + + protected void setSendConnection(HttpURLConnection conn) { + if (sendConnection != null) { + sendConnection.disconnect(); + } + sendConnection = conn; + } + + protected void setReceiveConnection(HttpURLConnection conn) { + if (receiveConnection != null) { + receiveConnection.disconnect(); + } + receiveConnection = conn; + } + + protected void doStop(ServiceStopper stopper) throws Exception { + if (sendConnection != null) { + stopper.run(new Callback() { + public void execute() throws Exception { + sendConnection.disconnect(); + } + }); + sendConnection = null; + } + if (receiveConnection != null) { + stopper.run(new Callback() { + public void execute() throws Exception { + receiveConnection.disconnect(); + } + }); + receiveConnection = null; + } + } + +} diff --git a/activemq-optional/src/main/java/org/activemq/transport/http/HttpTransportFactory.java b/activemq-optional/src/main/java/org/activemq/transport/http/HttpTransportFactory.java new file mode 100755 index 0000000000..e578256fb3 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/http/HttpTransportFactory.java @@ -0,0 +1,64 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.http; + +import org.activeio.command.WireFormat; +import org.activemq.transport.MutexTransport; +import org.activemq.transport.ResponseCorrelator; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportFactory; +import org.activemq.transport.TransportServer; +import org.activemq.transport.util.TextWireFormat; +import org.activemq.transport.xstream.XStreamWireFormat; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; + +/** + * @version $Revision$ + */ +public class HttpTransportFactory extends TransportFactory { + private static final Log log = LogFactory.getLog(HttpTransportFactory.class); + + public TransportServer doBind(String brokerId, URI location) throws IOException { + return new HttpTransportServer(location); + } + + protected TextWireFormat asTextWireFormat(WireFormat wireFormat) { + if (wireFormat instanceof TextWireFormat) { + return (TextWireFormat) wireFormat; + } + log.trace("Not created with a TextWireFromat: " + wireFormat); + return new XStreamWireFormat(); + } + + protected String getDefaultWireFormatType() { + return "xstream"; + } + + protected Transport createTransport(URI location, WireFormat wf) throws MalformedURLException { + Transport transport = new HttpTransport(asTextWireFormat(wf), location); + transport = new MutexTransport(transport); + transport = new ResponseCorrelator(transport); + return transport; + } + +} diff --git a/activemq-optional/src/main/java/org/activemq/transport/http/HttpTransportServer.java b/activemq-optional/src/main/java/org/activemq/transport/http/HttpTransportServer.java new file mode 100755 index 0000000000..9a605f23d0 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/http/HttpTransportServer.java @@ -0,0 +1,118 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.http; + +import org.activemq.command.BrokerInfo; +import org.activemq.transport.TransportServerSupport; +import org.activemq.transport.util.TextWireFormat; +import org.activemq.transport.xstream.XStreamWireFormat; +import org.mortbay.jetty.Connector; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.bio.SocketConnector; +import org.mortbay.jetty.handler.ContextHandler; +import org.mortbay.jetty.nio.SelectChannelConnector; +import org.mortbay.jetty.servlet.ServletHandler; +import org.mortbay.jetty.servlet.ServletHolder; +import org.mortbay.jetty.servlet.ServletMapping; +import org.mortbay.jetty.servlet.SessionHandler; + +import java.net.URI; + +/** + * @version $Revision$ + */ +public class HttpTransportServer extends TransportServerSupport { + private URI bindAddress; + private TextWireFormat wireFormat; + private Server server; + private Connector connector; + + public HttpTransportServer(URI uri) { + super(uri); + this.bindAddress = uri; + } + + public void start() throws Exception { + server = new Server(); + if (connector==null) + connector = new SocketConnector(); + connector.setHost(bindAddress.getHost()); + connector.setPort(bindAddress.getPort()); + connector.setServer(server); + server.setConnectors(new Connector[] { connector }); + + ContextHandler context_handler = new ContextHandler(); + context_handler.setContextPath("/"); + context_handler.setServer(server); + server.setHandler(context_handler); + + SessionHandler session_handler = new SessionHandler(); + context_handler.setHandler(session_handler); + + ServletHandler servlet_handler = new ServletHandler(); + session_handler.setHandler(servlet_handler); + + ServletHolder holder = new ServletHolder(); + holder.setName("httpTunnel"); + holder.setClassName(HttpTunnelServlet.class.getName()); + servlet_handler.setServlets(new ServletHolder[] { holder }); + + ServletMapping mapping = new ServletMapping(); + mapping.setServletName("httpTunnel"); + mapping.setPathSpec("/*"); + servlet_handler.setServletMappings(new ServletMapping[] { mapping }); + + context_handler.setAttribute("acceptListener", getAcceptListener()); + context_handler.setAttribute("wireFormat", getWireFormat()); + server.start(); + } + + public synchronized void stop() throws Exception { + Server temp = server; + server = null; + if (temp != null) { + temp.stop(); + } + } + + // Properties + // ------------------------------------------------------------------------- + public TextWireFormat getWireFormat() { + if (wireFormat == null) { + wireFormat = createWireFormat(); + } + return wireFormat; + } + + public void setWireFormat(TextWireFormat wireFormat) { + this.wireFormat = wireFormat; + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected TextWireFormat createWireFormat() { + return new XStreamWireFormat(); + } + + protected void setConnector(Connector connector) { + this.connector = connector; + } + + public void setBrokerInfo(BrokerInfo brokerInfo) { + } +} diff --git a/activemq-optional/src/main/java/org/activemq/transport/http/HttpTransportSupport.java b/activemq-optional/src/main/java/org/activemq/transport/http/HttpTransportSupport.java new file mode 100644 index 0000000000..d2aafcd110 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/http/HttpTransportSupport.java @@ -0,0 +1,56 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.http; + +import org.activemq.transport.TransportThreadSupport; +import org.activemq.transport.util.TextWireFormat; + +import java.net.URI; + +/** + * A useful base class for HTTP Transport implementations. + * + * @version $Revision: 1.1 $ + */ +public abstract class HttpTransportSupport extends TransportThreadSupport { + private TextWireFormat textWireFormat; + private URI remoteUrl; + + public HttpTransportSupport(TextWireFormat textWireFormat, URI remoteUrl) { + this.textWireFormat = textWireFormat; + this.remoteUrl = remoteUrl; + } + + public String toString() { + return "HTTP Reader " + getRemoteUrl(); + } + + // Properties + // ------------------------------------------------------------------------- + public URI getRemoteUrl() { + return remoteUrl; + } + + public TextWireFormat getTextWireFormat() { + return textWireFormat; + } + + public void setTextWireFormat(TextWireFormat textWireFormat) { + this.textWireFormat = textWireFormat; + } +} diff --git a/activemq-optional/src/main/java/org/activemq/transport/http/HttpTunnelServlet.java b/activemq-optional/src/main/java/org/activemq/transport/http/HttpTunnelServlet.java new file mode 100755 index 0000000000..e5bf086e33 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/http/HttpTunnelServlet.java @@ -0,0 +1,194 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.http; + +import edu.emory.mathcs.backport.java.util.concurrent.ArrayBlockingQueue; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +import org.activemq.command.Command; +import org.activemq.command.CommandTypes; +import org.activemq.command.ConnectionInfo; +import org.activemq.command.KeepAliveInfo; +import org.activemq.command.WireFormatInfo; +import org.activemq.transport.TransportAcceptListener; +import org.activemq.transport.util.TextWireFormat; +import org.activemq.transport.xstream.XStreamWireFormat; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * A servlet which handles server side HTTP transport, delegating to the + * ActiveMQ broker. This servlet is designed for being embedded inside an + * ActiveMQ Broker using an embedded Jetty or Tomcat instance. + * + * @version $Revision$ + */ +public class HttpTunnelServlet extends HttpServlet { + private static final long serialVersionUID = -3826714430767484333L; + private static final Log log = LogFactory.getLog(HttpTunnelServlet.class); + + private TransportAcceptListener listener; + private TextWireFormat wireFormat; + private Map clients = new HashMap(); + private long requestTimeout = 30000L; + private KeepAliveInfo ping = new KeepAliveInfo(); + + public void init() throws ServletException { + super.init(); + listener = (TransportAcceptListener) getServletContext().getAttribute("acceptListener"); + if (listener == null) { + throw new ServletException("No such attribute 'acceptListener' available in the ServletContext"); + } + wireFormat = (TextWireFormat) getServletContext().getAttribute("wireFormat"); + if (wireFormat == null) { + wireFormat = createWireFormat(); + } + } + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + // lets return the next response + Command packet = null; + try { + System.err.println("\nrequest="+request); + BlockingQueueTransport transportChannel = getTransportChannel(request); + if (transportChannel == null) { + log("No transport available! "); + return; + } + packet = (Command) transportChannel.getQueue().poll(requestTimeout, TimeUnit.MILLISECONDS); + System.err.println("packet="+packet); + } + catch (InterruptedException e) { + // ignore + } + if (packet == null) { + // TODO temporary hack to prevent busy loop. Replace with continuations + try{ Thread.sleep(250);}catch (InterruptedException e) { e.printStackTrace(); } + response.setStatus(HttpServletResponse.SC_REQUEST_TIMEOUT); + } + else { + wireFormat.marshal(packet, new DataOutputStream(response.getOutputStream())); + } + } + + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + // String body = readRequestBody(request); + // Command command = wireFormat.readCommand(body); + + Command command = wireFormat.readCommand(request.getReader()); + + if (command instanceof ConnectionInfo) { + ConnectionInfo info = (ConnectionInfo) command; + request.getSession(true).setAttribute("clientID", info.getClientId()); + } + if (command instanceof WireFormatInfo) { + WireFormatInfo info = (WireFormatInfo) command; + if (!canProcessWireFormatVersion(info.getVersion())) { + response.sendError(HttpServletResponse.SC_NOT_FOUND, "Cannot process wire format of version: " + info.getVersion()); + } + + } + else { + BlockingQueueTransport transport = getTransportChannel(request); + if (transport == null) { + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + } + else { + transport.doConsume(command); + } + } + } + + private boolean canProcessWireFormatVersion(int version) { + // TODO: + return true; + } + + protected String readRequestBody(HttpServletRequest request) throws IOException { + StringBuffer buffer = new StringBuffer(); + BufferedReader reader = request.getReader(); + while (true) { + String line = reader.readLine(); + if (line == null) { + break; + } + else { + buffer.append(line); + buffer.append("\n"); + } + } + return buffer.toString(); + } + + protected BlockingQueueTransport getTransportChannel(HttpServletRequest request) { + HttpSession session = request.getSession(true); + String clientID = null; + if (session != null) { + clientID = (String) session.getAttribute("clientID"); + } + if (clientID == null) { + clientID = request.getHeader("clientID"); + } + System.out.println("clientID="+clientID); + /** + * if (clientID == null) { clientID = request.getParameter("clientID"); } + */ + if (clientID == null) { + log.warn("No clientID header so ignoring request"); + return null; + } + synchronized (this) { + BlockingQueueTransport answer = (BlockingQueueTransport) clients.get(clientID); + if (answer == null) { + answer = createTransportChannel(); + clients.put(clientID, answer); + listener.onAccept(answer); + } + else { + try { + answer.asyncRequest(ping); + } + catch (IOException e) { + log.warn("Failed to ping transport: " + e, e); + } + } + return answer; + } + } + + protected BlockingQueueTransport createTransportChannel() { + return new BlockingQueueTransport(new ArrayBlockingQueue(10)); + } + + protected TextWireFormat createWireFormat() { + return new XStreamWireFormat(); + } +} diff --git a/activemq-optional/src/main/java/org/activemq/transport/http/package.html b/activemq-optional/src/main/java/org/activemq/transport/http/package.html new file mode 100755 index 0000000000..4375f99ba8 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/http/package.html @@ -0,0 +1,11 @@ + + + + + +

+ A transport using the HTTP protocol to allow ActiveMQ to tunnel through firewalls. +

+ + + diff --git a/activemq-optional/src/main/java/org/activemq/transport/https/HttpsTransport.java b/activemq-optional/src/main/java/org/activemq/transport/https/HttpsTransport.java new file mode 100755 index 0000000000..aecc950528 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/https/HttpsTransport.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.https; + +import org.activemq.transport.http.HttpTransport; +import org.activemq.transport.util.TextWireFormat; + +import javax.net.ssl.HttpsURLConnection; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URI; + +public class HttpsTransport extends HttpTransport { + + public HttpsTransport(TextWireFormat wireFormat, URI remoteUrl) throws MalformedURLException { + super(wireFormat, remoteUrl); + } + + protected synchronized HttpURLConnection createSendConnection() throws IOException { + HttpsURLConnection conn = (HttpsURLConnection) getRemoteURL().openConnection(); + conn.setDoOutput(true); + conn.setRequestMethod("POST"); + configureConnection(conn); + conn.connect(); + return conn; + } + + protected synchronized HttpURLConnection createReceiveConnection() throws IOException { + HttpsURLConnection conn = (HttpsURLConnection) getRemoteURL().openConnection(); + conn.setDoOutput(false); + conn.setDoInput(true); + conn.setRequestMethod("GET"); + configureConnection(conn); + conn.connect(); + return conn; + } + +} diff --git a/activemq-optional/src/main/java/org/activemq/transport/https/HttpsTransportFactory.java b/activemq-optional/src/main/java/org/activemq/transport/https/HttpsTransportFactory.java new file mode 100644 index 0000000000..d85b1724e6 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/https/HttpsTransportFactory.java @@ -0,0 +1,44 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.https; + +import org.activeio.command.WireFormat; +import org.activemq.transport.Transport; +import org.activemq.transport.TransportServer; +import org.activemq.transport.http.HttpTransportFactory; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; + +/** + * Factory of HTTPS based transports + * + * @version $Revision: 1.1 $ + */ +public class HttpsTransportFactory extends HttpTransportFactory { + + public TransportServer doBind(String brokerId, URI location) throws IOException { + return new HttpsTransportServer(location); + } + + protected Transport createTransport(URI location, WireFormat wf) throws MalformedURLException { + return new HttpsTransport(asTextWireFormat(wf), location); + } + +} diff --git a/activemq-optional/src/main/java/org/activemq/transport/https/HttpsTransportServer.java b/activemq-optional/src/main/java/org/activemq/transport/https/HttpsTransportServer.java new file mode 100755 index 0000000000..59c0f04ce1 --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/https/HttpsTransportServer.java @@ -0,0 +1,113 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.https; + +import org.activemq.transport.http.HttpTransportServer; +import org.mortbay.jetty.security.SslSocketConnector; + +import java.net.URI; + +public class HttpsTransportServer extends HttpTransportServer { + + private String keyPassword = System.getProperty( "javax.net.ssl.keyPassword" ); + private String keyStorePassword = System.getProperty( "javax.net.ssl.keyStorePassword" ); + private String keyStore = System.getProperty( "javax.net.ssl.keyStore" ); + private String keyStoreType = null; + private String certificateAlgorithm = null; + private String protocol = null; + + public HttpsTransportServer( URI uri ) { + super( uri ); + } + + public void start() throws Exception { + SslSocketConnector sslConnector = new SslSocketConnector(); + sslConnector.setKeystore( keyStore ); + sslConnector.setPassword( keyStorePassword ); + // if the keyPassword hasn't been set, default it to the + // key store password + if ( keyPassword == null ) { + sslConnector.setKeyPassword( keyStorePassword ); + } + if ( keyStoreType != null ) { + sslConnector.setKeystoreType( keyStoreType ); + } + if ( certificateAlgorithm != null ) { + sslConnector.setAlgorithm( certificateAlgorithm ); + } + if ( protocol != null ) { + sslConnector.setProtocol( protocol ); + } + + setConnector(sslConnector); + + super.start(); + } + + // Properties + //-------------------------------------------------------------------------------- + + public String getCertificateAlgorithm() { + return certificateAlgorithm; + } + + public void setCertificateAlgorithm( String certificateAlgorithm ) { + this.certificateAlgorithm = certificateAlgorithm; + } + + public String getKeyStore() { + return keyStore; + } + + public void setKeyStore( String keyStore ) { + this.keyStore = keyStore; + } + + public String getKeyPassword() { + return keyPassword; + } + + public void setKeyPassword( String keyPassword ) { + this.keyPassword = keyPassword; + } + + public String getKeyStoreType() { + return keyStoreType; + } + + public void setKeyStoreType( String keyStoreType ) { + this.keyStoreType = keyStoreType; + } + + public String getKeyStorePassword() { + return keyStorePassword; + } + + public void setKeyStorePassword( String keyStorePassword ) { + this.keyStorePassword = keyStorePassword; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol( String protocol ) { + this.protocol = protocol; + } + +} diff --git a/activemq-optional/src/main/java/org/activemq/transport/https/package.html b/activemq-optional/src/main/java/org/activemq/transport/https/package.html new file mode 100755 index 0000000000..dd0be55b3f --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/https/package.html @@ -0,0 +1,9 @@ + + + + + +A transport using the HTTPS protocol (built on top of the HTTP transport) to allow ActiveMQ to tunnel through firewalls using SSL. + + + diff --git a/activemq-optional/src/main/java/org/activemq/transport/ssl/SslTransportFactory.java b/activemq-optional/src/main/java/org/activemq/transport/ssl/SslTransportFactory.java new file mode 100644 index 0000000000..c3cf88532a --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/ssl/SslTransportFactory.java @@ -0,0 +1,41 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.ssl; + +import org.activemq.transport.tcp.TcpTransportFactory; + +import javax.net.ServerSocketFactory; +import javax.net.SocketFactory; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocketFactory; + +/** + * An SSL version of the TCP transport + * + * @version $Revision: 1.1 $ + */ +public class SslTransportFactory extends TcpTransportFactory { + + protected SocketFactory createSocketFactory() { + return SSLSocketFactory.getDefault(); + } + + protected ServerSocketFactory createServerSocketFactory() { + return SSLServerSocketFactory.getDefault(); + } +} diff --git a/activemq-optional/src/main/java/org/activemq/transport/ssl/package.html b/activemq-optional/src/main/java/org/activemq/transport/ssl/package.html new file mode 100755 index 0000000000..710ded68db --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/ssl/package.html @@ -0,0 +1,11 @@ + + + + + +

+ An implementation of the transport layer using SSL over TCP/IP sockets +

+ + + diff --git a/activemq-optional/src/main/java/org/activemq/transport/util/TextWireFormat.java b/activemq-optional/src/main/java/org/activemq/transport/util/TextWireFormat.java new file mode 100644 index 0000000000..3743a3314a --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/util/TextWireFormat.java @@ -0,0 +1,44 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.util; + +import org.activeio.command.WireFormat; +import org.activemq.command.Command; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.Reader; + +/** + * Adds the extra methods available to text based wire format implementations + * + * @version $Revision: 1.1 $ + */ +public abstract class TextWireFormat implements WireFormat { + + public abstract Command readCommand(String text); + + public abstract Command readCommand(Reader reader); + + public abstract String toString(Command command); + + public Command readCommand(DataInputStream in) throws IOException { + String text = in.readUTF(); + return readCommand(text); + } +} diff --git a/activemq-optional/src/main/java/org/activemq/transport/xstream/XStreamWireFormat.java b/activemq-optional/src/main/java/org/activemq/transport/xstream/XStreamWireFormat.java new file mode 100755 index 0000000000..7601a759ef --- /dev/null +++ b/activemq-optional/src/main/java/org/activemq/transport/xstream/XStreamWireFormat.java @@ -0,0 +1,149 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.xstream; + +import com.thoughtworks.xstream.XStream; + +import org.activeio.Packet; +import org.activeio.command.WireFormat; +import org.activemq.command.Command; +import org.activemq.transport.util.TextWireFormat; + +import javax.jms.JMSException; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.Reader; + +/** + * A {@link WireFormat} implementation which uses the ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.http; + +import org.activemq.transport.TransportBrokerTestSupport; + +import junit.framework.Test; +import junit.textui.TestRunner; + +public class HttpTransportBrokerTest extends TransportBrokerTestSupport { + + protected String getBindLocation() { + return "http://localhost:8081"; + } + + protected void setUp() throws Exception { + MAX_WAIT = 2000; + super.setUp(); + } + + public static Test suite() { + return suite(HttpTransportBrokerTest.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } + +} diff --git a/activemq-optional/src/test/java/org/activemq/transport/ssl/SslTransportBrokerTest.java b/activemq-optional/src/test/java/org/activemq/transport/ssl/SslTransportBrokerTest.java new file mode 100755 index 0000000000..8d9af3b852 --- /dev/null +++ b/activemq-optional/src/test/java/org/activemq/transport/ssl/SslTransportBrokerTest.java @@ -0,0 +1,53 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.transport.ssl; + +import org.activemq.transport.TransportBrokerTestSupport; + +import junit.framework.Test; +import junit.textui.TestRunner; + +public class SslTransportBrokerTest extends TransportBrokerTestSupport { + + protected String getBindLocation() { + return "ssl://localhost:0"; + } + + protected void setUp() throws Exception { + System.setProperty("javax.net.ssl.trustStore", "src/test/client.keystore"); + System.setProperty("javax.net.ssl.trustStorePassword", "password"); + System.setProperty("javax.net.ssl.trustStoreType", "jks"); + System.setProperty("javax.net.ssl.keyStore", "src/test/server.keystore"); + System.setProperty("javax.net.ssl.keyStorePassword", "password"); + System.setProperty("javax.net.ssl.keyStoreType", "jks"); + //System.setProperty("javax.net.debug", "ssl,handshake,data,trustmanager"); + + MAX_WAIT = 2000; + super.setUp(); + } + + public static Test suite() { + return suite(SslTransportBrokerTest.class); + } + + public static void main(String[] args) { + TestRunner.run(suite()); + } + +} diff --git a/activemq-optional/src/test/java/org/activemq/transport/xstream/XStreamWireFormatTest.java b/activemq-optional/src/test/java/org/activemq/transport/xstream/XStreamWireFormatTest.java new file mode 100755 index 0000000000..b41721bae6 --- /dev/null +++ b/activemq-optional/src/test/java/org/activemq/transport/xstream/XStreamWireFormatTest.java @@ -0,0 +1,46 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.xstream; + +import org.activeio.command.WireFormat; +import org.activemq.command.Command; +import org.activemq.command.MessageTest; + +import java.io.IOException; + +/** + * @version $Revision$ + */ +public class XStreamWireFormatTest extends MessageTest { + + public void assertBeanMarshalls(Object original) throws IOException { + super.assertBeanMarshalls(original); + + String xml = getXStreamWireFormat().toString((Command) original); + System.out.println(original.getClass().getName() + " as XML is:"); + System.out.println(xml); + } + + protected XStreamWireFormat getXStreamWireFormat() { + return (XStreamWireFormat) wireFormat; + } + + protected WireFormat createWireFormat() { + return new XStreamWireFormat(); + } +} diff --git a/activemq-optional/src/test/resources/client.keystore b/activemq-optional/src/test/resources/client.keystore new file mode 100755 index 0000000000..8580672777 Binary files /dev/null and b/activemq-optional/src/test/resources/client.keystore differ diff --git a/activemq-optional/src/test/resources/org/activemq/axis/activemq-connection-factory.properties b/activemq-optional/src/test/resources/org/activemq/axis/activemq-connection-factory.properties new file mode 100755 index 0000000000..1d71aaccdf --- /dev/null +++ b/activemq-optional/src/test/resources/org/activemq/axis/activemq-connection-factory.properties @@ -0,0 +1,7 @@ +# +# An example config file for using ActiveMQ with Apache Axis +# + +brokerURL=tcp://localhost:61616 +defaultUser=Administrator +defaultPassword=Administrator \ No newline at end of file diff --git a/activemq-optional/src/test/resources/server.keystore b/activemq-optional/src/test/resources/server.keystore new file mode 100755 index 0000000000..d9223d80a8 Binary files /dev/null and b/activemq-optional/src/test/resources/server.keystore differ diff --git a/activemq-optional/src/webapp/WEB-INF/web.xml b/activemq-optional/src/webapp/WEB-INF/web.xml new file mode 100755 index 0000000000..024527946e --- /dev/null +++ b/activemq-optional/src/webapp/WEB-INF/web.xml @@ -0,0 +1,37 @@ + + + + + + + ActiveMQ Message Broker Web Application + + Provides an embedded ActiveMQ Message Broker embedded inside a web application + + + + + org.activemq.brokerURL + tcp://localhost:61616 + The URL that the embedded broker should listen on in addition to HTTP + + + + + + + + + TunnelServlet + org.activemq.transport.http.HttpEmbeddedTunnelServlet + 1 + + + + TunnelServlet + /* + + + diff --git a/activemq-optional/src/webapp/index.html b/activemq-optional/src/webapp/index.html new file mode 100755 index 0000000000..dc6be4c903 --- /dev/null +++ b/activemq-optional/src/webapp/index.html @@ -0,0 +1,18 @@ + + + + ActiveMQ HTTP Tunnel + + + + +

ActiveMQ HTTP Tunnel

+ +

+This service allows you to tunnel ActiveMQ traffic through HTTP to work cleanly with firewalls. +For more information please see the ActiveMQ home page. +

+ + + + diff --git a/activemq-ra/.cvsignore b/activemq-ra/.cvsignore new file mode 100755 index 0000000000..9723f1f92c --- /dev/null +++ b/activemq-ra/.cvsignore @@ -0,0 +1,9 @@ +target +.classpath +.project +*.iws +*.ipr +*.iml +build.properties + +ActiveMQ diff --git a/activemq-ra/maven.xml b/activemq-ra/maven.xml new file mode 100755 index 0000000000..993a51bdc4 --- /dev/null +++ b/activemq-ra/maven.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-ra/pom.xml b/activemq-ra/pom.xml new file mode 100755 index 0000000000..c4011796be --- /dev/null +++ b/activemq-ra/pom.xml @@ -0,0 +1,76 @@ + + 4.0.0 + + + activemq + activemq + 4.0-SNAPSHOT + + + activemq-ra + rar + ActiveMQ JCA Managed Connections and Resource Adapters + + + + + activemq + activemq-core + ${version} + + + + activeio + activeio + + + + backport-util-concurrent + backport-util-concurrent + + + + commons-logging + commons-logging + + + + log4j + log4j + + + + geronimo-spec + geronimo-spec-jms + + + geronimo-spec + geronimo-spec-jta + + + geronimo-spec + geronimo-spec-j2ee-jacc + + + geronimo-spec + geronimo-spec-j2ee-connector + + + + springframework + spring + + + + org.apache.derby + derby + + + + activemq + jmdns + + + + + diff --git a/activemq-ra/project.properties b/activemq-ra/project.properties new file mode 100755 index 0000000000..2361cc92eb --- /dev/null +++ b/activemq-ra/project.properties @@ -0,0 +1,5 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=rar +maven.rar.src=${basedir}/src/main/rar diff --git a/activemq-ra/project.xml b/activemq-ra/project.xml new file mode 100755 index 0000000000..a58c4b1645 --- /dev/null +++ b/activemq-ra/project.xml @@ -0,0 +1,179 @@ + + + + 3 + ${basedir}/../etc/project.xml + + ActiveMQ :: Resource Adapter + activemq-ra + + JCA Managed Connections and Resource Adapters + + + The ActiveMQ Java Connector Architecture Resource Adapter used to + manage ActiveMQ connection when run inside a J2EE app server. + + + org.activemq.ra + + + JCA Managed Connections and Resource Adapters + org.activemq.ra + + + + + + + geronimo-spec + geronimo-spec-j2ee-connector + ${geronimo_spec_j2ee_connector_version} + + false + true + + + + + activemq + activemq-core + ${pom.currentVersion} + + true + + + + + jmock + jmock + ${jmock_version} + http://jmock.codehaus.org/ + + + + jmock + jmock-cglib + ${jmock_cglib_version} + http://jmock.codehaus.org/ + + + + cglib + cglib-full + ${cglib_full_version} + http://cglib.sourceforge.net/ + + + + activemq + jmdns + ${jmdns_version} + + true + + + + + activeio + activeio + ${activeio_version} + + true + + + + + org.apache.derby + derby + ${derby_version} + + true + + + + + + mx4j + mx4j + 2.1.1 + + + mx4j + mx4j-jmx + 2.1.1 + + + mx4j + mx4j-remote + 2.1.1 + + + mx4j + mx4j-tools + 2.1.1 + + + mx4j + mx4j-impl + 2.1.1 + + + + + + xbean + xbean-spring + ${xbean_spring_version} + http://www.gbean.org + + true + + + + + annogen + annogen + 0.1.0 + + + + qdox + qdox + 1.5 + + + + + + dev@activemq.codehaus.org + src/main/java + src/test/java + + + + + src/test/resources + + **/*.properties + **/*.xml + + + + + **/*Test.* + + + + + + + + src/main/resources + + **/* + + + + + + diff --git a/activemq-ra/src/main/java/org/activemq/ra/ActiveMQActivationSpec.java b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQActivationSpec.java new file mode 100755 index 0000000000..70aaf8864c --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQActivationSpec.java @@ -0,0 +1,539 @@ +/** + * + * Copyright 2004 Hiram Chirino + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.Topic; +import javax.resource.ResourceException; +import javax.resource.spi.ActivationSpec; +import javax.resource.spi.InvalidPropertyException; +import javax.resource.spi.ResourceAdapter; + +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.activemq.selector.SelectorParser; + +/** + * Configures the inbound JMS consumer specification using ActiveMQ + * + * @org.xbean.XBean + * + * @version $Revision$ $Date$ + */ +public class ActiveMQActivationSpec implements ActivationSpec { + + /** Auto-acknowledge constant for acknowledgeMode property **/ + public static final String AUTO_ACKNOWLEDGE_MODE = "Auto-acknowledge"; + /** Dups-ok-acknowledge constant for acknowledgeMode property * */ + public static final String DUPS_OK_ACKNOWLEDGE_MODE = "Dups-ok-acknowledge"; + /** Durable constant for subscriptionDurability property * */ + public static final String DURABLE_SUBSCRIPTION = "Durable"; + /** NonDurable constant for subscriptionDurability property * */ + public static final String NON_DURABLE_SUBSCRIPTION = "NonDurable"; + + public static final int INVALID_ACKNOWLEDGE_MODE = -1; + + private ActiveMQResourceAdapter resourceAdapter; + private String destinationType; + private String messageSelector; + private String destination; + private String acknowledgeMode = AUTO_ACKNOWLEDGE_MODE; + private String userName; + private String password; + private String clientId; + private String subscriptionName; + private String subscriptionDurability = NON_DURABLE_SUBSCRIPTION; + private String noLocal = "false"; + private String useRAManagedTransaction = "false"; + private String maxSessions="10"; + private String maxMessagesPerSessions="10"; + private String enableBatch = "false"; + private String maxMessagesPerBatch = "10"; + + + /** + * @see javax.resource.spi.ActivationSpec#validate() + */ + public void validate() throws InvalidPropertyException { + List errorMessages = new ArrayList(); + List propsNotSet = new ArrayList(); + try { + if (!isValidDestination(errorMessages)) + propsNotSet.add(new PropertyDescriptor("destination", ActiveMQActivationSpec.class)); + if (!isValidDestinationType(errorMessages)) + propsNotSet.add(new PropertyDescriptor("destinationType", ActiveMQActivationSpec.class)); + if (!isValidAcknowledgeMode(errorMessages)) + propsNotSet.add(new PropertyDescriptor("acknowledgeMode", ActiveMQActivationSpec.class)); + if (!isValidSubscriptionDurability(errorMessages)) + propsNotSet.add(new PropertyDescriptor("subscriptionDurability", ActiveMQActivationSpec.class)); + if (!isValidClientId(errorMessages)) + propsNotSet.add(new PropertyDescriptor("clientId", ActiveMQActivationSpec.class)); + if (!isValidSubscriptionName(errorMessages)) + propsNotSet.add(new PropertyDescriptor("subscriptionName", ActiveMQActivationSpec.class)); + if (!isValidMaxMessagesPerSessions(errorMessages)) + propsNotSet.add(new PropertyDescriptor("maxMessagesPerSessions", ActiveMQActivationSpec.class)); + if (!isValidMaxSessions(errorMessages)) + propsNotSet.add(new PropertyDescriptor("maxSessions", ActiveMQActivationSpec.class)); + if (!isValidMessageSelector(errorMessages)) + propsNotSet.add(new PropertyDescriptor("messageSelector", ActiveMQActivationSpec.class)); + if (!isValidNoLocal(errorMessages)) + propsNotSet.add(new PropertyDescriptor("noLocal", ActiveMQActivationSpec.class)); + if (!isValidUseRAManagedTransaction(errorMessages)) + propsNotSet.add(new PropertyDescriptor("useRAManagedTransaction", ActiveMQActivationSpec.class)); + if (!isValidEnableBatch(errorMessages)) + propsNotSet.add(new PropertyDescriptor("enableBatch", ActiveMQActivationSpec.class)); + if (!isValidMaxMessagesPerBatch(errorMessages)) + propsNotSet.add(new PropertyDescriptor("maxMessagesPerBatch", ActiveMQActivationSpec.class)); + + + } catch (IntrospectionException e) { + e.printStackTrace(); + } + + if (propsNotSet.size() > 0) { + StringBuffer b = new StringBuffer(); + b.append("Invalid settings:"); + for (Iterator iter = errorMessages.iterator(); iter.hasNext();) { + b.append(" "); + b.append(iter.next()); + } + InvalidPropertyException e = new InvalidPropertyException(b.toString()); + final PropertyDescriptor[] descriptors = (PropertyDescriptor[]) propsNotSet.toArray(new PropertyDescriptor[propsNotSet.size()]); + e.setInvalidPropertyDescriptors(descriptors); + throw e; + } + } + + private boolean isValidUseRAManagedTransaction(List errorMessages) { + try { + new Boolean(noLocal); + return true; + } catch (Throwable e) { + } + errorMessages.add("noLocal must be set to: true or false."); + return false; + } + + private boolean isValidNoLocal(List errorMessages) { + try { + new Boolean(noLocal); + return true; + } catch (Throwable e) { + } + errorMessages.add("noLocal must be set to: true or false."); + return false; + } + + private boolean isValidMessageSelector(List errorMessages) { + try { + if( !isEmpty(messageSelector) ) { + new SelectorParser().parse(messageSelector); + } + return true; + } catch (Throwable e) { + errorMessages.add("messageSelector not set to valid message selector: "+e.getMessage()); + return false; + } + } + + private boolean isValidMaxSessions(List errorMessages) { + try { + if( Integer.parseInt(maxSessions) > 0 ) { + return true; + } + } catch (NumberFormatException e) { + } + errorMessages.add("maxSessions must be set to number > 0"); + return false; + } + + private boolean isValidMaxMessagesPerSessions(List errorMessages) { + try { + if( Integer.parseInt(maxMessagesPerSessions) > 0 ) { + return true; + } + } catch (NumberFormatException e) { + } + errorMessages.add("maxMessagesPerSessions must be set to number > 0"); + return false; + } + + private boolean isValidMaxMessagesPerBatch(List errorMessages) { + try { + if( Integer.parseInt(maxMessagesPerBatch) > 0 ) { + return true; + } + } catch (NumberFormatException e) { + } + errorMessages.add("maxMessagesPerBatch must be set to number > 0"); + return false; + } + + private boolean isValidEnableBatch(List errorMessages) { + try { + new Boolean(enableBatch); + return true; + } catch (Throwable e) { + } + errorMessages.add("enableBatch must be set to: true or false"); + return false; + } + + /** + * @see javax.resource.spi.ResourceAdapterAssociation#getResourceAdapter() + */ + public ResourceAdapter getResourceAdapter() { + return resourceAdapter; + } + + /** + * @see javax.resource.spi.ResourceAdapterAssociation#setResourceAdapter(javax.resource.spi.ResourceAdapter) + */ + public void setResourceAdapter(ResourceAdapter resourceAdapter) throws ResourceException { + //spec section 5.3.3 + if (this.resourceAdapter != null) { + throw new ResourceException("ResourceAdapter already set"); + } + if (!(resourceAdapter instanceof ActiveMQResourceAdapter)) { + throw new ResourceException("ResourceAdapter is not of type: " + ActiveMQResourceAdapter.class.getName()); + } + this.resourceAdapter = (ActiveMQResourceAdapter) resourceAdapter; + } + + + ///////////////////////////////////////////////////////////////////////// + // + // Java Bean getters and setters for this ActivationSpec class. + // + ///////////////////////////////////////////////////////////////////////// + /** + * @return Returns the destinationType. + */ + public String getDestinationType() { + if (!isEmpty(destinationType)) { + return destinationType; + } + return null; + } + + /** + * @param destinationType The destinationType to set. + */ + public void setDestinationType(String destinationType) { + this.destinationType = destinationType; + } + + public String getPassword() { + if (!isEmpty(password)) { + return password; + } + return null; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getUserName() { + if (!isEmpty(userName)) { + return userName; + } + return null; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + /** + * @return Returns the messageSelector. + */ + public String getMessageSelector() { + if (!isEmpty(messageSelector)) { + return messageSelector; + } + return null; + } + + /** + * @param messageSelector The messageSelector to set. + */ + public void setMessageSelector(String messageSelector) { + this.messageSelector = messageSelector; + } + + /** + * @return Returns the noLocal. + */ + public String getNoLocal() { + return noLocal; + } + + /** + * @param noLocal The noLocal to set. + */ + public void setNoLocal(String noLocal) { + if( noLocal!=null ) { + this.noLocal = noLocal; + } + } + + public String getAcknowledgeMode() { + if (!isEmpty(acknowledgeMode)) { + return acknowledgeMode; + } + return null; + } + + public void setAcknowledgeMode(String acknowledgeMode) { + this.acknowledgeMode = acknowledgeMode; + } + + public String getClientId() { + if (!isEmpty(clientId)) { + return clientId; + } + return null; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getDestination() { + if (!isEmpty(destination)) { + return destination; + } + return null; + } + + public void setDestination(String destination) { + this.destination = destination; + } + + public String getSubscriptionDurability() { + if (!isEmpty(subscriptionDurability)) { + return subscriptionDurability; + } + return null; + } + + public void setSubscriptionDurability(String subscriptionDurability) { + this.subscriptionDurability = subscriptionDurability; + } + + public String getSubscriptionName() { + if (!isEmpty(subscriptionName)) { + return subscriptionName; + } + return null; + } + + public void setSubscriptionName(String subscriptionName) { + this.subscriptionName = subscriptionName; + } + + private boolean isValidSubscriptionName(List errorMessages) { + if( !isDurableSubscription() ? true : subscriptionName != null && subscriptionName.trim().length() > 0 ) { + return true; + } + errorMessages.add("subscriptionName must be set since durable subscription was requested."); + return false; + } + + private boolean isValidClientId(List errorMessages) { + if( !isDurableSubscription() ? true : clientId != null && clientId.trim().length() > 0 ) { + return true; + } + errorMessages.add("clientId must be set since durable subscription was requested."); + return false; + } + + public boolean isDurableSubscription() { + return DURABLE_SUBSCRIPTION.equals(subscriptionDurability); + } + + private boolean isValidSubscriptionDurability(List errorMessages) { + if (NON_DURABLE_SUBSCRIPTION.equals(subscriptionDurability) || DURABLE_SUBSCRIPTION.equals(subscriptionDurability)) + return true; + errorMessages.add("subscriptionDurability must be set to: "+NON_DURABLE_SUBSCRIPTION+" or "+DURABLE_SUBSCRIPTION+"."); + return false; + } + + private boolean isValidAcknowledgeMode(List errorMessages) { + if (AUTO_ACKNOWLEDGE_MODE.equals(acknowledgeMode) || DUPS_OK_ACKNOWLEDGE_MODE.equals(acknowledgeMode)) + return true; + errorMessages.add("acknowledgeMode must be set to: "+AUTO_ACKNOWLEDGE_MODE+" or "+DUPS_OK_ACKNOWLEDGE_MODE+"."); + return false; + } + + private boolean isValidDestinationType(List errorMessages) { + if (Queue.class.getName().equals(destinationType) || Topic.class.getName().equals(destinationType)) + return true; + errorMessages.add("destinationType must be set to: "+Queue.class.getName()+" or "+Topic.class.getName()+"."); + return false; + } + + private boolean isValidDestination(List errorMessages) { + if(!(destination == null || destination.equals(""))) + return true; + errorMessages.add("destination is a required field and must be set to the destination name."); + return false; + } + + private boolean isEmpty(String value) { + return value == null || "".equals(value.trim()); + } + + public String toString() { + return "ActiveMQActivationSpec{" + + "acknowledgeMode='" + acknowledgeMode + "'" + + ", destinationType='" + destinationType + "'" + + ", messageSelector='" + messageSelector + "'" + + ", destination='" + destination + "'" + + ", clientId='" + clientId + "'" + + ", subscriptionName='" + subscriptionName + "'" + + ", subscriptionDurability='" + subscriptionDurability + "'" + + "}"; + } + + public int getAcknowledgeModeForSession() { + if( AUTO_ACKNOWLEDGE_MODE.equals(acknowledgeMode) ) { + return Session.AUTO_ACKNOWLEDGE; + } else if( DUPS_OK_ACKNOWLEDGE_MODE.equals(acknowledgeMode) ) { + return Session.DUPS_OK_ACKNOWLEDGE; + } else { + return INVALID_ACKNOWLEDGE_MODE; + } + } + + /** + * A helper method mostly for use in Dependency Injection containers + * which allows you to customize the destination and destinationType properties + * from a single ActiveMQDestination POJO + */ + public void setActiveMQDestination(ActiveMQDestination destination) { + setDestination(destination.getPhysicalName()); + if (destination instanceof Queue) { + setDestinationType(Queue.class.getName()); + } + else { + setDestinationType(Topic.class.getName()); + } + } + + public ActiveMQDestination createDestination() { + if( isEmpty(destinationType) || isEmpty(destination) ) + return null; + + ActiveMQDestination dest = null; + if (Queue.class.getName().equals(destinationType)) { + dest = new ActiveMQQueue(destination); + } else if (Topic.class.getName().equals(destinationType)) { + dest = new ActiveMQTopic(destination); + } else { + assert false : "Execution should never reach here"; + } + return dest; + } + + public String getMaxMessagesPerSessions() { + return maxMessagesPerSessions.toString(); + } + + public void setMaxMessagesPerSessions(String maxMessagesPerSessions) { + if( maxMessagesPerSessions!=null ) { + this.maxMessagesPerSessions = maxMessagesPerSessions; + } + } + + + public String getMaxSessions() { + return maxSessions; + } + public void setMaxSessions(String maxSessions) { + if( maxSessions!=null ) { + this.maxSessions = maxSessions; + } + } + + public String getUseRAManagedTransaction() { + return useRAManagedTransaction; + } + + public void setUseRAManagedTransaction(String useRAManagedTransaction) { + if( useRAManagedTransaction!=null ) { + this.useRAManagedTransaction = useRAManagedTransaction; + } + } + + public int getMaxMessagesPerSessionsIntValue() { + return Integer.parseInt(maxMessagesPerSessions); + } + + public int getMaxSessionsIntValue() { + return Integer.parseInt(maxSessions); + } + + public boolean isUseRAManagedTransactionEnabled() { + return new Boolean(useRAManagedTransaction).booleanValue(); + } + + public boolean getNoLocalBooleanValue() { + return new Boolean(noLocal).booleanValue(); + } + + public String getEnableBatch() { + return enableBatch; + } + + public void setEnableBatch(String enableBatch) { + if (enableBatch != null) { + this.enableBatch = enableBatch; + } + } + + public boolean getEnableBatchBooleanValue() { + return new Boolean(enableBatch).booleanValue(); + } + + public int getMaxMessagesPerBatchIntValue() { + return Integer.parseInt(maxMessagesPerBatch); + } + + public String getMaxMessagesPerBatch() { + return maxMessagesPerBatch.toString(); + } + + public void setMaxMessagesPerBatch(String maxMessagesPerBatch) { + if (maxMessagesPerBatch != null) { + this.maxMessagesPerBatch = maxMessagesPerBatch; + } + } + +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/ActiveMQConnectionFactory.java b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQConnectionFactory.java new file mode 100755 index 0000000000..d3d725bf60 --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQConnectionFactory.java @@ -0,0 +1,131 @@ +/** + * + * Copyright 2004 Hiram Chirino + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.jms.QueueConnectionFactory; +import javax.jms.QueueConnection; +import javax.jms.TopicConnectionFactory; +import javax.jms.TopicConnection; +import javax.naming.Reference; +import javax.resource.Referenceable; +import javax.resource.ResourceException; +import javax.resource.spi.ConnectionManager; +import java.io.Serializable; + + +/** + * @version $Revision$ + */ +public class ActiveMQConnectionFactory implements ConnectionFactory, QueueConnectionFactory, TopicConnectionFactory, Referenceable, Serializable { + + private static final long serialVersionUID = -5754338187296859149L; + + private static final Log log = LogFactory.getLog(ActiveMQConnectionFactory.class); + transient private ConnectionManager manager; + transient private ActiveMQManagedConnectionFactory factory; + private Reference reference; + private final ActiveMQConnectionRequestInfo info; + + + /** + * @param factory + * @param manager + * @param info + */ + public ActiveMQConnectionFactory(ActiveMQManagedConnectionFactory factory, ConnectionManager manager, ActiveMQConnectionRequestInfo info) { + this.factory = factory; + this.manager = manager; + this.info = info; + } + + /** + * @see javax.jms.ConnectionFactory#createConnection() + */ + public Connection createConnection() throws JMSException { + return createConnection(info.copy()); + } + + /** + * @see javax.jms.ConnectionFactory#createConnection(java.lang.String, java.lang.String) + */ + public Connection createConnection(String userName, String password) throws JMSException { + ActiveMQConnectionRequestInfo i = info.copy(); + i.setUserName(userName); + i.setPassword(password); + return createConnection(i); + } + + /** + * @param info + * @return + * @throws JMSException + */ + private Connection createConnection(ActiveMQConnectionRequestInfo info) throws JMSException { + try { + if( info.isUseInboundSessionEnabled() ) { + return new InboundConnectionProxy(); + } + return (Connection) manager.allocateConnection(factory, info); + } + catch (ResourceException e) { + // Throw the root cause if it was a JMSException.. + if (e.getCause() instanceof JMSException) { + throw (JMSException) e.getCause(); + } + log.debug("Connection could not be created:", e); + throw new JMSException(e.getMessage()); + } + } + + /** + * @see javax.naming.Referenceable#getReference() + */ + public Reference getReference() { + return reference; + } + + /** + * @see javax.resource.Referenceable#setReference(javax.naming.Reference) + */ + public void setReference(Reference reference) { + this.reference = reference; + } + + public QueueConnection createQueueConnection() throws JMSException { + return (QueueConnection) createConnection(); + } + + public QueueConnection createQueueConnection(String userName, String password) throws JMSException { + return (QueueConnection) createConnection(userName, password); + } + + public TopicConnection createTopicConnection() throws JMSException { + return (TopicConnection) createConnection(); + } + + public TopicConnection createTopicConnection(String userName, String password) throws JMSException { + return (TopicConnection) createConnection(userName, password); + } +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/ActiveMQConnectionRequestInfo.java b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQConnectionRequestInfo.java new file mode 100755 index 0000000000..8a85ff880a --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQConnectionRequestInfo.java @@ -0,0 +1,174 @@ +/** + * + * Copyright 2004 Hiram Chirino + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import javax.resource.spi.ConnectionRequestInfo; +import java.io.Serializable; + + +/** + * @version $Revision$ + * + * Must override equals and hashCode (JCA spec 16.4) + */ +public class ActiveMQConnectionRequestInfo implements ConnectionRequestInfo, Serializable, Cloneable { + + private static final long serialVersionUID = -5754338187296859149L; + + private String userName; + private String password; + private String serverUrl; + private String clientid; + private Boolean useInboundSession; + + public ActiveMQConnectionRequestInfo copy() { + try { + return (ActiveMQConnectionRequestInfo) clone(); + } + catch (CloneNotSupportedException e) { + throw new RuntimeException("Could not clone: ", e); + } + } + + + /** + * @see javax.resource.spi.ConnectionRequestInfo#hashCode() + */ + public int hashCode() { + int rc = 0; + if (useInboundSession != null) { + rc ^= useInboundSession.hashCode(); + } + if (serverUrl != null) { + rc ^= serverUrl.hashCode(); + } + return rc; + } + + + /** + * @see javax.resource.spi.ConnectionRequestInfo#equals(java.lang.Object) + */ + public boolean equals(Object o) { + if (o == null) { + return false; + } + if (!getClass().equals(o.getClass())) { + return false; + } + ActiveMQConnectionRequestInfo i = (ActiveMQConnectionRequestInfo) o; + if ( notEqual(serverUrl, i.serverUrl) ) { + return false; + } + if ( notEqual(useInboundSession, i.useInboundSession) ) { + return false; + } + return true; + } + + + /** + * @param i + * @return + */ + private boolean notEqual(Object o1, Object o2) { + return (o1 == null ^ o2 == null) || (o1 != null && !o1.equals(o2)); + } + + /** + * @return Returns the url. + */ + public String getServerUrl() { + return serverUrl; + } + + /** + * @param url The url to set. + */ + public void setServerUrl(String url) { + this.serverUrl = url; + } + + /** + * @return Returns the password. + */ + public String getPassword() { + return password; + } + + /** + * @param password The password to set. + */ + public void setPassword(String password) { + this.password = password; + } + + /** + * @return Returns the userid. + */ + public String getUserName() { + return userName; + } + + /** + * @param userid The userid to set. + */ + public void setUserName(String userid) { + this.userName = userid; + } + + /** + * @return Returns the clientid. + */ + public String getClientid() { + return clientid; + } + + /** + * @param clientid The clientid to set. + */ + public void setClientid(String clientid) { + this.clientid = clientid; + } + + public String toString() { + return "ActiveMQConnectionRequestInfo{ " + + "userName = '" + userName + "' " + + ", serverUrl = '" + serverUrl + "' " + + ", clientid = '" + clientid + "' " + + ", userName = '" + userName + "' " + + ", useInboundSession = '" + useInboundSession + "' " + + " }"; + } + + + public Boolean getUseInboundSession() { + return useInboundSession; + } + + + public void setUseInboundSession(Boolean useInboundSession) { + this.useInboundSession = useInboundSession; + } + + + public boolean isUseInboundSessionEnabled() { + return useInboundSession!=null && useInboundSession.booleanValue(); + } +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/ActiveMQEndpointActivationKey.java b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQEndpointActivationKey.java new file mode 100755 index 0000000000..4a8c986e7f --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQEndpointActivationKey.java @@ -0,0 +1,82 @@ +/** + * + * Copyright 2004 Hiram Chirino + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import javax.resource.spi.endpoint.MessageEndpointFactory; + + +public class ActiveMQEndpointActivationKey { + final private MessageEndpointFactory messageEndpointFactory; + final private ActiveMQActivationSpec activationSpec; + + /** + * @return Returns the activationSpec. + */ + public ActiveMQActivationSpec getActivationSpec() { + return activationSpec; + } + + /** + * @return Returns the messageEndpointFactory. + */ + public MessageEndpointFactory getMessageEndpointFactory() { + return messageEndpointFactory; + } + + /** + * For testing + */ + ActiveMQEndpointActivationKey() { + this(null, null); + } + + /** + * @param messageEndpointFactory + * @param activationSpec + */ + public ActiveMQEndpointActivationKey(MessageEndpointFactory messageEndpointFactory, ActiveMQActivationSpec activationSpec) { + this.messageEndpointFactory = messageEndpointFactory; + this.activationSpec = activationSpec; + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return messageEndpointFactory.hashCode() ^ activationSpec.hashCode(); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + ActiveMQEndpointActivationKey o = (ActiveMQEndpointActivationKey) obj; + + //Per the 12.4.9 spec: + // MessageEndpointFactory does not implement equals() + // ActivationSpec does not implement equals() + return o.activationSpec == activationSpec && o.messageEndpointFactory == messageEndpointFactory; + } +} \ No newline at end of file diff --git a/activemq-ra/src/main/java/org/activemq/ra/ActiveMQEndpointWorker.java b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQEndpointWorker.java new file mode 100755 index 0000000000..4fd424efae --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQEndpointWorker.java @@ -0,0 +1,273 @@ +/** + * + * Copyright 2004 Hiram Chirino + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import java.lang.reflect.Method; + +import javax.jms.Connection; +import javax.jms.ConnectionConsumer; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.Session; +import javax.jms.Topic; +import javax.resource.ResourceException; +import javax.resource.spi.endpoint.MessageEndpointFactory; +import javax.resource.spi.work.Work; +import javax.resource.spi.work.WorkException; +import javax.resource.spi.work.WorkManager; + +import org.activemq.ActiveMQConnection; +import org.activemq.command.ActiveMQDestination; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @version $Revision$ $Date$ + */ +public class ActiveMQEndpointWorker { + + private static final Log log = LogFactory.getLog(ActiveMQEndpointWorker.class); + public static final Method ON_MESSAGE_METHOD; + + private static final long INITIAL_RECONNECT_DELAY = 1000; // 1 second. + private static final long MAX_RECONNECT_DELAY = 1000*30; // 30 seconds. + private static final ThreadLocal threadLocal = new ThreadLocal(); + + static { + try { + ON_MESSAGE_METHOD = MessageListener.class.getMethod("onMessage", new Class[]{Message.class}); + } + catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + protected ActiveMQResourceAdapter adapter; + protected ActiveMQEndpointActivationKey endpointActivationKey; + protected MessageEndpointFactory endpointFactory; + protected WorkManager workManager; + protected boolean transacted; + + + private ConnectionConsumer consumer; + private ServerSessionPoolImpl serverSessionPool; + private ActiveMQDestination dest; + private boolean running; + private Work connectWork; + protected ActiveMQConnection connection; + + private long reconnectDelay=INITIAL_RECONNECT_DELAY; + + + /** + * @param s + */ + public static void safeClose(Session s) { + try { + if (s != null) { + s.close(); + } + } + catch (JMSException e) { + } + } + + /** + * @param c + */ + public static void safeClose(Connection c) { + try { + if (c != null) { + c.close(); + } + } + catch (JMSException e) { + } + } + + /** + * @param cc + */ + public static void safeClose(ConnectionConsumer cc) { + try { + if (cc != null) { + cc.close(); + } + } + catch (JMSException e) { + } + } + + public ActiveMQEndpointWorker(final ActiveMQResourceAdapter adapter, ActiveMQEndpointActivationKey key) throws ResourceException { + this.endpointActivationKey = key; + this.adapter = adapter; + this.endpointFactory = endpointActivationKey.getMessageEndpointFactory(); + this.workManager = adapter.getBootstrapContext().getWorkManager(); + try { + this.transacted = endpointFactory.isDeliveryTransacted(ON_MESSAGE_METHOD); + } + catch (NoSuchMethodException e) { + throw new ResourceException("Endpoint does not implement the onMessage method."); + } + + connectWork = new Work() { + + public void release() { + } + + synchronized public void run() { + if( !isRunning() ) + return; + if( connection!=null ) + return; + + ActiveMQActivationSpec activationSpec = endpointActivationKey.getActivationSpec(); + try { + connection = adapter.makeConnection(activationSpec); + connection.start(); + connection.setExceptionListener(new ExceptionListener() { + public void onException(JMSException error) { + reconnect(error); + } + }); + + if (activationSpec.isDurableSubscription()) { + consumer = connection.createDurableConnectionConsumer( + (Topic) dest, + activationSpec.getSubscriptionName(), + emptyToNull(activationSpec.getMessageSelector()), + serverSessionPool, + activationSpec.getMaxMessagesPerSessionsIntValue(), + activationSpec.getNoLocalBooleanValue()); + } else { + consumer = connection.createConnectionConsumer( + dest, + emptyToNull(activationSpec.getMessageSelector()), + serverSessionPool, + activationSpec.getMaxMessagesPerSessionsIntValue(), + activationSpec.getNoLocalBooleanValue()); + } + + } catch (JMSException error) { + log.debug("Fail to to connect: "+error, error); + reconnect(error); + } + } + }; + + ActiveMQActivationSpec activationSpec = endpointActivationKey.getActivationSpec(); + if ("javax.jms.Queue".equals(activationSpec.getDestinationType())) { + dest = new ActiveMQQueue(activationSpec.getDestination()); + } else if ("javax.jms.Topic".equals(activationSpec.getDestinationType())) { + dest = new ActiveMQTopic(activationSpec.getDestination()); + } else { + throw new ResourceException("Unknown destination type: " + activationSpec.getDestinationType()); + } + + } + + synchronized public void start() throws WorkException, ResourceException { + if (running) + return; + running = true; + + log.debug("Starting"); + serverSessionPool = new ServerSessionPoolImpl(this, endpointActivationKey.getActivationSpec().getMaxSessionsIntValue()); + connect(); + log.debug("Started"); + } + + /** + * + */ + synchronized public void stop() throws InterruptedException { + if (!running) + return; + running = false; + serverSessionPool.close(); + disconnect(); + } + + private boolean isRunning() { + return running; + } + + synchronized private void connect() { + if (!running) + return; + + try { + workManager.scheduleWork(connectWork, WorkManager.INDEFINITE, null, null); + } catch (WorkException e) { + running = false; + log.error("Work Manager did not accept work: ",e); + } + } + + /** + * + */ + synchronized private void disconnect() { + safeClose(consumer); + consumer=null; + safeClose(connection); + connection=null; + } + + synchronized private void reconnect(JMSException error) { + log.debug("Reconnect cause: ", error); + // Only log errors if the server is really down.. And not a temp failure. + if( reconnectDelay == MAX_RECONNECT_DELAY ) { + log.info("Endpoint connection to JMS broker failed: "+error.getMessage()); + log.info("Endpoint will try to reconnect to the JMS broker in "+(MAX_RECONNECT_DELAY/1000)+" seconds"); + } + try { + disconnect(); + Thread.sleep(reconnectDelay); + + // Use exponential rollback. + reconnectDelay*=2; + if( reconnectDelay > MAX_RECONNECT_DELAY ) + reconnectDelay = MAX_RECONNECT_DELAY; + + connect(); + } catch (InterruptedException e) { + } + } + + protected void registerThreadSession(Session session) { + threadLocal.set(session); + } + + protected void unregisterThreadSession(Session session) { + threadLocal.set(null); + } + + private String emptyToNull(String value) { + if (value == null || value.length() == 0) { + return null; + } + return value; + } + +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/ActiveMQManagedConnection.java b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQManagedConnection.java new file mode 100755 index 0000000000..aa8321de3e --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQManagedConnection.java @@ -0,0 +1,421 @@ +/** + * + * Copyright 2004 Hiram Chirino + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Iterator; + +import javax.jms.Connection; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.resource.ResourceException; +import javax.resource.spi.ConnectionEvent; +import javax.resource.spi.ConnectionEventListener; +import javax.resource.spi.ConnectionRequestInfo; +import javax.resource.spi.LocalTransaction; +import javax.resource.spi.ManagedConnection; +import javax.resource.spi.ManagedConnectionMetaData; +import javax.security.auth.Subject; +import javax.transaction.xa.XAResource; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.activemq.ActiveMQConnection; +import org.activemq.LocalTransactionEventListener; +import org.activemq.TransactionContext; + +/** + * ActiveMQManagedConnection maps to real physical connection to the + * server. Since a ManagedConnection has to provide a transaction + * managment interface to the physical connection, and sessions + * are the objects implement transaction managment interfaces in + * the JMS API, this object also maps to a singe physical JMS session. + *

+ * The side-effect is that JMS connection the application gets + * will allways create the same session object. This is good if + * running in an app server since the sessions are elisted in the + * context transaction. This is bad if used outside of an app + * server since the user may be trying to create 2 different + * sessions to coordinate 2 different uow. + * + * @version $Revision$ + */ +public class ActiveMQManagedConnection implements ManagedConnection, ExceptionListener { // TODO: , DissociatableManagedConnection { + + private static final Log log = LogFactory.getLog(ActiveMQManagedConnection.class); + + private PrintWriter logWriter; + + private final ActiveMQConnection physicalConnection; + private final TransactionContext transactionContext; + private final ArrayList proxyConnections = new ArrayList(); + private final ArrayList listeners = new ArrayList(); + private final LocalAndXATransaction localAndXATransaction; + + private Subject subject; + private ActiveMQConnectionRequestInfo info; + private boolean destoryed; + + public ActiveMQManagedConnection(Subject subject, ActiveMQConnection physicalConnection, ActiveMQConnectionRequestInfo info) throws ResourceException { + try { + this.subject = subject; + this.info = info; + this.physicalConnection = physicalConnection; + this.transactionContext = new TransactionContext(physicalConnection); + + this.localAndXATransaction = new LocalAndXATransaction(transactionContext) { + public void setInManagedTx(boolean inManagedTx) throws JMSException { + super.setInManagedTx(inManagedTx); + Iterator iterator = proxyConnections.iterator(); + while (iterator.hasNext()) { + ManagedConnectionProxy proxy = (ManagedConnectionProxy) iterator.next(); + proxy.setUseSharedTxContext(inManagedTx); + } + } + }; + + this.transactionContext.setLocalTransactionEventListener( new LocalTransactionEventListener() { + public void beginEvent() { + fireBeginEvent(); + } + public void commitEvent() { + fireCommitEvent(); + } + public void rollbackEvent() { + fireRollbackEvent(); + } + }); + + physicalConnection.setExceptionListener(this); + } catch (JMSException e) { + throw new ResourceException("Could not create a new connection: "+e.getMessage(), e); + } + } + + public boolean isInManagedTx() { + return localAndXATransaction.isInManagedTx(); + } + + static public boolean matches(Object x, Object y) { + if (x == null ^ y == null) { + return false; + } + if (x != null && !x.equals(y)) { + return false; + } + return true; + } + + public void associate(Subject subject, ActiveMQConnectionRequestInfo info) throws JMSException { + + // Do we need to change the associated userid/password + if( !matches(info.getUserName(), this.info.getUserName()) || !matches(info.getPassword(), this.info.getPassword()) ) { + ((ActiveMQConnection)physicalConnection).changeUserInfo(info.getUserName(), info.getPassword()); + } + + // Do we need to set the clientId? + if( info.getClientid()!=null && info.getClientid().length()>0 ) + physicalConnection.setClientID(info.getClientid()); + + this.subject = subject; + this.info = info; + } + + public Connection getPhysicalConnection() { + return physicalConnection; + } + + private void fireBeginEvent() { + ConnectionEvent event = new ConnectionEvent(ActiveMQManagedConnection.this, + ConnectionEvent.LOCAL_TRANSACTION_STARTED); + Iterator iterator = listeners.iterator(); + while (iterator.hasNext()) { + ConnectionEventListener l = (ConnectionEventListener) iterator.next(); + l.localTransactionStarted(event); + } + } + + private void fireCommitEvent() { + ConnectionEvent event = new ConnectionEvent(ActiveMQManagedConnection.this, + ConnectionEvent.LOCAL_TRANSACTION_COMMITTED); + Iterator iterator = listeners.iterator(); + while (iterator.hasNext()) { + ConnectionEventListener l = (ConnectionEventListener) iterator.next(); + l.localTransactionCommitted(event); + } + } + + private void fireRollbackEvent() { + ConnectionEvent event = new ConnectionEvent(ActiveMQManagedConnection.this, + ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK); + Iterator iterator = listeners.iterator(); + while (iterator.hasNext()) { + ConnectionEventListener l = (ConnectionEventListener) iterator.next(); + l.localTransactionRolledback(event); + } + } + + private void fireCloseEvent(ManagedConnectionProxy proxy) { + ConnectionEvent event = new ConnectionEvent(ActiveMQManagedConnection.this, + ConnectionEvent.CONNECTION_CLOSED); + event.setConnectionHandle(proxy); + + Iterator iterator = listeners.iterator(); + while (iterator.hasNext()) { + ConnectionEventListener l = (ConnectionEventListener) iterator.next(); + l.connectionClosed(event); + } + } + + private void fireErrorOccurredEvent(Exception error) { + ConnectionEvent event = new ConnectionEvent(ActiveMQManagedConnection.this, + ConnectionEvent.CONNECTION_ERROR_OCCURRED, error); + Iterator iterator = listeners.iterator(); + while (iterator.hasNext()) { + ConnectionEventListener l = (ConnectionEventListener) iterator.next(); + l.connectionErrorOccurred(event); + } + } + + /** + * @see javax.resource.spi.ManagedConnection#getConnection(javax.security.auth.Subject, + * javax.resource.spi.ConnectionRequestInfo) + */ + public Object getConnection(Subject subject, ConnectionRequestInfo info) + throws ResourceException { + ManagedConnectionProxy proxy = new ManagedConnectionProxy(this); + proxyConnections.add(proxy); + return proxy; + } + + private boolean isDestroyed() { + return destoryed; + } + + /** + * Close down the physical connection to the server. + * + * @see javax.resource.spi.ManagedConnection#destroy() + */ + public void destroy() throws ResourceException { + // Have we allready been destroyed?? + if (isDestroyed()) { + return; + } + + cleanup(); + + try { + physicalConnection.close(); + destoryed = true; + } catch (JMSException e) { + log.info("Error occured during close of a JMS connection.", e); + } + } + + /** + * Cleans up all proxy handles attached to this physical connection so that + * they cannot be used anymore. + * + * @see javax.resource.spi.ManagedConnection#cleanup() + */ + public void cleanup() throws ResourceException { + + // Have we allready been destroyed?? + if (isDestroyed()) { + return; + } + + Iterator iterator = proxyConnections.iterator(); + while (iterator.hasNext()) { + ManagedConnectionProxy proxy = (ManagedConnectionProxy) iterator.next(); + proxy.cleanup(); + } + proxyConnections.clear(); + localAndXATransaction.cleanup(); + + try { + ((ActiveMQConnection)physicalConnection).cleanup(); + } catch (JMSException e) { + throw new ResourceException("Could cleanup the ActiveMQ connection: "+e, e); + } + + } + + /** + * @see javax.resource.spi.ManagedConnection#associateConnection(java.lang.Object) + */ + public void associateConnection(Object connection) throws ResourceException { + throw new ResourceException("Not supported."); + } + + /** + * @see javax.resource.spi.ManagedConnection#addConnectionEventListener(javax.resource.spi.ConnectionEventListener) + */ + public void addConnectionEventListener(ConnectionEventListener listener) { + listeners.add(listener); + } + + /** + * @see javax.resource.spi.ManagedConnection#removeConnectionEventListener(javax.resource.spi.ConnectionEventListener) + */ + public void removeConnectionEventListener(ConnectionEventListener listener) { + listeners.remove(listener); + } + + /** + * @see javax.resource.spi.ManagedConnection#getXAResource() + */ + public XAResource getXAResource() throws ResourceException { + return localAndXATransaction; + } + + /** + * @see javax.resource.spi.ManagedConnection#getLocalTransaction() + */ + public LocalTransaction getLocalTransaction() throws ResourceException { + return localAndXATransaction; + } + + /** + * @see javax.resource.spi.ManagedConnection#getMetaData() + */ + public ManagedConnectionMetaData getMetaData() throws ResourceException { + return new ManagedConnectionMetaData() { + + public String getEISProductName() throws ResourceException { + if (physicalConnection == null) { + throw new ResourceException("Not connected."); + } + try { + return physicalConnection.getMetaData().getJMSProviderName(); + } + catch (JMSException e) { + throw new ResourceException("Error accessing provider.", e); + } + } + + public String getEISProductVersion() throws ResourceException { + if (physicalConnection == null) { + throw new ResourceException("Not connected."); + } + try { + return physicalConnection.getMetaData().getProviderVersion(); + } + catch (JMSException e) { + throw new ResourceException("Error accessing provider.", e); + } + } + + public int getMaxConnections() throws ResourceException { + if (physicalConnection == null) { + throw new ResourceException("Not connected."); + } + return Integer.MAX_VALUE; + } + + public String getUserName() throws ResourceException { + if (physicalConnection == null) { + throw new ResourceException("Not connected."); + } + try { + return physicalConnection.getClientID(); + } + catch (JMSException e) { + throw new ResourceException("Error accessing provider.", e); + } + } + }; + } + + /** + * @see javax.resource.spi.ManagedConnection#setLogWriter(java.io.PrintWriter) + */ + public void setLogWriter(PrintWriter logWriter) throws ResourceException { + this.logWriter = logWriter; + } + + /** + * @see javax.resource.spi.ManagedConnection#getLogWriter() + */ + public PrintWriter getLogWriter() throws ResourceException { + return logWriter; + } + + /** + * @param subject + * @param info + * @return + */ + public boolean matches(Subject subject, ConnectionRequestInfo info) { + + // Check to see if it is our info class + if (info == null) { + return false; + } + if (info.getClass() != ActiveMQConnectionRequestInfo.class) { + return false; + } + + // Do the subjects match? + if (subject == null ^ this.subject == null) { + return false; + } + if (subject != null && !subject.equals(this.subject)) { + return false; + } + + // Does the info match? + return info.equals(this.info); + } + + /** + * When a proxy is closed this cleans up the proxy and notifys the + * ConnectionEventListeners that a connection closed. + * + * @param proxy + */ + public void proxyClosedEvent(ManagedConnectionProxy proxy) { + proxyConnections.remove(proxy); + proxy.cleanup(); + fireCloseEvent(proxy); + } + + public void onException(JMSException e) { + log.warn("Connection failed: "+e); + log.debug("Cause: ", e); + + // Let any active proxy connections know that exception occured. + for (Iterator iter = proxyConnections.iterator(); iter.hasNext();) { + ManagedConnectionProxy proxy = (ManagedConnectionProxy) iter.next(); + proxy.onException(e); + } + // Let the container know that the error occured. + fireErrorOccurredEvent(e); + } + + /** + * @return Returns the transactionContext. + */ + public TransactionContext getTransactionContext() { + return transactionContext; + } + +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/ActiveMQManagedConnectionFactory.java b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQManagedConnectionFactory.java new file mode 100755 index 0000000000..5dc3592895 --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQManagedConnectionFactory.java @@ -0,0 +1,180 @@ +/** + * + * Copyright 2004 Hiram Chirino + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import java.io.PrintWriter; +import java.util.Iterator; +import java.util.Set; + +import javax.jms.JMSException; +import javax.resource.ResourceException; +import javax.resource.spi.ConnectionManager; +import javax.resource.spi.ConnectionRequestInfo; +import javax.resource.spi.ManagedConnection; +import javax.resource.spi.ManagedConnectionFactory; +import javax.resource.spi.ResourceAdapter; +import javax.resource.spi.ResourceAdapterAssociation; +import javax.security.auth.Subject; + +/** + * @version $Revisio n$ + * + * TODO: Must override equals and hashCode (JCA spec 16.4) + */ +public class ActiveMQManagedConnectionFactory implements + ManagedConnectionFactory, ResourceAdapterAssociation { + + private static final long serialVersionUID = 6196921962230582875L; + + private ActiveMQResourceAdapter adapter; + private PrintWriter logWriter; + private ActiveMQConnectionRequestInfo info = new ActiveMQConnectionRequestInfo(); + + public void setResourceAdapter(ResourceAdapter adapter) throws ResourceException { + this.adapter = (ActiveMQResourceAdapter) adapter; + ActiveMQConnectionRequestInfo baseInfo = this.adapter.getInfo().copy(); + if( info.getClientid()==null ) + info.setClientid(baseInfo.getClientid()); + if( info.getPassword()==null ) + info.setPassword(baseInfo.getPassword()); + if( info.getServerUrl()==null ) + info.setServerUrl(baseInfo.getServerUrl()); + if( info.getUseInboundSession()==null ) + info.setUseInboundSession(baseInfo.getUseInboundSession()); + if( info.getUserName()==null ) + info.setUserName(baseInfo.getUserName()); + } + + public ResourceAdapter getResourceAdapter() { + return adapter; + } + + /** + * @see javax.resource.spi.ManagedConnectionFactory#createConnectionFactory(javax.resource.spi.ConnectionManager) + */ + public Object createConnectionFactory(ConnectionManager manager) throws ResourceException { + return new ActiveMQConnectionFactory(this, manager, info); + } + + /** + * This is used when not running in an app server. For now we are creating a + * ConnectionFactory that has our SimpleConnectionManager implementation but + * it may be a better idea to not support this. The JMS api will have many quirks + * the user may not expect when running through the resource adapter. + * + * @see javax.resource.spi.ManagedConnectionFactory#createConnectionFactory() + */ + public Object createConnectionFactory() throws ResourceException { + return new ActiveMQConnectionFactory(this, new SimpleConnectionManager(), info); + } + + /** + * @see javax.resource.spi.ManagedConnectionFactory#createManagedConnection(javax.security.auth.Subject, + * javax.resource.spi.ConnectionRequestInfo) + */ + public ManagedConnection createManagedConnection(Subject subject, ConnectionRequestInfo info) throws ResourceException { + try { + ActiveMQConnectionRequestInfo amqInfo = (ActiveMQConnectionRequestInfo)info; + return new ActiveMQManagedConnection(subject, adapter.makeConnection(amqInfo), amqInfo); + } catch (JMSException e) { + throw new ResourceException("Could not create connection.", e); + } + } + + /** + * @see javax.resource.spi.ManagedConnectionFactory#matchManagedConnections(java.util.Set, + * javax.security.auth.Subject, + * javax.resource.spi.ConnectionRequestInfo) + */ + public ManagedConnection matchManagedConnections(Set connections, Subject subject, ConnectionRequestInfo info) throws ResourceException { + Iterator iterator = connections.iterator(); + while (iterator.hasNext()) { + ActiveMQManagedConnection c = (ActiveMQManagedConnection) iterator.next(); + if (c.matches(subject, info)) { + try { + c.associate(subject, (ActiveMQConnectionRequestInfo) info); + return c; + } catch (JMSException e) { + throw new ResourceException(e); + } + } + } + return null; + } + + /** + * @see javax.resource.spi.ManagedConnectionFactory#setLogWriter(java.io.PrintWriter) + */ + public void setLogWriter(PrintWriter logWriter) throws ResourceException { + this.logWriter = logWriter; + } + + /** + * @see javax.resource.spi.ManagedConnectionFactory#getLogWriter() + */ + public PrintWriter getLogWriter() throws ResourceException { + return logWriter; + } + + /////////////////////////////////////////////////////////////////////////// + // + // Bean setters and getters. + // + /////////////////////////////////////////////////////////////////////////// + + public String getClientid() { + return info.getClientid(); + } + + public String getPassword() { + return info.getPassword(); + } + + public String getServerUrl() { + return info.getServerUrl(); + } + + public String getUserName() { + return info.getUserName(); + } + + public void setClientid(String clientid) { + info.setClientid(clientid); + } + + public void setPassword(String password) { + info.setPassword(password); + } + + public void setServerUrl(String url) { + info.setServerUrl(url); + } + + public void setUserName(String userid) { + info.setUserName(userid); + } + + public Boolean getUseInboundSession() { + return info.getUseInboundSession(); + } + + public void setUseInboundSession(Boolean useInboundSession) { + info.setUseInboundSession(useInboundSession); + } +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/ActiveMQResourceAdapter.java b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQResourceAdapter.java new file mode 100755 index 0000000000..251f6b9250 --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/ActiveMQResourceAdapter.java @@ -0,0 +1,384 @@ +/** + * + * Copyright 2004 Hiram Chirino + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.XAConnection; +import javax.jms.XASession; +import javax.resource.NotSupportedException; +import javax.resource.ResourceException; +import javax.resource.spi.ActivationSpec; +import javax.resource.spi.BootstrapContext; +import javax.resource.spi.ResourceAdapter; +import javax.resource.spi.ResourceAdapterInternalException; +import javax.resource.spi.endpoint.MessageEndpointFactory; +import javax.transaction.xa.XAResource; + +import org.activemq.ActiveMQConnection; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.broker.BrokerFactory; +import org.activemq.broker.BrokerService; +import org.activemq.util.ServiceSupport; + +/** + * Knows how to connect to one ActiveMQ server. It can then activate endpoints + * and deliver messages to those end points using the connection configure in the + * resource adapter.

Must override equals and hashCode (JCA spec 16.4) + * + * @org.xbean.XBean element="resourceAdapter" rootElement="true" + * description="The JCA Resource Adaptor for ActiveMQ" + * + * @version $Revision$ + */ +public class ActiveMQResourceAdapter implements ResourceAdapter { + + private final HashMap endpointWorkers = new HashMap(); + private final ActiveMQConnectionRequestInfo info = new ActiveMQConnectionRequestInfo(); + + private BootstrapContext bootstrapContext; + private String brokerXmlConfig; + private BrokerService broker; + + public ActiveMQResourceAdapter() { + } + + /** + * @see javax.resource.spi.ResourceAdapter#start(javax.resource.spi.BootstrapContext) + */ + public void start(BootstrapContext bootstrapContext) throws ResourceAdapterInternalException { + this.bootstrapContext = bootstrapContext; + if (brokerXmlConfig!=null && brokerXmlConfig.trim().length()>0 ) { + try { + broker = BrokerFactory.createBroker(new URI(brokerXmlConfig)); + broker.start(); + } catch (Throwable e) { + throw new ResourceAdapterInternalException("Failed to startup an embedded broker: "+e, e); + } + } + } + + public ActiveMQConnection makeConnection() throws JMSException { + return makeConnection(info); + } + + /** + */ + public ActiveMQConnection makeConnection(ActiveMQConnectionRequestInfo info) throws JMSException { + + ActiveMQConnectionFactory connectionFactory = createConnectionFactory(info); + String userName = info.getUserName(); + String password = info.getPassword(); + ActiveMQConnection physicalConnection = (ActiveMQConnection) connectionFactory.createConnection(userName, password); + + String clientId = info.getClientid(); + if (clientId != null) { + physicalConnection.setClientID(clientId); + } + return physicalConnection; + } + + /** + * @param activationSpec + */ + public ActiveMQConnection makeConnection(ActiveMQActivationSpec activationSpec) throws JMSException { + ActiveMQConnectionFactory connectionFactory = createConnectionFactory(info); + String userName = defaultValue(activationSpec.getUserName(), info.getUserName()); + String password = defaultValue(activationSpec.getPassword(), info.getPassword()); + ActiveMQConnection physicalConnection = (ActiveMQConnection) connectionFactory.createConnection(userName, password); + if (activationSpec.isDurableSubscription()) { + physicalConnection.setClientID(activationSpec.getClientId()); + } + return physicalConnection; + } + + /** + * @param info + * @return + * @throws JMSException + * @throws URISyntaxException + */ + synchronized private ActiveMQConnectionFactory createConnectionFactory(ActiveMQConnectionRequestInfo info) throws JMSException { + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(info.getServerUrl()); + + return factory; + } + + private String defaultValue(String value, String defaultValue) { + if (value != null) + return value; + return defaultValue; + } + + /** + * @see javax.resource.spi.ResourceAdapter#stop() + */ + public void stop() { + while (endpointWorkers.size() > 0) { + ActiveMQEndpointActivationKey key = (ActiveMQEndpointActivationKey) endpointWorkers.keySet().iterator().next(); + endpointDeactivation(key.getMessageEndpointFactory(), key.getActivationSpec()); + } + if (broker != null) { + ServiceSupport.dispose(broker); + broker = null; + } + this.bootstrapContext = null; + } + + /** + * @return + */ + public BootstrapContext getBootstrapContext() { + return bootstrapContext; + } + + /** + * @see javax.resource.spi.ResourceAdapter#endpointActivation(javax.resource.spi.endpoint.MessageEndpointFactory, + * javax.resource.spi.ActivationSpec) + */ + public void endpointActivation(MessageEndpointFactory endpointFactory, ActivationSpec activationSpec) + throws ResourceException { + + // spec section 5.3.3 + if (activationSpec.getResourceAdapter() != this) { + throw new ResourceException("Activation spec not initialized with this ResourceAdapter instance"); + } + + if (activationSpec.getClass().equals(ActiveMQActivationSpec.class)) { + + ActiveMQEndpointActivationKey key = new ActiveMQEndpointActivationKey(endpointFactory, + (ActiveMQActivationSpec) activationSpec); + // This is weird.. the same endpoint activated twice.. must be a + // container error. + if (endpointWorkers.containsKey(key)) { + throw new IllegalStateException("Endpoint previously activated"); + } + + ActiveMQEndpointWorker worker = new ActiveMQEndpointWorker(this, key); + + endpointWorkers.put(key, worker); + worker.start(); + + } else { + throw new NotSupportedException("That type of ActicationSpec not supported: " + activationSpec.getClass()); + } + + } + + /** + * @see javax.resource.spi.ResourceAdapter#endpointDeactivation(javax.resource.spi.endpoint.MessageEndpointFactory, + * javax.resource.spi.ActivationSpec) + */ + public void endpointDeactivation(MessageEndpointFactory endpointFactory, ActivationSpec activationSpec) { + + if (activationSpec.getClass().equals(ActiveMQActivationSpec.class)) { + ActiveMQEndpointActivationKey key = new ActiveMQEndpointActivationKey(endpointFactory, (ActiveMQActivationSpec) activationSpec); + ActiveMQEndpointWorker worker = (ActiveMQEndpointWorker) endpointWorkers.remove(key); + if (worker == null) { + // This is weird.. that endpoint was not activated.. oh well.. + // this method + // does not throw exceptions so just return. + return; + } + try { + worker.stop(); + } catch (InterruptedException e) { + // We interrupted.. we won't throw an exception but will stop + // waiting for the worker + // to stop.. we tried our best. Keep trying to interrupt the + // thread. + Thread.currentThread().interrupt(); + } + + } + + } + + /** + * We only connect to one resource manager per ResourceAdapter instance, so + * any ActivationSpec will return the same XAResource. + * + * @see javax.resource.spi.ResourceAdapter#getXAResources(javax.resource.spi.ActivationSpec[]) + */ + public XAResource[] getXAResources(ActivationSpec[] activationSpecs) throws ResourceException { + Connection connection = null; + try { + connection = makeConnection(); + if (connection instanceof XAConnection) { + XASession session = ((XAConnection) connection).createXASession(); + XAResource xaResource = session.getXAResource(); + return new XAResource[] { xaResource }; + } else { + return new XAResource[] {}; + } + } catch (JMSException e) { + throw new ResourceException(e); + } finally { + try { + connection.close(); + } catch (Throwable ignore) { + } + } + } + + // /////////////////////////////////////////////////////////////////////// + // + // Java Bean getters and setters for this ResourceAdapter class. + // + // /////////////////////////////////////////////////////////////////////// + + /** + * @return + */ + public String getClientid() { + return emptyToNull(info.getClientid()); + } + + /** + * @return + */ + public String getPassword() { + return emptyToNull(info.getPassword()); + } + + /** + * @return + */ + public String getServerUrl() { + return info.getServerUrl(); + } + + /** + * @return + */ + public String getUserName() { + return emptyToNull(info.getUserName()); + } + + /** + * @param clientid + */ + public void setClientid(String clientid) { + info.setClientid(clientid); + } + + /** + * @param password + */ + public void setPassword(String password) { + info.setPassword(password); + } + + /** + * @param url + */ + public void setServerUrl(String url) { + info.setServerUrl(url); + } + + /** + * @param userid + */ + public void setUserName(String userid) { + info.setUserName(userid); + } + + public String getBrokerXmlConfig() { + return brokerXmlConfig; + } + + /** + * Sets the XML + * configuration file used to configure the ActiveMQ broker via Spring + * if using embedded mode. + * + * @param brokerXmlConfig + * is the filename which is assumed to be on the classpath unless + * a URL is specified. So a value of foo/bar.xml + * would be assumed to be on the classpath whereas + * file:dir/file.xml would use the file system. + * Any valid URL string is supported. + * @see #setUseEmbeddedBroker(Boolean) + */ + public void setBrokerXmlConfig(String brokerXmlConfig) { + this.brokerXmlConfig=brokerXmlConfig; + } + + /** + * @return Returns the info. + */ + public ActiveMQConnectionRequestInfo getInfo() { + return info; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ActiveMQResourceAdapter)) { + return false; + } + + final ActiveMQResourceAdapter activeMQResourceAdapter = (ActiveMQResourceAdapter) o; + + if (!info.equals(activeMQResourceAdapter.info)) { + return false; + } + if ( notEqual(brokerXmlConfig, activeMQResourceAdapter.brokerXmlConfig) ) { + return false; + } + + return true; + } + + private boolean notEqual(Object o1, Object o2) { + return (o1 == null ^ o2 == null) || (o1 != null && !o1.equals(o2)); + } + + + public int hashCode() { + int result; + result = info.hashCode(); + if( brokerXmlConfig !=null ) { + result ^= brokerXmlConfig.hashCode(); + } + return result; + } + + private String emptyToNull(String value) { + if (value == null || value.length() == 0) { + return null; + } + return value; + } + + public Boolean getUseInboundSession() { + return info.getUseInboundSession(); + } + + public void setUseInboundSession(Boolean useInboundSession) { + info.setUseInboundSession(useInboundSession); + } + + +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/InboundConnectionProxy.java b/activemq-ra/src/main/java/org/activemq/ra/InboundConnectionProxy.java new file mode 100755 index 0000000000..766d6270e3 --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/InboundConnectionProxy.java @@ -0,0 +1,100 @@ +/** + * + * Copyright 2004 Protique Ltd + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import javax.jms.*; + +import org.activemq.ActiveMQConnectionMetaData; + +/** + * A {@link Connection} implementation which can be used with the ActiveMQ JCA + * Resource Adapter to publish messages using the same JMS session that is used to dispatch + * messages. + * + * @version $Revision$ + */ +public class InboundConnectionProxy implements Connection, QueueConnection, TopicConnection { + + public Session createSession(boolean transacted, int ackMode) throws JMSException { + // TODO we could decide to barf if someone passes in incompatible options + return new InboundSessionProxy(); + } + + public QueueSession createQueueSession(boolean transacted, int ackMode) throws JMSException { + // TODO we could decide to barf if someone passes in incompatible options + return new InboundSessionProxy(); + } + + public TopicSession createTopicSession(boolean transacted, int ackMode) throws JMSException { + // TODO we could decide to barf if someone passes in incompatible options + return new InboundSessionProxy(); + } + + public void start() throws JMSException { + // the JCA RA is in control of this + } + + public void stop() throws JMSException { + // the JCA RA is in control of this + } + + public void close() throws JMSException { + // the JCA RA is in control of this + } + + public ConnectionMetaData getMetaData() throws JMSException { + return ActiveMQConnectionMetaData.INSTANCE; + } + + public String getClientID() throws JMSException { + throw createNotSupported("getClientID()"); + } + + public void setClientID(String s) throws JMSException { + throw createNotSupported("setClient()"); + } + + public ExceptionListener getExceptionListener() throws JMSException { + throw createNotSupported("getExceptionListener()"); + } + + public void setExceptionListener(ExceptionListener exceptionListener) throws JMSException { + throw createNotSupported("setExceptionListener()"); + } + + public ConnectionConsumer createConnectionConsumer(Destination destination, String s, ServerSessionPool serverSessionPool, int i) throws JMSException { + throw createNotSupported("createConnectionConsumer()"); + } + + public ConnectionConsumer createDurableConnectionConsumer(Topic topic, String s, String s1, ServerSessionPool serverSessionPool, int i) throws JMSException { + throw createNotSupported("createDurableConnectionConsumer()"); + } + + public ConnectionConsumer createConnectionConsumer(Queue queue, String s, ServerSessionPool serverSessionPool, int i) throws JMSException { + throw createNotSupported("createConnectionConsumer()"); + } + + public ConnectionConsumer createConnectionConsumer(Topic topic, String s, ServerSessionPool serverSessionPool, int i) throws JMSException { + throw createNotSupported("createConnectionConsumer()"); + } + + protected JMSException createNotSupported(String text) { + return new JMSException("Operation: " + text + " is not supported for this proxy JCA ResourceAdapter provider"); + } +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/InboundConnectionProxyFactory.java b/activemq-ra/src/main/java/org/activemq/ra/InboundConnectionProxyFactory.java new file mode 100755 index 0000000000..fefaef10ab --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/InboundConnectionProxyFactory.java @@ -0,0 +1,61 @@ +/** + * + * Copyright 2004 Protique Ltd + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.jms.QueueConnection; +import javax.jms.QueueConnectionFactory; +import javax.jms.TopicConnection; +import javax.jms.TopicConnectionFactory; + +/** + * A {@link ConnectionFactory} implementation which creates connections which can + * be used with the ActiveMQ JCA Resource Adapter to publish messages using the + * same underlying JMS session that is used to dispatch messages. + * + * @version $Revision$ + */ +public class InboundConnectionProxyFactory implements ConnectionFactory, QueueConnectionFactory, TopicConnectionFactory { + + public Connection createConnection() throws JMSException { + return new InboundConnectionProxy(); + } + + public Connection createConnection(String userName, String password) throws JMSException { + return createConnection(); + } + + public QueueConnection createQueueConnection() throws JMSException { + return new InboundConnectionProxy(); + } + + public QueueConnection createQueueConnection(String userName, String password) throws JMSException { + return createQueueConnection(); + } + + public TopicConnection createTopicConnection() throws JMSException { + return new InboundConnectionProxy(); + } + + public TopicConnection createTopicConnection(String userName, String password) throws JMSException { + return createTopicConnection(); + } +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/InboundContext.java b/activemq-ra/src/main/java/org/activemq/ra/InboundContext.java new file mode 100755 index 0000000000..192ee52f39 --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/InboundContext.java @@ -0,0 +1,51 @@ +/** + * + * Copyright 2004 Protique Ltd + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.jms.Session; + +/** + * Represents an object which has-a {@link Session} instance and + * an optional, lazily created {@link MessageProducer} instance + * which can them be used by a pooling based JMS provider for publishing + * messages using the session used by the current thread. + * + * @version $Revision$ + */ +public interface InboundContext { + + /** + * Returns the current session being used to process a JMS message in the current thread. + */ + public Session getSession() throws JMSException; + + /** + * Lazily creates a message producer that can be used to send messages using the + * same JMS Session which is being used to dispatch messages which minimises the XA + * overheard of consuming and producing or allows JMS transactions to be used for consuming + * and producing messages. + * + * @return the current message producer or a new one is lazily created, using a null + * destination so the destination must be specified on a send() method. + * @throws javax.jms.JMSException + */ + public MessageProducer getMessageProducer() throws JMSException; +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/InboundContextSupport.java b/activemq-ra/src/main/java/org/activemq/ra/InboundContextSupport.java new file mode 100755 index 0000000000..f2ad61c821 --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/InboundContextSupport.java @@ -0,0 +1,65 @@ +/** + * + * Copyright 2004 Protique Ltd + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +/** + * A helper class used to provide access to the current active + * {@link InboundContext} instance being used to process a message + * in the current thread so that messages can be produced using the same + * session. + * + * @version $Revision$ + */ +public class InboundContextSupport { + private static final ThreadLocal threadLocal = new ThreadLocal(); + + /** + * Returns the current {@link InboundContext} used by the current thread which is processing a message. + * This allows us to access the current Session to send a message using the same underlying + * session to avoid unnecessary XA or to use regular JMS transactions while using message driven POJOs. + * + * @return + */ + public static InboundContext getActiveSessionAndProducer() { + return (InboundContext) threadLocal.get(); + } + + + /** + * Registers the session and producer which should be called before the + * {@link javax.resource.spi.endpoint.MessageEndpoint#beforeDelivery(java.lang.reflect.Method)} + * method is called. + * + * @param sessionAndProducer + */ + public static void register(InboundContext sessionAndProducer) { + threadLocal.set(sessionAndProducer); + } + + /** + * Unregisters the session and producer which should be called after the + * {@link javax.resource.spi.endpoint.MessageEndpoint#afterDelivery()} + * method is called. + * + * @param sessionAndProducer + */ + public static void unregister(InboundContext sessionAndProducer) { + threadLocal.set(null); + } +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/InboundMessageProducerProxy.java b/activemq-ra/src/main/java/org/activemq/ra/InboundMessageProducerProxy.java new file mode 100755 index 0000000000..66fc62e461 --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/InboundMessageProducerProxy.java @@ -0,0 +1,164 @@ +/** + * + * Copyright 2004 Protique Ltd + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.QueueSender; +import javax.jms.Topic; +import javax.jms.TopicPublisher; + +/** + * An implementation of {@link MessageProducer} which uses the ActiveMQ JCA ResourceAdapter's + * current thread's JMS {@link javax.jms.Session} to send messages. + * + * @version $Revision$ + */ +public class InboundMessageProducerProxy implements MessageProducer, QueueSender, TopicPublisher { + + private MessageProducer messageProducer; + private Destination destination; + private int deliveryMode; + private boolean disableMessageID; + private boolean disableMessageTimestamp; + private int priority; + private long timeToLive; + + public InboundMessageProducerProxy(MessageProducer messageProducer, Destination destination) throws JMSException { + this.messageProducer = messageProducer; + this.destination = destination; + + this.deliveryMode = messageProducer.getDeliveryMode(); + this.disableMessageID = messageProducer.getDisableMessageID(); + this.disableMessageTimestamp = messageProducer.getDisableMessageTimestamp(); + this.priority = messageProducer.getPriority(); + this.timeToLive = messageProducer.getTimeToLive(); + } + + public void close() throws JMSException { + // do nothing as we just go back into the pool + // though lets reset the various settings which may have been changed + messageProducer.setDeliveryMode(deliveryMode); + messageProducer.setDisableMessageID(disableMessageID); + messageProducer.setDisableMessageTimestamp(disableMessageTimestamp); + messageProducer.setPriority(priority); + messageProducer.setTimeToLive(timeToLive); + } + + public Destination getDestination() throws JMSException { + return destination; + } + + public int getDeliveryMode() throws JMSException { + return messageProducer.getDeliveryMode(); + } + + public boolean getDisableMessageID() throws JMSException { + return messageProducer.getDisableMessageID(); + } + + public boolean getDisableMessageTimestamp() throws JMSException { + return messageProducer.getDisableMessageTimestamp(); + } + + public int getPriority() throws JMSException { + return messageProducer.getPriority(); + } + + public long getTimeToLive() throws JMSException { + return messageProducer.getTimeToLive(); + } + + public void send(Destination destination, Message message) throws JMSException { + if (destination == null) { + destination = this.destination; + } + messageProducer.send(destination, message); + } + + public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive) throws JMSException { + if (destination == null) { + destination = this.destination; + } + messageProducer.send(destination, message, deliveryMode, priority, timeToLive); + } + + public void send(Message message) throws JMSException { + messageProducer.send(destination, message); + } + + public void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException { + messageProducer.send(destination, message, deliveryMode, priority, timeToLive); + } + + public void setDeliveryMode(int i) throws JMSException { + messageProducer.setDeliveryMode(i); + } + + public void setDisableMessageID(boolean b) throws JMSException { + messageProducer.setDisableMessageID(b); + } + + public void setDisableMessageTimestamp(boolean b) throws JMSException { + messageProducer.setDisableMessageTimestamp(b); + } + + public void setPriority(int i) throws JMSException { + messageProducer.setPriority(i); + } + + public void setTimeToLive(long l) throws JMSException { + messageProducer.setTimeToLive(l); + } + + public Queue getQueue() throws JMSException { + return (Queue) messageProducer.getDestination(); + } + + public void send(Queue arg0, Message arg1) throws JMSException { + messageProducer.send(arg0, arg1); + } + + public void send(Queue arg0, Message arg1, int arg2, int arg3, long arg4) throws JMSException { + messageProducer.send(arg0, arg1, arg2, arg3, arg4); + } + + public Topic getTopic() throws JMSException { + return (Topic) messageProducer.getDestination(); + } + + public void publish(Message arg0) throws JMSException { + messageProducer.send(arg0); + } + + public void publish(Message arg0, int arg1, int arg2, long arg3) throws JMSException { + messageProducer.send(arg0, arg1, arg2, arg3); + } + + public void publish(Topic arg0, Message arg1) throws JMSException { + messageProducer.send(arg0, arg1); + } + + public void publish(Topic arg0, Message arg1, int arg2, int arg3, long arg4) throws JMSException { + messageProducer.send(arg0, arg1, arg2, arg3, arg4); + } +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/InboundSessionProxy.java b/activemq-ra/src/main/java/org/activemq/ra/InboundSessionProxy.java new file mode 100755 index 0000000000..e98dd619db --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/InboundSessionProxy.java @@ -0,0 +1,222 @@ +/** + * + * Copyright 2004 Protique Ltd + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + + +import javax.jms.*; +import java.io.Serializable; + +/** + * A {@link Session} implementation which can be used with the ActiveMQ JCA + * Resource Adapter to publish messages using the same JMS session that is used to dispatch + * messages. + * + * @version $Revision$ + */ +public class InboundSessionProxy implements Session, QueueSession, TopicSession { + + private InboundContext sessionAndProducer; + + public Session getSession() throws JMSException { + return getSessionAndProducer().getSession(); + } + + public QueueSession getQueueSession() throws JMSException { + Session session = getSession(); + if (session instanceof QueueSession) { + return (QueueSession) session; + } + else { + throw new JMSException("The underlying JMS Session does not support QueueSession semantics: " + session); + } + } + + public TopicSession getTopicSession() throws JMSException { + Session session = getSession(); + if (session instanceof TopicSession) { + return (TopicSession) session; + } + else { + throw new JMSException("The underlying JMS Session does not support TopicSession semantics: " + session); + } + } + + public InboundContext getSessionAndProducer() throws JMSException { + if( sessionAndProducer==null ) { + sessionAndProducer = InboundContextSupport.getActiveSessionAndProducer(); + if (sessionAndProducer == null) { + throw new JMSException("No currently active Session. This JMS provider cannot be used outside a MessageListener.onMessage() invocation"); + } + } + return sessionAndProducer; + } + + public MessageProducer createProducer(Destination destination) throws JMSException { + return new InboundMessageProducerProxy(getSessionAndProducer().getMessageProducer(), destination); + } + + public void close() throws JMSException { + // we don't allow users to close this session + // as its used by the JCA container + } + + public void commit() throws JMSException { + // the JCA container will handle transactions + } + + public void rollback() throws JMSException { + // the JCA container will handle transactions + } + + public void recover() throws JMSException { + // the JCA container will handle recovery + } + + public void run() { + try { + getSession().run(); + } + catch (JMSException e) { + throw new RuntimeException("Failed to run() on session due to: " + e, e); + } + } + + // Straightforward delegation methods + //------------------------------------------------------------------------- + + public QueueBrowser createBrowser(Queue queue) throws JMSException { + return getSession().createBrowser(queue); + } + + public QueueBrowser createBrowser(Queue queue, String s) throws JMSException { + return getSession().createBrowser(queue, s); + } + + public BytesMessage createBytesMessage() throws JMSException { + return getSession().createBytesMessage(); + } + + public MessageConsumer createConsumer(Destination destination) throws JMSException { + return getSession().createConsumer(destination); + } + + public MessageConsumer createConsumer(Destination destination, String s) throws JMSException { + return getSession().createConsumer(destination, s); + } + + public MessageConsumer createConsumer(Destination destination, String s, boolean b) throws JMSException { + return getSession().createConsumer(destination, s, b); + } + + public TopicSubscriber createDurableSubscriber(Topic topic, String s) throws JMSException { + return getSession().createDurableSubscriber(topic, s); + } + + public TopicSubscriber createDurableSubscriber(Topic topic, String s, String s1, boolean b) throws JMSException { + return getSession().createDurableSubscriber(topic, s, s1, b); + } + + public MapMessage createMapMessage() throws JMSException { + return getSession().createMapMessage(); + } + + public Message createMessage() throws JMSException { + return getSession().createMessage(); + } + + public ObjectMessage createObjectMessage() throws JMSException { + return getSession().createObjectMessage(); + } + + public ObjectMessage createObjectMessage(Serializable serializable) throws JMSException { + return getSession().createObjectMessage(serializable); + } + + public Queue createQueue(String s) throws JMSException { + return getSession().createQueue(s); + } + + public StreamMessage createStreamMessage() throws JMSException { + return getSession().createStreamMessage(); + } + + public TemporaryQueue createTemporaryQueue() throws JMSException { + return getSession().createTemporaryQueue(); + } + + public TemporaryTopic createTemporaryTopic() throws JMSException { + return getSession().createTemporaryTopic(); + } + + public TextMessage createTextMessage() throws JMSException { + return getSession().createTextMessage(); + } + + public TextMessage createTextMessage(String s) throws JMSException { + return getSession().createTextMessage(s); + } + + public Topic createTopic(String s) throws JMSException { + return getSession().createTopic(s); + } + + public int getAcknowledgeMode() throws JMSException { + return getSession().getAcknowledgeMode(); + } + + public MessageListener getMessageListener() throws JMSException { + return getSession().getMessageListener(); + } + + public boolean getTransacted() throws JMSException { + return getSession().getTransacted(); + } + + public void setMessageListener(MessageListener messageListener) throws JMSException { + getSession().setMessageListener(messageListener); + } + + public void unsubscribe(String s) throws JMSException { + getSession().unsubscribe(s); + } + + public QueueReceiver createReceiver(Queue queue) throws JMSException { + return getQueueSession().createReceiver(queue); + } + + public QueueReceiver createReceiver(Queue queue, String s) throws JMSException { + return getQueueSession().createReceiver(queue, s); + } + + public QueueSender createSender(Queue queue) throws JMSException { + return new InboundMessageProducerProxy(getSessionAndProducer().getMessageProducer(), queue); + } + + public TopicSubscriber createSubscriber(Topic topic) throws JMSException { + return getTopicSession().createSubscriber(topic); + } + + public TopicSubscriber createSubscriber(Topic topic, String s, boolean b) throws JMSException { + return getTopicSession().createSubscriber(topic, s, b); + } + + public TopicPublisher createPublisher(Topic topic) throws JMSException { + return getTopicSession().createPublisher(topic); + } +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/InvalidMessageEndpointException.java b/activemq-ra/src/main/java/org/activemq/ra/InvalidMessageEndpointException.java new file mode 100755 index 0000000000..9f389ca48f --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/InvalidMessageEndpointException.java @@ -0,0 +1,85 @@ +/** + * + * Copyright 2004 Michael Gaffney + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +/** + * Thrown to indicate that a MessageEndpoint is no longer valid + * and should be discarded. + * + * @author Michael Gaffney + */ +public class InvalidMessageEndpointException extends RuntimeException { + + private static final long serialVersionUID = -9007051892399939057L; + + /** + * Constructs a new exception with null as its detail message. + * The cause is not initialized, and may subsequently be initialized by a + * call to {@link #initCause}. + */ + public InvalidMessageEndpointException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. The + * cause is not initialized, and may subsequently be initialized by + * a call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + */ + public InvalidMessageEndpointException(final String message) { + super(message); + } + + /** + * Constructs a new exception with the specified detail message and + * cause.

Note that the detail message associated with + * cause is not automatically incorporated in + * this exception's detail message. + * + * @param message the detail message (which is saved for later retrieval + * by the {@link #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A null value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + */ + public InvalidMessageEndpointException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail + * message of (cause==null ? null : cause.toString()) (which + * typically contains the class and detail message of cause). + * This constructor is useful for exceptions that are little more than + * wrappers for other throwables (for example, {@link + * java.security.PrivilegedActionException}). + * + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A null value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + */ + public InvalidMessageEndpointException(final Throwable cause) { + super(cause); + } +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/LocalAndXATransaction.java b/activemq-ra/src/main/java/org/activemq/ra/LocalAndXATransaction.java new file mode 100755 index 0000000000..4c9aa7cc0d --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/LocalAndXATransaction.java @@ -0,0 +1,153 @@ +/** + * + * Copyright 2004 Hiram Chirino + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import javax.jms.JMSException; +import javax.resource.ResourceException; +import javax.resource.spi.LocalTransaction; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import org.activemq.TransactionContext; + +/** + * Used to provide a LocalTransaction and XAResource to a JMS session. + */ +public class LocalAndXATransaction implements XAResource, LocalTransaction { + + final private TransactionContext transactionContext; + private boolean inManagedTx; + + public LocalAndXATransaction(TransactionContext transactionContext) { + this.transactionContext=transactionContext; + } + + public void setInManagedTx(boolean inManagedTx) throws JMSException { + this.inManagedTx=inManagedTx; + if( !inManagedTx ) + transactionContext.cleanup(); + } + + public void begin() throws ResourceException { + try { + transactionContext.begin(); + setInManagedTx(true); + } catch (JMSException e) { + throw new ResourceException("begin failed.", e); + } + } + + public void commit() throws ResourceException { + try { + transactionContext.commit(); + } catch (JMSException e) { + throw new ResourceException("commit failed.", e); + } finally { + try { + setInManagedTx(false); + } catch (JMSException e) { + throw new ResourceException("commit failed.",e); + } + } + } + + public void rollback() throws ResourceException { + try { + transactionContext.rollback(); + } catch (JMSException e) { + throw new ResourceException("rollback failed.", e); + } finally { + try { + setInManagedTx(false); + } catch (JMSException e) { + throw new ResourceException("rollback failed.",e); + } + } + } + + public void commit(Xid arg0, boolean arg1) throws XAException { + transactionContext.commit(arg0, arg1); + } + + public void end(Xid arg0, int arg1) throws XAException { + try { + transactionContext.end(arg0, arg1); + } finally { + try { + setInManagedTx(false); + } catch (JMSException e) { + throw (XAException)new XAException(XAException.XAER_PROTO).initCause(e); + } + } + } + + public void forget(Xid arg0) throws XAException { + transactionContext.forget(arg0); + } + + public int getTransactionTimeout() throws XAException { + return transactionContext.getTransactionTimeout(); + } + + public boolean isSameRM(XAResource xaresource) throws XAException { + if (xaresource == null) + return false; + // Do we have to unwrap? + if (xaresource instanceof LocalAndXATransaction) { + xaresource = ((LocalAndXATransaction)xaresource).transactionContext; + } + return transactionContext.isSameRM(xaresource); + } + + public int prepare(Xid arg0) throws XAException { + return transactionContext.prepare(arg0); + } + + public Xid[] recover(int arg0) throws XAException { + return transactionContext.recover(arg0); + } + + public void rollback(Xid arg0) throws XAException { + transactionContext.rollback(arg0); + } + + public boolean setTransactionTimeout(int arg0) throws XAException { + return transactionContext.setTransactionTimeout(arg0); + } + + + public void start(Xid arg0, int arg1) throws XAException { + transactionContext.start(arg0, arg1); + try { + setInManagedTx(true); + } catch (JMSException e) { + throw (XAException)new XAException(XAException.XAER_PROTO).initCause(e); + } + } + + public boolean isInManagedTx() { + return inManagedTx; + } + + public void cleanup() { + transactionContext.cleanup(); + inManagedTx=false; + } +} \ No newline at end of file diff --git a/activemq-ra/src/main/java/org/activemq/ra/ManagedConnectionProxy.java b/activemq-ra/src/main/java/org/activemq/ra/ManagedConnectionProxy.java new file mode 100755 index 0000000000..048b7ab01e --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/ManagedConnectionProxy.java @@ -0,0 +1,286 @@ +/** + * + * Copyright 2004 Hiram Chirino + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import java.util.ArrayList; +import java.util.Iterator; + +import javax.jms.Connection; +import javax.jms.ConnectionConsumer; +import javax.jms.ConnectionMetaData; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.IllegalStateException; +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueSession; +import javax.jms.ServerSessionPool; +import javax.jms.Session; +import javax.jms.Topic; +import javax.jms.TopicConnection; +import javax.jms.TopicSession; + +import org.activemq.ActiveMQQueueSession; +import org.activemq.ActiveMQSession; +import org.activemq.ActiveMQTopicSession; + + +/** + * Acts as a pass through proxy for a JMS Connection object. + * It intercepts events that are of interest of the ActiveMQManagedConnection. + * + * @version $Revision$ + */ +public class ManagedConnectionProxy implements Connection, QueueConnection, TopicConnection, ExceptionListener { + + private ActiveMQManagedConnection managedConnection; + private ArrayList sessions = new ArrayList(); + private ExceptionListener exceptionListener; + + public ManagedConnectionProxy(ActiveMQManagedConnection managedConnection) { + this.managedConnection = managedConnection; + } + + /** + * Used to let the ActiveMQManagedConnection that this connection + * handel is not needed by the app. + * + * @throws JMSException + */ + public void close() throws JMSException { + if( managedConnection!=null ) { + managedConnection.proxyClosedEvent(this); + } + } + + /** + * Called by the ActiveMQManagedConnection to invalidate this proxy. + */ + public void cleanup() { + exceptionListener=null; + managedConnection = null; + for (Iterator iter = sessions.iterator(); iter.hasNext();) { + ManagedSessionProxy p = (ManagedSessionProxy) iter.next(); + try { + p.cleanup(); + } catch (JMSException ignore) { + } + iter.remove(); + } + } + + /** + * + */ + private Connection getConnection() throws JMSException { + if (managedConnection == null) { + throw new IllegalStateException("The Connection is closed"); + } + return managedConnection.getPhysicalConnection(); + } + + /** + * @param transacted + * @param acknowledgeMode + * @return + * @throws JMSException + */ + public Session createSession(boolean transacted, int acknowledgeMode) + throws JMSException { + return createSessionProxy(transacted, acknowledgeMode); + } + + /** + * @param acknowledgeMode + * @param transacted + * @return + * @throws JMSException + */ + private ManagedSessionProxy createSessionProxy(boolean transacted, int acknowledgeMode) throws JMSException { + ActiveMQSession session = (ActiveMQSession) getConnection().createSession(transacted, acknowledgeMode); + ManagedTransactionContext txContext = new ManagedTransactionContext(managedConnection.getTransactionContext()); + session.setTransactionContext(txContext); + ManagedSessionProxy p = new ManagedSessionProxy(session); + p.setUseSharedTxContext(managedConnection.isInManagedTx()); + sessions.add(p); + return p; + } + + public void setUseSharedTxContext(boolean enable) throws JMSException { + for (Iterator iter = sessions.iterator(); iter.hasNext();) { + ManagedSessionProxy p = (ManagedSessionProxy) iter.next(); + p.setUseSharedTxContext(enable); + } + } + + /** + * @param transacted + * @param acknowledgeMode + * @return + * @throws JMSException + */ + public QueueSession createQueueSession(boolean transacted, + int acknowledgeMode) throws JMSException { + return new ActiveMQQueueSession(createSessionProxy(transacted, acknowledgeMode)); + } + + /** + * @param transacted + * @param acknowledgeMode + * @return + * @throws JMSException + */ + public TopicSession createTopicSession(boolean transacted, + int acknowledgeMode) throws JMSException { + return new ActiveMQTopicSession(createSessionProxy(transacted, acknowledgeMode)); + } + + /** + * @return + * @throws JMSException + */ + public String getClientID() throws JMSException { + return getConnection().getClientID(); + } + + /** + * @return + * @throws JMSException + */ + public ExceptionListener getExceptionListener() throws JMSException { + return getConnection().getExceptionListener(); + } + + /** + * @return + * @throws JMSException + */ + public ConnectionMetaData getMetaData() throws JMSException { + return getConnection().getMetaData(); + } + + /** + * @param clientID + * @throws JMSException + */ + public void setClientID(String clientID) throws JMSException { + getConnection().setClientID(clientID); + } + + /** + * @param listener + * @throws JMSException + */ + public void setExceptionListener(ExceptionListener listener) + throws JMSException { + getConnection(); + exceptionListener = listener; + } + + /** + * @throws JMSException + */ + public void start() throws JMSException { + getConnection().start(); + } + + /** + * @throws JMSException + */ + public void stop() throws JMSException { + getConnection().stop(); + } + + + /** + * @param queue + * @param messageSelector + * @param sessionPool + * @param maxMessages + * @return + * @throws JMSException + */ + public ConnectionConsumer createConnectionConsumer(Queue queue, + String messageSelector, ServerSessionPool sessionPool, + int maxMessages) throws JMSException { + throw new JMSException("Not Supported."); + } + + /** + * @param topic + * @param messageSelector + * @param sessionPool + * @param maxMessages + * @return + * @throws JMSException + */ + public ConnectionConsumer createConnectionConsumer(Topic topic, + String messageSelector, ServerSessionPool sessionPool, + int maxMessages) throws JMSException { + throw new JMSException("Not Supported."); + } + + /** + * @param destination + * @param messageSelector + * @param sessionPool + * @param maxMessages + * @return + * @throws JMSException + */ + public ConnectionConsumer createConnectionConsumer(Destination destination, + String messageSelector, ServerSessionPool sessionPool, + int maxMessages) throws JMSException { + throw new JMSException("Not Supported."); + } + + /** + * @param topic + * @param subscriptionName + * @param messageSelector + * @param sessionPool + * @param maxMessages + * @return + * @throws JMSException + */ + public ConnectionConsumer createDurableConnectionConsumer(Topic topic, + String subscriptionName, String messageSelector, + ServerSessionPool sessionPool, int maxMessages) throws JMSException { + throw new JMSException("Not Supported."); + } + + /** + * @return Returns the managedConnection. + */ + public ActiveMQManagedConnection getManagedConnection() { + return managedConnection; + } + + public void onException(JMSException e) { + if(exceptionListener!=null && managedConnection!=null) { + try { + exceptionListener.onException(e); + } catch (Throwable ignore) { + // We can never trust user code so ignore any exceptions. + } + } + } + +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/ManagedSessionProxy.java b/activemq-ra/src/main/java/org/activemq/ra/ManagedSessionProxy.java new file mode 100755 index 0000000000..d45d2d1b18 --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/ManagedSessionProxy.java @@ -0,0 +1,406 @@ +/** + * + * Copyright 2004 Hiram Chirino + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import java.io.Serializable; + +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.IllegalStateException; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.QueueBrowser; +import javax.jms.QueueReceiver; +import javax.jms.QueueSender; +import javax.jms.QueueSession; +import javax.jms.Session; +import javax.jms.StreamMessage; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicPublisher; +import javax.jms.TopicSession; +import javax.jms.TopicSubscriber; + +import org.activemq.ActiveMQSession; + +/** + * Acts as a pass through proxy for a JMS Session object. It intercepts events + * that are of interest of the ActiveMQManagedConnection. + * + * @version $Revision$ + */ +public class ManagedSessionProxy implements Session, QueueSession, TopicSession { + + private final ActiveMQSession session; + boolean closed = false; + + public ManagedSessionProxy(ActiveMQSession session) { + this.session = session; + } + + public void setUseSharedTxContext(boolean enable) throws JMSException { + if( session.getTransactionContext() !=null ) { + ((ManagedTransactionContext)session.getTransactionContext()).setUseSharedTxContext(enable); + } + } + + /** + * @throws JMSException + */ + public void close() throws JMSException { + cleanup(); + } + + /** + * Called by the ActiveMQManagedConnection to invalidate this proxy. + * @throws JMSException + * + * @throws JMSException + */ + public void cleanup() throws JMSException { + closed = true; + session.close(); + } + + /** + * + */ + private Session getSession() throws JMSException { + if (closed) { + throw new IllegalStateException("The Session is closed"); + } + return session; + } + + /** + * @throws JMSException + */ + public void commit() throws JMSException { + getSession().commit(); + } + + /** + * @param queue + * @return + * @throws JMSException + */ + public QueueBrowser createBrowser(Queue queue) throws JMSException { + return getSession().createBrowser(queue); + } + + /** + * @param queue + * @param messageSelector + * @return + * @throws JMSException + */ + public QueueBrowser createBrowser(Queue queue, String messageSelector) throws JMSException { + return getSession().createBrowser(queue, messageSelector); + } + + /** + * @return + * @throws JMSException + */ + public BytesMessage createBytesMessage() throws JMSException { + return getSession().createBytesMessage(); + } + + /** + * @param destination + * @return + * @throws JMSException + */ + public MessageConsumer createConsumer(Destination destination) throws JMSException { + return getSession().createConsumer(destination); + } + + /** + * @param destination + * @param messageSelector + * @return + * @throws JMSException + */ + public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException { + return getSession().createConsumer(destination, messageSelector); + } + + /** + * @param destination + * @param messageSelector + * @param NoLocal + * @return + * @throws JMSException + */ + public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean NoLocal) + throws JMSException { + return getSession().createConsumer(destination, messageSelector, NoLocal); + } + + /** + * @param topic + * @param name + * @return + * @throws JMSException + */ + public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException { + return getSession().createDurableSubscriber(topic, name); + } + + /** + * @param topic + * @param name + * @param messageSelector + * @param noLocal + * @return + * @throws JMSException + */ + public TopicSubscriber createDurableSubscriber(Topic topic, String name, String messageSelector, boolean noLocal) + throws JMSException { + return getSession().createDurableSubscriber(topic, name, messageSelector, noLocal); + } + + /** + * @return + * @throws JMSException + */ + public MapMessage createMapMessage() throws JMSException { + return getSession().createMapMessage(); + } + + /** + * @return + * @throws JMSException + */ + public Message createMessage() throws JMSException { + return getSession().createMessage(); + } + + /** + * @return + * @throws JMSException + */ + public ObjectMessage createObjectMessage() throws JMSException { + return getSession().createObjectMessage(); + } + + /** + * @param object + * @return + * @throws JMSException + */ + public ObjectMessage createObjectMessage(Serializable object) throws JMSException { + return getSession().createObjectMessage(object); + } + + /** + * @param destination + * @return + * @throws JMSException + */ + public MessageProducer createProducer(Destination destination) throws JMSException { + return getSession().createProducer(destination); + } + + /** + * @param queueName + * @return + * @throws JMSException + */ + public Queue createQueue(String queueName) throws JMSException { + return getSession().createQueue(queueName); + } + + /** + * @return + * @throws JMSException + */ + public StreamMessage createStreamMessage() throws JMSException { + return getSession().createStreamMessage(); + } + + /** + * @return + * @throws JMSException + */ + public TemporaryQueue createTemporaryQueue() throws JMSException { + return getSession().createTemporaryQueue(); + } + + /** + * @return + * @throws JMSException + */ + public TemporaryTopic createTemporaryTopic() throws JMSException { + return getSession().createTemporaryTopic(); + } + + /** + * @return + * @throws JMSException + */ + public TextMessage createTextMessage() throws JMSException { + return getSession().createTextMessage(); + } + + /** + * @param text + * @return + * @throws JMSException + */ + public TextMessage createTextMessage(String text) throws JMSException { + return getSession().createTextMessage(text); + } + + /** + * @param topicName + * @return + * @throws JMSException + */ + public Topic createTopic(String topicName) throws JMSException { + return getSession().createTopic(topicName); + } + + /** + * @return + * @throws JMSException + */ + public int getAcknowledgeMode() throws JMSException { + return getSession().getAcknowledgeMode(); + } + + /** + * @return + * @throws JMSException + */ + public MessageListener getMessageListener() throws JMSException { + return getSession().getMessageListener(); + } + + /** + * @return + * @throws JMSException + */ + public boolean getTransacted() throws JMSException { + return getSession().getTransacted(); + } + + /** + * @throws JMSException + */ + public void recover() throws JMSException { + getSession().recover(); + } + + /** + * @throws JMSException + */ + public void rollback() throws JMSException { + getSession().rollback(); + } + + /** + * @param listener + * @throws JMSException + */ + public void setMessageListener(MessageListener listener) throws JMSException { + getSession(); // .setMessageListener(listener); + } + + /** + * @param name + * @throws JMSException + */ + public void unsubscribe(String name) throws JMSException { + getSession().unsubscribe(name); + } + + /** + * @param queue + * @return + * @throws JMSException + */ + public QueueReceiver createReceiver(Queue queue) throws JMSException { + return ((QueueSession) getSession()).createReceiver(queue); + } + + /** + * @param queue + * @param messageSelector + * @return + * @throws JMSException + */ + public QueueReceiver createReceiver(Queue queue, String messageSelector) throws JMSException { + return ((QueueSession) getSession()).createReceiver(queue, messageSelector); + } + + /** + * @param queue + * @return + * @throws JMSException + */ + public QueueSender createSender(Queue queue) throws JMSException { + return ((QueueSession) getSession()).createSender(queue); + } + + /** + * @param topic + * @return + * @throws JMSException + */ + public TopicPublisher createPublisher(Topic topic) throws JMSException { + return ((TopicSession) getSession()).createPublisher(topic); + } + + /** + * @param topic + * @return + * @throws JMSException + */ + public TopicSubscriber createSubscriber(Topic topic) throws JMSException { + return ((TopicSession) getSession()).createSubscriber(topic); + } + + /** + * @param topic + * @param messageSelector + * @param noLocal + * @return + * @throws JMSException + */ + public TopicSubscriber createSubscriber(Topic topic, String messageSelector, boolean noLocal) throws JMSException { + return ((TopicSession) getSession()).createSubscriber(topic, messageSelector, noLocal); + } + + /** + * @see javax.jms.Session#run() + */ + public void run() { + throw new RuntimeException("Operation not supported."); + } + +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/ManagedTransactionContext.java b/activemq-ra/src/main/java/org/activemq/ra/ManagedTransactionContext.java new file mode 100755 index 0000000000..83bb79bef4 --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/ManagedTransactionContext.java @@ -0,0 +1,170 @@ +/** + * + * Copyright 2004 Hiram Chirino + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import javax.jms.JMSException; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import org.activemq.TransactionContext; +import org.activemq.command.TransactionId; +import org.activemq.transaction.Synchronization; + +/** + * Allows us to switch between using a shared transaction context, + * or using a local transaction context. + * + * @version $Revision$ + */ +public class ManagedTransactionContext extends TransactionContext { + + private final TransactionContext sharedContext; + boolean useSharedTxContext=false; + + public ManagedTransactionContext(TransactionContext sharedContext) { + super(sharedContext.getConnection()); + this.sharedContext = sharedContext; + setLocalTransactionEventListener(sharedContext.getLocalTransactionEventListener()); + } + + public void setUseSharedTxContext(boolean enable) throws JMSException { + if( isInLocalTransaction() || isInXATransaction() ) + throw new JMSException("The resource is allready being used in transaction context."); + useSharedTxContext = enable; + } + + public void begin() throws JMSException { + if( useSharedTxContext ) + sharedContext.begin(); + else + super.begin(); + } + public void commit() throws JMSException { + if( useSharedTxContext ) + sharedContext.commit(); + else + super.commit(); + } + + public void commit(Xid xid, boolean onePhase) throws XAException { + if( useSharedTxContext ) + sharedContext.commit(xid, onePhase); + else + super.commit(xid, onePhase); + } + + public void end(Xid xid, int flags) throws XAException { + if( useSharedTxContext ) + sharedContext.end(xid, flags); + else + super.end(xid, flags); + } + + public void forget(Xid xid) throws XAException { + if( useSharedTxContext ) + sharedContext.forget(xid); + else + super.forget(xid); + } + + public TransactionId getTransactionId() { + if( useSharedTxContext ) + return sharedContext.getTransactionId(); + else + return super.getTransactionId(); + } + + public int getTransactionTimeout() throws XAException { + if( useSharedTxContext ) + return sharedContext.getTransactionTimeout(); + else + return super.getTransactionTimeout(); + } + + public boolean isInLocalTransaction() { + if( useSharedTxContext ) + return sharedContext.isInLocalTransaction(); + else + return super.isInLocalTransaction(); + } + + public boolean isInXATransaction() { + if( useSharedTxContext ) + return sharedContext.isInXATransaction(); + else + return super.isInXATransaction(); + } + + public boolean isSameRM(XAResource xaResource) throws XAException { + if( useSharedTxContext ) + return sharedContext.isSameRM(xaResource); + else + return super.isSameRM(xaResource); + } + + public int prepare(Xid xid) throws XAException { + if( useSharedTxContext ) + return sharedContext.prepare(xid); + else + return super.prepare(xid); + } + + public Xid[] recover(int flag) throws XAException { + if( useSharedTxContext ) + return sharedContext.recover(flag); + else + return super.recover(flag); + } + + public void rollback() throws JMSException { + if( useSharedTxContext ) + sharedContext.rollback(); + else + super.rollback(); + } + + public void rollback(Xid xid) throws XAException { + if( useSharedTxContext ) + sharedContext.rollback(xid); + else + super.rollback(xid); + } + + public boolean setTransactionTimeout(int seconds) throws XAException { + if( useSharedTxContext ) + return sharedContext.setTransactionTimeout(seconds); + else + return super.setTransactionTimeout(seconds); + } + + public void start(Xid xid, int flags) throws XAException { + if( useSharedTxContext ) + sharedContext.start(xid, flags); + else + super.start(xid, flags); + } + + public void addSynchronization(Synchronization s) { + if( useSharedTxContext ) + sharedContext.addSynchronization(s); + else + super.addSynchronization(s); + } +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/MessageEndpointProxy.java b/activemq-ra/src/main/java/org/activemq/ra/MessageEndpointProxy.java new file mode 100755 index 0000000000..ce0c6ebbff --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/MessageEndpointProxy.java @@ -0,0 +1,184 @@ +/** + * + * Copyright 2004 Michael Gaffney + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import java.lang.reflect.Method; + +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.resource.ResourceException; +import javax.resource.spi.endpoint.MessageEndpoint; + +/** + * @author Michael Gaffney + */ +public class MessageEndpointProxy implements MessageListener, MessageEndpoint { + + private static final MessageEndpointState ALIVE = new MessageEndpointAlive(); + private static final MessageEndpointState GOING_TO_DIE = new MessageEndpointInTheElectricChair(); + private static final MessageEndpointState DEAD = new MessageEndpointDead(); + + + private static int proxyCount = 0; + private final int proxyID; + + private MessageEndpoint endpoint; + private MessageEndpointState state = ALIVE; + + private static int getID() { + return ++proxyCount; + } + + public MessageEndpointProxy(MessageEndpoint endpoint) { + if (!(endpoint instanceof MessageListener)) { + throw new IllegalArgumentException("MessageEndpoint is not a MessageListener"); + } + proxyID = getID(); + this.endpoint = endpoint; + } + + public void beforeDelivery(Method method) throws NoSuchMethodException, ResourceException { + state.beforeDelivery(this, method); + } + + public void onMessage(Message message) { +// log.warn("Delivery Count: " + getNextDeliveryCount() ); + state.onMessage(this, message); + } + + public void afterDelivery() throws ResourceException { + state.afterDelivery(this); + } + + public void release() { + state.release(this); + } + + public String toString() { + return "MessageEndpointProxy{ " + + "proxyID: " + proxyID + + ", endpoint: " + endpoint + + " }"; + } + + private abstract static class MessageEndpointState { + + public void beforeDelivery(MessageEndpointProxy proxy, Method method) throws NoSuchMethodException, ResourceException { + throw new IllegalStateException(); + } + + public void onMessage(MessageEndpointProxy proxy, Message message) { + throw new IllegalStateException(); + } + + public void afterDelivery(MessageEndpointProxy proxy) throws ResourceException { + throw new IllegalStateException(); + } + + public void release(MessageEndpointProxy proxy) { + throw new IllegalStateException(); + } + + protected final void transition(MessageEndpointProxy proxy, MessageEndpointState nextState) { + proxy.state = nextState; + nextState.enter(proxy); + } + + protected void enter(MessageEndpointProxy proxy) { + } + } + + private static class MessageEndpointAlive extends MessageEndpointState { + + public void beforeDelivery(MessageEndpointProxy proxy, Method method) throws NoSuchMethodException, ResourceException { + try { + proxy.endpoint.beforeDelivery(method); + } catch (NoSuchMethodException e) { + transition(proxy, DEAD); + throw e; + } catch (ResourceException e) { + transition(proxy, DEAD); + throw e; + } + } + + public void onMessage(MessageEndpointProxy proxy, Message message) { + try { + ((MessageListener) proxy.endpoint).onMessage(message); + } catch (RuntimeException e) { + transition(proxy, GOING_TO_DIE); + throw e; + } + } + + public void afterDelivery(MessageEndpointProxy proxy) throws ResourceException { + try { + proxy.endpoint.afterDelivery(); + } catch (ResourceException e) { + transition(proxy, DEAD); + throw e; + } + } + + public void release(MessageEndpointProxy proxy) { + transition(proxy, DEAD); + } + } + + private static class MessageEndpointInTheElectricChair extends MessageEndpointState { + + public void afterDelivery(MessageEndpointProxy proxy) throws ResourceException { + try { + proxy.endpoint.afterDelivery(); + } catch (ResourceException e) { + throw e; + } finally { + transition(proxy, DEAD); + } + } + + public void release(MessageEndpointProxy proxy) { + transition(proxy, DEAD); + } + } + + private static class MessageEndpointDead extends MessageEndpointState { + + protected void enter(MessageEndpointProxy proxy) { + proxy.endpoint.release(); + proxy.endpoint = null; + } + + public void beforeDelivery(MessageEndpointProxy proxy, Method method) throws NoSuchMethodException, ResourceException { + throw new InvalidMessageEndpointException(); + } + + public void onMessage(MessageEndpointProxy proxy, Message message) { + throw new InvalidMessageEndpointException(); + } + + public void afterDelivery(MessageEndpointProxy proxy) throws ResourceException { + throw new InvalidMessageEndpointException(); + } + + public void release(MessageEndpointProxy proxy) { + throw new InvalidMessageEndpointException(); + } + } +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/ServerSessionImpl.java b/activemq-ra/src/main/java/org/activemq/ra/ServerSessionImpl.java new file mode 100755 index 0000000000..c941564a3a --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/ServerSessionImpl.java @@ -0,0 +1,263 @@ +/** + * + * Copyright 2004 Protique Ltd + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import java.lang.reflect.Method; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ServerSession; +import javax.jms.Session; +import javax.resource.spi.endpoint.MessageEndpoint; +import javax.resource.spi.work.Work; +import javax.resource.spi.work.WorkEvent; +import javax.resource.spi.work.WorkException; +import javax.resource.spi.work.WorkListener; +import javax.resource.spi.work.WorkManager; + +import org.activemq.ActiveMQSession; +import org.activemq.ActiveMQSession.DeliveryListener; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @version $Revision$ + */ +public class ServerSessionImpl implements ServerSession, InboundContext, Work, DeliveryListener { + + public static final Method ON_MESSAGE_METHOD; + + static { + try { + ON_MESSAGE_METHOD = MessageListener.class.getMethod("onMessage", new Class[]{Message.class}); + } + catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + private static int nextLogId=0; + synchronized static private int getNextLogId() { + return nextLogId++; + } + + private int serverSessionId = getNextLogId(); + private final Log log = LogFactory.getLog( ServerSessionImpl.class.getName()+":"+serverSessionId ); + + private ActiveMQSession session; + private WorkManager workManager; + private MessageEndpoint endpoint; + private MessageProducer messageProducer; + private final ServerSessionPoolImpl pool; + + private Object runControlMutex = new Object(); + private boolean runningFlag = false; + /** + * True if an error was detected that cause this session to be stale. When a session + * is stale, it should not be used again for proccessing. + */ + private boolean stale; + /** + * Does the TX commit need to be managed by the RA? + */ + private final boolean useRAManagedTx; + /** + * The maximum number of messages to batch + */ + private final int batchSize; + /** + * The current number of messages in the batch + */ + private int currentBatchSize; + + public ServerSessionImpl(ServerSessionPoolImpl pool, ActiveMQSession session, WorkManager workManager, MessageEndpoint endpoint, boolean useRAManagedTx, int batchSize) throws JMSException { + this.pool = pool; + this.session = session; + this.workManager = workManager; + this.endpoint = endpoint; + this.useRAManagedTx = useRAManagedTx; + this.session.setMessageListener((MessageListener) endpoint); + this.session.setDeliveryListener(this); + this.batchSize = batchSize; + } + + public Session getSession() throws JMSException { + return session; + } + + public MessageProducer getMessageProducer() throws JMSException { + if (messageProducer == null) { + messageProducer = getSession().createProducer(null); + } + return messageProducer; + } + + /** + * @see javax.jms.ServerSession#start() + */ + public void start() throws JMSException { + + synchronized (runControlMutex) { + if (runningFlag) { + log.debug("Start request ignored, already running."); + return; + } + runningFlag = true; + } + + // We get here because we need to start a async worker. + log.debug("Starting run."); + try { + workManager.scheduleWork(this, WorkManager.INDEFINITE, null, + new WorkListener() { + //The work listener is useful only for debugging... + public void workAccepted(WorkEvent event) { + log.debug("Work accepted: " + event); + } + + public void workRejected(WorkEvent event) { + log.debug("Work rejected: " + event); + } + + public void workStarted(WorkEvent event) { + log.debug("Work started: " + event); + } + + public void workCompleted(WorkEvent event) { + log.debug("Work completed: " + event); + } + + }); + } + catch (WorkException e) { + throw (JMSException) new JMSException("Start failed: " + e).initCause(e); + } + } + + /** + * @see java.lang.Runnable#run() + */ + synchronized public void run() { + log.debug("Running"); + while (true) { + log.debug("run loop start"); + try { + InboundContextSupport.register(this); + currentBatchSize = 0; + session.run(); + } + catch (Throwable e) { + stale=true; + log.debug("Endpoint failed to process message.", e); + log.info("Endpoint failed to process message. Reason: " + e); + } + finally { + InboundContextSupport.unregister(this); + log.debug("run loop end"); + synchronized (runControlMutex) { + // This endpoint may have gone stale due to error + if( stale) { + runningFlag = false; + pool.removeFromPool(this); + break; + } + if( !session.hasUncomsumedMessages() ) { + runningFlag = false; + pool.returnToPool(this); + break; + } + } + } + } + log.debug("Run finished"); + } + + + /** + * The ActiveMQSession's run method will call back to this method before + * dispactching a message to the MessageListener. + */ + public void beforeDelivery(ActiveMQSession session, Message msg) { + if (currentBatchSize == 0) { + try { + endpoint.beforeDelivery(ON_MESSAGE_METHOD); + } catch (Throwable e) { + throw new RuntimeException("Endpoint before delivery notification failure", e); + } + } + } + + /** + * The ActiveMQSession's run method will call back to this method after + * dispactching a message to the MessageListener. + */ + public void afterDelivery(ActiveMQSession session, Message msg) { + if (++currentBatchSize >= batchSize || !session.hasUncomsumedMessages()) { + currentBatchSize = 0; + try { + endpoint.afterDelivery(); + } catch (Throwable e) { + throw new RuntimeException("Endpoint after delivery notification failure", e); + } finally { + if( session.getTransactionContext().isInLocalTransaction() ) { + if( !useRAManagedTx ) { + // Sanitiy Check: If the local transaction has not been commited.. + // Commit it now. + log.warn("Local transaction had not been commited. Commiting now."); + } + try { + session.commit(); + } catch (JMSException e) { + log.info("Commit failed:", e); + } + } + } + } + } + + /** + * @see javax.resource.spi.work.Work#release() + */ + public void release() { + log.debug("release called"); + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + return "ServerSessionImpl:"+serverSessionId; + } + + public void close() { + try { + endpoint.release(); + } catch (Throwable e) { + log.debug("Endpoint did not release properly: "+e,e); + } + try { + session.close(); + } catch (Throwable e) { + log.debug("Session did not close properly: "+e,e); + } + } + +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/ServerSessionPoolImpl.java b/activemq-ra/src/main/java/org/activemq/ra/ServerSessionPoolImpl.java new file mode 100755 index 0000000000..6bd3044091 --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/ServerSessionPoolImpl.java @@ -0,0 +1,204 @@ +/** + * + * Copyright 2004 Hiram Chirino + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.jms.JMSException; +import javax.jms.ServerSession; +import javax.jms.ServerSessionPool; +import javax.jms.Session; +import javax.resource.spi.UnavailableException; +import javax.resource.spi.endpoint.MessageEndpoint; + +import org.activemq.ActiveMQQueueSession; +import org.activemq.ActiveMQSession; +import org.activemq.ActiveMQTopicSession; +import org.activemq.command.MessageDispatch; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @version $Revision$ $Date$ + */ +public class ServerSessionPoolImpl implements ServerSessionPool { + + private static final Log log = LogFactory.getLog(ServerSessionPoolImpl.class); + + private final ActiveMQEndpointWorker activeMQAsfEndpointWorker; + private final int maxSessions; + + private ArrayList idleSessions = new ArrayList(); + private LinkedList activeSessions = new LinkedList(); + private boolean closing = false; + + public ServerSessionPoolImpl(ActiveMQEndpointWorker activeMQAsfEndpointWorker, int maxSessions) { + this.activeMQAsfEndpointWorker = activeMQAsfEndpointWorker; + this.maxSessions=maxSessions; + } + + private ServerSessionImpl createServerSessionImpl() throws JMSException { + ActiveMQActivationSpec activationSpec = activeMQAsfEndpointWorker.endpointActivationKey.getActivationSpec(); + int acknowledge = (activeMQAsfEndpointWorker.transacted) ? Session.SESSION_TRANSACTED : activationSpec.getAcknowledgeModeForSession(); + final ActiveMQSession session = (ActiveMQSession) activeMQAsfEndpointWorker.connection.createSession(activeMQAsfEndpointWorker.transacted,acknowledge); + MessageEndpoint endpoint; + try { + int batchSize = 0; + if (activationSpec.getEnableBatchBooleanValue()) { + batchSize = activationSpec.getMaxMessagesPerBatchIntValue(); + } + if( activationSpec.isUseRAManagedTransactionEnabled() ) { + // The RA will manage the transaction commit. + endpoint = createEndpoint(null); + return new ServerSessionImpl(this, (ActiveMQSession)session, activeMQAsfEndpointWorker.workManager, endpoint, true, batchSize); + } else { + // Give the container an object to manage to transaction with. + endpoint = createEndpoint(new LocalAndXATransaction(session.getTransactionContext())); + return new ServerSessionImpl(this, (ActiveMQSession)session, activeMQAsfEndpointWorker.workManager, endpoint, false, batchSize); + } + } catch (UnavailableException e) { + // The container could be limiting us on the number of endpoints + // that are being created. + session.close(); + return null; + } + } + + private MessageEndpoint createEndpoint(LocalAndXATransaction txResourceProxy) throws UnavailableException { + MessageEndpoint endpoint; + endpoint = activeMQAsfEndpointWorker.endpointFactory.createEndpoint(txResourceProxy); + MessageEndpointProxy endpointProxy = new MessageEndpointProxy(endpoint); + return endpointProxy; + } + + /** + */ + synchronized public ServerSession getServerSession() throws JMSException { + log.debug("ServerSession requested."); + if (closing) { + throw new JMSException("Session Pool Shutting Down."); + } + + if (idleSessions.size() > 0) { + ServerSessionImpl ss = (ServerSessionImpl) idleSessions.remove(idleSessions.size() - 1); + activeSessions.addLast(ss); + log.debug("Using idle session: " + ss); + return ss; + } else { + // Are we at the upper limit? + if (activeSessions.size() >= maxSessions) { + // then reuse the allready created sessions.. + // This is going to queue up messages into a session for + // processing. + return getExistingServerSession(); + } + ServerSessionImpl ss = createServerSessionImpl(); + // We may not be able to create a session due to the conatiner + // restricting us. + if (ss == null) { + return getExistingServerSession(); + } + activeSessions.addLast(ss); + log.debug("Created a new session: " + ss); + return ss; + } + } + + /** + * @param message + * @throws JMSException + */ + private void dispatchToSession(MessageDispatch messageDispatch) throws JMSException { + + ServerSession serverSession = getServerSession(); + Session s = serverSession.getSession(); + ActiveMQSession session = null; + if( s instanceof ActiveMQSession ) { + session = (ActiveMQSession) s; + } else if(s instanceof ActiveMQQueueSession) { + session = (ActiveMQSession) s; + } else if(s instanceof ActiveMQTopicSession) { + session = (ActiveMQSession) s; + } else { + activeMQAsfEndpointWorker.connection.onAsyncException(new JMSException("Session pool provided an invalid session type: "+s.getClass())); + } + session.dispatch(messageDispatch); + serverSession.start(); + } + + + /** + * @return + */ + private ServerSession getExistingServerSession() { + ServerSessionImpl ss = (ServerSessionImpl) activeSessions.removeFirst(); + activeSessions.addLast(ss); + log.debug("Reusing an active session: " + ss); + return ss; + } + + synchronized public void returnToPool(ServerSessionImpl ss) { + log.debug("Session returned to pool: " + ss); + activeSessions.remove(ss); + idleSessions.add(ss); + notify(); + } + + synchronized public void removeFromPool(ServerSessionImpl ss) { + activeSessions.remove(ss); + try { + ActiveMQSession session = (ActiveMQSession) ss.getSession(); + List l = session.getUnconsumedMessages(); + for (Iterator i = l.iterator(); i.hasNext();) { + dispatchToSession((MessageDispatch) i.next()); + } + } catch (Throwable t) { + log.error("Error redispatching unconsumed messages from stale session", t); + } + ss.close(); + notify(); + } + + public void close() { + synchronized (this) { + closing = true; + closeIdleSessions(); + while( activeSessions.size() > 0 ) { + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + closeIdleSessions(); + } + } + } + + private void closeIdleSessions() { + for (Iterator iter = idleSessions.iterator(); iter.hasNext();) { + ServerSessionImpl ss = (ServerSessionImpl) iter.next(); + ss.close(); + } + } + +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/SimpleConnectionManager.java b/activemq-ra/src/main/java/org/activemq/ra/SimpleConnectionManager.java new file mode 100755 index 0000000000..2c4b444f4c --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/SimpleConnectionManager.java @@ -0,0 +1,111 @@ +/** + * + * Copyright 2004 Hiram Chirino + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.resource.ResourceException; +import javax.resource.spi.ConnectionEvent; +import javax.resource.spi.ConnectionEventListener; +import javax.resource.spi.ConnectionManager; +import javax.resource.spi.ConnectionRequestInfo; +import javax.resource.spi.ManagedConnection; +import javax.resource.spi.ManagedConnectionFactory; +import javax.security.auth.Subject; + + +/** + * A simple implementation of a ConnectionManager. + * An Application Server will have a better implementation with pooling and security etc. + * + * @version $Revision$ + */ +public class SimpleConnectionManager implements ConnectionManager, ConnectionEventListener { + + private static final long serialVersionUID = -7662970495944876239L; + + private static final Log log = LogFactory.getLog(SimpleConnectionManager.class); + + /** + * @see javax.resource.spi.ConnectionManager#allocateConnection(javax.resource.spi.ManagedConnectionFactory, javax.resource.spi.ConnectionRequestInfo) + */ + public Object allocateConnection(ManagedConnectionFactory connectionFactory, ConnectionRequestInfo info) throws ResourceException { + Subject subject = null; + ManagedConnection connection = connectionFactory.createManagedConnection(subject, info); + connection.addConnectionEventListener(this); + return connection.getConnection(subject, info); + } + + /** + * @see javax.resource.spi.ConnectionEventListener#connectionClosed(javax.resource.spi.ConnectionEvent) + */ + public void connectionClosed(ConnectionEvent event) { + try { + ((ManagedConnection) event.getSource()).cleanup(); + } + catch (ResourceException e) { + log.warn("Error occured during the cleanup of a managed connection: ", e); + } + try { + ((ManagedConnection) event.getSource()).destroy(); + } + catch (ResourceException e) { + log.warn("Error occured during the destruction of a managed connection: ", e); + } + } + + /** + * @see javax.resource.spi.ConnectionEventListener#localTransactionStarted(javax.resource.spi.ConnectionEvent) + */ + public void localTransactionStarted(ConnectionEvent event) { + } + + /** + * @see javax.resource.spi.ConnectionEventListener#localTransactionCommitted(javax.resource.spi.ConnectionEvent) + */ + public void localTransactionCommitted(ConnectionEvent event) { + } + + /** + * @see javax.resource.spi.ConnectionEventListener#localTransactionRolledback(javax.resource.spi.ConnectionEvent) + */ + public void localTransactionRolledback(ConnectionEvent event) { + } + + /** + * @see javax.resource.spi.ConnectionEventListener#connectionErrorOccurred(javax.resource.spi.ConnectionEvent) + */ + public void connectionErrorOccurred(ConnectionEvent event) { + log.warn("Managed connection experiened an error: ", event.getException()); + try { + ((ManagedConnection) event.getSource()).cleanup(); + } + catch (ResourceException e) { + log.warn("Error occured during the cleanup of a managed connection: ", e); + } + try { + ((ManagedConnection) event.getSource()).destroy(); + } + catch (ResourceException e) { + log.warn("Error occured during the destruction of a managed connection: ", e); + } + } + +} diff --git a/activemq-ra/src/main/java/org/activemq/ra/package.html b/activemq-ra/src/main/java/org/activemq/ra/package.html new file mode 100755 index 0000000000..bb49a7c4b7 --- /dev/null +++ b/activemq-ra/src/main/java/org/activemq/ra/package.html @@ -0,0 +1,11 @@ + + + + + +

+ JCA managed connections and resource adapters for working with ActiveMQ +

+ + + diff --git a/activemq-ra/src/main/rar/META-INF/ra.xml b/activemq-ra/src/main/rar/META-INF/ra.xml new file mode 100755 index 0000000000..a2a71c465d --- /dev/null +++ b/activemq-ra/src/main/rar/META-INF/ra.xml @@ -0,0 +1,152 @@ + + + + ActiveMQ inbound and outbound JMS ResourceAdapter + ActiveMQ JMS Resource Adapter + activemq.org + JMS 1.1 + 1.0 + + + Copyright 2004 Hiram Chirino + Copyright 2004 Protique Ltd + + Licensed 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. + + true + + + org.activemq.ra.ActiveMQResourceAdapter + + + The URL to the ActiveMQ server that you want this connection to connect to. If using + an embedded broker, this value should be 'vm://localhost'. + + ServerUrl + java.lang.String + tcp://localhost:61616 + + + + The default user name that will be used to establish connections to the ActiveMQ server. + UserName + java.lang.String + defaultUser + + + The default password that will be used to log the default user into the ActiveMQ server. + Password + java.lang.String + defaultPassword + + + The client id that will be set on the connection that is established to the ActiveMQ server. + Clientid + java.lang.String + + + + Boolean to configure if outbound connections should reuse the inbound connection's session for sending messages. + UseInboundSession + java.lang.Boolean + false + + + + Sets the XML configuration file used to configure the ActiveMQ broker via + Spring if using embedded mode. + + BrokerXmlConfig is the filename which is assumed to be on the classpath unless + a URL is specified. So a value of foo/bar.xml would be assumed to be on the + classpath whereas file:dir/file.xml would use the file system. + Any valid URL string is supported. + + BrokerXmlConfig + java.lang.String + xbean:broker-config.xml + + + + org.activemq.ra.ActiveMQManagedConnectionFactory + javax.jms.ConnectionFactory + org.activemq.ra.ActiveMQConnectionFactory + javax.jms.Connection + org.activemq.ra.JMSConnectionProxy + + + org.activemq.ra.ActiveMQManagedConnectionFactory + javax.jms.QueueConnectionFactory + org.activemq.ra.ActiveMQConnectionFactory + javax.jms.QueueConnection + org.activemq.ra.JMSConnectionProxy + + + org.activemq.ra.ActiveMQManagedConnectionFactory + javax.jms.TopicConnectionFactory + org.activemq.ra.ActiveMQConnectionFactory + javax.jms.TopicConnection + org.activemq.ra.JMSConnectionProxy + + XATransaction + + BasicPassword + javax.resource.spi.security.PasswordCredential + + false + + + + + javax.jms.MessageListener + + org.activemq.ra.ActiveMQActivationSpec + + + destination + + + destinationType + + + + + + + + javax.jms.Queue + org.activemq.command.ActiveMQQueue + + PhysicalName + java.lang.String + + + + javax.jms.Topic + org.activemq.command.ActiveMQTopic + + PhysicalName + java.lang.String + + + + diff --git a/activemq-ra/src/main/rar/broker-config.xml b/activemq-ra/src/main/rar/broker-config.xml new file mode 100755 index 0000000000..71ac62d620 --- /dev/null +++ b/activemq-ra/src/main/rar/broker-config.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/activemq-ra/src/test/java/org/activemq/ra/ActiveMQActivationSpecTest.java b/activemq-ra/src/test/java/org/activemq/ra/ActiveMQActivationSpecTest.java new file mode 100755 index 0000000000..e0f412ea95 --- /dev/null +++ b/activemq-ra/src/test/java/org/activemq/ra/ActiveMQActivationSpecTest.java @@ -0,0 +1,313 @@ +/** + * + * Copyright 2004 Michael Gaffney + * + * Licensed 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.activemq.ra; + +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; +import java.util.Arrays; +import java.util.List; + +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.Topic; +import javax.resource.spi.InvalidPropertyException; + +import junit.framework.TestCase; + +import org.activemq.command.ActiveMQDestination; + +/** + * @version $Revision$ + */ +public class ActiveMQActivationSpecTest extends TestCase { + + private static final String DESTINATION = "defaultQueue"; + private static final String DESTINATION_TYPE = Queue.class.getName(); + + private ActiveMQActivationSpec activationSpec; + private PropertyDescriptor destinationProperty; + private PropertyDescriptor destinationTypeProperty; + private PropertyDescriptor acknowledgeModeProperty; + private PropertyDescriptor subscriptionDurabilityProperty; + private PropertyDescriptor clientIdProperty; + private PropertyDescriptor subscriptionNameProperty; + private static final String EMPTY_STRING = " "; + + public ActiveMQActivationSpecTest(String name) { + super(name); + } + + protected void setUp() throws Exception { + super.setUp(); + + activationSpec = new ActiveMQActivationSpec(); + activationSpec.setDestination(DESTINATION); + activationSpec.setDestinationType(DESTINATION_TYPE); + + destinationProperty = new PropertyDescriptor("destination", ActiveMQActivationSpec.class); + destinationTypeProperty = new PropertyDescriptor("destinationType", ActiveMQActivationSpec.class); + acknowledgeModeProperty = new PropertyDescriptor("acknowledgeMode", ActiveMQActivationSpec.class); + subscriptionDurabilityProperty = new PropertyDescriptor("subscriptionDurability", ActiveMQActivationSpec.class); + clientIdProperty = new PropertyDescriptor("clientId", ActiveMQActivationSpec.class); + subscriptionNameProperty = new PropertyDescriptor("subscriptionName", ActiveMQActivationSpec.class); + } + + public void testDefaultContructionValidation() throws IntrospectionException { + PropertyDescriptor[] expected = {destinationTypeProperty, destinationProperty}; + assertActivationSpecInvalid(new ActiveMQActivationSpec(), expected); + } + + public void testMinimalSettings() { + assertEquals(DESTINATION, activationSpec.getDestination()); + assertEquals(DESTINATION_TYPE, activationSpec.getDestinationType()); + assertActivationSpecValid(); + } + + public void testNoDestinationTypeFailure() { + activationSpec.setDestinationType(null); + PropertyDescriptor[] expected = {destinationTypeProperty}; + assertActivationSpecInvalid(expected); + } + + public void testInvalidDestinationTypeFailure() { + activationSpec.setDestinationType("foobar"); + PropertyDescriptor[] expected = {destinationTypeProperty}; + assertActivationSpecInvalid(expected); + } + + public void testQueueDestinationType() { + activationSpec.setDestinationType(Queue.class.getName()); + assertActivationSpecValid(); + } + + public void testTopicDestinationType() { + activationSpec.setDestinationType(Topic.class.getName()); + assertActivationSpecValid(); + } + + public void testSuccessfulCreateQueueDestination() { + activationSpec.setDestinationType(Queue.class.getName()); + activationSpec.setDestination(DESTINATION); + assertActivationSpecValid(); + ActiveMQDestination destination = activationSpec.createDestination(); + assertNotNull("ActiveMQDestination not created", destination); + assertEquals("Physical name not the same", activationSpec.getDestination(), destination.getPhysicalName()); + assertTrue("Destination is not a Queue", destination instanceof Queue); + } + + public void testSuccessfulCreateTopicDestination() { + activationSpec.setDestinationType(Topic.class.getName()); + activationSpec.setDestination(DESTINATION); + assertActivationSpecValid(); + ActiveMQDestination destination = activationSpec.createDestination(); + assertNotNull("ActiveMQDestination not created", destination); + assertEquals("Physical name not the same", activationSpec.getDestination(), destination.getPhysicalName()); + assertTrue("Destination is not a Topic", destination instanceof Topic); + } + + public void testCreateDestinationIncorrectType() { + activationSpec.setDestinationType(null); + activationSpec.setDestination(DESTINATION); + ActiveMQDestination destination = activationSpec.createDestination(); + assertNull("ActiveMQDestination should not have been created", destination); + } + + public void testCreateDestinationIncorrectDestinationName() { + activationSpec.setDestinationType(Topic.class.getName()); + activationSpec.setDestination(null); + ActiveMQDestination destination = activationSpec.createDestination(); + assertNull("ActiveMQDestination should not have been created", destination); + } + +//----------- acknowledgeMode tests + public void testDefaultAcknowledgeModeSetCorrectly() { + assertEquals("Incorrect default value", ActiveMQActivationSpec.AUTO_ACKNOWLEDGE_MODE, + activationSpec.getAcknowledgeMode()); + assertEquals("Incorrect default value", Session.AUTO_ACKNOWLEDGE, + activationSpec.getAcknowledgeModeForSession()); + } + + public void testInvalidAcknowledgeMode() { + activationSpec.setAcknowledgeMode("foobar"); + PropertyDescriptor[] expected = {acknowledgeModeProperty}; + assertActivationSpecInvalid(expected); + assertEquals("Incorrect acknowledge mode", ActiveMQActivationSpec.INVALID_ACKNOWLEDGE_MODE, + activationSpec.getAcknowledgeModeForSession()); + } + + public void testNoAcknowledgeMode() { + activationSpec.setAcknowledgeMode(null); + PropertyDescriptor[] expected = {acknowledgeModeProperty}; + assertActivationSpecInvalid(expected); + assertEquals("Incorrect acknowledge mode", ActiveMQActivationSpec.INVALID_ACKNOWLEDGE_MODE, + activationSpec.getAcknowledgeModeForSession()); + } + + public void testSettingAutoAcknowledgeMode() { + activationSpec.setAcknowledgeMode(ActiveMQActivationSpec.AUTO_ACKNOWLEDGE_MODE); + assertActivationSpecValid(); + assertEquals("Incorrect acknowledge mode", Session.AUTO_ACKNOWLEDGE, + activationSpec.getAcknowledgeModeForSession()); + } + + public void testSettingDupsOkAcknowledgeMode() { + activationSpec.setAcknowledgeMode(ActiveMQActivationSpec.DUPS_OK_ACKNOWLEDGE_MODE); + assertActivationSpecValid(); + assertEquals("Incorrect acknowledge mode", Session.DUPS_OK_ACKNOWLEDGE, + activationSpec.getAcknowledgeModeForSession()); + } + +//----------- subscriptionDurability tests + public void testDefaultSubscriptionDurabilitySetCorrectly() { + assertEquals("Incorrect default value", ActiveMQActivationSpec.NON_DURABLE_SUBSCRIPTION, + activationSpec.getSubscriptionDurability()); + } + + public void testInvalidSubscriptionDurability() { + activationSpec.setSubscriptionDurability("foobar"); + PropertyDescriptor[] expected = {subscriptionDurabilityProperty}; + assertActivationSpecInvalid(expected); + } + + public void testNullSubscriptionDurability() { + activationSpec.setSubscriptionDurability(null); + PropertyDescriptor[] expected = {subscriptionDurabilityProperty}; + assertActivationSpecInvalid(expected); + } + + public void testSettingNonDurableSubscriptionDurability() { + activationSpec.setSubscriptionDurability(ActiveMQActivationSpec.NON_DURABLE_SUBSCRIPTION); + assertActivationSpecValid(); + } + +//----------- durable subscriber tests + public void testValidDurableSubscriber() { + activationSpec.setSubscriptionDurability(ActiveMQActivationSpec.DURABLE_SUBSCRIPTION); + activationSpec.setClientId("foobar"); + activationSpec.setSubscriptionName("foobar"); + assertActivationSpecValid(); + assertTrue(activationSpec.isDurableSubscription()); + } + + public void testDurableSubscriberNoClientIdNoSubscriptionNameFailure() { + activationSpec.setSubscriptionDurability(ActiveMQActivationSpec.DURABLE_SUBSCRIPTION); + activationSpec.setClientId(null); + assertNull(activationSpec.getClientId()); + activationSpec.setSubscriptionName(null); + assertNull(activationSpec.getSubscriptionName()); + PropertyDescriptor[] expected = {clientIdProperty, subscriptionNameProperty}; + assertActivationSpecInvalid(expected); + } + + public void testDurableSubscriberEmptyClientIdEmptySubscriptionNameFailure() { + activationSpec.setSubscriptionDurability(ActiveMQActivationSpec.DURABLE_SUBSCRIPTION); + activationSpec.setClientId(EMPTY_STRING); + assertNull(activationSpec.getClientId()); + activationSpec.setSubscriptionName(EMPTY_STRING); + assertNull(activationSpec.getSubscriptionName()); + PropertyDescriptor[] expected = {clientIdProperty, subscriptionNameProperty}; + assertActivationSpecInvalid(expected); + } + + public void testSetEmptyStringButGetNullValue() { + ActiveMQActivationSpec activationSpec = new ActiveMQActivationSpec(); + + activationSpec.setDestinationType(EMPTY_STRING); + assertNull("Property not null", activationSpec.getDestinationType()); + + activationSpec.setMessageSelector(EMPTY_STRING); + assertNull("Property not null", activationSpec.getMessageSelector()); + + activationSpec.setDestination(EMPTY_STRING); + assertNull("Property not null", activationSpec.getDestination()); + + activationSpec.setUserName(EMPTY_STRING); + assertNull("Property not null", activationSpec.getUserName()); + + activationSpec.setPassword(EMPTY_STRING); + assertNull("Property not null", activationSpec.getPassword()); + + activationSpec.setClientId(EMPTY_STRING); + assertNull("Property not null", activationSpec.getClientId()); + + activationSpec.setSubscriptionName(EMPTY_STRING); + assertNull("Property not null", activationSpec.getSubscriptionName()); + } + +//----------- helper methods + private void assertActivationSpecValid() { + try { + activationSpec.validate(); + } catch (InvalidPropertyException e) { + fail("InvalidPropertyException should not be thrown"); + } + } + + private void assertActivationSpecInvalid(PropertyDescriptor[] expected) { + assertActivationSpecInvalid(activationSpec, expected); + } + + private void assertActivationSpecInvalid(ActiveMQActivationSpec testActivationSpec, PropertyDescriptor[] expected) { + try { + testActivationSpec.validate(); + fail("InvalidPropertyException should have been thrown"); + } catch (InvalidPropertyException e) { + PropertyDescriptor[] actual = e.getInvalidPropertyDescriptors(); + assertEquals(expected, actual); + } + } + + private static void assertEquals(PropertyDescriptor[] expected, PropertyDescriptor[] actual) { + /* + * This is kind of ugly. I originally created two HashSets and did an assertEquals(set1, set2) + * but because of a bug in the PropertyDescriptor class, it incorrectly fails. The problem is that the + * PropertyDescriptor class implements the equals() method but not the hashCode() method and almost all + * of the java collection classes use hashCode() for testing equality. The one exception I found was + * the ArrayList class which uses equals() for testing equality. Since Arrays.asList(...) returns an + * ArrayList, I use it below. Yes, ugly ... I know. + * + * see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4634390 + */ + assertNotNull("No PropertyDescriptors returned", expected); + assertEquals("PropertyDescriptor array size is incorrect ", expected.length, actual.length); + List expectedList = Arrays.asList(expected); + List actualList = Arrays.asList(actual); + assertTrue("Incorrect PropertyDescriptors returned", expectedList.containsAll(actualList)); + } + + public void testSelfEquality() { + assertEquality(activationSpec, activationSpec); + } + + public void testSamePropertiesButNotEqual() { + assertNonEquality(new ActiveMQActivationSpec(), new ActiveMQActivationSpec()); + } + + private void assertEquality(ActiveMQActivationSpec leftSpec, ActiveMQActivationSpec rightSpec) { + assertTrue("ActiveMQActivationSpecs are not equal", leftSpec.equals(rightSpec)); + assertTrue("ActiveMQActivationSpecs are not equal", rightSpec.equals(leftSpec)); + assertTrue("HashCodes are not equal", leftSpec.hashCode() == rightSpec.hashCode()); + } + + private void assertNonEquality(ActiveMQActivationSpec leftSpec, ActiveMQActivationSpec rightSpec) { + assertFalse("ActiveMQActivationSpecs are equal", leftSpec.equals(rightSpec)); + assertFalse("ActiveMQActivationSpecs are equal", rightSpec.equals(leftSpec)); + assertFalse("HashCodes are equal", leftSpec.hashCode() == rightSpec.hashCode()); + } + +} diff --git a/activemq-ra/src/test/java/org/activemq/ra/ActiveMQAsfEndpointWorkerTest.java b/activemq-ra/src/test/java/org/activemq/ra/ActiveMQAsfEndpointWorkerTest.java new file mode 100755 index 0000000000..e969b8f633 --- /dev/null +++ b/activemq-ra/src/test/java/org/activemq/ra/ActiveMQAsfEndpointWorkerTest.java @@ -0,0 +1,124 @@ +/** + * + * Copyright 2004 Michael Gaffney + * + * Licensed 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.activemq.ra; + +import org.jmock.cglib.Mock; +import org.jmock.cglib.MockObjectTestCase; +import org.jmock.core.Constraint; + +import javax.jms.Connection; +import javax.jms.Topic; +import javax.resource.spi.BootstrapContext; +import javax.resource.spi.endpoint.MessageEndpointFactory; + + +/** + * @author Michael Gaffney + */ +public class ActiveMQAsfEndpointWorkerTest extends MockObjectTestCase { + + private ActiveMQEndpointWorker worker; + private Mock mockResourceAdapter; + private Mock mockActivationKey; + private Mock mockEndpointFactory; + private Mock mockBootstrapContext; + private ActiveMQActivationSpec stubActivationSpec; + private Mock mockConnection; + + public ActiveMQAsfEndpointWorkerTest(String name) { + setName(name); + } + + public void testTopicSubscriberDurableNoDups() throws Exception { + + /* + Constraint[] args = {isA(Topic.class), eq(stubActivationSpec.getSubscriptionId()), NULL, + ANYTHING, ANYTHING}; + + mockConnection.expects(once()) + .method("createDurableConnectionConsumer") + .with(args) + .will(returnValue(null)); + worker.start(); + verifyMocks(); + */ + } + + protected void setUp() throws Exception { + setupStubs(); + setupMocks(); + setupEndpointWorker(); + } + + private void setupStubs() { + stubActivationSpec = new ActiveMQActivationSpec(); + stubActivationSpec.setDestination("some.topic"); + stubActivationSpec.setDestinationType("javax.jms.Topic"); + stubActivationSpec.setSubscriptionDurability(ActiveMQActivationSpec.DURABLE_SUBSCRIPTION); + stubActivationSpec.setClientId("foo"); + stubActivationSpec.setSubscriptionName("bar"); + } + + private void setupMocks() { + mockResourceAdapter = new Mock(ActiveMQResourceAdapter.class); + mockActivationKey = new Mock(ActiveMQEndpointActivationKey.class); + mockEndpointFactory = new Mock(MessageEndpointFactory.class); + mockBootstrapContext = new Mock(BootstrapContext.class); + mockConnection = new Mock(Connection.class); + + mockActivationKey.expects(atLeastOnce()) + .method("getMessageEndpointFactory") + .will(returnValue((MessageEndpointFactory) mockEndpointFactory.proxy())); + + mockActivationKey.expects(atLeastOnce()) + .method("getActivationSpec") + .will(returnValue(stubActivationSpec)); + + mockResourceAdapter.expects(atLeastOnce()) + .method("getBootstrapContext") + .will(returnValue((BootstrapContext) mockBootstrapContext.proxy())); + + mockBootstrapContext.expects(atLeastOnce()) + .method("getWorkManager") + .will(returnValue(null)); + + final boolean isTransactedResult = true; + setupIsTransacted(isTransactedResult); + } + + private void setupIsTransacted(final boolean transactedResult) { + mockEndpointFactory.expects(atLeastOnce()) + .method("isDeliveryTransacted") + .with(ANYTHING) + .will(returnValue(transactedResult)); + } + + private void setupEndpointWorker() throws Exception { + worker = new ActiveMQEndpointWorker((ActiveMQResourceAdapter)mockResourceAdapter.proxy(), + (ActiveMQEndpointActivationKey)mockActivationKey.proxy()); + } + + private void verifyMocks() { + mockResourceAdapter.verify(); + mockActivationKey.verify(); + mockEndpointFactory.verify(); + mockBootstrapContext.verify(); + mockConnection.verify(); + } + +} diff --git a/activemq-ra/src/test/java/org/activemq/ra/ActiveMQResourceAdapterJavaBeanEqualityTest.java b/activemq-ra/src/test/java/org/activemq/ra/ActiveMQResourceAdapterJavaBeanEqualityTest.java new file mode 100755 index 0000000000..b18e07c293 --- /dev/null +++ b/activemq-ra/src/test/java/org/activemq/ra/ActiveMQResourceAdapterJavaBeanEqualityTest.java @@ -0,0 +1,121 @@ +/** + * + * Copyright 2004 Michael Gaffney + * + * Licensed 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.activemq.ra; + +import junit.framework.TestCase; + +/** + * @version $Revision$ + */ +public class ActiveMQResourceAdapterJavaBeanEqualityTest extends TestCase { + + private ActiveMQResourceAdapter raOne; + private ActiveMQResourceAdapter raTwo; + + public ActiveMQResourceAdapterJavaBeanEqualityTest(String name) { + super(name); + } + + protected void setUp() throws Exception { + super.setUp(); + raOne = new ActiveMQResourceAdapter(); + raTwo = new ActiveMQResourceAdapter(); + } + + public void testSelfEquality() { + assertEquality(raOne, raOne); + } + + public void testEmptyEquality() { + assertEquality(raOne, raTwo); + } + + public void testNullEqualityFailure() { + assertFalse(raOne.equals(null)); + } + + public void testServerUrlEquality() { + raOne.setServerUrl("one"); + raTwo.setServerUrl("one"); + assertEquality(raOne,raTwo); + } + + public void testServerUrlInequality() { + raOne.setServerUrl("one"); + raTwo.setServerUrl("two"); + assertNonEquality(raOne,raTwo); + } + + public void testServerUrlInequalityDifferentCase() { + raOne.setServerUrl("one"); + raTwo.setServerUrl("ONE"); + assertNonEquality(raOne, raTwo); + } + + public void testNullServerUrlInequality() { + raOne.setServerUrl("one"); + raTwo.setServerUrl(null); + assertNonEquality(raOne, raTwo); + } + + public void testBrokerXMLConfigEquality() { + raOne.setBrokerXmlConfig("one"); + raTwo.setBrokerXmlConfig("one"); + assertEquality(raOne, raTwo); + } + + public void testBrokerXMLConfigInequality() { + raOne.setBrokerXmlConfig("one"); + raTwo.setBrokerXmlConfig("two"); + assertNonEquality(raOne, raTwo); + } + + public void testBrokerXMLConfigInequalityDifferentCase() { + raOne.setBrokerXmlConfig("one"); + raTwo.setBrokerXmlConfig("ONE"); + assertNonEquality(raOne, raTwo); + } + + public void testNullBrokerXMLConfigInequality() { + raOne.setBrokerXmlConfig("one"); + raTwo.setBrokerXmlConfig(null); + assertNonEquality(raOne, raTwo); + } + + public void testPasswordNotPartOfEquality() { + raOne.setClientid("one"); + raTwo.setClientid("one"); + raOne.setPassword("foo"); + raTwo.setPassword("bar"); + assertEquality(raOne, raTwo); + } + + private void assertEquality(ActiveMQResourceAdapter leftRa, ActiveMQResourceAdapter rightRa) { + assertTrue("ActiveMQResourceAdapters are not equal", leftRa.equals(rightRa)); + assertTrue("ActiveMQResourceAdapters are not equal", rightRa.equals(leftRa)); + assertTrue("HashCodes are not equal", leftRa.hashCode() == rightRa.hashCode()); + } + + private void assertNonEquality(ActiveMQResourceAdapter leftRa, ActiveMQResourceAdapter rightRa) { + assertFalse("ActiveMQResourceAdapters are equal", leftRa.equals(rightRa)); + assertFalse("ActiveMQResourceAdapters are equal", rightRa.equals(leftRa)); + assertFalse("HashCodes are equal", leftRa.hashCode() == rightRa.hashCode()); + } + + +} diff --git a/activemq-ra/src/test/java/org/activemq/ra/ConnectionEventListenerAdapter.java b/activemq-ra/src/test/java/org/activemq/ra/ConnectionEventListenerAdapter.java new file mode 100755 index 0000000000..8c36f59ae0 --- /dev/null +++ b/activemq-ra/src/test/java/org/activemq/ra/ConnectionEventListenerAdapter.java @@ -0,0 +1,58 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activemq.ra; + +import javax.resource.spi.ConnectionEvent; +import javax.resource.spi.ConnectionEventListener; + + +/** + * @version $Revision$ + */ +public class ConnectionEventListenerAdapter implements ConnectionEventListener { + + /** + * @see javax.resource.spi.ConnectionEventListener#connectionClosed(javax.resource.spi.ConnectionEvent) + */ + public void connectionClosed(ConnectionEvent arg0) { + } + + /** + * @see javax.resource.spi.ConnectionEventListener#localTransactionStarted(javax.resource.spi.ConnectionEvent) + */ + public void localTransactionStarted(ConnectionEvent arg0) { + } + + /** + * @see javax.resource.spi.ConnectionEventListener#localTransactionCommitted(javax.resource.spi.ConnectionEvent) + */ + public void localTransactionCommitted(ConnectionEvent arg0) { + } + + /** + * @see javax.resource.spi.ConnectionEventListener#localTransactionRolledback(javax.resource.spi.ConnectionEvent) + */ + public void localTransactionRolledback(ConnectionEvent arg0) { + } + + /** + * @see javax.resource.spi.ConnectionEventListener#connectionErrorOccurred(javax.resource.spi.ConnectionEvent) + */ + public void connectionErrorOccurred(ConnectionEvent arg0) { + } +} diff --git a/activemq-ra/src/test/java/org/activemq/ra/ConnectionManagerAdapter.java b/activemq-ra/src/test/java/org/activemq/ra/ConnectionManagerAdapter.java new file mode 100755 index 0000000000..29909da550 --- /dev/null +++ b/activemq-ra/src/test/java/org/activemq/ra/ConnectionManagerAdapter.java @@ -0,0 +1,131 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activemq.ra; + +import java.util.ArrayList; +import java.util.Iterator; + +import javax.resource.ResourceException; +import javax.resource.spi.ConnectionEvent; +import javax.resource.spi.ConnectionEventListener; +import javax.resource.spi.ConnectionManager; +import javax.resource.spi.ConnectionRequestInfo; +import javax.resource.spi.ManagedConnection; +import javax.resource.spi.ManagedConnectionFactory; +import javax.security.auth.Subject; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * A simple implementation of a ConnectionManager that can be extended so that it can + * see how the RA connections are interacting with it. + * + * @version $Revision$ + */ +public class ConnectionManagerAdapter implements ConnectionManager, ConnectionEventListener { + + private static final long serialVersionUID = 5205646563916645831L; + + private static final Log log = LogFactory.getLog(ConnectionManagerAdapter.class); + ArrayList listners = new ArrayList(); + ArrayList connections = new ArrayList(); + + /** + * Adds a listner to all connections created by this connection manager. + * This listner will be added to all previously created connections. + * + * @param l + */ + public void addConnectionEventListener(ConnectionEventListener l ) { + for (Iterator iter = connections.iterator(); iter.hasNext();) { + ManagedConnection c = (ManagedConnection) iter.next(); + c.addConnectionEventListener(l); + } + listners.add(l); + } + + /** + * @see javax.resource.spi.ConnectionManager#allocateConnection(javax.resource.spi.ManagedConnectionFactory, javax.resource.spi.ConnectionRequestInfo) + */ + public Object allocateConnection(ManagedConnectionFactory connectionFactory, ConnectionRequestInfo info) throws ResourceException { + Subject subject = null; + ManagedConnection connection = connectionFactory.createManagedConnection(subject, info); + connection.addConnectionEventListener(this); + for (Iterator iter = listners.iterator(); iter.hasNext();) { + ConnectionEventListener l = (ConnectionEventListener) iter.next(); + connection.addConnectionEventListener(l); + } + connections.add(connection); + return connection.getConnection(subject, info); + } + + /** + * @see javax.resource.spi.ConnectionEventListener#connectionClosed(javax.resource.spi.ConnectionEvent) + */ + public void connectionClosed(ConnectionEvent event) { + connections.remove(event.getSource()); + try { + ((ManagedConnection)event.getSource()).cleanup(); + } catch (ResourceException e) { + log.warn("Error occured during the cleanup of a managed connection: ",e); + } + try { + ((ManagedConnection)event.getSource()).destroy(); + } catch (ResourceException e) { + log.warn("Error occured during the destruction of a managed connection: ",e); + } + } + + /** + * @see javax.resource.spi.ConnectionEventListener#localTransactionStarted(javax.resource.spi.ConnectionEvent) + */ + public void localTransactionStarted(ConnectionEvent event) { + } + + /** + * @see javax.resource.spi.ConnectionEventListener#localTransactionCommitted(javax.resource.spi.ConnectionEvent) + */ + public void localTransactionCommitted(ConnectionEvent event) { + } + + /** + * @see javax.resource.spi.ConnectionEventListener#localTransactionRolledback(javax.resource.spi.ConnectionEvent) + */ + public void localTransactionRolledback(ConnectionEvent event) { + } + + /** + * @see javax.resource.spi.ConnectionEventListener#connectionErrorOccurred(javax.resource.spi.ConnectionEvent) + */ + public void connectionErrorOccurred(ConnectionEvent event) { + log.warn("Managed connection experiened an error: ",event.getException()); + try { + ((ManagedConnection)event.getSource()).cleanup(); + } catch (ResourceException e) { + log.warn("Error occured during the cleanup of a managed connection: ",e); + } + try { + ((ManagedConnection)event.getSource()).destroy(); + } catch (ResourceException e) { + log.warn("Error occured during the destruction of a managed connection: ",e); + } + } + +} diff --git a/activemq-ra/src/test/java/org/activemq/ra/MDBTest.java b/activemq-ra/src/test/java/org/activemq/ra/MDBTest.java new file mode 100644 index 0000000000..ffe91e404a --- /dev/null +++ b/activemq-ra/src/test/java/org/activemq/ra/MDBTest.java @@ -0,0 +1,215 @@ +/** + * + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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.activemq.ra; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.Timer; + +import javax.jms.Connection; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.resource.ResourceException; +import javax.resource.spi.BootstrapContext; +import javax.resource.spi.UnavailableException; +import javax.resource.spi.XATerminator; +import javax.resource.spi.endpoint.MessageEndpoint; +import javax.resource.spi.endpoint.MessageEndpointFactory; +import javax.resource.spi.work.ExecutionContext; +import javax.resource.spi.work.Work; +import javax.resource.spi.work.WorkException; +import javax.resource.spi.work.WorkListener; +import javax.resource.spi.work.WorkManager; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import junit.framework.TestCase; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.command.ActiveMQQueue; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; + +public class MDBTest extends TestCase { + + private final class StubBootstrapContext implements BootstrapContext { + public WorkManager getWorkManager() { + return new WorkManager() { + public void doWork(Work work) throws WorkException { + new Thread(work).start(); + } + + public void doWork(Work work, long arg1, ExecutionContext arg2, WorkListener arg3) + throws WorkException { + new Thread(work).start(); + } + + public long startWork(Work work) throws WorkException { + new Thread(work).start(); + return 0; + } + + public long startWork(Work work, long arg1, ExecutionContext arg2, WorkListener arg3) + throws WorkException { + new Thread(work).start(); + return 0; + } + + public void scheduleWork(Work work) throws WorkException { + new Thread(work).start(); + } + + public void scheduleWork(Work work, long arg1, ExecutionContext arg2, WorkListener arg3) + throws WorkException { + new Thread(work).start(); + } + }; + } + + public XATerminator getXATerminator() { + return null; + } + + public Timer createTimer() throws UnavailableException { + return null; + } + } + + public class StubMessageEndpoint implements MessageEndpoint, MessageListener { + public int messageCount; + public XAResource xaresource; + public Xid xid=null; + + public void beforeDelivery(Method method) throws NoSuchMethodException, ResourceException { + try { + if( xid==null ) + xid = createXid(); + xaresource.start(xid,0); + } catch (Throwable e) { + throw new ResourceException(e); + } + } + + public void afterDelivery() throws ResourceException { + try { + xaresource.end(xid,0); + xaresource.prepare(xid); + xaresource.commit(xid,false); + } catch (Throwable e) { + throw new ResourceException(e); + } + } + + public void release() { + } + + public void onMessage(Message message) { + messageCount++; + } + + } + + public void testMessageDelivery() throws Exception { + + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false"); + Connection connection = factory.createConnection(); + Session session = connection.createSession(false, 0); + + ActiveMQResourceAdapter adapter = new ActiveMQResourceAdapter(); + adapter.setServerUrl("vm://localhost?broker.persistent=false"); + adapter.start(new StubBootstrapContext()); + + final CountDownLatch messageDelivered = new CountDownLatch(1); + + final StubMessageEndpoint endpoint = new StubMessageEndpoint() { + public void onMessage(Message message) { + super.onMessage(message); + messageDelivered.countDown(); + }; + }; + + ActiveMQActivationSpec activationSpec = new ActiveMQActivationSpec(); + activationSpec.setDestinationType(Queue.class.getName()); + activationSpec.setDestination("TEST"); + activationSpec.setResourceAdapter(adapter); + activationSpec.validate(); + + MessageEndpointFactory messageEndpointFactory = new MessageEndpointFactory() { + public MessageEndpoint createEndpoint(XAResource resource) throws UnavailableException { + endpoint.xaresource = resource; + return endpoint; + } + public boolean isDeliveryTransacted(Method method) throws NoSuchMethodException { + return true; + } + }; + + // Activate an Endpoint + adapter.endpointActivation(messageEndpointFactory, activationSpec); + + // Give endpoint a chance to setup and register its listeners + try { + Thread.sleep(1000); + } catch (Exception e) { + + } + + // Send the broker a message to that endpoint + MessageProducer producer = session.createProducer(new ActiveMQQueue("TEST")); + producer.send(session.createTextMessage("Hello!")); + connection.close(); + + // Wait for the message to be delivered. + assertTrue(messageDelivered.await(5000, TimeUnit.MILLISECONDS)); + + // Shut the Endpoint down. + adapter.endpointDeactivation(messageEndpointFactory, activationSpec); + adapter.stop(); + + } + + long txGenerator = System.currentTimeMillis(); + + public Xid createXid() throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream os = new DataOutputStream(baos); + os.writeLong(++txGenerator); + os.close(); + final byte[] bs = baos.toByteArray(); + + return new Xid() { + public int getFormatId() { + return 86; + } + public byte[] getGlobalTransactionId() { + return bs; + } + public byte[] getBranchQualifier() { + return bs; + } + }; + + } + +} diff --git a/activemq-ra/src/test/java/org/activemq/ra/ManagedConnectionFactoryTest.java b/activemq-ra/src/test/java/org/activemq/ra/ManagedConnectionFactoryTest.java new file mode 100755 index 0000000000..fbb791f633 --- /dev/null +++ b/activemq-ra/src/test/java/org/activemq/ra/ManagedConnectionFactoryTest.java @@ -0,0 +1,160 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activemq.ra; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Timer; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.jms.QueueConnectionFactory; +import javax.jms.TopicConnectionFactory; +import javax.resource.Referenceable; +import javax.resource.ResourceException; +import javax.resource.spi.BootstrapContext; +import javax.resource.spi.ConnectionRequestInfo; +import javax.resource.spi.ManagedConnection; +import javax.resource.spi.ManagedConnectionFactory; +import javax.resource.spi.UnavailableException; +import javax.resource.spi.XATerminator; +import javax.resource.spi.work.WorkManager; + +import junit.framework.TestCase; + +import org.activemq.ActiveMQConnectionFactory; + + +/** + * @version $Revision$ + */ +public class ManagedConnectionFactoryTest extends TestCase { + + private static final String DEFAULT_HOST = "vm://localhost?broker.persistent=false"; + private static final String REMOTE_HOST = "vm://remotehost?broker.persistent=false"; + private ActiveMQManagedConnectionFactory managedConnectionFactory; + + /** + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + + ActiveMQResourceAdapter adapter = new ActiveMQResourceAdapter(); + adapter.setServerUrl(DEFAULT_HOST); + adapter.setUserName(ActiveMQConnectionFactory.DEFAULT_USER); + adapter.setPassword(ActiveMQConnectionFactory.DEFAULT_PASSWORD); + adapter.start(new BootstrapContext(){ + public WorkManager getWorkManager() { + return null; + } + public XATerminator getXATerminator() { + return null; + } + + public Timer createTimer() throws UnavailableException { + return null; + } + }); + + managedConnectionFactory = new ActiveMQManagedConnectionFactory(); + managedConnectionFactory.setResourceAdapter(adapter); + + } + + public void testConnectionFactoryAllocation() throws ResourceException, JMSException { + + // Make sure that the ConnectionFactory is asking the connection manager to + // allocate the connection. + final boolean allocateRequested[] = new boolean[]{false}; + Object cf = managedConnectionFactory.createConnectionFactory( + new ConnectionManagerAdapter() { + private static final long serialVersionUID = 1699499816530099939L; + + public Object allocateConnection(ManagedConnectionFactory connectionFactory, ConnectionRequestInfo info) + throws ResourceException { + allocateRequested[0]=true; + return super.allocateConnection(connectionFactory, info); + } + } + ); + + // We should be getting a JMS Connection Factory. + assertTrue( cf instanceof ConnectionFactory ); + ConnectionFactory connectionFactory = (ConnectionFactory)cf; + + // Make sure that the connection factory is using the ConnectionManager.. + Connection connection = connectionFactory.createConnection(); + assertTrue(allocateRequested[0]); + + // Make sure that the returned connection is of the expected type. + assertTrue( connection!=null ); + assertTrue( connection instanceof ManagedConnectionProxy ); + + } + + + public void testConnectionFactoryConnectionMatching() throws ResourceException, JMSException { + + ActiveMQConnectionRequestInfo ri1 = new ActiveMQConnectionRequestInfo(); + ri1.setServerUrl(DEFAULT_HOST); + ri1.setUserName(ActiveMQConnectionFactory.DEFAULT_USER); + ri1.setPassword(ActiveMQConnectionFactory.DEFAULT_PASSWORD); + + ActiveMQConnectionRequestInfo ri2 = new ActiveMQConnectionRequestInfo(); + ri2.setServerUrl(REMOTE_HOST); + ri2.setUserName(ActiveMQConnectionFactory.DEFAULT_USER); + ri2.setPassword(ActiveMQConnectionFactory.DEFAULT_PASSWORD); + assertNotSame(ri1, ri2); + + ManagedConnection connection1 = managedConnectionFactory.createManagedConnection(null, ri1); + ManagedConnection connection2 = managedConnectionFactory.createManagedConnection(null, ri2); + assertTrue(connection1!=connection2); + + HashSet set = new HashSet(); + set.add(connection1); + set.add(connection2); + + // Can we match for the first connection? + ActiveMQConnectionRequestInfo ri3 = ri1.copy(); + assertTrue( ri1!=ri3 && ri1.equals(ri3) ); + ManagedConnection test = managedConnectionFactory.matchManagedConnections(set,null, ri3); + assertTrue( connection1==test ); + + // Can we match for the second connection? + ri3 = ri2.copy(); + assertTrue( ri2!=ri3 && ri2.equals(ri3) ); + test = managedConnectionFactory.matchManagedConnections(set,null, ri2); + assertTrue( connection2==test ); + + } + + public void testConnectionFactoryIsSerializableAndReferenceable() throws ResourceException, JMSException { + Object cf = managedConnectionFactory.createConnectionFactory(new ConnectionManagerAdapter()); + assertTrue( cf!=null ); + assertTrue( cf instanceof Serializable ); + assertTrue( cf instanceof Referenceable ); + } + + public void testImplementsQueueAndTopicConnectionFactory() throws Exception { + Object cf = managedConnectionFactory.createConnectionFactory(new ConnectionManagerAdapter()); + assertTrue( cf instanceof QueueConnectionFactory ); + assertTrue( cf instanceof TopicConnectionFactory ); + } + +} diff --git a/activemq-ra/src/test/java/org/activemq/ra/ManagedConnectionTest.java b/activemq-ra/src/test/java/org/activemq/ra/ManagedConnectionTest.java new file mode 100755 index 0000000000..c356c3444c --- /dev/null +++ b/activemq-ra/src/test/java/org/activemq/ra/ManagedConnectionTest.java @@ -0,0 +1,234 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activemq.ra; + +import java.util.Timer; + +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueConnectionFactory; +import javax.jms.Session; +import javax.jms.TopicConnection; +import javax.jms.TopicConnectionFactory; +import javax.resource.ResourceException; +import javax.resource.spi.BootstrapContext; +import javax.resource.spi.ConnectionEvent; +import javax.resource.spi.UnavailableException; +import javax.resource.spi.XATerminator; +import javax.resource.spi.work.WorkManager; + +import junit.framework.TestCase; + +import org.activemq.ActiveMQConnectionFactory; + + +/** + * @version $Revision$ + */ +public class ManagedConnectionTest extends TestCase { + + + private static final String DEFAULT_HOST = "vm://localhost"; + + private ConnectionManagerAdapter connectionManager = new ConnectionManagerAdapter(); + private ActiveMQManagedConnectionFactory managedConnectionFactory; + private ConnectionFactory connectionFactory; + private ManagedConnectionProxy connection; + private ActiveMQManagedConnection managedConnection; + + /** + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception { + + ActiveMQResourceAdapter adapter = new ActiveMQResourceAdapter(); + adapter.setServerUrl(DEFAULT_HOST); + adapter.setUserName(ActiveMQConnectionFactory.DEFAULT_USER); + adapter.setPassword(ActiveMQConnectionFactory.DEFAULT_PASSWORD); + adapter.start(new BootstrapContext(){ + public WorkManager getWorkManager() { + return null; + } + public XATerminator getXATerminator() { + return null; + } + + public Timer createTimer() throws UnavailableException { + return null; + } + }); + + managedConnectionFactory = new ActiveMQManagedConnectionFactory(); + managedConnectionFactory.setResourceAdapter(adapter); + + connectionFactory = (ConnectionFactory) managedConnectionFactory.createConnectionFactory(connectionManager); + connection = (ManagedConnectionProxy) connectionFactory.createConnection(); + managedConnection = connection.getManagedConnection(); + + } + + public void testConnectionCloseEvent() throws ResourceException, JMSException { + + final boolean test[] = new boolean[]{false}; + connectionManager.addConnectionEventListener(new ConnectionEventListenerAdapter() { + public void connectionClosed(ConnectionEvent arg0) { + test[0]=true; + } + }); + connection.close(); + assertTrue( test[0] ); + } + + public void testLocalTransactionCommittedEvent() throws ResourceException, JMSException { + + final boolean test[] = new boolean[]{false}; + connectionManager.addConnectionEventListener(new ConnectionEventListenerAdapter() { + public void localTransactionCommitted(ConnectionEvent arg0) { + test[0]=true; + } + }); + + managedConnection.getLocalTransaction().begin(); + Session session = connection.createSession(true,0); + + doWork(session); + session.commit(); + + assertTrue( test[0] ); + + } + + public void testLocalTransactionRollbackEvent() throws ResourceException, JMSException { + + final boolean test[] = new boolean[]{false}; + connectionManager.addConnectionEventListener(new ConnectionEventListenerAdapter() { + public void localTransactionRolledback(ConnectionEvent arg0) { + test[0]=true; + } + }); + managedConnection.getLocalTransaction().begin(); + Session session = connection.createSession(true,0); + doWork(session); + session.rollback(); + + assertTrue( test[0] ); + } + + public void testLocalTransactionStartedEvent() throws ResourceException, JMSException { + + final boolean test[] = new boolean[]{false}; + connectionManager.addConnectionEventListener(new ConnectionEventListenerAdapter() { + public void localTransactionStarted(ConnectionEvent arg0) { + test[0]=true; + } + }); + + // Begin the transaction... that should kick off the event. + managedConnection.getLocalTransaction().begin(); + Session session = connection.createSession(true,0); + doWork(session); + + assertTrue( test[0] ); + } + + /** + * A managed connection that has been clean up should throw exceptions + * when it used. + */ + public void testCleanup() throws ResourceException, JMSException { + + // Do some work and close it... + Session session = connection.createSession(true,0); + doWork(session); + connection.close(); + try { + // This should throw expection + doWork(session); + fail("Using a session after the connection is closed should throw exception."); + } catch ( JMSException e) { + } + } + + public void testSessionCloseIndependance() throws ResourceException, JMSException { + + Session session1 = connection.createSession(true,0); + Session session2 = connection.createSession(true,0); + assertTrue( session1!=session2 ); + + doWork(session1); + session1.close(); + try { + // This should throw expection + doWork(session1); + fail("Using a session after the connection is closed should throw exception."); + } catch ( JMSException e) { + } + + // Make sure that closing session 1 does not close session 2 + doWork(session2); + session2.close(); + try { + // This should throw expection + doWork(session2); + fail("Using a session after the connection is closed should throw exception."); + } catch ( JMSException e) { + } + } + + /** + * Does some work so that we can test commit/rollback etc. + * @throws JMSException + */ + public void doWork(Session session) throws JMSException { + Queue t = session.createQueue("TEST"); + MessageProducer producer = session.createProducer(t); + producer.send(session.createTextMessage("test message.")); + } + + public void testImplementsQueueAndTopicConnection() throws Exception { + QueueConnection qc = ((QueueConnectionFactory)connectionFactory).createQueueConnection(); + assertNotNull(qc); + TopicConnection tc = ((TopicConnectionFactory)connectionFactory).createTopicConnection(); + assertNotNull(tc); + } + + public void testSelfEquality() { + assertEquality(managedConnection, managedConnection); + } + + public void testSamePropertiesButNotEqual() throws Exception { + ManagedConnectionProxy newConnection = (ManagedConnectionProxy) connectionFactory.createConnection(); + assertNonEquality(managedConnection, newConnection.getManagedConnection()); + } + + private void assertEquality(ActiveMQManagedConnection leftCon, ActiveMQManagedConnection rightCon) { + assertTrue("ActiveMQManagedConnection are not equal", leftCon.equals(rightCon)); + assertTrue("ActiveMQManagedConnection are not equal", rightCon.equals(leftCon)); + assertTrue("HashCodes are not equal", leftCon.hashCode() == rightCon.hashCode()); + } + + private void assertNonEquality(ActiveMQManagedConnection leftCon, ActiveMQManagedConnection rightCon) { + assertFalse("ActiveMQManagedConnection are equal", leftCon.equals(rightCon)); + assertFalse("ActiveMQManagedConnection are equal", rightCon.equals(leftCon)); + assertFalse("HashCodes are equal", leftCon.hashCode() == rightCon.hashCode()); + } + +} diff --git a/activemq-ra/src/test/java/org/activemq/ra/MessageEndpointProxyTest.java b/activemq-ra/src/test/java/org/activemq/ra/MessageEndpointProxyTest.java new file mode 100755 index 0000000000..90d34825b1 --- /dev/null +++ b/activemq-ra/src/test/java/org/activemq/ra/MessageEndpointProxyTest.java @@ -0,0 +1,214 @@ +/** + * + * Copyright 2004 Michael Gaffney + * + * Licensed 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.activemq.ra; + +import org.jmock.MockObjectTestCase; +import org.jmock.Mock; + +import javax.jms.MessageListener; +import javax.jms.Message; +import javax.resource.spi.endpoint.MessageEndpoint; +import javax.resource.ResourceException; +import java.lang.reflect.Method; + +/** + * @author Michael Gaffney + */ +public class MessageEndpointProxyTest extends MockObjectTestCase { + + private Mock mockEndpoint; + private Mock stubMessage; + private MessageEndpointProxy endpointProxy; + + public MessageEndpointProxyTest(String name) { + super(name); + } + + protected void setUp() { + mockEndpoint = new Mock(EndpointAndListener.class); + stubMessage = new Mock(Message.class); + endpointProxy = new MessageEndpointProxy((MessageEndpoint) mockEndpoint.proxy()); + } + + public void testInvalidConstruction() { + Mock mockEndpoint = new Mock(MessageEndpoint.class); + try { + MessageEndpointProxy proxy = new MessageEndpointProxy((MessageEndpoint) mockEndpoint.proxy()); + fail("An exception should have been thrown"); + } catch (IllegalArgumentException e) { + assertTrue(true); + } + } + + public void testSuccessfulCallSequence() { + setupBeforeDeliverySuccessful(); + setupOnMessageSuccessful(); + setupAfterDeliverySuccessful(); + + doBeforeDeliveryExpectSuccess(); + doOnMessageExpectSuccess(); + doAfterDeliveryExpectSuccess(); + } + + public void testBeforeDeliveryFailure() { + mockEndpoint.expects(once()).method("beforeDelivery").with(isA(Method.class)) + .will(throwException(new ResourceException())); + mockEndpoint.expects(never()).method("onMessage"); + mockEndpoint.expects(never()).method("afterDelivery"); + setupExpectRelease(); + + try { + endpointProxy.beforeDelivery(ActiveMQEndpointWorker.ON_MESSAGE_METHOD); + fail("An exception should have been thrown"); + } catch (Exception e) { + assertTrue(true); + } + doOnMessageExpectInvalidMessageEndpointException(); + doAfterDeliveryExpectInvalidMessageEndpointException(); + + doFullyDeadCheck(); + } + + public void testOnMessageFailure() { + setupBeforeDeliverySuccessful(); + mockEndpoint.expects(once()).method("onMessage").with(same(stubMessage.proxy())) + .will(throwException(new RuntimeException())); + setupAfterDeliverySuccessful(); + setupExpectRelease(); + + doBeforeDeliveryExpectSuccess(); + try { + endpointProxy.onMessage((Message) stubMessage.proxy()); + fail("An exception should have been thrown"); + } catch (Exception e) { + assertTrue(true); + } + doAfterDeliveryExpectSuccess(); + + doFullyDeadCheck(); + } + + public void testAfterDeliveryFailure() { + setupBeforeDeliverySuccessful(); + setupOnMessageSuccessful(); + mockEndpoint.expects(once()).method("afterDelivery") + .will(throwException(new ResourceException())); + setupExpectRelease(); + + doBeforeDeliveryExpectSuccess(); + doOnMessageExpectSuccess(); + try { + endpointProxy.afterDelivery(); + fail("An exception should have been thrown"); + } catch (Exception e) { + assertTrue(true); + } + + doFullyDeadCheck(); + } + + private void doFullyDeadCheck() { + doBeforeDeliveryExpectInvalidMessageEndpointException(); + doOnMessageExpectInvalidMessageEndpointException(); + doAfterDeliveryExpectInvalidMessageEndpointException(); + doReleaseExpectInvalidMessageEndpointException(); + } + + private void setupAfterDeliverySuccessful() { + mockEndpoint.expects(once()).method("afterDelivery"); + } + + private void setupOnMessageSuccessful() { + mockEndpoint.expects(once()).method("onMessage").with(same(stubMessage.proxy())); + } + + private void setupBeforeDeliverySuccessful() { + mockEndpoint.expects(once()).method("beforeDelivery").with(isA(Method.class)); + } + + private void setupExpectRelease() { + mockEndpoint.expects(once()).method("release"); + } + + private void doBeforeDeliveryExpectSuccess() { + try { + endpointProxy.beforeDelivery(ActiveMQEndpointWorker.ON_MESSAGE_METHOD); + } catch (Exception e) { + fail("No exception should have been thrown"); + } + } + + private void doOnMessageExpectSuccess() { + try { + endpointProxy.onMessage((Message) stubMessage.proxy()); + } catch (Exception e) { + fail("No exception should have been thrown"); + } + } + + private void doAfterDeliveryExpectSuccess() { + try { + endpointProxy.afterDelivery(); + } catch (Exception e) { + fail("No exception should have been thrown"); + } + } + + private void doBeforeDeliveryExpectInvalidMessageEndpointException() { + try { + endpointProxy.beforeDelivery(ActiveMQEndpointWorker.ON_MESSAGE_METHOD); + fail("An InvalidMessageEndpointException should have been thrown"); + } catch (InvalidMessageEndpointException e) { + assertTrue(true); + } catch (Exception e) { + fail("An InvalidMessageEndpointException should have been thrown"); + } + } + + private void doOnMessageExpectInvalidMessageEndpointException() { + try { + endpointProxy.onMessage((Message) stubMessage.proxy()); + fail("An InvalidMessageEndpointException should have been thrown"); + } catch (InvalidMessageEndpointException e) { + assertTrue(true); + } + } + + private void doAfterDeliveryExpectInvalidMessageEndpointException() { + try { + endpointProxy.afterDelivery(); + fail("An InvalidMessageEndpointException should have been thrown"); + } catch (InvalidMessageEndpointException e) { + assertTrue(true); + } catch (Exception e) { + fail("An InvalidMessageEndpointException should have been thrown"); + } + } + + private void doReleaseExpectInvalidMessageEndpointException() { + try { + endpointProxy.release(); + fail("An InvalidMessageEndpointException should have been thrown"); + } catch (InvalidMessageEndpointException e) { + assertTrue(true); + } + } + + private interface EndpointAndListener extends MessageListener, MessageEndpoint { + } +} diff --git a/activemq-ra/src/test/java/org/activemq/ra/ServerSessionImplTest.java b/activemq-ra/src/test/java/org/activemq/ra/ServerSessionImplTest.java new file mode 100644 index 0000000000..b819e17436 --- /dev/null +++ b/activemq-ra/src/test/java/org/activemq/ra/ServerSessionImplTest.java @@ -0,0 +1,94 @@ +/** + * + * Copyright 2005 Guillaume Nodet + * + * Licensed 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.activemq.ra; + +import org.jmock.MockObjectTestCase; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class ServerSessionImplTest extends MockObjectTestCase { + + /** + * Need to re-work this test case, it broke since the amq4 internals changed and + * mocks were being using against the internals. + * + */ + public void testDummy() { + } + +/* + public void testBatch() throws Exception { + DummyActiveMQConnection connection = new DummyActiveMQConnection(new ActiveMQConnectionFactory(), + null, + null, + getMockTransportChannel()); + ServerSessionPoolImpl pool = new ServerSessionPoolImpl(null, 1); + DummyActiveMQSession session = new DummyActiveMQSession(connection); + MemoryBoundedQueue queue = connection.getMemoryBoundedQueue("Session(" + session.getSessionId() + ")"); + queue.enqueue(new ActiveMQTextMessage()); + queue.enqueue(new ActiveMQTextMessage()); + queue.enqueue(new ActiveMQTextMessage()); + DummyMessageEndpoint endpoint = new DummyMessageEndpoint(); + ServerSessionImpl serverSession = new ServerSessionImpl(pool, session, null, endpoint, true, 2); + serverSession.run(); + assertEquals(2, endpoint.messagesPerBatch.size()); + assertEquals(new Integer(2), endpoint.messagesPerBatch.get(0)); + assertEquals(new Integer(1), endpoint.messagesPerBatch.get(1)); + } + + private class DummyMessageEndpoint implements MessageEndpoint, MessageListener { + protected List messagesPerBatch = new ArrayList(); + protected int nbMessages = -1000; + public void beforeDelivery(Method arg0) throws NoSuchMethodException, ResourceException { + nbMessages = 0; + } + public void afterDelivery() throws ResourceException { + messagesPerBatch.add(new Integer(nbMessages)); + nbMessages = -1000; + } + public void release() { + } + public void onMessage(Message arg0) { + nbMessages ++; + } + } + + private class DummyActiveMQSession extends ActiveMQSession { + protected DummyActiveMQSession(ActiveMQConnection connection, SessionId sessionId, int acknowledgeMode, boolean asyncDispatch) throws JMSException { + super(connection, sessionId, acknowledgeMode, asyncDispatch); + } + } + + private class DummyActiveMQConnection extends ActiveMQConnection { + protected DummyActiveMQConnection(Transport transport, String userName, String password, JMSStatsImpl factoryStats) throws IOException { + super(transport, userName, password, factoryStats); + } + } + + private TransportChannel getMockTransportChannel() { + Mock tc = new Mock(TransportChannel.class); + tc.expects(once()).method("setPacketListener"); + tc.expects(once()).method("setExceptionListener"); + tc.expects(once()).method("addTransportStatusEventListener"); + tc.expects(atLeastOnce()).method("asyncSend"); + tc.expects(atLeastOnce()).method("send"); + return (TransportChannel) tc.proxy(); + } + */ +} diff --git a/activemq-ra/src/test/resources/log4j.properties b/activemq-ra/src/test/resources/log4j.properties new file mode 100755 index 0000000000..eaa74a84d6 --- /dev/null +++ b/activemq-ra/src/test/resources/log4j.properties @@ -0,0 +1,19 @@ +# +# The logging properties used during tests.. +# +log4j.rootLogger=INFO, out + +log4j.logger.org.activemq.spring=WARN +log4j.logger.org.activemq=INFO + +# CONSOLE appender not used by default +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n + +# File appender +log4j.appender.out=org.apache.log4j.FileAppender +log4j.appender.out.layout=org.apache.log4j.PatternLayout +log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +log4j.appender.out.file=target/test-reports/activemq-test.log +log4j.appender.out.append=false diff --git a/activemq-systest/README.txt b/activemq-systest/README.txt new file mode 100644 index 0000000000..a259e39200 --- /dev/null +++ b/activemq-systest/README.txt @@ -0,0 +1,8 @@ +This module creates a system testing framework for ActiveMQ. + +Requirements +============ + +This module requires Java 5. + +Also you must have the ACTIVEMQ_HOME environment variable defined diff --git a/activemq-systest/maven.xml b/activemq-systest/maven.xml new file mode 100755 index 0000000000..24231bd6d3 --- /dev/null +++ b/activemq-systest/maven.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-systest/project.properties b/activemq-systest/project.properties new file mode 100755 index 0000000000..22d42346cd --- /dev/null +++ b/activemq-systest/project.properties @@ -0,0 +1,14 @@ +maven.repo.remote=\ +http://dist.codehaus.org,\ +http://www.ibiblio.org/maven,\ +http://cvs.apache.org/repository + +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=jar +maven.eclipse.classpath.include=src/test/resources + +maven.changelog.range=730 + +maven.junit.jvmargs=-Xmx160m diff --git a/activemq-systest/project.xml b/activemq-systest/project.xml new file mode 100755 index 0000000000..9b494a9139 --- /dev/null +++ b/activemq-systest/project.xml @@ -0,0 +1,162 @@ + + + + 3 + ${basedir}/../etc/project.xml + + ActiveMQ :: System Test + activemq-systest + + System Testing Framework for ActiveMQ + + System Testing Framework for ActiveMQ + + org.activemq.systest + + + The core APIs of the system testing agents + org.activemq.systest + + + The single VM implementations + org.activemq.systest.impl.vm + + + The testing use cases and scenarios + org.activemq.systest.usecase.* + + + + + + + geronimo-spec + geronimo-spec-j2ee-connector + ${geronimo_spec_j2ee_connector_version} + + false + true + + + + + activemq + activemq-core + ${pom.currentVersion} + + true + + + + + springframework + spring + ${spring_version} + http://www.springframework.org + + true + + + + + cglib + cglib-full + ${cglib_full_version} + http://cglib.sourceforge.net/ + + + + activemq + jmdns + ${jmdns_version} + + true + + + + + activeio + activeio + ${activeio_version} + + true + + + + + org.apache.derby + derby + ${derby_version} + + true + + + + + + annogen + annogen + 0.1.0 + + + ant + ant + 1.6.2 + + + + + dev@activemq.codehaus.org + src/main/java + target/test + + + + src/main/resources + + **/*.properties + + + + + + + + target/test + + **/*.properties + + + + + + src/test/resources + + **/*.properties + **/*.xml + + + + + **/*Test.* + + + + **/broker_separate_process/** + **/broker/topic/nonDurable/TwoBrokerNetworkScenarioTest.* + **/broker/topic/nonDurableTransacted/TwoBrokerNetworkScenarioTest.* + **/broker_non_persistent/topic/nonDurable/TwoBrokerNetworkScenarioTest.* + **/broker_non_persistent/topic/nonDurableTransacted/TwoBrokerNetworkConnectedBeforeStartScenarioTest.* + **/broker_non_persistent/topic/nonDurableTransacted/TwoBrokerNetworkScenarioTest.* + + **/broker/topic/durable/TwoBrokerNetworkConnectedBeforeStartScenarioTest.* + **/broker/topic/durableTransacted/TwoBrokerNetworkConnectedBeforeStartScenarioTest.* + **/broker/topic/nonDurable/TwoBrokerNetworkConnectedBeforeStartScenarioTest.* + **/broker_non_persistent/topic/durable/TwoBrokerNetworkScenarioTest.* + **/broker_non_persistent/topic/durableTransacted.TwoBrokerNetworkScenarioTest.* + + + + + + diff --git a/activemq-systest/src/main/java/org/activemq/systest/Agent.java b/activemq-systest/src/main/java/org/activemq/systest/Agent.java new file mode 100644 index 0000000000..82594cf5ef --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/Agent.java @@ -0,0 +1,36 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest; + +/** + * + * @version $Revision: 1.1 $ + */ +public interface Agent { + + public void start() throws Exception; + + public void stop() throws Exception; + + /** + * A helper method to make it easy to track exceptions when stopping complex agents + * + * @param stopper + */ + public void stop(AgentStopper stopper); +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/AgentStopper.java b/activemq-systest/src/main/java/org/activemq/systest/AgentStopper.java new file mode 100644 index 0000000000..0a696fdd15 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/AgentStopper.java @@ -0,0 +1,65 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest; + +/** + * A helper class used to stop a bunch of services, catching and logging any + * exceptions and then throwing the first exception when everything is stoped. + * + * @version $Revision: 1.1 $ + */ +public class AgentStopper { + private Exception firstException; + + /** + * Stops the given service, catching any exceptions that are thrown. + */ + public void stop(Agent service) { + if (service != null) { + try { + service.stop(this); + } + catch (Exception e) { + onException(service, e); + } + } + } + + public void onException(Object owner, Exception e) { + logError(owner, e); + if (firstException == null) { + firstException = e; + } + } + + /** + * Throws the first exception that was thrown if there was one. + */ + public void throwFirstException() throws Exception { + if (firstException != null) { + throw firstException; + } + } + + protected void logError(Object service, Exception e) { + System.err.println("Could not stop service: " + service + ". Reason: " + e); + e.printStackTrace(System.err); + } + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/AgentSupport.java b/activemq-systest/src/main/java/org/activemq/systest/AgentSupport.java new file mode 100644 index 0000000000..3bd473886a --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/AgentSupport.java @@ -0,0 +1,38 @@ +/** +* ActiveMQ: The Open Source Message Fabric +* +* Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com +* +* Licensed 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.activemq.systest; + +/** + * A helper class for working with agents + * + * @version $Revision: 1.1 $ + */ +public abstract class AgentSupport implements Agent { + + public void stop() throws Exception { + AgentStopper stopper = new AgentStopper(); + stop(stopper); + stopper.throwFirstException(); + } + + /** + * Provides a way for derived classes to stop resources cleanly, handling exceptions + */ + public abstract void stop(AgentStopper stopper); +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/BrokerAgent.java b/activemq-systest/src/main/java/org/activemq/systest/BrokerAgent.java new file mode 100644 index 0000000000..55318e68fd --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/BrokerAgent.java @@ -0,0 +1,51 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest; + +import javax.jms.ConnectionFactory; + +/** + * + * @version $Revision: 1.1 $ + */ +public interface BrokerAgent extends Agent { + + /** + * Kills the given broker, if possible avoiding a clean shutdown + * + * @throws Exception + */ + void kill() throws Exception; + + /** + * Returns the connection factory to connect to this broker + */ + ConnectionFactory getConnectionFactory(); + + /** + * Returns the connection URI to use to connect to this broker + */ + String getConnectionURI(); + + /** + * Sets up a network connection to the given broker + * @throws Exception + */ + void connectTo(BrokerAgent broker) throws Exception; + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/ClientAgent.java b/activemq-systest/src/main/java/org/activemq/systest/ClientAgent.java new file mode 100644 index 0000000000..25e0cc352b --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/ClientAgent.java @@ -0,0 +1,33 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest; + +import javax.jms.Destination; + +/** + * + * @version $Revision: 1.1 $ + */ +public interface ClientAgent extends Agent { + + public void connectTo(BrokerAgent broker) throws Exception; + + public void setDestination(Destination destination); + +} \ No newline at end of file diff --git a/activemq-systest/src/main/java/org/activemq/systest/ConsumerAgent.java b/activemq-systest/src/main/java/org/activemq/systest/ConsumerAgent.java new file mode 100644 index 0000000000..b78fe9f3b4 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/ConsumerAgent.java @@ -0,0 +1,37 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest; + +/** + * + * @version $Revision: 1.1 $ + */ +public interface ConsumerAgent extends ClientAgent { + + /** + * Asserts that the the given message list has been consumed, in order in an + * acceptable amount of time. + */ + void assertConsumed(MessageList messageList) throws Exception; + + /** + * Waits until a percentage of the list is consumed + */ + void waitUntilConsumed(MessageList messageList, int percentOfList) throws Exception; + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/DestinationFactory.java b/activemq-systest/src/main/java/org/activemq/systest/DestinationFactory.java new file mode 100644 index 0000000000..23ebfae4bc --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/DestinationFactory.java @@ -0,0 +1,36 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest; + +import javax.jms.Destination; +import javax.jms.JMSException; + +/** + * A factory to create destinations which works in any version of any JMS provider + * + * @version $Revision: 1.1 $ + */ +public interface DestinationFactory { + + public Destination createDestination(String physicalName, int destinationType) throws JMSException; + + public static final int TOPIC = 1; + public static final int QUEUE = 2; + public static final int TEMPORARY_TOPIC = 3; + public static final int TEMPORARY_QUEUE = 4; +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/MessageList.java b/activemq-systest/src/main/java/org/activemq/systest/MessageList.java new file mode 100644 index 0000000000..b65ecad00b --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/MessageList.java @@ -0,0 +1,50 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest; + +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.jms.Session; + +import java.util.List; + +/** + * Represents a list of messages + * + * @version $Revision: 1.1 $ + */ +public interface MessageList extends Agent { + + int getSize(); + + void assertMessagesCorrect(List list) throws JMSException; + + /** + * Sends the messages on the given producer + * + * @throws JMSException + */ + void sendMessages(Session session, MessageProducer producer) throws JMSException; + + /** + * Sends a percent of the messages to the given producer + * @throws JMSException + */ + void sendMessages(Session session, MessageProducer producer, int percent) throws JMSException; + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/ProducerAgent.java b/activemq-systest/src/main/java/org/activemq/systest/ProducerAgent.java new file mode 100644 index 0000000000..799c0c8f55 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/ProducerAgent.java @@ -0,0 +1,36 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest; + +import javax.jms.JMSException; + +/** + * + * @version $Revision: 1.1 $ + */ +public interface ProducerAgent extends ClientAgent { + + void sendMessages(MessageList messageList) throws JMSException; + + /** + * Sends a given percentage of the messages + * @throws JMSException + */ + void sendMessages(MessageList messageList, int percent) throws JMSException; + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/QueueOnlyScenario.java b/activemq-systest/src/main/java/org/activemq/systest/QueueOnlyScenario.java new file mode 100644 index 0000000000..cc1ad166fd --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/QueueOnlyScenario.java @@ -0,0 +1,27 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest; + +/** + * A marker interface to indicate that a Scenario only works with queues + * + * @version $Revision: 1.1 $ + */ +public interface QueueOnlyScenario extends Scenario { + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/Scenario.java b/activemq-systest/src/main/java/org/activemq/systest/Scenario.java new file mode 100644 index 0000000000..837fbb0e84 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/Scenario.java @@ -0,0 +1,33 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest; + +import javax.jms.Destination; + +/** + * + * @version $Revision: 1.1 $ + */ +public interface Scenario extends Agent { + + public void run() throws Exception; + + public void setDestination(Destination destination); + +} \ No newline at end of file diff --git a/activemq-systest/src/main/java/org/activemq/systest/ScenarioSupport.java b/activemq-systest/src/main/java/org/activemq/systest/ScenarioSupport.java new file mode 100644 index 0000000000..00a301abde --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/ScenarioSupport.java @@ -0,0 +1,65 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Iterator; +import java.util.LinkedList; + +/** + * + * @version $Revision: 1.1 $ + */ +public abstract class ScenarioSupport implements Scenario { + private static final Log log = LogFactory.getLog(ScenarioSupport.class); + + private LinkedList agents = new LinkedList(); + + // Helper methods + // ------------------------------------------------------------------------- + + /** + * Starts the given agent and adds it to the list of agents to stop when + * the test is complete + */ + public void start(Agent agent) throws Exception { + agent.start(); + agents.addFirst(agent); + } + + /** + * Stops all the added agents + */ + public void stop() throws Exception { + AgentStopper stopper = new AgentStopper(); + stop(stopper); + stopper.throwFirstException(); + } + + public void stop(AgentStopper stopper) { + for (Iterator iter = agents.iterator(); iter.hasNext();) { + Agent agent = (Agent) iter.next(); + log.info("Stopping agent: " + agent); + stopper.stop(agent); + } + } + + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/ScenarioTestCase.java b/activemq-systest/src/main/java/org/activemq/systest/ScenarioTestCase.java new file mode 100644 index 0000000000..b1f7a6483d --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/ScenarioTestCase.java @@ -0,0 +1,74 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestResult; + +/** + * A JUnit {@link Test} for running a Scenario in a JUnit test suite. + * + * @version $Revision: 1.1 $ + */ +public class ScenarioTestCase implements Test { + + private Scenario scenario; + private String description; + + public ScenarioTestCase(Scenario scenario, String description) { + this.scenario = scenario; + this.description = description; + } + + public int countTestCases() { + return 1; + } + + public void run(TestResult result) { + result.startTest(this); + try { + scenario.start(); + scenario.run(); + scenario.stop(); + result.endTest(this); + } + catch (AssertionFailedError e) { + result.addFailure(this, e); + } + catch (Throwable e) { + System.out.println("Failed to run test: " + e); + e.printStackTrace(); + result.addError(this, e); + } + finally { + try { + scenario.stop(); + scenario = null; + } + catch (Exception e) { + System.out.println("Failed to close down test: " + e); + e.printStackTrace(); + } + } + } + + public String toString() { + return description; + } +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/ScenarioTestSuite.java b/activemq-systest/src/main/java/org/activemq/systest/ScenarioTestSuite.java new file mode 100644 index 0000000000..cda6f27a78 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/ScenarioTestSuite.java @@ -0,0 +1,176 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest; + +import org.springframework.context.ApplicationContext; + +import javax.jms.Destination; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * A helper class for creating a test suite + * + * @version $Revision: 1.1 $ + */ +public class ScenarioTestSuite extends TestCase { + + private List brokers = new ArrayList(); + private boolean cacheBrokers; + + public static TestSuite createSuite(ApplicationContext clientContext, ApplicationContext brokerContext, Class[] scenarios, int destinationType) throws Exception { + TestSuite suite = new TestSuite(); + + ScenarioTestSuite test = new ScenarioTestSuite(); + test.appendTestCases(suite, clientContext, brokerContext, scenarios, destinationType); + return suite; + } + + public void appendTestCases(TestSuite suite, ApplicationContext clientContext, ApplicationContext brokerContext, Class[] scenarios, int destinationType) throws Exception { + for (int i = 0; i < scenarios.length; i++) { + Class scenario = scenarios[i]; + appendTestCase(suite, clientContext, brokerContext, scenario, destinationType); + } + } + + public void appendTestCase(TestSuite suite, ApplicationContext clientContext, ApplicationContext brokerContext, Class scenario, int destinationType) throws Exception { + // lets figure out how to create the scenario from all the options + // available + + Constructor[] constructors = scenario.getConstructors(); + for (int i = 0; i < constructors.length; i++) { + Constructor constructor = constructors[i]; + appendTestCase(suite, clientContext, brokerContext, scenario, constructor, destinationType); + } + } + + public void appendTestCase(TestSuite suite, ApplicationContext clientContext, ApplicationContext brokerContext, Class scenario, Constructor constructor, + int destinationType) throws Exception { + // lets configure the test case + Class[] parameterTypes = constructor.getParameterTypes(); + int size = parameterTypes.length; + String[][] namesForParameters = new String[size][]; + for (int i = 0; i < size; i++) { + Class parameterType = parameterTypes[i]; + String[] names = clientContext.getBeanNamesForType(parameterType); + if (names == null || names.length == 0) { + if (parameterType.equals(BrokerAgent.class)) { + names = new String[1]; + } + else { + fail("No bean instances available in the ApplicationContext for type: " + parameterType.getName()); + } + } + namesForParameters[i] = names; + } + + // lets try out each permutation of configuration + int[] counters = new int[size]; + boolean completed = false; + while (!completed) { + Object[] parameters = new Object[size]; + StringBuffer buffer = new StringBuffer(scenario.getName()); + int brokerCounter = 1; + for (int i = 0; i < size; i++) { + String beanName = namesForParameters[i][counters[i]]; + if (beanName != null) { + parameters[i] = clientContext.getBean(beanName); + buffer.append("."); + buffer.append(beanName); + } + else { + parameters[i] = getBrokerAgent(brokerContext, brokerCounter++); + } + } + + String destinationName = buffer.toString(); + addTestsFor(suite, clientContext, brokerContext, constructor, parameters, destinationName, destinationType); + + // now lets count though the options + int pivot = 0; + while (++counters[pivot] >= namesForParameters[pivot].length) { + counters[pivot] = 0; + pivot++; + if (pivot >= size) { + completed = true; + break; + } + } + } + } + + protected BrokerAgent getBrokerAgent(ApplicationContext brokerContext, int brokerCounter) { + if (cacheBrokers) { + BrokerAgent broker = null; + int index = brokerCounter - 1; + + // lets reuse broker instances across test cases + if (brokers.size() >= brokerCounter) { + broker = (BrokerAgent) brokers.get(index); + } + if (broker == null) { + broker = (BrokerAgent) brokerContext.getBean("broker" + brokerCounter); + brokers.add(index, broker); + } + return broker; + } + else { + return (BrokerAgent) brokerContext.getBean("broker" + brokerCounter); + } + } + + protected void addTestsFor(TestSuite suite, ApplicationContext clientContext, ApplicationContext brokerContext, Constructor constructor, + Object[] parameters, String destinationName, int destinationType) throws Exception { + Collection values = clientContext.getBeansOfType(DestinationFactory.class).values(); + assertTrue("we should at least one DestinationFactory in the ApplicationContext", values.size() > 0); + + for (Iterator iter = values.iterator(); iter.hasNext();) { + DestinationFactory destinationFactory = (DestinationFactory) iter.next(); + + Object instance = constructor.newInstance(parameters); + assertTrue("Instance is not a Scenario: " + instance, instance instanceof Scenario); + Scenario scenarioInstance = (Scenario) instance; + + String testName = destinationDescription(destinationType) + "." + destinationName; + Destination destination = destinationFactory.createDestination(testName, destinationType); + scenarioInstance.setDestination(destination); + + suite.addTest(new ScenarioTestCase(scenarioInstance, testName)); + } + } + + public static String destinationDescription(int destinationType) { + switch (destinationType) { + case DestinationFactory.QUEUE: + return "queue"; + + case DestinationFactory.TOPIC: + return "topic"; + + default: + return "Unknown"; + } + } +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/TopicOnlyScenario.java b/activemq-systest/src/main/java/org/activemq/systest/TopicOnlyScenario.java new file mode 100644 index 0000000000..f57b0a9917 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/TopicOnlyScenario.java @@ -0,0 +1,27 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest; + +/** + * A marker interface to indicate that a Scenario only works with queues + * + * @version $Revision: 1.1 $ + */ +public interface TopicOnlyScenario extends Scenario { + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/impl/AgentMessageListener.java b/activemq-systest/src/main/java/org/activemq/systest/impl/AgentMessageListener.java new file mode 100644 index 0000000000..83a1ce1921 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/impl/AgentMessageListener.java @@ -0,0 +1,114 @@ +/** + * ActiveMQ: The Open Source Message Fabric + * + * Copyright 2005 (C) LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.impl; + +import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; + +import javax.jms.Message; +import javax.jms.MessageListener; + +import java.util.ArrayList; +import java.util.List; + +/** + * A simple consumer which is useful for testing which can be used to wait until + * the consumer has received a specific number of messages. + * + * @author Mike Perham + * @version $Revision$ + */ +public class AgentMessageListener implements MessageListener { + private List messages = new CopyOnWriteArrayList(); + private Object semaphore = new Object();; + + public void stop() { + messages.clear(); + } + + /** + * @return all the messages on the list so far, clearing the buffer + */ + public List flushMessages() { + List answer = new ArrayList(messages); + messages.clear(); + return answer; + } + + public void onMessage(Message message) { + System.out.println("Received message: " + message); + + messages.add(message); + + synchronized (semaphore) { + semaphore.notifyAll(); + } + } + + public void waitForMessageToArrive() { + System.out.println("Waiting for message to arrive"); + + long start = System.currentTimeMillis(); + + try { + if (hasReceivedMessage()) { + synchronized (semaphore) { + semaphore.wait(4000); + } + } + } + catch (InterruptedException e) { + System.out.println("Caught: " + e); + } + long end = System.currentTimeMillis() - start; + + System.out.println("End of wait for " + end + " millis"); + } + + public void waitForMessagesToArrive(int messageCount) { + System.out.println("Waiting for message to arrive"); + + long start = System.currentTimeMillis(); + + for (int i = 0; i < 10; i++) { + try { + if (hasReceivedMessages(messageCount)) { + break; + } + synchronized (semaphore) { + semaphore.wait(1000); + } + } + catch (InterruptedException e) { + System.out.println("Caught: " + e); + } + } + long end = System.currentTimeMillis() - start; + + System.out.println("End of wait for " + end + " millis"); + } + + protected boolean hasReceivedMessage() { + return messages.isEmpty(); + } + + protected boolean hasReceivedMessages(int messageCount) { + return messages.size() >= messageCount; + } + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/impl/BrokerAgentImpl.java b/activemq-systest/src/main/java/org/activemq/systest/impl/BrokerAgentImpl.java new file mode 100644 index 0000000000..2c0e65b77d --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/impl/BrokerAgentImpl.java @@ -0,0 +1,135 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.impl; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.broker.BrokerService; +import org.activemq.network.NetworkConnector; +import org.activemq.systest.AgentStopper; +import org.activemq.systest.AgentSupport; +import org.activemq.systest.BrokerAgent; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.jms.ConnectionFactory; + +/** + * A simple in-memory broker implementation + * + * @version $Revision: 1.1 $ + */ +public class BrokerAgentImpl extends AgentSupport implements BrokerAgent { + private static final Log log = LogFactory.getLog(BrokerAgentImpl.class); + + private static int counter; + private static int port = 61616; + + private BrokerService broker; + private String brokerName; + private boolean persistent; + private String connectionURI; + private boolean started; + private boolean deleteAllMessage=true; + + public BrokerAgentImpl() throws Exception { + brokerName = "broker-" + (++counter); + connectionURI = "tcp://localhost:" + (port++); + + log.info("Creating broker on URI: " + getConnectionURI()); + } + + public void kill() throws Exception { + stop(); + } + + public ConnectionFactory getConnectionFactory() { + return new ActiveMQConnectionFactory(getConnectionURI()); + } + + public String getConnectionURI() { + return connectionURI; + } + + public void connectTo(BrokerAgent remoteBroker) throws Exception { + String remoteURI = "static://"+remoteBroker.getConnectionURI(); + log.info("Broker is connecting to network using: " + remoteURI); + NetworkConnector connector = getBroker().addNetworkConnector(remoteURI); + if (started) { + connector.start(); + } + } + + public void start() throws Exception { + started = true; + getBroker().start(); + } + + public void stop(AgentStopper stopper) { + started = false; + if (broker != null) { + try { + broker.stop(); + } + catch (Exception e) { + stopper.onException(this, e); + } + finally { + broker = null; + } + } + } + + public boolean isPersistent() { + return persistent; + } + + + public boolean isStarted() { + return started; + } + + public void setPersistent(boolean persistent) { + this.persistent = persistent; + } + + public BrokerService getBroker() throws Exception { + if (broker == null) { + broker = createBroker(); + } + return broker; + } + + protected BrokerService createBroker() throws Exception { + BrokerService answer = new BrokerService(); + answer.setBrokerName(brokerName); + answer.setPersistent(isPersistent()); + + // Delete all the message the first time the broker is started. + answer.setDeleteAllMessagesOnStartup(deleteAllMessage); + deleteAllMessage=false; + + answer.addConnector(getConnectionURI()); + return answer; + } + + public String getBrokerName() { + return brokerName; + } + + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/impl/ConsumerAgentImpl.java b/activemq-systest/src/main/java/org/activemq/systest/impl/ConsumerAgentImpl.java new file mode 100644 index 0000000000..249845b9f6 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/impl/ConsumerAgentImpl.java @@ -0,0 +1,146 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.impl; + +import org.activemq.systest.AgentStopper; +import org.activemq.systest.ConsumerAgent; +import org.activemq.systest.MessageList; + +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.Topic; + +/** + * A simple in JVM implementation of a {@link ConsumerAgent} + * + * @version $Revision: 1.1 $ + */ +public class ConsumerAgentImpl extends JmsClientSupport implements ConsumerAgent { + + private String selector; + private String durableSubscriber; + private boolean noLocal; + private MessageConsumer consumer; + private AgentMessageListener listener; + + public void start() throws Exception { + listener = new AgentMessageListener(); + getConsumer().setMessageListener(listener); + super.start(); + } + + public void assertConsumed(MessageList messageList) throws JMSException { + int size = messageList.getSize(); + listener.waitForMessagesToArrive(size); + + // now we've received them, lets check that they are identical + messageList.assertMessagesCorrect(listener.flushMessages()); + + System.out.println("Consumer received all: " + size + " message(s)"); + } + + public void waitUntilConsumed(MessageList messageList, int percentOfList) { + int size = messageList.getSize(); + int limit = (size * percentOfList) / 100; + listener.waitForMessagesToArrive(limit); + } + + // Properties + // ------------------------------------------------------------------------- + public MessageConsumer getConsumer() throws JMSException { + if (consumer == null) { + consumer = createConsumer(); + } + return consumer; + } + + public void setConsumer(MessageConsumer consumer) { + this.consumer = consumer; + } + + public String getDurableSubscriber() { + return durableSubscriber; + } + + public void setDurableSubscriber(String durableSubscriber) { + this.durableSubscriber = durableSubscriber; + } + + public boolean isNoLocal() { + return noLocal; + } + + public void setNoLocal(boolean noLocal) { + this.noLocal = noLocal; + } + + public String getSelector() { + return selector; + } + + public void setSelector(String selector) { + this.selector = selector; + } + + public void stop(AgentStopper stopper) { + if (listener != null) { + listener.stop(); + listener = null; + } + + if (consumer != null) { + try { + consumer.close(); + } + catch (JMSException e) { + stopper.onException(this, e); + } + finally { + consumer = null; + } + } + super.stop(stopper); + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected MessageConsumer createConsumer() throws JMSException { + if (durableSubscriber != null) { + if (selector != null) { + return getSession().createDurableSubscriber((Topic) getDestination(), durableSubscriber, selector, noLocal); + } + else { + return getSession().createDurableSubscriber((Topic) getDestination(), durableSubscriber); + } + } + else { + if (selector != null) { + if (noLocal) { + return getSession().createConsumer(getDestination(), selector, noLocal); + } + else { + return getSession().createConsumer(getDestination(), selector); + } + } + else { + return getSession().createConsumer(getDestination()); + } + } + + } +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/impl/DestinationFactoryImpl.java b/activemq-systest/src/main/java/org/activemq/systest/impl/DestinationFactoryImpl.java new file mode 100644 index 0000000000..813bf678dc --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/impl/DestinationFactoryImpl.java @@ -0,0 +1,55 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.impl; + +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTempQueue; +import org.activemq.command.ActiveMQTempTopic; +import org.activemq.command.ActiveMQTopic; +import org.activemq.systest.DestinationFactory; + +import javax.jms.Destination; +import javax.jms.JMSException; + +/** + * + * @version $Revision: 1.1 $ + */ +public class DestinationFactoryImpl implements DestinationFactory { + + public Destination createDestination(String physicalName, int destinationType) throws JMSException { + switch (destinationType) { + + case TOPIC: + return new ActiveMQTopic(physicalName); + + case QUEUE: + return new ActiveMQQueue(physicalName); + + case TEMPORARY_QUEUE: + return new ActiveMQTempQueue(physicalName); + + case TEMPORARY_TOPIC: + return new ActiveMQTempTopic(physicalName); + + default: + throw new JMSException("Invalid destinationType: " + destinationType); + } + } + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/impl/DiscoveryBrokerAgentImpl.java b/activemq-systest/src/main/java/org/activemq/systest/impl/DiscoveryBrokerAgentImpl.java new file mode 100644 index 0000000000..3fbbc4f7ed --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/impl/DiscoveryBrokerAgentImpl.java @@ -0,0 +1,49 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.impl; + +import org.activemq.broker.BrokerService; +import org.activemq.network.NetworkConnector; +import org.activemq.systest.BrokerAgent; + +/** + * A broker using discovery + * + * @version $Revision: 1.1 $ + */ +public class DiscoveryBrokerAgentImpl extends BrokerAgentImpl { + + public DiscoveryBrokerAgentImpl() throws Exception { + } + + public void connectTo(BrokerAgent remoteBroker) throws Exception { + NetworkConnector connector = getBroker().addNetworkConnector("rendezvous"); + if (isStarted()) { + connector.start(); + } + } + + protected BrokerService createBroker() throws Exception { + BrokerService answer = new BrokerService(); + answer.setBrokerName(getBrokerName()); + answer.setPersistent(isPersistent()); + answer.addConnector("discovery:" + getConnectionURI()); + return answer; + } + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/impl/JmsClientSupport.java b/activemq-systest/src/main/java/org/activemq/systest/impl/JmsClientSupport.java new file mode 100644 index 0000000000..ed48628d81 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/impl/JmsClientSupport.java @@ -0,0 +1,145 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.impl; + +import org.activemq.systest.AgentStopper; +import org.activemq.systest.AgentSupport; +import org.activemq.systest.BrokerAgent; +import org.activemq.systest.ClientAgent; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Session; + +/** + * Simple base class for JMS client agents. + * + * @version $Revision: 1.1 $ + */ +public class JmsClientSupport extends AgentSupport implements ClientAgent { + + private ConnectionFactory connectionFactory; + private Connection connection; + private Session session; + private boolean transacted; + private int acknowledgementMode = Session.CLIENT_ACKNOWLEDGE; + private Destination destination; + + public void start() throws Exception { + getConnection().start(); + } + + public void connectTo(BrokerAgent broker) throws Exception { + // lets stop the connection then start a new connection + stop(); + setConnectionFactory(broker.getConnectionFactory()); + start(); + } + + // Properties + // ------------------------------------------------------------------------- + public Destination getDestination() { + if (destination == null) { + throw new IllegalArgumentException("You must set the 'destination' property"); + } + return destination; + } + + public void setDestination(Destination destination) { + this.destination = destination; + } + + public int getAcknowledgementMode() { + return acknowledgementMode; + } + + public void setAcknowledgementMode(int acknowledgementMode) { + this.acknowledgementMode = acknowledgementMode; + } + + public ConnectionFactory getConnectionFactory() { + if (connectionFactory == null) { + throw new IllegalArgumentException("You must set the 'connectionFactory' property"); + } + return connectionFactory; + } + + public void setConnectionFactory(ConnectionFactory connectionFactory) { + this.connectionFactory = connectionFactory; + } + + public boolean isTransacted() { + return transacted; + } + + public void setTransacted(boolean transacted) { + this.transacted = transacted; + } + + public Connection getConnection() throws JMSException { + if (connection == null) { + connection = createConnection(); + } + return connection; + } + + public Session getSession() throws JMSException { + if (session == null) { + session = createSession(); + } + return session; + } + + public void stop(AgentStopper stopper) { + if (session != null) { + try { + session.close(); + } + catch (JMSException e) { + stopper.onException(this, e); + } + finally { + session = null; + } + } + if (connection != null) { + try { + connection.close(); + } + catch (JMSException e) { + stopper.onException(this, e); + } + finally { + connection = null; + } + } + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected Connection createConnection() throws JMSException { + return getConnectionFactory().createConnection(); + } + + protected Session createSession() throws JMSException { + return getConnection().createSession(transacted, acknowledgementMode); + } + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/impl/MessageListImpl.java b/activemq-systest/src/main/java/org/activemq/systest/impl/MessageListImpl.java new file mode 100644 index 0000000000..dc8b59ff19 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/impl/MessageListImpl.java @@ -0,0 +1,184 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.impl; + +import org.activemq.systest.AgentStopper; +import org.activemq.systest.MessageList; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Session; +import javax.jms.TextMessage; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import junit.framework.Assert; + +/** + * A simple in-JVM implementation of a {@link MessageList} + * + * @version $Revision: 1.1 $ + */ +public class MessageListImpl extends Assert implements MessageList { + + private int numberOfMessages; + private int charactersPerMessage; + private List payloads; + private String customHeader = "testHeader"; + private boolean useTextMessage = true; + private int sentCounter; + + public MessageListImpl(int numberOfMessages, int charactersPerMessage) { + this.numberOfMessages = numberOfMessages; + this.charactersPerMessage = charactersPerMessage; + } + + public void start() throws Exception { + payloads = new ArrayList(numberOfMessages); + for (int i = 0; i < numberOfMessages; i++) { + payloads.add(createTextPayload(i, charactersPerMessage)); + } + } + + public void stop() throws Exception { + payloads = null; + } + + public void stop(AgentStopper stopper) { + payloads = null; + } + + public void assertMessagesCorrect(List actual) throws JMSException { + int actualSize = actual.size(); + + System.out.println("Consumed so far: " + actualSize + " message(s)"); + + assertTrue("Not enough messages received: " + actualSize + " when expected: " + getSize(), actualSize >= getSize()); + + for (int i = 0; i < actualSize; i++) { + assertMessageCorrect(i, actual); + } + + assertEquals("Number of messages received", getSize(), actualSize); + } + + public void assertMessageCorrect(int index, List actualMessages) throws JMSException { + Object expected = getPayloads().get(index); + + Message message = (Message) actualMessages.get(index); + + assertHeadersCorrect(message, index); + + Object actual = getMessagePayload(message); + assertEquals("Message payload for message: " + index, expected, actual); + } + + public void sendMessages(Session session, MessageProducer producer) throws JMSException { + int counter = 0; + for (Iterator iter = getPayloads().iterator(); iter.hasNext(); counter++) { + Object element = (Object) iter.next(); + if (counter == sentCounter) { + sentCounter++; + Message message = createMessage(session, element, counter); + appendHeaders(message, counter); + producer.send(message); + } + } + } + + public void sendMessages(Session session, MessageProducer producer, int percent) throws JMSException { + int numberOfMessages = (getPayloads().size() * percent) / 100; + int counter = 0; + int lastMessageId = sentCounter + numberOfMessages; + for (Iterator iter = getPayloads().iterator(); iter.hasNext() && counter < lastMessageId; counter++) { + Object element = (Object) iter.next(); + if (counter == sentCounter) { + sentCounter++; + Message message = createMessage(session, element, counter); + appendHeaders(message, counter); + producer.send(message); + } + } + } + + public int getSize() { + return getPayloads().size(); + } + + // Properties + // ------------------------------------------------------------------------- + public String getCustomHeader() { + return customHeader; + } + + public void setCustomHeader(String customHeader) { + this.customHeader = customHeader; + } + + public List getPayloads() { + return payloads; + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected void appendHeaders(Message message, int counter) throws JMSException { + message.setIntProperty(getCustomHeader(), counter); + message.setStringProperty("cheese", "Edam"); + } + + protected void assertHeadersCorrect(Message message, int index) throws JMSException { + assertEquals("String property 'cheese' for message: " + index, "Edam", message.getStringProperty("cheese")); + assertEquals("int property '" + getCustomHeader() + "' for message: " + index, index, message.getIntProperty(getCustomHeader())); + } + + protected Object getMessagePayload(Message message) throws JMSException { + if (message instanceof TextMessage) { + TextMessage textMessage = (TextMessage) message; + return textMessage.getText(); + } + else if (message instanceof ObjectMessage) { + ObjectMessage objectMessage = (ObjectMessage) message; + return objectMessage.getObject(); + } + else { + return null; + } + } + + protected Message createMessage(Session session, Object element, int counter) throws JMSException { + if (element instanceof String && useTextMessage) { + return session.createTextMessage((String) element); + } + else if (element == null) { + return session.createMessage(); + } + else { + return session.createObjectMessage((Serializable) element); + } + } + + protected Object createTextPayload(int messageCounter, int charactersPerMessage) { + return "Body of message: " + messageCounter + " sent at: " + new Date(); + } +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/impl/ProducerAgentImpl.java b/activemq-systest/src/main/java/org/activemq/systest/impl/ProducerAgentImpl.java new file mode 100644 index 0000000000..0e6e341696 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/impl/ProducerAgentImpl.java @@ -0,0 +1,92 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.impl; + +import org.activemq.systest.AgentStopper; +import org.activemq.systest.MessageList; +import org.activemq.systest.ProducerAgent; + +import javax.jms.DeliveryMode; +import javax.jms.JMSException; +import javax.jms.MessageProducer; + +/** + * A simple in JVM implementation of a {@link ProducerAgent} + * + * @version $Revision: 1.1 $ + */ +public class ProducerAgentImpl extends JmsClientSupport implements ProducerAgent { + + private MessageProducer producer; + private boolean persistent = true; + + public void start() throws Exception { + super.start(); + producer = createProducer(); + } + + public void sendMessages(MessageList messageList) throws JMSException { + System.out.println("About to send: " + messageList.getSize() + " message(s) to destination: " + getDestination()); + messageList.sendMessages(getSession(), producer); + if (isTransacted()) { + getSession().commit(); + } + System.out.println("Sent: " + messageList.getSize() + " message(s) to destination: " + getDestination()); + } + + public void sendMessages(MessageList messageList, int percent) throws JMSException { + System.out.println("About to send: " + percent + " % of the message(s) to destination: " + getDestination()); + messageList.sendMessages(getSession(), producer, percent); + if (isTransacted()) { + getSession().commit(); + } + System.out.println("Sent: " + percent + " % of the message(s) to destination: " + getDestination()); + } + + public boolean isPersistent() { + return persistent; + } + + public void setPersistent(boolean persistent) { + this.persistent = persistent; + } + + public void stop(AgentStopper stopper) { + if (producer != null) { + try { + producer.close(); + } + catch (JMSException e) { + stopper.onException(this, e); + } + finally { + producer = null; + } + } + super.stop(stopper); + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected MessageProducer createProducer() throws JMSException { + MessageProducer answer = getSession().createProducer(getDestination()); + answer.setDeliveryMode(isPersistent() ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT); + return answer; + } + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/impl/RandomMessageListImpl.java b/activemq-systest/src/main/java/org/activemq/systest/impl/RandomMessageListImpl.java new file mode 100644 index 0000000000..0d3a5621ac --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/impl/RandomMessageListImpl.java @@ -0,0 +1,44 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.impl; + +import org.activemq.systest.MessageList; + +/** + * A {@link MessageList} implementation which generates a random body + * which can help test performance when using compression. + * + * @version $Revision: 1.1 $ + */ +public class RandomMessageListImpl extends MessageListImpl { + + public RandomMessageListImpl(int numberOfMessages, int charactersPerMessage) { + super(numberOfMessages, charactersPerMessage); + } + + protected Object createTextPayload(int messageCounter, int charactersPerMessage) { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < charactersPerMessage; i++) { + char ch = (char) (32 + (int) (Math.random() * 230)); + buffer.append(ch); + } + String answer = buffer.toString(); + assertEquals("String length should equal the requested length", charactersPerMessage, answer.length()); + return answer; + } +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/impl/SeparateBrokerProcessAgentImpl.java b/activemq-systest/src/main/java/org/activemq/systest/impl/SeparateBrokerProcessAgentImpl.java new file mode 100644 index 0000000000..122178959c --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/impl/SeparateBrokerProcessAgentImpl.java @@ -0,0 +1,158 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.impl; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.systest.BrokerAgent; + +import javax.jms.ConnectionFactory; + +import java.io.*; +import java.util.Iterator; + +/** + * Runs a broker in a separate process + * + * @version $Revision: 1.1 $ + */ +public class SeparateBrokerProcessAgentImpl extends SeparateProcessAgent implements BrokerAgent { + + private static final String ENV_HOME = "ACTIVEMQ_HOME"; + + private static int portCounter = 61616; + + private int port; + private String connectionURI; + private String brokerScript; + private File workingDirectory = new File("target/test-brokers"); + private String defaultPrefix = "~/activemq"; + private String coreURI; + + public SeparateBrokerProcessAgentImpl(String host) throws Exception { + port = portCounter++; + coreURI = "tcp://" + host + ":" + port; + connectionURI = "failover:(" + coreURI + ")?useExponentialBackOff=false&initialReconnectDelay=500&&maxReconnectAttempts=20"; + } + + public void kill() throws Exception { + stop(); + } + + public ConnectionFactory getConnectionFactory() { + return new ActiveMQConnectionFactory(getConnectionURI()); + } + + public String getConnectionURI() { + return connectionURI; + } + + public void connectTo(BrokerAgent remoteBroker) throws Exception { + // lets assume discovery works! :) + } + + public String getBrokerScript() { + if (brokerScript == null) { + brokerScript = createBrokerScript(); + } + return brokerScript; + } + + public void setBrokerScript(String activemqScript) { + this.brokerScript = activemqScript; + } + + public String getDefaultPrefix() { + return defaultPrefix; + } + + public void setDefaultPrefix(String defaultPrefix) { + this.defaultPrefix = defaultPrefix; + } + + public File getWorkingDirectory() { + return workingDirectory; + } + + public void setWorkingDirectory(File workingDirectory) { + this.workingDirectory = workingDirectory; + } + + // Implementation methods + // ------------------------------------------------------------------------- + protected Process createProcess() throws Exception { + ProcessBuilder builder = new ProcessBuilder(getCommands()); + File workingDir = createBrokerWorkingDirectory(); + + System.out.print("About to execute command:"); + for (Iterator iter = builder.command().iterator(); iter.hasNext();) { + System.out.print(" " + iter.next()); + } + System.out.println(); + System.out.println("In directory: " + workingDir); + + builder = builder.directory(workingDir); + builder = builder.redirectErrorStream(true); + + Process answer = builder.start(); + return answer; + } + + protected File createBrokerWorkingDirectory() { + workingDirectory.mkdirs(); + + // now lets create a new temporary directory + File brokerDir = new File(workingDirectory, "broker_" + port); + brokerDir.mkdirs(); + + File varDir = new File(brokerDir, "data"); + varDir.mkdirs(); + + File workDir = new File(brokerDir, "work"); + workDir.mkdirs(); + return workDir; + } + + protected String createBrokerScript() { + String version = null; + Package p = Package.getPackage("org.activemq"); + if (p != null) { + version = p.getImplementationVersion(); + } + if (version == null) { + version = "activemq-4.0-SNAPSHOT"; + } + return "../../../../../assembly/target/" + version + "/bin/" + version + "/bin/activemq"; + } + + protected String[] createCommand() { + // lets try load the broker script from a system property + String script = System.getProperty("brokerScript"); + if (script == null) { + String home = System.getenv(ENV_HOME); + if (home == null) { + script = getBrokerScript(); + } + else { + script = home + "/bin/" + brokerScript; + } + } + + String[] answer = { "/bin/bash", script, "broker:" + coreURI }; + return answer; + } +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/impl/SeparateProcessAgent.java b/activemq-systest/src/main/java/org/activemq/systest/impl/SeparateProcessAgent.java new file mode 100644 index 0000000000..bc1b3341cb --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/impl/SeparateProcessAgent.java @@ -0,0 +1,131 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.impl; + +import org.activemq.systest.AgentStopper; +import org.activemq.systest.AgentSupport; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * Starts a separate process on this machine until its asked to be killed. + * + * @version $Revision: 1.1 $ + */ +public class SeparateProcessAgent extends AgentSupport { + + private String[] commands; + private Process process; + private long sleepTime = 10000; + + public SeparateProcessAgent() { + } + + public String[] getCommands() { + if (commands == null) { + commands = createCommand(); + } + return commands; + } + + public void setCommands(String[] command) { + this.commands = command; + } + + public Process getProcess() { + return process; + } + + public void start() throws Exception { + process = createProcess(); + Thread thread = new Thread(new Runnable() { + public void run() { + readInputStream(process.getInputStream()); + } + }); + thread.start(); + + Thread thread2 = new Thread(new Runnable() { + public void run() { + waitForProcessExit(); + } + }); + thread2.start(); + + // lets wait for the process to startup + + System.out.println("Waiting a little while to give the broker process to start"); + Thread.sleep(sleepTime); + } + + public void stop(AgentStopper stopper) { + if (process != null) { + try { + process.destroy(); + } + catch (Exception e) { + stopper.onException(this, e); + } + finally { + process = null; + } + } + } + + protected Process createProcess() throws Exception { + return Runtime.getRuntime().exec(commands); + } + + protected String[] createCommand() { + throw new IllegalArgumentException("You must configure the 'commands' property"); + } + + protected void readInputStream(InputStream inputStream) { + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + while (true) { + String line = reader.readLine(); + if (line == null) { + break; + } + System.out.println(line); + } + } + catch (IOException e) { + // ignore exceptions + // probably end of file + } + } + + protected void waitForProcessExit() { + Process p = process; + try { + p.waitFor(); + } + catch (InterruptedException e) { + System.out.println("Interrupted while waiting for process to complete: " + e); + e.printStackTrace(); + } + int value = p.exitValue(); + System.out.println("Process completed with exit value: " + value); + + } +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/task/ScenarioJClassStub.java b/activemq-systest/src/main/java/org/activemq/systest/task/ScenarioJClassStub.java new file mode 100644 index 0000000000..6ce00525a0 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/task/ScenarioJClassStub.java @@ -0,0 +1,269 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.task; + +import org.codehaus.jam.JAnnotation; +import org.codehaus.jam.JAnnotationValue; +import org.codehaus.jam.JClass; +import org.codehaus.jam.JComment; +import org.codehaus.jam.JConstructor; +import org.codehaus.jam.JElement; +import org.codehaus.jam.JField; +import org.codehaus.jam.JMethod; +import org.codehaus.jam.JPackage; +import org.codehaus.jam.JProperty; +import org.codehaus.jam.JSourcePosition; +import org.codehaus.jam.JamClassLoader; +import org.codehaus.jam.visitor.JVisitor; + +/** + * + * @version $Revision: 1.1 $ + */ +public class ScenarioJClassStub implements JClass { + protected static final String[] EMPTY_ARRAY = {}; + + private final String qualifiedName; + private final String[] interfaceNames; + private final String simpleName; + private JClass[] interfaces; + + public ScenarioJClassStub(String qualifiedName, String[] interfaceNames) { + this.qualifiedName = qualifiedName; + this.interfaceNames = interfaceNames; + + int idx = qualifiedName.lastIndexOf('.'); + if (idx > 0) { + this.simpleName = qualifiedName.substring(idx + 1); + } + else { + this.simpleName = qualifiedName; + } + } + + public JPackage getContainingPackage() { + return null; + } + + public JClass getSuperclass() { + return null; + } + + public JClass[] getInterfaces() { + if (interfaces == null) { + int size = interfaceNames.length; + interfaces = new JClass[size]; + for (int i = 0; i < interfaceNames.length; i++) { + interfaces[i] = new ScenarioJClassStub(interfaceNames[i], EMPTY_ARRAY); + } + } + return interfaces; + } + + public JField[] getFields() { + return null; + } + + public JField[] getDeclaredFields() { + return null; + } + + public JMethod[] getMethods() { + return null; + } + + public JMethod[] getDeclaredMethods() { + return null; + } + + public JConstructor[] getConstructors() { + return null; + } + + public JProperty[] getProperties() { + return null; + } + + public JProperty[] getDeclaredProperties() { + return null; + } + + public boolean isInterface() { + return false; + } + + public boolean isAnnotationType() { + return false; + } + + public boolean isPrimitiveType() { + return false; + } + + public boolean isBuiltinType() { + return false; + } + + public Class getPrimitiveClass() { + return null; + } + + public boolean isFinal() { + return false; + } + + public boolean isStatic() { + return false; + } + + public boolean isAbstract() { + return false; + } + + public boolean isVoidType() { + return false; + } + + public boolean isObjectType() { + return false; + } + + public boolean isArrayType() { + return false; + } + + public JClass getArrayComponentType() { + return null; + } + + public int getArrayDimensions() { + return 0; + } + + public boolean isAssignableFrom(JClass arg0) { + return false; + } + + public JClass[] getClasses() { + return null; + } + + public JClass getContainingClass() { + return null; + } + + public String getFieldDescriptor() { + return null; + } + + public boolean isEnumType() { + return false; + } + + public JamClassLoader getClassLoader() { + return null; + } + + public JClass forName(String arg0) { + return null; + } + + public JClass[] getImportedClasses() { + return null; + } + + public JPackage[] getImportedPackages() { + return null; + } + + public boolean isUnresolvedType() { + return false; + } + + public int getModifiers() { + return 0; + } + + public boolean isPackagePrivate() { + return false; + } + + public boolean isPrivate() { + return false; + } + + public boolean isProtected() { + return false; + } + + public boolean isPublic() { + return false; + } + + public JAnnotation[] getAnnotations() { + return null; + } + + public JAnnotation getAnnotation(Class arg0) { + return null; + } + + public Object getAnnotationProxy(Class arg0) { + return null; + } + + public JAnnotation getAnnotation(String arg0) { + return null; + } + + public JAnnotationValue getAnnotationValue(String arg0) { + return null; + } + + public JComment getComment() { + return null; + } + + public JAnnotation[] getAllJavadocTags() { + return null; + } + + public JElement getParent() { + return null; + } + + public String getSimpleName() { + return simpleName; + } + + public String getQualifiedName() { + return qualifiedName; + } + + public JSourcePosition getSourcePosition() { + return null; + } + + public void accept(JVisitor arg0) { + } + + public Object getArtifact() { + return null; + } + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/task/SystemTestGenerator.java b/activemq-systest/src/main/java/org/activemq/systest/task/SystemTestGenerator.java new file mode 100644 index 0000000000..fdcd4574b5 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/task/SystemTestGenerator.java @@ -0,0 +1,278 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.task; + +import org.activemq.systest.DestinationFactory; +import org.activemq.systest.QueueOnlyScenario; +import org.activemq.systest.Scenario; +import org.activemq.systest.ScenarioTestSuite; +import org.activemq.systest.TopicOnlyScenario; +import org.apache.tools.ant.DirectoryScanner; +import org.codehaus.jam.JClass; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * + * @version $Revision: 1.1 $ + */ +public class SystemTestGenerator { + + private JClass[] classes; + private final File destDir; + private final DirectoryScanner clientsScanner; + private final DirectoryScanner brokersScanner; + private final File baseDir; + private final File scenariosFile; + + public SystemTestGenerator(JClass[] classes, File destDir, DirectoryScanner clientsScanner, DirectoryScanner brokersScanner, File baseDir, + File scenariosFile) { + this.classes = classes; + this.destDir = destDir; + this.clientsScanner = clientsScanner; + this.brokersScanner = brokersScanner; + this.baseDir = baseDir; + this.scenariosFile = scenariosFile; + } + + public void generate() throws IOException { + List list = new ArrayList(); + for (int i = 0; i < classes.length; i++) { + JClass type = classes[i]; + if (implementsInterface(type, Scenario.class) && !type.isAbstract() && !type.isInterface()) { + generateTestsFor(type); + list.add(type); + } + } + + // now lets generate a list of all the available + if (scenariosFile != null) { + generatePropertiesFile(list); + } + } + + protected void generatePropertiesFile(List list) throws IOException { + PrintWriter writer = new PrintWriter(new FileWriter(scenariosFile)); + try { + for (Iterator iter = list.iterator(); iter.hasNext();) { + JClass type = (JClass) iter.next(); + writer.print(type.getQualifiedName()); + writer.print(" = "); + writeInterfaces(writer, type); + writer.println(); + } + } + finally { + writer.close(); + } + } + + protected void writeInterfaces(PrintWriter writer, JClass type) { + List interfaces = new ArrayList(); + addAllInterfaces(interfaces, type); + boolean first = true; + for (Iterator iter = interfaces.iterator(); iter.hasNext();) { + JClass interfaceType = (JClass) iter.next(); + if (first) { + first = false; + } + else { + writer.print(", "); + } + writer.print(interfaceType.getQualifiedName()); + } + } + + protected void addAllInterfaces(List list, JClass type) { + JClass[] interfaces = type.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + JClass interfaceType = interfaces[i]; + list.add(interfaceType); + } + JClass superclass = type.getSuperclass(); + if (superclass != null) { + addAllInterfaces(list, superclass); + } + } + + protected void generateTestsFor(JClass type) throws IOException { + String[] files = clientsScanner.getIncludedFiles(); + for (int i = 0; i < files.length; i++) { + String name = files[i]; + File file = new File(clientsScanner.getBasedir(), name); + generateTestsFor(type, name, file); + } + } + + protected void generateTestsFor(JClass type, String clientName, File clientFile) throws IOException { + String[] files = brokersScanner.getIncludedFiles(); + for (int i = 0; i < files.length; i++) { + String name = files[i]; + File basedir = brokersScanner.getBasedir(); + File file = new File(basedir, name); + + if (!implementsInterface(type, TopicOnlyScenario.class)) { + generateTestsFor(type, clientName, clientFile, name, file, DestinationFactory.QUEUE); + } + if (!implementsInterface(type, QueueOnlyScenario.class)) { + generateTestsFor(type, clientName, clientFile, name, file, DestinationFactory.TOPIC); + } + } + } + + protected void generateTestsFor(JClass type, String clientName, File clientFile, String brokerName, File brokerFile, int destinationType) + throws IOException { + String clientPrefix = trimPostFix(clientName); + String brokerPrefix = trimPostFix(brokerName); + + String destinationName = ScenarioTestSuite.destinationDescription(destinationType); + String[] paths = { "org", "activemq", "systest", brokerPrefix, destinationName, clientPrefix }; + String packageName = asPackageName(paths); + + File dir = makeDirectories(paths); + File file = new File(dir, type.getSimpleName() + "Test.java"); + PrintWriter writer = new PrintWriter(new FileWriter(file)); + try { + System.out.println("Generating: " + file); + generateFile(writer, type, clientFile, brokerFile, packageName, destinationType); + } + finally { + writer.close(); + } + } + + protected void generateFile(PrintWriter writer, JClass type, File clientFile, File brokerFile, String packageName, int destinationType) throws IOException { + writer.println("/**"); + writer.println(" * ActiveMQ: The Open Source Message Fabric "); + writer.println(" * "); + writer.println(" * Copyright 2005 LogicBlaze, Inc."); + writer.println(" * "); + writer.println(" * Licensed under the Apache License, Version 2.0 (the 'License');"); + writer.println(" * you may not use this file except in compliance with the License."); + writer.println(" *"); + writer.println(" * You may obtain a copy of the License at"); + writer.println(" *"); + writer.println(" * http://www.apache.org/licenses/LICENSE-2.0"); + writer.println(" *"); + writer.println(" * Unless required by applicable law or agreed to in writing, software"); + writer.println(" * distributed under the License is distributed on an 'AS IS' BASIS,"); + writer.println(" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied."); + writer.println(" * See the License for the specific language governing permissions and"); + writer.println(" * limitations under the License."); + writer.println(" *"); + writer.println(" **/"); + writer.println("package " + packageName + ";"); + writer.println(); + writer.println("import org.activemq.systest.DestinationFactory;"); + writer.println("import org.activemq.systest.ScenarioTestSuite;"); + writer.println("import " + type.getQualifiedName() + ";"); + writer.println("import org.springframework.context.ApplicationContext;"); + writer.println("import org.springframework.context.support.FileSystemXmlApplicationContext;"); + writer.println(); + writer.println("import junit.framework.TestSuite;"); + writer.println(); + writer.println("/**"); + writer.println(" * System test case for Scenario: " + type.getSimpleName()); + writer.println(" *"); + writer.println(" *"); + writer.println(" * NOTE!: This file is auto generated - do not modify!"); + writer.println(" * if you need to make a change, please see SystemTestGenerator code"); + writer.println(" * in the activemq-systest module in ActiveMQ 4.x"); + writer.println(" *"); + writer.println(" * @version $Revision:$"); + writer.println(" */"); + writer.println("public class " + type.getSimpleName() + "Test extends ScenarioTestSuite {"); + writer.println(); + writer.println(" public static TestSuite suite() throws Exception {"); + writer.println(" ApplicationContext clientContext = new FileSystemXmlApplicationContext(\"" + relativePath(clientFile) + "\");"); + writer.println(" ApplicationContext brokerContext = new FileSystemXmlApplicationContext(\"" + relativePath(brokerFile) + "\");"); + writer.println(" Class[] scenarios = { " + type.getSimpleName() + ".class };"); + writer.println(" return createSuite(clientContext, brokerContext, scenarios, " + destinationExpression(destinationType) + ");"); + writer.println(" }"); + writer.println("}"); + } + + protected String destinationExpression(int destinationType) { + switch (destinationType) { + case DestinationFactory.QUEUE: + return "DestinationFactory.QUEUE"; + + default: + return "DestinationFactory.TOPIC"; + } + } + + protected String relativePath(File file) { + String name = file.toString(); + String prefix = baseDir.toString(); + if (name.startsWith(prefix)) { + return name.substring(prefix.length() + 1); + } + return name; + } + + protected File makeDirectories(String[] paths) { + File dir = destDir; + for (int i = 0; i < paths.length; i++) { + dir = new File(dir, paths[i]); + } + dir.mkdirs(); + return dir; + } + + protected String asPackageName(String[] paths) { + StringBuffer buffer = new StringBuffer(paths[0]); + for (int i = 1; i < paths.length; i++) { + buffer.append("."); + buffer.append(paths[i]); + } + return buffer.toString(); + } + + protected String trimPostFix(String uri) { + int idx = uri.lastIndexOf('.'); + if (idx > 0) { + return uri.substring(0, idx); + } + return uri; + } + + protected boolean implementsInterface(JClass type, Class interfaceClass) { + JClass[] interfaces = type.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + JClass anInterface = interfaces[i]; + if (anInterface.getQualifiedName().equals(interfaceClass.getName())) { + return true; + } + } + JClass superclass = type.getSuperclass(); + if (superclass == null || superclass == type) { + return false; + } + else { + return implementsInterface(superclass, interfaceClass); + } + } + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/task/SystemTestTask.java b/activemq-systest/src/main/java/org/activemq/systest/task/SystemTestTask.java new file mode 100644 index 0000000000..bd0b94f943 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/task/SystemTestTask.java @@ -0,0 +1,231 @@ +package org.activemq.systest.task; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.taskdefs.MatchingTask; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Reference; +import org.codehaus.jam.JClass; +import org.codehaus.jam.JamService; +import org.codehaus.jam.JamServiceFactory; +import org.codehaus.jam.JamServiceParams; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; + +/** + * An Ant task for executing Gram scripts, which are Groovy scripts executed on + * the JAM context. + * + * @version $Revision: 1.2 $ + */ +public class SystemTestTask extends MatchingTask { + + private static final String SCENARIOS_PROPERTIES_FILE = "activemq-scenarios.properties"; + + private Path srcDir = null; + private Path mToolpath = null; + private Path mClasspath = null; + private String mIncludes = "**/*.java"; + private File destDir; + private FileSet clientFiles; + private FileSet brokerFiles; + private File scenariosFile; + + public File getScenariosFile() { + return scenariosFile; + } + + public void setScenariosFile(File scenariosFile) { + this.scenariosFile = scenariosFile; + } + + /** + * Sets the directory into which source files should be generated. + */ + public void setDestDir(File destDir) { + this.destDir = destDir; + } + + public void setSrcDir(Path srcDir) { + this.srcDir = srcDir; + } + + public void setToolpath(Path path) { + if (mToolpath == null) { + mToolpath = path; + } + else { + mToolpath.append(path); + } + } + + public void setToolpathRef(Reference r) { + createToolpath().setRefid(r); + } + + public FileSet createBrokerFiles() { + return new FileSet(); + } + + public FileSet getBrokerFiles() { + return brokerFiles; + } + + public void setBrokerFiles(FileSet brokerFiles) { + this.brokerFiles = brokerFiles; + } + + public FileSet createClientFiles() { + return new FileSet(); + } + + public FileSet getClientFiles() { + return clientFiles; + } + + public void setClientFiles(FileSet clientFiles) { + this.clientFiles = clientFiles; + } + + public Path createToolpath() { + if (mToolpath == null) { + mToolpath = new Path(getProject()); + } + return mToolpath.createPath(); + } + + public void setClasspath(Path path) { + if (mClasspath == null) { + mClasspath = path; + } + else { + mClasspath.append(path); + } + } + + public void setClasspathRef(Reference r) { + createClasspath().setRefid(r); + } + + public Path createClasspath() { + if (mClasspath == null) { + mClasspath = new Path(getProject()); + } + return mClasspath.createPath(); + } + + public void execute() throws BuildException { + /* + * if (srcDir == null) { throw new BuildException("'srcDir' must be + * specified"); } + */ + if (scenariosFile == null) { + throw new BuildException("'scenariosFile' must be specified"); + } + if (destDir == null) { + throw new BuildException("'destDir' must be specified"); + } + if (clientFiles == null) { + throw new BuildException("'clientFiles' must be specified"); + } + if (brokerFiles == null) { + throw new BuildException("'clientFiles' must be specified"); + } + JamServiceFactory jamServiceFactory = JamServiceFactory.getInstance(); + JamServiceParams serviceParams = jamServiceFactory.createServiceParams(); + if (mToolpath != null) { + File[] tcp = path2files(mToolpath); + for (int i = 0; i < tcp.length; i++) { + serviceParams.addToolClasspath(tcp[i]); + } + } + if (mClasspath != null) { + File[] cp = path2files(mClasspath); + for (int i = 0; i < cp.length; i++) { + serviceParams.addClasspath(cp[i]); + } + } + + JClass[] classes = null; + File propertiesFile = scenariosFile; + try { + if (srcDir != null) { + serviceParams.includeSourcePattern(path2files(srcDir), mIncludes); + JamService jam = jamServiceFactory.createService(serviceParams); + classes = jam.getAllClasses(); + } + else { + // lets try load the properties file + classes = loadScenarioClasses(); + propertiesFile = null; + } + DirectoryScanner clientsScanner = clientFiles.getDirectoryScanner(getProject()); + DirectoryScanner brokersScanner = brokerFiles.getDirectoryScanner(getProject()); + SystemTestGenerator generator = new SystemTestGenerator(classes, destDir, clientsScanner, brokersScanner, getProject().getBaseDir(), propertiesFile); + generator.generate(); + + log("...done."); + } + catch (Exception e) { + throw new BuildException(e); + } + } + + protected JClass[] loadScenarioClasses() throws IOException { + InputStream in = getClass().getClassLoader().getResourceAsStream(SCENARIOS_PROPERTIES_FILE); + if (in == null) { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if (contextClassLoader != null) { + in = contextClassLoader.getResourceAsStream(SCENARIOS_PROPERTIES_FILE); + } + if (in == null) { + throw new IOException("Could not find ActiveMQ scenarios properties file on the classpath: " + SCENARIOS_PROPERTIES_FILE); + } + } + Properties properties = new Properties(); + properties.load(in); + List list = new ArrayList(); + for (Iterator iter = properties.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry = (Map.Entry) iter.next(); + String className = (String) entry.getKey(); + String names = (String) entry.getValue(); + String[] interfaceNameArray = parseNames(names); + list.add(new ScenarioJClassStub(className, interfaceNameArray)); + } + JClass[] answer = new JClass[list.size()]; + list.toArray(answer); + return answer; + } + + protected String[] parseNames(String names) { + StringTokenizer iter = new StringTokenizer(names); + List list = new ArrayList(); + while (iter.hasMoreTokens()) { + String text = iter.nextToken().trim(); + if (text.length() > 0) { + list.add(text); + } + } + String[] answer = new String[list.size()]; + list.toArray(answer); + return answer; + } + + protected File[] path2files(Path path) { + String[] list = path.list(); + File[] out = new File[list.length]; + for (int i = 0; i < out.length; i++) { + out[i] = new File(list[i]).getAbsoluteFile(); + } + return out; + } +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/usecase/network/ProducerConsumerScenarioSupport.java b/activemq-systest/src/main/java/org/activemq/systest/usecase/network/ProducerConsumerScenarioSupport.java new file mode 100644 index 0000000000..c61a3542c2 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/usecase/network/ProducerConsumerScenarioSupport.java @@ -0,0 +1,53 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.usecase.network; + +import org.activemq.systest.ConsumerAgent; +import org.activemq.systest.MessageList; +import org.activemq.systest.ProducerAgent; +import org.activemq.systest.ScenarioSupport; + +import javax.jms.Destination; + +/** + * + * @version $Revision: 1.1 $ + */ +public abstract class ProducerConsumerScenarioSupport extends ScenarioSupport { + + protected ProducerAgent producer; + protected ConsumerAgent consumer; + protected MessageList messageList; + + public ProducerConsumerScenarioSupport(ProducerAgent producer, ConsumerAgent consumer, MessageList messageList) { + this.consumer = consumer; + this.producer = producer; + this.messageList = messageList; + } + + public void setDestination(Destination destination) { + producer.setDestination(destination); + consumer.setDestination(destination); + } + + public void start() throws Exception { + start(messageList); + start(consumer); + start(producer); + } +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/usecase/network/SingleBrokerScenario.java b/activemq-systest/src/main/java/org/activemq/systest/usecase/network/SingleBrokerScenario.java new file mode 100644 index 0000000000..2a3ba94514 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/usecase/network/SingleBrokerScenario.java @@ -0,0 +1,52 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.usecase.network; + +import org.activemq.systest.BrokerAgent; +import org.activemq.systest.ConsumerAgent; +import org.activemq.systest.MessageList; +import org.activemq.systest.ProducerAgent; + +/** + * + * @version $Revision: 1.1 $ + */ +public class SingleBrokerScenario extends ProducerConsumerScenarioSupport { + + private BrokerAgent broker; + + public SingleBrokerScenario(BrokerAgent broker, ProducerAgent producer, ConsumerAgent consumer, MessageList list) { + super(producer, consumer, list); + this.broker = broker; + } + + public void run() throws Exception { + producer.sendMessages(messageList); + consumer.assertConsumed(messageList); + } + + public void start() throws Exception { + start(broker); + + consumer.connectTo(broker); + producer.connectTo(broker); + + super.start(); + } + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/usecase/network/TwoBrokerFailoverScenario.java b/activemq-systest/src/main/java/org/activemq/systest/usecase/network/TwoBrokerFailoverScenario.java new file mode 100644 index 0000000000..40c1b76f8f --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/usecase/network/TwoBrokerFailoverScenario.java @@ -0,0 +1,50 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.usecase.network; + +import org.activemq.systest.BrokerAgent; +import org.activemq.systest.ConsumerAgent; +import org.activemq.systest.MessageList; +import org.activemq.systest.ProducerAgent; +import org.activemq.systest.QueueOnlyScenario; + +/** + * + * @version $Revision: 1.1 $ + */ +public class TwoBrokerFailoverScenario extends TwoBrokerNetworkScenario implements QueueOnlyScenario { + + public TwoBrokerFailoverScenario(BrokerAgent brokera, BrokerAgent brokerb, ProducerAgent producer, ConsumerAgent consumer, MessageList list) { + super(brokera, brokerb, producer, consumer, list); + } + + public void run() throws Exception { + producer.sendMessages(messageList, 30); + consumer.waitUntilConsumed(messageList, 20); + + consumer.stop(); + brokerB.kill(); + consumer.connectTo(brokerA); + consumer.start(); + + producer.sendMessages(messageList); + + consumer.assertConsumed(messageList); + } + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/usecase/network/TwoBrokerNetworkConnectedBeforeStartScenario.java b/activemq-systest/src/main/java/org/activemq/systest/usecase/network/TwoBrokerNetworkConnectedBeforeStartScenario.java new file mode 100644 index 0000000000..54c5ce9701 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/usecase/network/TwoBrokerNetworkConnectedBeforeStartScenario.java @@ -0,0 +1,44 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.usecase.network; + +import org.activemq.systest.BrokerAgent; +import org.activemq.systest.ConsumerAgent; +import org.activemq.systest.MessageList; +import org.activemq.systest.ProducerAgent; + +/** + * + * @version $Revision: 1.1 $ + */ +public class TwoBrokerNetworkConnectedBeforeStartScenario extends TwoBrokerNetworkScenario { + + public TwoBrokerNetworkConnectedBeforeStartScenario(BrokerAgent brokera, BrokerAgent brokerb, ProducerAgent producer, ConsumerAgent consumer, + MessageList list) { + super(brokera, brokerb, producer, consumer, list); + } + + protected void startBrokers() throws Exception { + brokerB.connectTo(brokerA); + brokerA.connectTo(brokerB); + + start(brokerA); + start(brokerB); + } + +} diff --git a/activemq-systest/src/main/java/org/activemq/systest/usecase/network/TwoBrokerNetworkScenario.java b/activemq-systest/src/main/java/org/activemq/systest/usecase/network/TwoBrokerNetworkScenario.java new file mode 100644 index 0000000000..4e91adbe86 --- /dev/null +++ b/activemq-systest/src/main/java/org/activemq/systest/usecase/network/TwoBrokerNetworkScenario.java @@ -0,0 +1,65 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.systest.usecase.network; + +import org.activemq.systest.BrokerAgent; +import org.activemq.systest.ConsumerAgent; +import org.activemq.systest.MessageList; +import org.activemq.systest.ProducerAgent; + +/** + * + * @version $Revision: 1.1 $ + */ +public class TwoBrokerNetworkScenario extends ProducerConsumerScenarioSupport { + + protected BrokerAgent brokerA; + protected BrokerAgent brokerB; + + public TwoBrokerNetworkScenario(BrokerAgent brokera, BrokerAgent brokerb, ProducerAgent producer, + ConsumerAgent consumer, MessageList list) { + super(producer, consumer, list); + brokerA = brokera; + brokerB = brokerb; + } + + public void run() throws Exception { + producer.sendMessages(messageList); + consumer.assertConsumed(messageList); + } + + public void start() throws Exception { + startBrokers(); + + consumer.connectTo(brokerB); + producer.connectTo(brokerA); + + super.start(); + + // Wait a little bit so that the network state can be propagated. + Thread.sleep(1000); + } + + protected void startBrokers() throws Exception { + start(brokerA); + start(brokerB); + + brokerB.connectTo(brokerA); + brokerA.connectTo(brokerB); + } +} diff --git a/activemq-systest/src/test/resources/brokers/broker.xml b/activemq-systest/src/test/resources/brokers/broker.xml new file mode 100644 index 0000000000..31827a0b94 --- /dev/null +++ b/activemq-systest/src/test/resources/brokers/broker.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/activemq-systest/src/test/resources/brokers/broker_non_persistent.xml b/activemq-systest/src/test/resources/brokers/broker_non_persistent.xml new file mode 100644 index 0000000000..76e475b29e --- /dev/null +++ b/activemq-systest/src/test/resources/brokers/broker_non_persistent.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/activemq-systest/src/test/resources/brokers/broker_non_persistent_discovery.xml b/activemq-systest/src/test/resources/brokers/broker_non_persistent_discovery.xml new file mode 100644 index 0000000000..263d9de21d --- /dev/null +++ b/activemq-systest/src/test/resources/brokers/broker_non_persistent_discovery.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/activemq-systest/src/test/resources/brokers/broker_separate_process.xml b/activemq-systest/src/test/resources/brokers/broker_separate_process.xml new file mode 100644 index 0000000000..c42f51d085 --- /dev/null +++ b/activemq-systest/src/test/resources/brokers/broker_separate_process.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/activemq-systest/src/test/resources/clients/durable.xml b/activemq-systest/src/test/resources/clients/durable.xml new file mode 100644 index 0000000000..e00dc64e85 --- /dev/null +++ b/activemq-systest/src/test/resources/clients/durable.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-systest/src/test/resources/clients/durableTransacted.xml b/activemq-systest/src/test/resources/clients/durableTransacted.xml new file mode 100644 index 0000000000..c4831f6b7c --- /dev/null +++ b/activemq-systest/src/test/resources/clients/durableTransacted.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-systest/src/test/resources/clients/nonDurable.xml b/activemq-systest/src/test/resources/clients/nonDurable.xml new file mode 100644 index 0000000000..ebe900ba85 --- /dev/null +++ b/activemq-systest/src/test/resources/clients/nonDurable.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-systest/src/test/resources/clients/nonDurableTransacted.xml b/activemq-systest/src/test/resources/clients/nonDurableTransacted.xml new file mode 100644 index 0000000000..348478fd64 --- /dev/null +++ b/activemq-systest/src/test/resources/clients/nonDurableTransacted.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/activemq-systest/src/test/resources/log4j.properties b/activemq-systest/src/test/resources/log4j.properties new file mode 100755 index 0000000000..5620bbb624 --- /dev/null +++ b/activemq-systest/src/test/resources/log4j.properties @@ -0,0 +1,21 @@ +# +# The logging properties used during tests.. +# +log4j.rootLogger=INFO, out +#log4j.rootLogger=DEBUG, stdout + +log4j.logger.org.activemq.spring=WARN +log4j.logger.org.activemq.store.journal=INFO +log4j.logger.org.activeio.journal=INFO + +# CONSOLE appender not used by default +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n + +# File appender +log4j.appender.out=org.apache.log4j.FileAppender +log4j.appender.out.layout=org.apache.log4j.PatternLayout +log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +log4j.appender.out.file=target/test-reports/activemq-test.log +log4j.appender.out.append=true diff --git a/activemq-web/.cvsignore b/activemq-web/.cvsignore new file mode 100755 index 0000000000..14db14acd9 --- /dev/null +++ b/activemq-web/.cvsignore @@ -0,0 +1,3 @@ +target +.project +.classpath diff --git a/activemq-web/README.txt b/activemq-web/README.txt new file mode 100755 index 0000000000..1299307669 --- /dev/null +++ b/activemq-web/README.txt @@ -0,0 +1,2 @@ +Here be the home of the web-module. + diff --git a/activemq-web/maven.xml b/activemq-web/maven.xml new file mode 100755 index 0000000000..4fdf7cf737 --- /dev/null +++ b/activemq-web/maven.xml @@ -0,0 +1,25 @@ + + + + + + + + Running the Web Application + + + + + + + + + + + + + + + + diff --git a/activemq-web/project.properties b/activemq-web/project.properties new file mode 100755 index 0000000000..413af37bb2 --- /dev/null +++ b/activemq-web/project.properties @@ -0,0 +1,4 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=jar diff --git a/activemq-web/project.xml b/activemq-web/project.xml new file mode 100755 index 0000000000..dc34662cf6 --- /dev/null +++ b/activemq-web/project.xml @@ -0,0 +1,96 @@ + + + + 3 + ${basedir}/../etc/project.xml + + ActiveMQ :: Web + activemq-web + + Web Connector for REST API and Streamlets support + + + Web Connector for REST API and Streamlets support + + + org.activemq.web + + + Web Connector for REST API and Streamlets support + org.activemq.web + + + + + + + + + + + activemq + activemq + ${pom.currentVersion} + + true + truex + + + + + activemq + activemq-core-test + ${pom.currentVersion} + + + + + springframework + spring + ${spring_version} + http://www.springframework.org + + true + + + + + activeio + activeio + ${activeio_version} + + + + + geronimo-spec + geronimo-spec-jsp + ${geronimo_spec_jsp_version} + + false + + + + + + + + + jetty + servlet-api + ${servlet_api_version} + + false + + + + jetty + jetty + ${jetty_version} + + true + + + + + + diff --git a/activemq-web/src/java/org/activemq/web/ConnectionManager.java b/activemq-web/src/java/org/activemq/web/ConnectionManager.java new file mode 100755 index 0000000000..434851f9db --- /dev/null +++ b/activemq-web/src/java/org/activemq/web/ConnectionManager.java @@ -0,0 +1,50 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.web; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.servlet.http.HttpSessionEvent; +import javax.servlet.http.HttpSessionListener; + +/** + * Listens to sessions closing to ensure that JMS connections are + * cleaned up nicely + * + * @version $Revision: 1.1.1.1 $ + */ +public class ConnectionManager implements HttpSessionListener { + private static final Log log = LogFactory.getLog(ConnectionManager.class); + + public void sessionCreated(HttpSessionEvent event) { + } + + public void sessionDestroyed(HttpSessionEvent event) { + /** TODO we can't use the session any more now! + WebClient client = WebClient.getWebClient(event.getSession()); + try { + client.stop(); + } + catch (JMSException e) { + log.warn("Error closing connection: " + e, e); + } + */ + } +} diff --git a/activemq-web/src/java/org/activemq/web/MessageServlet.java b/activemq-web/src/java/org/activemq/web/MessageServlet.java new file mode 100755 index 0000000000..c52ec6cfc8 --- /dev/null +++ b/activemq-web/src/java/org/activemq/web/MessageServlet.java @@ -0,0 +1,320 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.web; + +import org.activemq.MessageAvailableConsumer; +import org.activemq.MessageAvailableListener; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.mortbay.util.ajax.Continuation; +import org.mortbay.util.ajax.ContinuationSupport; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.ObjectMessage; +import javax.jms.TextMessage; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.LinkedList; +import java.util.List; + +/** + * A servlet for sending and receiving messages to/from JMS destinations using + * HTTP POST for sending and HTTP GET for receiving.

You can specify the + * destination and whether it is a topic or queue via configuration details on + * the servlet or as request parameters.

For reading messages you can + * specify a readTimeout parameter to determine how long the servlet should + * block for. + * + * @version $Revision: 1.1.1.1 $ + */ +public class MessageServlet extends MessageServletSupport { + private static final Log log = LogFactory.getLog(MessageServlet.class); + + private String readTimeoutParameter = "readTimeout"; + private long defaultReadTimeout = -1; + private long maximumReadTimeout = 20000; + + public void init() throws ServletException { + ServletConfig servletConfig = getServletConfig(); + String name = servletConfig.getInitParameter("defaultReadTimeout"); + if (name != null) { + defaultReadTimeout = asLong(name); + } + name = servletConfig.getInitParameter("maximumReadTimeout"); + if (name != null) { + maximumReadTimeout = asLong(name); + } + } + + /** + * Sends a message to a destination + * + * @param request + * @param response + * @throws ServletException + * @throws IOException + */ + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + // lets turn the HTTP post into a JMS Message + try { + WebClient client = getWebClient(request); + + String text = getPostedMessageBody(request); + + // lets create the destination from the URI? + Destination destination = getDestination(client, request); + + if (log.isDebugEnabled()) { + log.debug("Sending message to: " + destination + " with text: " + text); + } + + TextMessage message = client.getSession().createTextMessage(text); + appendParametersToMessage(request, message); + client.send(destination, message); + + // lets return a unique URI for reliable messaging + response.setHeader("messageID", message.getJMSMessageID()); + response.setStatus(HttpServletResponse.SC_OK); + } + catch (JMSException e) { + throw new ServletException("Could not post JMS message: " + e, e); + } + } + + /** + * Supports a HTTP DELETE to be equivlanent of consuming a singe message + * from a queue + */ + protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doMessages(request, response, 1); + } + + /** + * Supports a HTTP DELETE to be equivlanent of consuming a singe message + * from a queue + */ + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doMessages(request, response, -1); + } + + /** + * Reads a message from a destination up to some specific timeout period + * + * @param request + * @param response + * @throws ServletException + * @throws IOException + */ + protected void doMessages(HttpServletRequest request, HttpServletResponse response, int maxMessages) throws ServletException, IOException { + + int messages = 0; + try { + WebClient client = getWebClient(request); + Destination destination = getDestination(client, request); + long timeout = getReadTimeout(request); + boolean ajax = isRicoAjax(request); + if (!ajax) + maxMessages = 1; + + if (log.isDebugEnabled()) { + log.debug("Receiving message(s) from: " + destination + " with timeout: " + timeout); + } + + MessageAvailableConsumer consumer = (MessageAvailableConsumer) client.getConsumer(destination); + Continuation continuation = null; + Listener listener = null; + Message message = null; + + synchronized (consumer) { + // Fetch the listeners + listener = (Listener) consumer.getAvailableListener(); + if (listener == null) { + listener = new Listener(consumer); + consumer.setAvailableListener(listener); + } + // Look for any available messages + message = consumer.receiveNoWait(); + + // Get an existing Continuation or create a new one if there are + // no events. + if (message == null) { + continuation = ContinuationSupport.getContinuation(request, consumer); + + // register this continuation with our listener. + listener.setContinuation(continuation); + + // Get the continuation object (may wait and/or retry + // request here). + continuation.suspend(timeout); + } + + // Try again now + if (message == null) + message = consumer.receiveNoWait(); + + // write a responds + response.setContentType("text/xml"); + PrintWriter writer = response.getWriter(); + + if (ajax) + writer.println(""); + + // handle any message(s) + if (message == null) { + // No messages so OK response of for ajax else no content. + response.setStatus(ajax ? HttpServletResponse.SC_OK : HttpServletResponse.SC_NO_CONTENT); + } + else { + // We have at least one message so set up the response + response.setStatus(HttpServletResponse.SC_OK); + String type = getContentType(request); + if (type != null) + response.setContentType(type); + + // send a response for each available message (up to max + // messages) + while ((maxMessages < 0 || messages < maxMessages) && message != null) { + // System.err.println("message["+messages+"]="+message); + if (ajax) { + writer.print(""); + } + else + // only ever 1 message for non ajax! + setResponseHeaders(response, message); + + writeMessageResponse(writer, message); + + if (ajax) + writer.println(""); + + // look for next message + message = consumer.receiveNoWait(); + messages++; + } + } + + if (ajax) { + writer.println(""); + writer.println(""); + } + } + } + catch (JMSException e) { + throw new ServletException("Could not post JMS message: " + e, e); + } + finally { + if (log.isDebugEnabled()) { + log.debug("Received " + messages + " message(s)"); + } + } + } + + protected void writeMessageResponse(PrintWriter writer, Message message) throws JMSException, IOException { + if (message instanceof TextMessage) { + TextMessage textMsg = (TextMessage) message; + writer.print(textMsg.getText()); + } + else if (message instanceof ObjectMessage) { + ObjectMessage objectMsg = (ObjectMessage) message; + Object object = objectMsg.getObject(); + writer.print(object.toString()); + } + } + + protected boolean isRicoAjax(HttpServletRequest request) { + String rico = request.getParameter("rico"); + return rico != null && rico.equals("true"); + } + + protected String getContentType(HttpServletRequest request) { + /* + * log("Params: " + request.getParameterMap()); Enumeration iter = + * request.getHeaderNames(); while (iter.hasMoreElements()) { String + * name = (String) iter.nextElement(); log("Header: " + name + " = " + + * request.getHeader(name)); } + */ + String value = request.getParameter("xml"); + if (value != null && "true".equalsIgnoreCase(value)) { + return "text/xml"; + } + return null; + } + + protected void setResponseHeaders(HttpServletResponse response, Message message) throws JMSException { + response.setHeader("destination", message.getJMSDestination().toString()); + response.setHeader("id", message.getJMSMessageID()); + } + + /** + * @return the timeout value for read requests which is always >= 0 and <= + * maximumReadTimeout to avoid DoS attacks + */ + protected long getReadTimeout(HttpServletRequest request) { + long answer = defaultReadTimeout; + + String name = request.getParameter(readTimeoutParameter); + if (name != null) { + answer = asLong(name); + } + if (answer < 0 || answer > maximumReadTimeout) { + answer = maximumReadTimeout; + } + return answer; + } + + /* + * Listen for available messages and wakeup any continuations. + */ + private class Listener implements MessageAvailableListener { + MessageConsumer consumer; + Continuation continuation; + List queue = new LinkedList(); + + Listener(MessageConsumer consumer) { + this.consumer = consumer; + } + + public void setContinuation(Continuation continuation) { + synchronized (consumer) { + this.continuation = continuation; + } + } + + public void onMessageAvailable(MessageConsumer consumer) { + assert this.consumer == consumer; + + synchronized (this.consumer) { + if (continuation != null) + continuation.resume(); + continuation = null; + } + } + } + +} diff --git a/activemq-web/src/java/org/activemq/web/MessageServletSupport.java b/activemq-web/src/java/org/activemq/web/MessageServletSupport.java new file mode 100755 index 0000000000..2577513efc --- /dev/null +++ b/activemq-web/src/java/org/activemq/web/MessageServletSupport.java @@ -0,0 +1,223 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.web; + +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.TextMessage; +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.io.BufferedReader; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +/** + * A useful base class for any JMS related servlet; + * there are various ways to map JMS operations to web requests + * so we put most of the common behaviour in a reusable base class. + * + * @version $Revision: 1.1.1.1 $ + */ +public abstract class MessageServletSupport extends HttpServlet { + + private boolean defaultTopicFlag = true; + private Destination defaultDestination; + private String destinationParameter = "destination"; + private String topicParameter = "topic"; + private String bodyParameter = "body"; + + + public void init(ServletConfig servletConfig) throws ServletException { + super.init(servletConfig); + + String name = servletConfig.getInitParameter("topic"); + if (name != null) { + defaultTopicFlag = asBoolean(name); + } + + log("Defaulting to use topics: " + defaultTopicFlag); + + name = servletConfig.getInitParameter("destination"); + if (name != null) { + if (defaultTopicFlag) { + defaultDestination = new ActiveMQTopic(name); + } + else { + defaultDestination = new ActiveMQQueue(name); + } + } + + // lets check to see if there's a connection factory set + WebClient.initContext(getServletContext()); + } + + protected WebClient createWebClient(HttpServletRequest request) { + return new WebClient(getServletContext()); + } + + public static boolean asBoolean(String param) { + return asBoolean(param, false); + } + + public static boolean asBoolean(String param, boolean defaultValue) { + if (param == null) { + return defaultValue; + } + else { + return param.equalsIgnoreCase("true"); + } + } + + /** + * Helper method to get the client for the current session + * + * @param request is the current HTTP request + * @return the current client or a newly creates + */ + protected WebClient getWebClient(HttpServletRequest request) { + HttpSession session = request.getSession(true); + WebClient client = WebClient.getWebClient(session); + if (client == null) { + client = createWebClient(request); + session.setAttribute(WebClient.webClientAttribute, client); + } + return client; + } + + + protected void appendParametersToMessage(HttpServletRequest request, TextMessage message) throws JMSException { + for (Iterator iter = request.getParameterMap().entrySet().iterator(); iter.hasNext();) { + Map.Entry entry = (Map.Entry) iter.next(); + String name = (String) entry.getKey(); + if (!destinationParameter.equals(name) && !topicParameter.equals(name) && !bodyParameter.equals(name)) { + Object value = entry.getValue(); + if (value instanceof Object[]) { + Object[] array = (Object[]) value; + if (array.length == 1) { + value = array[0]; + } + else { + log("Can't use property: " + name + " which is of type: " + value.getClass().getName() + " value"); + value = null; + for (int i = 0, size = array.length; i < size; i++) { + log("value[" + i + "] = " + array[i]); + } + } + } + if (value != null) { + message.setObjectProperty(name, value); + } + } + } + } + + /** + * @return the destination to use for the current request + */ + protected Destination getDestination(WebClient client, HttpServletRequest request) throws JMSException, NoDestinationSuppliedException { + String destinationName = request.getParameter(destinationParameter); + if (destinationName == null) { + if (defaultDestination == null) { + return getDestinationFromURI(client, request); + } + else { + return defaultDestination; + } + } + + return getDestination(client, request, destinationName); + } + + /** + * @return the destination to use for the current request using the relative URI from + * where this servlet was invoked as the destination name + */ + protected Destination getDestinationFromURI(WebClient client, HttpServletRequest request) throws NoDestinationSuppliedException, JMSException { + String uri = request.getPathInfo(); + if (uri == null) { + throw new NoDestinationSuppliedException(); + } + // replace URI separator with JMS destination separator + if (uri.startsWith("/")) { + uri = uri.substring(1); + } + uri = uri.replace('/', '.'); + return getDestination(client, request, uri); + } + + /** + * @return the Destination object for the given destination name + */ + protected Destination getDestination(WebClient client, HttpServletRequest request, String destinationName) throws JMSException { + if (isTopic(request)) { + return client.getSession().createTopic(destinationName); + } + else { + return client.getSession().createQueue(destinationName); + } + } + + /** + * @return true if the current request is for a topic destination, else false if its for a queue + */ + protected boolean isTopic + (HttpServletRequest + request) { + boolean aTopic = defaultTopicFlag; + String aTopicText = request.getParameter(topicParameter); + if (aTopicText != null) { + aTopic = asBoolean(aTopicText); + } + return aTopic; + } + + protected long asLong(String name) { + return Long.parseLong(name); + } + + /** + * @return the text that was posted to the servlet which is used as the body + * of the message to be sent + */ + protected String getPostedMessageBody(HttpServletRequest request) throws IOException { + String answer = request.getParameter(bodyParameter); + if (answer == null) { + // lets read the message body instead + BufferedReader reader = request.getReader(); + StringBuffer buffer = new StringBuffer(); + while (true) { + String line = reader.readLine(); + if (line == null) { + break; + } + buffer.append(line); + buffer.append("\n"); + } + return buffer.toString(); + } + return answer; + } +} diff --git a/activemq-web/src/java/org/activemq/web/NoDestinationSuppliedException.java b/activemq-web/src/java/org/activemq/web/NoDestinationSuppliedException.java new file mode 100755 index 0000000000..5c29612de4 --- /dev/null +++ b/activemq-web/src/java/org/activemq/web/NoDestinationSuppliedException.java @@ -0,0 +1,32 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.web; + +import javax.servlet.ServletException; + +/** + * Exception thrown if there was no destination available + * + * @version $Revision: 1.1.1.1 $ + */ +public class NoDestinationSuppliedException extends ServletException { + + public NoDestinationSuppliedException() { + super("Could not perform the JMS operation as no Destination was supplied"); + } +} diff --git a/activemq-web/src/java/org/activemq/web/PortfolioPublishServlet.java b/activemq-web/src/java/org/activemq/web/PortfolioPublishServlet.java new file mode 100755 index 0000000000..78b92425a8 --- /dev/null +++ b/activemq-web/src/java/org/activemq/web/PortfolioPublishServlet.java @@ -0,0 +1,138 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.web; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.Session; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Hashtable; +import java.util.Map; + +/** + * A servlet which will publish dummy market data prices + * + * @version $Revision: 1.1.1.1 $ + */ +public class PortfolioPublishServlet extends MessageServletSupport { + + private static final int maxDeltaPercent = 1; + private static final Map lastPrices = new Hashtable(); + private boolean ricoStyle = true; + + + public void init() throws ServletException { + super.init(); + + ricoStyle = asBoolean(getServletConfig().getInitParameter("rico"), true); + } + + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + PrintWriter out = response.getWriter(); + String[] stocks = request.getParameterValues("stocks"); + if (stocks == null || stocks.length == 0) { + out.println("No stocks query parameter specified. Cannot publish market data"); + } + else { + Integer total=(Integer)request.getSession(true).getAttribute("total"); + if (total==null) + total=new Integer(0); + + + int count = getNumberOfMessages(request); + total=new Integer(total.intValue()+count); + request.getSession().setAttribute("total",total); + + try { + WebClient client = getWebClient(request); + for (int i = 0; i < count; i++) { + sendMessage(client, stocks); + } + out.print(""); + out.println("Published " + count + " of "+total+ " price messages. Refresh = "+refreshRate+"s"); + out.println(""); + + } + catch (JMSException e) { + out.println("Failed sending price messages due to " + e + ""); + log("Failed to send message: " + e, e); + } + } + } + + protected void sendMessage(WebClient client, String[] stocks) throws JMSException { + Session session = client.getSession(); + + int idx = 0; + while (true) { + idx = (int) Math.round(stocks.length * Math.random()); + if (idx < stocks.length) { + break; + } + } + String stock = stocks[idx]; + Destination destination = session.createTopic("STOCKS." + stock); + String stockText = createStockText(stock); + log("Sending: " + stockText + " on destination: " + destination); + Message message = session.createTextMessage(stockText); + client.send(destination, message); + } + + protected String createStockText(String stock) { + Double value = (Double) lastPrices.get(stock); + if (value == null) { + value = new Double(Math.random() * 100); + } + + // lets mutate the value by some percentage + double oldPrice = value.doubleValue(); + value = new Double(mutatePrice(oldPrice)); + lastPrices.put(stock, value); + double price = value.doubleValue(); + + double offer = price * 1.001; + + String movement = (price > oldPrice) ? "up" : "down"; + return ""; + } + + protected double mutatePrice(double price) { + double percentChange = (2 * Math.random() * maxDeltaPercent) - maxDeltaPercent; + + return price * (100 + percentChange) / 100; + } + + protected int getNumberOfMessages(HttpServletRequest request) { + String name = request.getParameter("count"); + if (name != null) { + return Integer.parseInt(name); + } + return 1; + } +} diff --git a/activemq-web/src/java/org/activemq/web/SpringBrokerContextListener.java b/activemq-web/src/java/org/activemq/web/SpringBrokerContextListener.java new file mode 100644 index 0000000000..46c652afb7 --- /dev/null +++ b/activemq-web/src/java/org/activemq/web/SpringBrokerContextListener.java @@ -0,0 +1,111 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.web; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextListener; +import javax.servlet.ServletContextEvent; +import javax.jms.JMSException; + +import org.activemq.broker.BrokerService; +import org.springframework.core.io.Resource; +import org.springframework.web.context.support.ServletContextResource; + +/** + * Used to configure and instance of ActiveMQ BrokerService using + * ActiveMQ/Spring's xml configuration.

The configuration file is specified + * via the context init parameter brokerURI, typically: + * <context-param> + * <param-name>brokerURI</param-name> + * <param-value>/WEB-INF/activemq.xml</param-value> + * </context-param> + * + * + * As a a default, if a brokerURI is not specified it will look up + * for activemq.xml + * + * @version $Revision: 1.1 $ + */ +public class SpringBrokerContextListener implements ServletContextListener { + + /** broker uri context parameter name: brokerURI */ + public static final String INIT_PARAM_BROKER_URI = "brokerURI"; + + /** the broker container instance */ + private BrokerService brokerContainer; + + /** + * Set the broker container to be used by this listener + * + * @param container + * the container to be used. + */ + protected void setBrokerService(BrokerService container) { + this.brokerContainer = container; + } + + /** + * Return the broker container. + */ + protected BrokerService getBrokerService() { + return this.brokerContainer; + } + + public void contextInitialized(ServletContextEvent event) { + ServletContext context = event.getServletContext(); + context.log("Creating ActiveMQ Broker..."); + brokerContainer = createBroker(context); + + context.log("Starting ActiveMQ Broker"); + try { + brokerContainer.start(); + + context.log("Started ActiveMQ Broker"); + } + catch (Exception e) { + context.log("Failed to start ActiveMQ broker: " + e, e); + } + } + + public void contextDestroyed(ServletContextEvent event) { + ServletContext context = event.getServletContext(); + if (brokerContainer != null) { + try { + brokerContainer.stop(); + } + catch (Exception e) { + context.log("Failed to stop the ActiveMQ Broker: " + e, e); + } + brokerContainer = null; + } + } + + /** + * Factory method to create a new ActiveMQ Broker + */ + protected BrokerService createBroker(ServletContext context) { + String brokerURI = context.getInitParameter(INIT_PARAM_BROKER_URI); + if (brokerURI == null) { + brokerURI = "activemq.xml"; + } + context.log("Loading ActiveMQ Broker configuration from: " + brokerURI); + Resource resource = new ServletContextResource(context, brokerURI); + // return SpringBrokerServiceFactory.newInstance(resource); + return null; + } +} diff --git a/activemq-web/src/java/org/activemq/web/WebClient.java b/activemq-web/src/java/org/activemq/web/WebClient.java new file mode 100755 index 0000000000..67bfd9f857 --- /dev/null +++ b/activemq-web/src/java/org/activemq/web/WebClient.java @@ -0,0 +1,250 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.web; + +import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap; + +import org.activemq.ActiveMQConnection; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.ActiveMQSession; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.Topic; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionActivationListener; +import javax.servlet.http.HttpSessionEvent; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.HashMap; +import java.util.Map; + +/** + * Represents a messaging client used from inside a web container + * typically stored inside a HttpSession + * + * @version $Revision: 1.1.1.1 $ + */ +public class WebClient implements HttpSessionActivationListener, Externalizable { + public static final String webClientAttribute = "org.activemq.webclient"; + public static final String connectionFactoryAttribute = "org.activemq.connectionFactory"; + public static final String queueConsumersAttribute = "org.activemq.queueConsumers"; + public static final String brokerUrlInitParam = "org.activemq.brokerURL"; + public static final String embeddedBrokerInitParam = "org.activemq.embeddedBroker"; + + private static final Log log = LogFactory.getLog(WebClient.class); + + private static transient ConnectionFactory factory; + private static transient Map queueConsumers; + + private transient ServletContext context; + private transient ActiveMQConnection connection; + private transient ActiveMQSession session; + private transient MessageProducer producer; + private transient Map topicConsumers = new ConcurrentHashMap(); + private int deliveryMode = DeliveryMode.NON_PERSISTENT; + + + /** + * @return the web client for the current HTTP session or null if there is not a web client created yet + */ + public static WebClient getWebClient(HttpSession session) { + return (WebClient) session.getAttribute(webClientAttribute); + } + + + public static void initContext(ServletContext context) { + factory = initConnectionFactory(context); + if (factory == null) { + log.warn("No ConnectionFactory available in the ServletContext for: " + connectionFactoryAttribute); + factory = new ActiveMQConnectionFactory("vm://localhost"); + context.setAttribute(connectionFactoryAttribute, factory); + } + queueConsumers = initQueueConsumers(context); + } + + /** + * Only called by serialization + */ + public WebClient() { + } + + public WebClient(ServletContext context) { + this.context = context; + initContext(context); + } + + + public int getDeliveryMode() { + return deliveryMode; + } + + + public void setDeliveryMode(int deliveryMode) { + this.deliveryMode = deliveryMode; + } + + + public void start() throws JMSException { + } + + public void stop() throws JMSException { + System.out.println("Closing the WebClient!!! " + this); + + try { + connection.close(); + } + finally { + producer = null; + session = null; + connection = null; + topicConsumers.clear(); + } + } + + public void writeExternal(ObjectOutput out) throws IOException { + } + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + topicConsumers = new HashMap(); + } + + public void send(Destination destination, Message message) throws JMSException { + if (producer == null) { + producer = getSession().createProducer(null); + producer.setDeliveryMode(deliveryMode ); + } + producer.send(destination, message); + if (log.isDebugEnabled()) { + log.debug("Sent! to destination: " + destination + " message: " + message); + } + } + + public Session getSession() throws JMSException { + if (session == null) { + session = createSession(); + } + return session; + } + + public ActiveMQConnection getConnection() throws JMSException { + if (connection == null) { + connection = (ActiveMQConnection) factory.createConnection(); + connection.start(); + } + return connection; + } + + public void sessionWillPassivate(HttpSessionEvent event) { + try { + stop(); + } + catch (JMSException e) { + log.warn("Could not close connection: " + e, e); + } + } + + public void sessionDidActivate(HttpSessionEvent event) { + // lets update the connection factory from the servlet context + context = event.getSession().getServletContext(); + initContext(context); + } + + public static Map initQueueConsumers(ServletContext context) { + Map answer = (Map) context.getAttribute(queueConsumersAttribute); + if (answer == null) { + answer = new HashMap(); + context.setAttribute(queueConsumersAttribute, answer); + } + return answer; + } + + + public static ConnectionFactory initConnectionFactory(ServletContext servletContext) { + ConnectionFactory connectionFactory = (ConnectionFactory) servletContext.getAttribute(connectionFactoryAttribute); + if (connectionFactory == null) { + String brokerURL = (String) servletContext.getInitParameter(brokerUrlInitParam); + + servletContext.log("Value of: " + brokerUrlInitParam + " is: " + brokerURL); + + if (brokerURL == null) { + brokerURL = "vm://localhost"; + } + + boolean embeddedBroker = MessageServletSupport.asBoolean(servletContext.getInitParameter(embeddedBrokerInitParam)); + servletContext.log("Use embedded broker: " + embeddedBroker); + + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(brokerURL); + factory.setUseEmbeddedBroker(embeddedBroker); + + connectionFactory = factory; + servletContext.setAttribute(connectionFactoryAttribute, connectionFactory); + } + return connectionFactory; + } + + public synchronized MessageConsumer getConsumer(Destination destination) throws JMSException { + if (destination instanceof Topic) { + MessageConsumer consumer = (MessageConsumer) topicConsumers.get(destination); + if (consumer == null) { + consumer = getSession().createConsumer(destination); + topicConsumers.put(destination, consumer); + } + return consumer; + } + else { + synchronized (queueConsumers) { + SessionConsumerPair pair = (SessionConsumerPair) queueConsumers.get(destination); + if (pair == null) { + pair = createSessionConsumerPair(destination); + queueConsumers.put(destination, pair); + } + return pair.consumer; + } + } + } + + protected ActiveMQSession createSession() throws JMSException { + return (ActiveMQSession) getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE); + } + + protected SessionConsumerPair createSessionConsumerPair(Destination destination) throws JMSException { + SessionConsumerPair answer = new SessionConsumerPair(); + answer.session = createSession(); + answer.consumer = answer.session.createConsumer(destination); + return answer; + } + + protected static class SessionConsumerPair { + public Session session; + public MessageConsumer consumer; + } +} diff --git a/activemq-web/src/java/org/activemq/web/package.html b/activemq-web/src/java/org/activemq/web/package.html new file mode 100755 index 0000000000..e89fd5ac27 --- /dev/null +++ b/activemq-web/src/java/org/activemq/web/package.html @@ -0,0 +1,13 @@ + + + + + +

+ Web Connectors so that messages can be sent via HTTP POST or read via + HTTP POST or GET as well as support for web streaming to we browser or + JavaScript clients. +

+ + + diff --git a/activemq-web/src/test/log4j.properties b/activemq-web/src/test/log4j.properties new file mode 100755 index 0000000000..9bd5d778b4 --- /dev/null +++ b/activemq-web/src/test/log4j.properties @@ -0,0 +1,18 @@ +# +# The logging properties used during tests.. +# +log4j.rootLogger=INFO, out + +log4j.logger.org.activemq.spring=WARN + +# CONSOLE appender not used by default +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n + +# File appender +log4j.appender.out=org.apache.log4j.FileAppender +log4j.appender.out.layout=org.apache.log4j.PatternLayout +log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +log4j.appender.out.file=target/test-reports/activemq-test.log +log4j.appender.out.append=true diff --git a/activemq-web/src/test/org/activemq/web/JettyServer.java b/activemq-web/src/test/org/activemq/web/JettyServer.java new file mode 100644 index 0000000000..37f5f1053c --- /dev/null +++ b/activemq-web/src/test/org/activemq/web/JettyServer.java @@ -0,0 +1,62 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.web; + +import org.mortbay.jetty.Connector; +import org.mortbay.jetty.Handler; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.nio.SelectChannelConnector; +import org.mortbay.jetty.webapp.WebAppClassLoader; +import org.mortbay.jetty.webapp.WebAppContext; + + +/** + * A simple bootstrap class for starting Jetty in your IDE using the local web application. + * + * @version $Revision$ + */ +public class JettyServer { + + public static final int PORT = 8080; + + public static final String WEBAPP_DIR = "src/webapp"; + + public static final String WEBAPP_CTX = "/"; + + public static void main(String[] args) throws Exception { + int port = PORT; + if (args.length > 0) { + String text = args[0]; + port = Integer.parseInt(text); + } + System.out.println("Starting Web Server on port: " + port); + Server server = new Server(); + SelectChannelConnector connector = new SelectChannelConnector(); + connector.setPort(port); + connector.setServer(server); + WebAppContext context = new WebAppContext(); + + context.setResourceBase(WEBAPP_DIR); + context.setContextPath(WEBAPP_CTX); + context.setServer(server); + server.setHandlers(new Handler[]{context}); + server.setConnectors(new Connector[]{connector}); + server.start(); + } +} diff --git a/activemq-web/src/webapp/WEB-INF/web.xml b/activemq-web/src/webapp/WEB-INF/web.xml new file mode 100755 index 0000000000..638ec8b934 --- /dev/null +++ b/activemq-web/src/webapp/WEB-INF/web.xml @@ -0,0 +1,58 @@ + + + + + + + ActiveMQ Message Broker Web Application + + Provides a REST API to the ActiveMQ Message Broker + + + + + org.activemq.brokerURL + vm://localhost + The URL of the Message Broker to connect to + + + + org.activemq.embeddedBroker + true + Whether we should include an embedded broker or not + + + + + org.activemq.web.ConnectionManager + + + + + + + + MessageServlet + org.activemq.web.MessageServlet + 1 + + + + + PortfolioPublishServlet + org.activemq.web.PortfolioPublishServlet + 1 + + + + MessageServlet + /jms/* + + + PortfolioPublishServlet + /portfolioPublish + + + diff --git a/activemq-web/src/webapp/chat.html b/activemq-web/src/webapp/chat.html new file mode 100755 index 0000000000..5bd86b4d66 --- /dev/null +++ b/activemq-web/src/webapp/chat.html @@ -0,0 +1,20 @@ + + + Chat + + + + +

Chat Example

+ + Welcome to this little chat example

+ +
+ Type here: + + + + + + + \ No newline at end of file diff --git a/activemq-web/src/webapp/chat.js b/activemq-web/src/webapp/chat.js new file mode 100755 index 0000000000..ab4fcfaea6 --- /dev/null +++ b/activemq-web/src/webapp/chat.js @@ -0,0 +1,55 @@ +// ----------------- +// Original code by Joe Walnes +// ----------------- + +function chooseNickName() { + var newNickName = prompt("Please choose a nick name", nickName) + if (newNickName) { + connection.sendMessage(chatTopic, "" + nickName + " is now known as " + newNickName + "") + nickName = newNickName + } +} + +// when user clicks 'send', broadcast a message +function saySomething() { + var text = document.getElementById("userInput").value + connection.sendMessage(chatTopic, "" + text + "") + document.getElementById("userInput").value = "" +} + +// when message is received from topic, display it in chat log +function receiveMessage(message) { + var root = message.documentElement + var chatLog = document.getElementById("chatLog") + + var type = root.getAttribute('type') + if (type == 'status') { + chatLog.value += "*** " + elementText(root) + "\n" + } + else if (type == 'chat') { + chatLog.value += "<" + root.getAttribute('from') + "> " + elementText(root) + "\n" + } + else { + chatLog.value += "*** Unknown type: " + type + " for: " + root + "\n" + } +} + +// returns the text of an XML element +function elementText(element) { + var answer = "" + var node = element.firstChild + while (node != null) { + var tmp = node.nodeValue + if (tmp != null) { + answer += tmp + } + node = node.nextSibling + } + return answer +} + +var connection = new Connection("jms/FOO/BAR") +var chatTopic = "FOO.BAR" +connection.addMessageListener(chatTopic, receiveMessage) +var nickName = "unknown" +document.getElementById("chatLog").value = '' diff --git a/activemq-web/src/webapp/index.html b/activemq-web/src/webapp/index.html new file mode 100755 index 0000000000..0292b92b42 --- /dev/null +++ b/activemq-web/src/webapp/index.html @@ -0,0 +1,46 @@ + + + + ActiveMQ Web Connector + + + + +

ActiveMQ Web Connector

+ +

+This service allows you to send messages to the JMS network using a +normal HTTP POST and to receive messages from a destination using a +HTTP GET. +

+ +

Market data example

+ +

+Market data publisher starts publishing +some mock market data prices +

+ +

+Porfolio example shows how you could make an interactive trading portfolio which +updates in real time as the market prices change +

+ +

Chat example

+ +

+Chat room example shows how you can use Streamlets and ActiveMQ to create a simple chat room +

+ +

Simple Form based browser example

+ +

+Send a message +

+ +

+Receive a message +

+ + + \ No newline at end of file diff --git a/activemq-web/src/webapp/js/behaviour.js b/activemq-web/src/webapp/js/behaviour.js new file mode 100644 index 0000000000..74b5735cf3 --- /dev/null +++ b/activemq-web/src/webapp/js/behaviour.js @@ -0,0 +1,254 @@ +/* + Behaviour v1.0 by Ben Nolan, June 2005. Based largely on the work + of Simon Willison (see comments by Simon below). + + Description: + + Uses css selectors to apply javascript behaviours to enable + unobtrusive javascript in html documents. + + Usage: + + var myrules = { + 'b.someclass' : function(element){ + element.onclick = function(){ + alert(this.innerHTML); + } + }, + '#someid u' : function(element){ + element.onmouseover = function(){ + this.innerHTML = "BLAH!"; + } + } + }; + + Behaviour.register(myrules); + + // Call Behaviour.apply() to re-apply the rules (if you + // update the dom, etc). + + License: + + My stuff is BSD licensed. Not sure about Simon's. + + More information: + + http://ripcord.co.nz/behaviour/ + +*/ + +var Behaviour = { + list : new Array, + + register : function(sheet){ + Behaviour.list.push(sheet); + }, + + start : function(){ + Behaviour.addLoadEvent(function(){ + Behaviour.apply(); + }); + }, + + apply : function(){ + for (h=0;sheet=Behaviour.list[h];h++){ + for (selector in sheet){ + list = document.getElementsBySelector(selector); + + if (!list){ + continue; + } + + for (i=0;element=list[i];i++){ + sheet[selector](element); + } + } + } + }, + + addLoadEvent : function(func){ + var oldonload = window.onload; + + if (typeof window.onload != 'function') { + window.onload = func; + } else { + window.onload = function() { + oldonload(); + func(); + } + } + } +} + +Behaviour.start(); + +/* + The following code is Copyright (C) Simon Willison 2004. + + document.getElementsBySelector(selector) + - returns an array of element objects from the current document + matching the CSS selector. Selectors can contain element names, + class names and ids and can be nested. For example: + + elements = document.getElementsBySelect('div#main p a.external') + + Will return an array of all 'a' elements with 'external' in their + class attribute that are contained inside 'p' elements that are + contained inside the 'div' element which has id="main" + + New in version 0.4: Support for CSS2 and CSS3 attribute selectors: + See http://www.w3.org/TR/css3-selectors/#attribute-selectors + + Version 0.4 - Simon Willison, March 25th 2003 + -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows + -- Opera 7 fails +*/ + +function getAllChildren(e) { + // Returns all children of element. Workaround required for IE5/Windows. Ugh. + return e.all ? e.all : e.getElementsByTagName('*'); +} + +document.getElementsBySelector = function(selector) { + // Attempt to fail gracefully in lesser browsers + if (!document.getElementsByTagName) { + return new Array(); + } + // Split selector in to tokens + var tokens = selector.split(' '); + var currentContext = new Array(document); + for (var i = 0; i < tokens.length; i++) { + token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');; + if (token.indexOf('#') > -1) { + // Token is an ID selector + var bits = token.split('#'); + var tagName = bits[0]; + var id = bits[1]; + var element = document.getElementById(id); + if (tagName && element.nodeName.toLowerCase() != tagName) { + // tag with that ID not found, return false + return new Array(); + } + // Set currentContext to contain just this element + currentContext = new Array(element); + continue; // Skip to next token + } + if (token.indexOf('.') > -1) { + // Token contains a class selector + var bits = token.split('.'); + var tagName = bits[0]; + var className = bits[1]; + if (!tagName) { + tagName = '*'; + } + // Get elements matching tag, filter them for class selector + var found = new Array; + var foundCount = 0; + for (var h = 0; h < currentContext.length; h++) { + var elements; + if (tagName == '*') { + elements = getAllChildren(currentContext[h]); + } else { + elements = currentContext[h].getElementsByTagName(tagName); + } + for (var j = 0; j < elements.length; j++) { + found[foundCount++] = elements[j]; + } + } + currentContext = new Array; + var currentContextIndex = 0; + for (var k = 0; k < found.length; k++) { + if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) { + currentContext[currentContextIndex++] = found[k]; + } + } + continue; // Skip to next token + } + // Code to deal with attribute selectors + if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) { + var tagName = RegExp.$1; + var attrName = RegExp.$2; + var attrOperator = RegExp.$3; + var attrValue = RegExp.$4; + if (!tagName) { + tagName = '*'; + } + // Grab all of the tagName elements within current context + var found = new Array; + var foundCount = 0; + for (var h = 0; h < currentContext.length; h++) { + var elements; + if (tagName == '*') { + elements = getAllChildren(currentContext[h]); + } else { + elements = currentContext[h].getElementsByTagName(tagName); + } + for (var j = 0; j < elements.length; j++) { + found[foundCount++] = elements[j]; + } + } + currentContext = new Array; + var currentContextIndex = 0; + var checkFunction; // This function will be used to filter the elements + switch (attrOperator) { + case '=': // Equality + checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); }; + break; + case '~': // Match one of space seperated words + checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); }; + break; + case '|': // Match start with value followed by optional hyphen + checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); }; + break; + case '^': // Match starts with value + checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); }; + break; + case '$': // Match ends with value - fails with "Warning" in Opera 7 + checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); }; + break; + case '*': // Match ends with value + checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); }; + break; + default : + // Just test for existence of attribute + checkFunction = function(e) { return e.getAttribute(attrName); }; + } + currentContext = new Array; + var currentContextIndex = 0; + for (var k = 0; k < found.length; k++) { + if (checkFunction(found[k])) { + currentContext[currentContextIndex++] = found[k]; + } + } + // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue); + continue; // Skip to next token + } + + if (!currentContext[0]){ + return; + } + + // If we get here, token is JUST an element (not a class or ID selector) + tagName = token; + var found = new Array; + var foundCount = 0; + for (var h = 0; h < currentContext.length; h++) { + var elements = currentContext[h].getElementsByTagName(tagName); + for (var j = 0; j < elements.length; j++) { + found[foundCount++] = elements[j]; + } + } + currentContext = found; + } + return currentContext; +} + +/* That revolting regular expression explained +/^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/ + \---/ \---/\-------------/ \-------/ + | | | | + | | | The value + | | ~,|,^,$,* or = + | Attribute + Tag +*/ diff --git a/activemq-web/src/webapp/js/default.js b/activemq-web/src/webapp/js/default.js new file mode 100644 index 0000000000..3d92fef460 --- /dev/null +++ b/activemq-web/src/webapp/js/default.js @@ -0,0 +1,28 @@ +// Technique borrowed from scriptaculous to do includes. + +var DefaultJS = { + Version: 'Jetty Test', + script: function(libraryName) { + document.write(''); + }, + load: function() { + var scriptTags = document.getElementsByTagName("script"); + for(var i=0;i + * + * THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff + * against the source tree, available from the Prototype darcs repository. + * + * Prototype is freely distributable under the terms of an MIT-style license. + * + * For details, see the Prototype web site: http://prototype.conio.net/ + * +/*--------------------------------------------------------------------------*/ + +var Prototype = { + Version: '1.3.1', + emptyFunction: function() {} +} + +var Class = { + create: function() { + return function() { + this.initialize.apply(this, arguments); + } + } +} + +var Abstract = new Object(); + +Object.extend = function(destination, source) { + for (property in source) { + destination[property] = source[property]; + } + return destination; +} + +Object.prototype.extend = function(object) { + return Object.extend.apply(this, [this, object]); +} + +Function.prototype.bind = function(object) { + var __method = this; + return function() { + __method.apply(object, arguments); + } +} + +Function.prototype.bindAsEventListener = function(object) { + var __method = this; + return function(event) { + __method.call(object, event || window.event); + } +} + +Number.prototype.toColorPart = function() { + var digits = this.toString(16); + if (this < 16) return '0' + digits; + return digits; +} + +var Try = { + these: function() { + var returnValue; + + for (var i = 0; i < arguments.length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) {} + } + + return returnValue; + } +} + +/*--------------------------------------------------------------------------*/ + +var PeriodicalExecuter = Class.create(); +PeriodicalExecuter.prototype = { + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.callback(); + } finally { + this.currentlyExecuting = false; + } + } + } +} + +/*--------------------------------------------------------------------------*/ + +function $() { + var elements = new Array(); + + for (var i = 0; i < arguments.length; i++) { + var element = arguments[i]; + if (typeof element == 'string') + element = document.getElementById(element); + + if (arguments.length == 1) + return element; + + elements.push(element); + } + + return elements; +} + +if (!Array.prototype.push) { + Array.prototype.push = function() { + var startLength = this.length; + for (var i = 0; i < arguments.length; i++) + this[startLength + i] = arguments[i]; + return this.length; + } +} + +if (!Function.prototype.apply) { + // Based on code from http://www.youngpup.net/ + Function.prototype.apply = function(object, parameters) { + var parameterStrings = new Array(); + if (!object) object = window; + if (!parameters) parameters = new Array(); + + for (var i = 0; i < parameters.length; i++) + parameterStrings[i] = 'parameters[' + i + ']'; + + object.__apply__ = this; + var result = eval('object.__apply__(' + + parameterStrings.join(', ') + ')'); + object.__apply__ = null; + + return result; + } +} + +String.prototype.extend({ + stripTags: function() { + return this.replace(/<\/?[^>]+>/gi, ''); + }, + + escapeHTML: function() { + var div = document.createElement('div'); + var text = document.createTextNode(this); + div.appendChild(text); + return div.innerHTML; + }, + + unescapeHTML: function() { + var div = document.createElement('div'); + div.innerHTML = this.stripTags(); + return div.childNodes[0].nodeValue; + } +}); + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')}, + function() {return new XMLHttpRequest()} + ) || false; + } +} + +Ajax.Base = function() {}; +Ajax.Base.prototype = { + setOptions: function(options) { + this.options = { + method: 'post', + asynchronous: true, + parameters: '' + }.extend(options || {}); + }, + + responseIsSuccess: function() { + return this.transport.status == undefined + || this.transport.status == 0 + || (this.transport.status >= 200 && this.transport.status < 300); + }, + + responseIsFailure: function() { + return !this.responseIsSuccess(); + } +} + +Ajax.Request = Class.create(); +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Request.prototype = (new Ajax.Base()).extend({ + initialize: function(url, options) { + this.transport = Ajax.getTransport(); + this.setOptions(options); + this.request(url); + }, + + request: function(url) { + var parameters = this.options.parameters || ''; + if (parameters.length > 0) parameters += '&_='; + + try { + if (this.options.method == 'get') + url += (url.indexOf('?')<0?'?':'&') + parameters; + + this.transport.open(this.options.method, url, + this.options.asynchronous); + + if (this.options.asynchronous) { + this.transport.onreadystatechange = this.onStateChange.bind(this); + setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); + } + + this.setRequestHeaders(); + + var body = this.options.postBody ? this.options.postBody : parameters; + this.transport.send(this.options.method == 'post' ? body : null); + + } catch (e) { + } + }, + + setRequestHeaders: function() { + var requestHeaders = + ['X-Requested-With', 'XMLHttpRequest', + 'X-Prototype-Version', Prototype.Version]; + + if (this.options.method == 'post') { + requestHeaders.push('Content-type', + 'application/x-www-form-urlencoded'); + + /* Force "Connection: close" for Mozilla browsers to work around + * a bug where XMLHttpReqeuest sends an incorrect Content-length + * header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType) + requestHeaders.push('Connection', 'close'); + } + + if (this.options.requestHeaders) + requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); + + for (var i = 0; i < requestHeaders.length; i += 2) + this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState != 1) + this.respondToReadyState(this.transport.readyState); + }, + + respondToReadyState: function(readyState) { + var event = Ajax.Request.Events[readyState]; + + if (event == 'Complete') + (this.options['on' + this.transport.status] + || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(this.transport); + + (this.options['on' + event] || Prototype.emptyFunction)(this.transport); + + /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ + if (event == 'Complete') + this.transport.onreadystatechange = Prototype.emptyFunction; + } +}); + +Ajax.Updater = Class.create(); +Ajax.Updater.ScriptFragment = '(?:)((\n|.)*?)(?:<\/script>)'; + +Ajax.Updater.prototype.extend(Ajax.Request.prototype).extend({ + initialize: function(container, url, options) { + this.containers = { + success: container.success ? $(container.success) : $(container), + failure: container.failure ? $(container.failure) : + (container.success ? null : $(container)) + } + + this.transport = Ajax.getTransport(); + this.setOptions(options); + + var onComplete = this.options.onComplete || Prototype.emptyFunction; + this.options.onComplete = (function() { + this.updateContent(); + onComplete(this.transport); + }).bind(this); + + this.request(url); + }, + + updateContent: function() { + var receiver = this.responseIsSuccess() ? + this.containers.success : this.containers.failure; + + var match = new RegExp(Ajax.Updater.ScriptFragment, 'img'); + var response = this.transport.responseText.replace(match, ''); + var scripts = this.transport.responseText.match(match); + + if (receiver) { + if (this.options.insertion) { + new this.options.insertion(receiver, response); + } else { + receiver.innerHTML = response; + } + } + + if (this.responseIsSuccess()) { + if (this.onComplete) + setTimeout((function() {this.onComplete( + this.transport)}).bind(this), 10); + } + + if (this.options.evalScripts && scripts) { + match = new RegExp(Ajax.Updater.ScriptFragment, 'im'); + setTimeout((function() { + for (var i = 0; i < scripts.length; i++) + eval(scripts[i].match(match)[1]); + }).bind(this), 10); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(); +Ajax.PeriodicalUpdater.prototype = (new Ajax.Base()).extend({ + initialize: function(container, url, options) { + this.setOptions(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = 1; + + this.updater = {}; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Ajax.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(request) { + if (this.options.decay) { + this.decay = (request.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = request.responseText; + } + this.timer = setTimeout(this.onTimerEvent.bind(this), + this.decay * this.frequency * 1000); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); + +document.getElementsByClassName = function(className) { + var children = document.getElementsByTagName('*') || document.all; + var elements = new Array(); + + for (var i = 0; i < children.length; i++) { + var child = children[i]; + var classNames = child.className.split(' '); + for (var j = 0; j < classNames.length; j++) { + if (classNames[j] == className) { + elements.push(child); + break; + } + } + } + + return elements; +} + +/*--------------------------------------------------------------------------*/ + +if (!window.Element) { + var Element = new Object(); +} + +Object.extend(Element, { + toggle: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = + (element.style.display == 'none' ? '' : 'none'); + } + }, + + hide: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = 'none'; + } + }, + + show: function() { + for (var i = 0; i < arguments.length; i++) { + var element = $(arguments[i]); + element.style.display = ''; + } + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + }, + + getHeight: function(element) { + element = $(element); + return element.offsetHeight; + }, + + hasClassName: function(element, className) { + element = $(element); + if (!element) + return; + var a = element.className.split(' '); + for (var i = 0; i < a.length; i++) { + if (a[i] == className) + return true; + } + return false; + }, + + addClassName: function(element, className) { + element = $(element); + Element.removeClassName(element, className); + element.className += ' ' + className; + }, + + removeClassName: function(element, className) { + element = $(element); + if (!element) + return; + var newClassName = ''; + var a = element.className.split(' '); + for (var i = 0; i < a.length; i++) { + if (a[i] != className) { + if (i > 0) + newClassName += ' '; + newClassName += a[i]; + } + } + element.className = newClassName; + }, + + // removes whitespace-only text node children + cleanWhitespace: function(element) { + var element = $(element); + for (var i = 0; i < element.childNodes.length; i++) { + var node = element.childNodes[i]; + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) + Element.remove(node); + } + } +}); + +var Toggle = new Object(); +Toggle.display = Element.toggle; + +/*--------------------------------------------------------------------------*/ + +Abstract.Insertion = function(adjacency) { + this.adjacency = adjacency; +} + +Abstract.Insertion.prototype = { + initialize: function(element, content) { + this.element = $(element); + this.content = content; + + if (this.adjacency && this.element.insertAdjacentHTML) { + this.element.insertAdjacentHTML(this.adjacency, this.content); + } else { + this.range = this.element.ownerDocument.createRange(); + if (this.initializeRange) this.initializeRange(); + this.fragment = this.range.createContextualFragment(this.content); + this.insertContent(); + } + } +} + +var Insertion = new Object(); + +Insertion.Before = Class.create(); +Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({ + initializeRange: function() { + this.range.setStartBefore(this.element); + }, + + insertContent: function() { + this.element.parentNode.insertBefore(this.fragment, this.element); + } +}); + +Insertion.Top = Class.create(); +Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({ + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(true); + }, + + insertContent: function() { + this.element.insertBefore(this.fragment, this.element.firstChild); + } +}); + +Insertion.Bottom = Class.create(); +Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({ + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(this.element); + }, + + insertContent: function() { + this.element.appendChild(this.fragment); + } +}); + +Insertion.After = Class.create(); +Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({ + initializeRange: function() { + this.range.setStartAfter(this.element); + }, + + insertContent: function() { + this.element.parentNode.insertBefore(this.fragment, + this.element.nextSibling); + } +}); + +var Field = { + clear: function() { + for (var i = 0; i < arguments.length; i++) + $(arguments[i]).value = ''; + }, + + focus: function(element) { + $(element).focus(); + }, + + present: function() { + for (var i = 0; i < arguments.length; i++) + if ($(arguments[i]).value == '') return false; + return true; + }, + + select: function(element) { + $(element).select(); + }, + + activate: function(element) { + $(element).focus(); + $(element).select(); + } +} + +/*--------------------------------------------------------------------------*/ + +var Form = { + serialize: function(form) { + var elements = Form.getElements($(form)); + var queryComponents = new Array(); + + for (var i = 0; i < elements.length; i++) { + var queryComponent = Form.Element.serialize(elements[i]); + if (queryComponent) + queryComponents.push(queryComponent); + } + + return queryComponents.join('&'); + }, + + getElements: function(form) { + var form = $(form); + var elements = new Array(); + + for (tagName in Form.Element.Serializers) { + var tagElements = form.getElementsByTagName(tagName); + for (var j = 0; j < tagElements.length; j++) + elements.push(tagElements[j]); + } + return elements; + }, + + getInputs: function(form, typeName, name) { + var form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) + return inputs; + + var matchingInputs = new Array(); + for (var i = 0; i < inputs.length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || + (name && input.name != name)) + continue; + matchingInputs.push(input); + } + + return matchingInputs; + }, + + disable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.blur(); + element.disabled = 'true'; + } + }, + + enable: function(form) { + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + element.disabled = ''; + } + }, + + focusFirstElement: function(form) { + var form = $(form); + var elements = Form.getElements(form); + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + if (element.type != 'hidden' && !element.disabled) { + Field.activate(element); + break; + } + } + }, + + reset: function(form) { + $(form).reset(); + } +} + +Form.Element = { + serialize: function(element) { + var element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) + return encodeURIComponent(parameter[0]) + '=' + + encodeURIComponent(parameter[1]); + }, + + getValue: function(element) { + var element = $(element); + var method = element.tagName.toLowerCase(); + var parameter = Form.Element.Serializers[method](element); + + if (parameter) + return parameter[1]; + } +} + +Form.Element.Serializers = { + input: function(element) { + switch (element.type.toLowerCase()) { + case 'submit': + case 'hidden': + case 'password': + case 'text': + return Form.Element.Serializers.textarea(element); + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element); + } + return false; + }, + + inputSelector: function(element) { + if (element.checked) + return [element.name, element.value]; + }, + + textarea: function(element) { + return [element.name, element.value]; + }, + + select: function(element) { + var value = ''; + if (element.type == 'select-one') { + var index = element.selectedIndex; + if (index >= 0) + value = element.options[index].value || element.options[index].text; + } else { + value = new Array(); + for (var i = 0; i < element.length; i++) { + var opt = element.options[i]; + if (opt.selected) + value.push(opt.value || opt.text); + } + } + return [element.name, value]; + } +} + +/*--------------------------------------------------------------------------*/ + +var $F = Form.Element.getValue; + +/*--------------------------------------------------------------------------*/ + +Abstract.TimedObserver = function() {} +Abstract.TimedObserver.prototype = { + initialize: function(element, frequency, callback) { + this.frequency = frequency; + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + } +} + +Form.Element.Observer = Class.create(); +Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({ + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(); +Form.Observer.prototype = (new Abstract.TimedObserver()).extend({ + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = function() {} +Abstract.EventObserver.prototype = { + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + var elements = Form.getElements(this.element); + for (var i = 0; i < elements.length; i++) + this.registerCallback(elements[i]); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + element.target = this; + element.prev_onclick = element.onclick || Prototype.emptyFunction; + element.onclick = function() { + this.prev_onclick(); + this.target.onElementEvent(); + } + break; + case 'password': + case 'text': + case 'textarea': + case 'select-one': + case 'select-multiple': + element.target = this; + element.prev_onchange = element.onchange || Prototype.emptyFunction; + element.onchange = function() { + this.prev_onchange(); + this.target.onElementEvent(); + } + break; + } + } + } +} + +Form.Element.EventObserver = Class.create(); +Form.Element.EventObserver.prototype = (new Abstract.EventObserver()).extend({ + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(); +Form.EventObserver.prototype = (new Abstract.EventObserver()).extend({ + getValue: function() { + return Form.serialize(this.element); + } +}); + + +if (!window.Event) { + var Event = new Object(); +} + +Object.extend(Event, { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + + element: function(event) { + return event.target || event.srcElement; + }, + + isLeftClick: function(event) { + return (((event.which) && (event.which == 1)) || + ((event.button) && (event.button == 1))); + }, + + pointerX: function(event) { + return event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)); + }, + + pointerY: function(event) { + return event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)); + }, + + stop: function(event) { + if (event.preventDefault) { + event.preventDefault(); + event.stopPropagation(); + } else { + event.returnValue = false; + } + }, + + // find the first node with the given tagName, starting from the + // node the event was triggered on; traverses the DOM upwards + findElement: function(event, tagName) { + var element = Event.element(event); + while (element.parentNode && (!element.tagName || + (element.tagName.toUpperCase() != tagName.toUpperCase()))) + element = element.parentNode; + return element; + }, + + observers: false, + + _observeAndCache: function(element, name, observer, useCapture) { + if (!this.observers) this.observers = []; + if (element.addEventListener) { + this.observers.push([element, name, observer, useCapture]); + element.addEventListener(name, observer, useCapture); + } else if (element.attachEvent) { + this.observers.push([element, name, observer, useCapture]); + element.attachEvent('on' + name, observer); + } + }, + + unloadCache: function() { + if (!Event.observers) return; + for (var i = 0; i < Event.observers.length; i++) { + Event.stopObserving.apply(this, Event.observers[i]); + Event.observers[i][0] = null; + } + Event.observers = false; + }, + + observe: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + ((navigator.appVersion.indexOf('AppleWebKit') > 0) + || element.attachEvent)) + name = 'keydown'; + + this._observeAndCache(element, name, observer, useCapture); + }, + + stopObserving: function(element, name, observer, useCapture) { + var element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + ((navigator.appVersion.indexOf('AppleWebKit') > 0) + || element.detachEvent)) + name = 'keydown'; + + if (element.removeEventListener) { + element.removeEventListener(name, observer, useCapture); + } else if (element.detachEvent) { + element.detachEvent('on' + name, observer); + } + } +}); + +/* prevent memory leaks in IE */ +Event.observe(window, 'unload', Event.unloadCache, false); + +var Position = { + + // set to true if needed, warning: firefox performance problems + // NOT neeeded for page scrolling, only if draggable contained in + // scrollable elements + includeScrollOffsets: false, + + // must be called before calling withinIncludingScrolloffset, every time the + // page is scrolled + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + realOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return [valueL, valueT]; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return [valueL, valueT]; + }, + + // caches x/y coordinate pair to use with overlap + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = this.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = this.realOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = this.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + // within must be called directly before + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + clone: function(source, target) { + source = $(source); + target = $(target); + target.style.position = 'absolute'; + var offsets = this.cumulativeOffset(source); + target.style.top = offsets[1] + 'px'; + target.style.left = offsets[0] + 'px'; + target.style.width = source.offsetWidth + 'px'; + target.style.height = source.offsetHeight + 'px'; + } +} diff --git a/activemq-web/src/webapp/js/rico.js b/activemq-web/src/webapp/js/rico.js new file mode 100644 index 0000000000..4bdc95b437 --- /dev/null +++ b/activemq-web/src/webapp/js/rico.js @@ -0,0 +1,2667 @@ +/** + * + * Copyright 2005 Sabre Airline Solutions + * + * Licensed 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. + **/ + + +//-------------------- rico.js +var Rico = { + Version: '1.1-beta2' +} + +Rico.ArrayExtensions = new Array(); + +if (Object.prototype.extend) { + // in prototype.js... + Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend; +} + +if (Array.prototype.push) { + // in prototype.js... + Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.push; +} + +if (!Array.prototype.remove) { + Array.prototype.remove = function(dx) { + if( isNaN(dx) || dx > this.length ) + return false; + for( var i=0,n=0; i 1 ) + queryString = this._createQueryString(arguments, 1); + + new Ajax.Request(requestURL, this._requestOptions(queryString)); + }, + + sendRequestWithData: function(requestName, xmlDocument) { + var requestURL = this.requestURLS[requestName]; + if ( requestURL == null ) + return; + + var queryString = ""; + if ( arguments.length > 2 ) + queryString = this._createQueryString(arguments, 2); + + new Ajax.Request(requestURL + (requestURL.indexOf("?")<0?"?":"&") + queryString, this._requestOptions(null,xmlDocument)); + }, + + sendRequestAndUpdate: function(requestName,container,options) { + var requestURL = this.requestURLS[requestName]; + if ( requestURL == null ) + return; + + var queryString = ""; + if ( arguments.length > 3 ) + queryString = this._createQueryString(arguments, 3); + + var updaterOptions = this._requestOptions(queryString); + updaterOptions.onComplete = null; + updaterOptions.extend(options); + + new Ajax.Updater(container, requestURL, updaterOptions); + }, + + sendRequestWithDataAndUpdate: function(requestName,xmlDocument,container,options) { + var requestURL = this.requestURLS[requestName]; + if ( requestURL == null ) + return; + + var queryString = ""; + if ( arguments.length > 4 ) + queryString = this._createQueryString(arguments, 4); + + + var updaterOptions = this._requestOptions(queryString,xmlDocument); + updaterOptions.onComplete = null; + updaterOptions.extend(options); + + new Ajax.Updater(container, requestURL + (requestURL.indexOf("?")<0?"?":"&") + queryString, updaterOptions); + }, + + // Private -- not part of intended engine API -------------------------------------------------------------------- + + _requestOptions: function(queryString,xmlDoc) { + var self = this; + + var requestHeaders = ['X-Rico-Version', Rico.Version ]; + var sendMethod = "post" + if ( arguments[1] ) + requestHeaders.push( 'Content-type', 'text/xml' ); + else + sendMethod = "get"; + + return { requestHeaders: requestHeaders, + parameters: queryString, + postBody: arguments[1] ? xmlDoc : null, + method: sendMethod, + onComplete: self._onRequestComplete.bind(self) }; + }, + + _createQueryString: function( theArgs, offset ) { + var queryString = "" + for ( var i = offset ; i < theArgs.length ; i++ ) { + if ( i != offset ) + queryString += "&"; + + var anArg = theArgs[i]; + + if ( anArg.name != undefined && anArg.value != undefined ) { + queryString += anArg.name + "=" + escape(anArg.value); + } + else { + var ePos = anArg.indexOf('='); + var argName = anArg.substring( 0, ePos ); + var argValue = anArg.substring( ePos + 1 ); + queryString += argName + "=" + escape(argValue); + } + } + + return queryString; + }, + + _onRequestComplete : function(request) { + + //!!TODO: error handling infrastructure?? + if (request.status != 200) + return; + + var response = request.responseXML.getElementsByTagName("ajax-response"); + + if (response == null || response.length != 1) + return; + this._processAjaxResponse( response[0].childNodes ); + }, + + _processAjaxResponse: function( xmlResponseElements ) { + for ( var i = 0 ; i < xmlResponseElements.length ; i++ ) { + var responseElement = xmlResponseElements[i]; + + // only process nodes of type element..... + if ( responseElement.nodeType != 1 ) + continue; + + var responseType = responseElement.getAttribute("type"); + var responseId = responseElement.getAttribute("id"); + + if ( responseType == "object" ) + this._processAjaxObjectUpdate( this.ajaxObjects[ responseId ], responseElement ); + else if ( responseType == "element" ) + this._processAjaxElementUpdate( this.ajaxElements[ responseId ], responseElement ); + else + alert('unrecognized AjaxResponse type : ' + responseType ); + } + }, + + _processAjaxObjectUpdate: function( ajaxObject, responseElement ) { + ajaxObject.ajaxUpdate( responseElement ); + }, + + _processAjaxElementUpdate: function( ajaxElement, responseElement ) { + ajaxElement.innerHTML = RicoUtil.getContentAsString(responseElement); + } + +} + +var ajaxEngine = new Rico.AjaxEngine(); + + +//-------------------- ricoColor.js +Rico.Color = Class.create(); + +Rico.Color.prototype = { + + initialize: function(red, green, blue) { + this.rgb = { r: red, g : green, b : blue }; + }, + + setRed: function(r) { + this.rgb.r = r; + }, + + setGreen: function(g) { + this.rgb.g = g; + }, + + setBlue: function(b) { + this.rgb.b = b; + }, + + setHue: function(h) { + + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.h = h; + + // convert back to RGB... + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b); + }, + + setSaturation: function(s) { + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.s = s; + + // convert back to RGB and set values... + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b); + }, + + setBrightness: function(b) { + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.b = b; + + // convert back to RGB and set values... + this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b ); + }, + + darken: function(percent) { + var hsb = this.asHSB(); + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0)); + }, + + brighten: function(percent) { + var hsb = this.asHSB(); + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1)); + }, + + blend: function(other) { + this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2); + this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2); + this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2); + }, + + isBright: function() { + var hsb = this.asHSB(); + return this.asHSB().b > 0.5; + }, + + isDark: function() { + return ! this.isBright(); + }, + + asRGB: function() { + return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")"; + }, + + asHex: function() { + return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart(); + }, + + asHSB: function() { + return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b); + }, + + toString: function() { + return this.asHex(); + } + +}; + +Rico.Color.createFromHex = function(hexCode) { + + if ( hexCode.indexOf('#') == 0 ) + hexCode = hexCode.substring(1); + var red = hexCode.substring(0,2); + var green = hexCode.substring(2,4); + var blue = hexCode.substring(4,6); + return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) ); +} + +/** + * Factory method for creating a color from the background of + * an HTML element. + */ +Rico.Color.createColorFromBackground = function(elem) { + + var actualColor = RicoUtil.getElementsComputedStyle($(elem), "backgroundColor", "background-color"); + + if ( actualColor == "transparent" && elem.parent ) + return Rico.Color.createColorFromBackground(elem.parent); + + if ( actualColor == null ) + return new Rico.Color(255,255,255); + + if ( actualColor.indexOf("rgb(") == 0 ) { + var colors = actualColor.substring(4, actualColor.length - 1 ); + var colorArray = colors.split(","); + return new Rico.Color( parseInt( colorArray[0] ), + parseInt( colorArray[1] ), + parseInt( colorArray[2] ) ); + + } + else if ( actualColor.indexOf("#") == 0 ) { + var redPart = parseInt(actualColor.substring(1,3), 16); + var greenPart = parseInt(actualColor.substring(3,5), 16); + var bluePart = parseInt(actualColor.substring(5), 16); + return new Rico.Color( redPart, greenPart, bluePart ); + } + else + return new Rico.Color(255,255,255); +} + +Rico.Color.HSBtoRGB = function(hue, saturation, brightness) { + + var red = 0; + var green = 0; + var blue = 0; + + if (saturation == 0) { + red = parseInt(brightness * 255.0 + 0.5); + green = red; + blue = red; + } + else { + var h = (hue - Math.floor(hue)) * 6.0; + var f = h - Math.floor(h); + var p = brightness * (1.0 - saturation); + var q = brightness * (1.0 - saturation * f); + var t = brightness * (1.0 - (saturation * (1.0 - f))); + + switch (parseInt(h)) { + case 0: + red = (brightness * 255.0 + 0.5); + green = (t * 255.0 + 0.5); + blue = (p * 255.0 + 0.5); + break; + case 1: + red = (q * 255.0 + 0.5); + green = (brightness * 255.0 + 0.5); + blue = (p * 255.0 + 0.5); + break; + case 2: + red = (p * 255.0 + 0.5); + green = (brightness * 255.0 + 0.5); + blue = (t * 255.0 + 0.5); + break; + case 3: + red = (p * 255.0 + 0.5); + green = (q * 255.0 + 0.5); + blue = (brightness * 255.0 + 0.5); + break; + case 4: + red = (t * 255.0 + 0.5); + green = (p * 255.0 + 0.5); + blue = (brightness * 255.0 + 0.5); + break; + case 5: + red = (brightness * 255.0 + 0.5); + green = (p * 255.0 + 0.5); + blue = (q * 255.0 + 0.5); + break; + } + } + + return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) }; +} + +Rico.Color.RGBtoHSB = function(r, g, b) { + + var hue; + var saturaton; + var brightness; + + var cmax = (r > g) ? r : g; + if (b > cmax) + cmax = b; + + var cmin = (r < g) ? r : g; + if (b < cmin) + cmin = b; + + brightness = cmax / 255.0; + if (cmax != 0) + saturation = (cmax - cmin)/cmax; + else + saturation = 0; + + if (saturation == 0) + hue = 0; + else { + var redc = (cmax - r)/(cmax - cmin); + var greenc = (cmax - g)/(cmax - cmin); + var bluec = (cmax - b)/(cmax - cmin); + + if (r == cmax) + hue = bluec - greenc; + else if (g == cmax) + hue = 2.0 + redc - bluec; + else + hue = 4.0 + greenc - redc; + + hue = hue / 6.0; + if (hue < 0) + hue = hue + 1.0; + } + + return { h : hue, s : saturation, b : brightness }; +} + + +//-------------------- ricoCorner.js + +Rico.Corner = { + + round: function(e, options) { + var e = $(e); + this._setOptions(options); + + var color = this.options.color; + if ( this.options.color == "fromElement" ) + color = this._background(e); + + var bgColor = this.options.bgColor; + if ( this.options.bgColor == "fromParent" ) + bgColor = this._background(e.offsetParent); + + this._roundCornersImpl(e, color, bgColor); + }, + + _roundCornersImpl: function(e, color, bgColor) { + if(this.options.border) + this._renderBorder(e,bgColor); + if(this._isTopRounded()) + this._roundTopCorners(e,color,bgColor); + if(this._isBottomRounded()) + this._roundBottomCorners(e,color,bgColor); + }, + + _renderBorder: function(el,bgColor) { + var borderValue = "1px solid " + this._borderColor(bgColor); + var borderL = "border-left: " + borderValue; + var borderR = "border-right: " + borderValue; + var style = "style='" + borderL + ";" + borderR + "'"; + el.innerHTML = "
" + el.innerHTML + "
" + }, + + _roundTopCorners: function(el, color, bgColor) { + var corner = this._createCorner(bgColor); + for(var i=0 ; i < this.options.numSlices ; i++ ) + corner.appendChild(this._createCornerSlice(color,bgColor,i,"top")); + el.style.paddingTop = 0; + el.insertBefore(corner,el.firstChild); + }, + + _roundBottomCorners: function(el, color, bgColor) { + var corner = this._createCorner(bgColor); + for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- ) + corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom")); + el.style.paddingBottom = 0; + el.appendChild(corner); + }, + + _createCorner: function(bgColor) { + var corner = document.createElement("div"); + corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor); + return corner; + }, + + _createCornerSlice: function(color,bgColor, n, position) { + var slice = document.createElement("span"); + + var inStyle = slice.style; + inStyle.backgroundColor = color; + inStyle.display = "block"; + inStyle.height = "1px"; + inStyle.overflow = "hidden"; + inStyle.fontSize = "1px"; + + var borderColor = this._borderColor(color,bgColor); + if ( this.options.border && n == 0 ) { + inStyle.borderTopStyle = "solid"; + inStyle.borderTopWidth = "1px"; + inStyle.borderLeftWidth = "0px"; + inStyle.borderRightWidth = "0px"; + inStyle.borderBottomWidth = "0px"; + inStyle.height = "0px"; // assumes css compliant box model + inStyle.borderColor = borderColor; + } + else if(borderColor) { + inStyle.borderColor = borderColor; + inStyle.borderStyle = "solid"; + inStyle.borderWidth = "0px 1px"; + } + + if ( !this.options.compact && (n == (this.options.numSlices-1)) ) + inStyle.height = "2px"; + + this._setMargin(slice, n, position); + this._setBorder(slice, n, position); + + return slice; + }, + + _setOptions: function(options) { + this.options = { + corners : "all", + color : "fromElement", + bgColor : "fromParent", + blend : true, + border : false, + compact : false + }.extend(options || {}); + + this.options.numSlices = this.options.compact ? 2 : 4; + if ( this._isTransparent() ) + this.options.blend = false; + }, + + _whichSideTop: function() { + if ( this._hasString(this.options.corners, "all", "top") ) + return ""; + + if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 ) + return ""; + + if (this.options.corners.indexOf("tl") >= 0) + return "left"; + else if (this.options.corners.indexOf("tr") >= 0) + return "right"; + return ""; + }, + + _whichSideBottom: function() { + if ( this._hasString(this.options.corners, "all", "bottom") ) + return ""; + + if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 ) + return ""; + + if(this.options.corners.indexOf("bl") >=0) + return "left"; + else if(this.options.corners.indexOf("br")>=0) + return "right"; + return ""; + }, + + _borderColor : function(color,bgColor) { + if ( color == "transparent" ) + return bgColor; + else if ( this.options.border ) + return this.options.border; + else if ( this.options.blend ) + return this._blend( bgColor, color ); + else + return ""; + }, + + + _setMargin: function(el, n, corners) { + var marginSize = this._marginSize(n); + var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom(); + + if ( whichSide == "left" ) { + el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px"; + } + else if ( whichSide == "right" ) { + el.style.marginRight = marginSize + "px"; el.style.marginLeft = "0px"; + } + else { + el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px"; + } + }, + + _setBorder: function(el,n,corners) { + var borderSize = this._borderSize(n); + var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom(); + + if ( whichSide == "left" ) { + el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px"; + } + else if ( whichSide == "right" ) { + el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth = "0px"; + } + else { + el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px"; + } + }, + + _marginSize: function(n) { + if ( this._isTransparent() ) + return 0; + + var marginSizes = [ 5, 3, 2, 1 ]; + var blendedMarginSizes = [ 3, 2, 1, 0 ]; + var compactMarginSizes = [ 2, 1 ]; + var smBlendedMarginSizes = [ 1, 0 ]; + + if ( this.options.compact && this.options.blend ) + return smBlendedMarginSizes[n]; + else if ( this.options.compact ) + return compactMarginSizes[n]; + else if ( this.options.blend ) + return blendedMarginSizes[n]; + else + return marginSizes[n]; + }, + + _borderSize: function(n) { + var transparentBorderSizes = [ 5, 3, 2, 1 ]; + var blendedBorderSizes = [ 2, 1, 1, 1 ]; + var compactBorderSizes = [ 1, 0 ]; + var actualBorderSizes = [ 0, 2, 0, 0 ]; + + if ( this.options.compact && (this.options.blend || this._isTransparent()) ) + return 1; + else if ( this.options.compact ) + return compactBorderSizes[n]; + else if ( this.options.blend ) + return blendedBorderSizes[n]; + else if ( this.options.border ) + return actualBorderSizes[n]; + else if ( this._isTransparent() ) + return transparentBorderSizes[n]; + return 0; + }, + + _hasString: function(str) { for(var i=1 ; i= 0) return true; return false; }, + _blend: function(c1, c2) { var cc1 = Rico.Color.createFromHex(c1); cc1.blend(Rico.Color.createFromHex(c2)); return cc1; }, + _background: function(el) { try { return Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } }, + _isTransparent: function() { return this.options.color == "transparent"; }, + _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); }, + _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); }, + _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; } +} + + +//-------------------- ricoDragAndDrop.js +Rico.DragAndDrop = Class.create(); + +Rico.DragAndDrop.prototype = { + + initialize: function() { + this.dropZones = new Array(); + this.draggables = new Array(); + this.currentDragObjects = new Array(); + this.dragElement = null; + this.lastSelectedDraggable = null; + this.currentDragObjectVisible = false; + this.interestedInMotionEvents = false; + }, + + registerDropZone: function(aDropZone) { + this.dropZones[ this.dropZones.length ] = aDropZone; + }, + + deregisterDropZone: function(aDropZone) { + var newDropZones = new Array(); + var j = 0; + for ( var i = 0 ; i < this.dropZones.length ; i++ ) { + if ( this.dropZones[i] != aDropZone ) + newDropZones[j++] = this.dropZones[i]; + } + + this.dropZones = newDropZones; + }, + + clearDropZones: function() { + this.dropZones = new Array(); + }, + + registerDraggable: function( aDraggable ) { + this.draggables[ this.draggables.length ] = aDraggable; + this._addMouseDownHandler( aDraggable ); + }, + + clearSelection: function() { + for ( var i = 0 ; i < this.currentDragObjects.length ; i++ ) + this.currentDragObjects[i].deselect(); + this.currentDragObjects = new Array(); + this.lastSelectedDraggable = null; + }, + + hasSelection: function() { + return this.currentDragObjects.length > 0; + }, + + setStartDragFromElement: function( e, mouseDownElement ) { + this.origPos = RicoUtil.toDocumentPosition(mouseDownElement); + this.startx = e.screenX - this.origPos.x + this.starty = e.screenY - this.origPos.y + //this.startComponentX = e.layerX ? e.layerX : e.offsetX; + //this.startComponentY = e.layerY ? e.layerY : e.offsetY; + //this.adjustedForDraggableSize = false; + + this.interestedInMotionEvents = this.hasSelection(); + this._terminateEvent(e); + }, + + updateSelection: function( draggable, extendSelection ) { + if ( ! extendSelection ) + this.clearSelection(); + + if ( draggable.isSelected() ) { + this.currentDragObjects.removeItem(draggable); + draggable.deselect(); + if ( draggable == this.lastSelectedDraggable ) + this.lastSelectedDraggable = null; + } + else { + this.currentDragObjects[ this.currentDragObjects.length ] = draggable; + draggable.select(); + this.lastSelectedDraggable = draggable; + } + }, + + _mouseDownHandler: function(e) { + if ( arguments.length == 0 ) + e = event; + + // if not button 1 ignore it... + var nsEvent = e.which != undefined; + if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1)) + return; + + var eventTarget = e.target ? e.target : e.srcElement; + var draggableObject = eventTarget.draggable; + + var candidate = eventTarget; + while (draggableObject == null && candidate.parentNode) { + candidate = candidate.parentNode; + draggableObject = candidate.draggable; + } + + if ( draggableObject == null ) + return; + + this.updateSelection( draggableObject, e.ctrlKey ); + + // clear the drop zones postion cache... + if ( this.hasSelection() ) + for ( var i = 0 ; i < this.dropZones.length ; i++ ) + this.dropZones[i].clearPositionCache(); + + this.setStartDragFromElement( e, draggableObject.getMouseDownHTMLElement() ); + }, + + + _mouseMoveHandler: function(e) { + var nsEvent = e.which != undefined; + if ( !this.interestedInMotionEvents ) { + this._terminateEvent(e); + return; + } + + if ( ! this.hasSelection() ) + return; + + if ( ! this.currentDragObjectVisible ) + this._startDrag(e); + + if ( !this.activatedDropZones ) + this._activateRegisteredDropZones(); + + //if ( !this.adjustedForDraggableSize ) + // this._adjustForDraggableSize(e); + + this._updateDraggableLocation(e); + this._updateDropZonesHover(e); + + this._terminateEvent(e); + }, + + _makeDraggableObjectVisible: function(e) + { + if ( !this.hasSelection() ) + return; + + var dragElement; + if ( this.currentDragObjects.length > 1 ) + dragElement = this.currentDragObjects[0].getMultiObjectDragGUI(this.currentDragObjects); + else + dragElement = this.currentDragObjects[0].getSingleObjectDragGUI(); + + // go ahead and absolute position it... + if ( RicoUtil.getElementsComputedStyle(dragElement, "position") != "absolute" ) + dragElement.style.position = "absolute"; + + // need to parent him into the document... + if ( dragElement.parentNode == null || dragElement.parentNode.nodeType == 11 ) + document.body.appendChild(dragElement); + + this.dragElement = dragElement; + this._updateDraggableLocation(e); + + this.currentDragObjectVisible = true; + }, + + /** + _adjustForDraggableSize: function(e) { + var dragElementWidth = this.dragElement.offsetWidth; + var dragElementHeight = this.dragElement.offsetHeight; + if ( this.startComponentX > dragElementWidth ) + this.startx -= this.startComponentX - dragElementWidth + 2; + if ( e.offsetY ) { + if ( this.startComponentY > dragElementHeight ) + this.starty -= this.startComponentY - dragElementHeight + 2; + } + this.adjustedForDraggableSize = true; + }, + **/ + + _updateDraggableLocation: function(e) { + var dragObjectStyle = this.dragElement.style; + dragObjectStyle.left = (e.screenX - this.startx) + "px" + dragObjectStyle.top = (e.screenY - this.starty) + "px"; + }, + + _updateDropZonesHover: function(e) { + var n = this.dropZones.length; + for ( var i = 0 ; i < n ; i++ ) { + if ( ! this._mousePointInDropZone( e, this.dropZones[i] ) ) + this.dropZones[i].hideHover(); + } + + for ( var i = 0 ; i < n ; i++ ) { + if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) { + if ( this.dropZones[i].canAccept(this.currentDragObjects) ) + this.dropZones[i].showHover(); + } + } + }, + + _startDrag: function(e) { + for ( var i = 0 ; i < this.currentDragObjects.length ; i++ ) + this.currentDragObjects[i].startDrag(); + + this._makeDraggableObjectVisible(e); + }, + + _mouseUpHandler: function(e) { + if ( ! this.hasSelection() ) + return; + + var nsEvent = e.which != undefined; + if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1)) + return; + + this.interestedInMotionEvents = false; + + if ( this.dragElement == null ) { + this._terminateEvent(e); + return; + } + + if ( this._placeDraggableInDropZone(e) ) + this._completeDropOperation(e); + else { + this._terminateEvent(e); + new Effect.Position( this.dragElement, + this.origPos.x, + this.origPos.y, + 200, + 20, + { complete : this._doCancelDragProcessing.bind(this) } ); + } + }, + + _completeDropOperation: function(e) { + if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) { + if ( this.dragElement.parentNode != null ) + this.dragElement.parentNode.removeChild(this.dragElement); + } + + this._deactivateRegisteredDropZones(); + this._endDrag(); + this.clearSelection(); + this.dragElement = null; + this.currentDragObjectVisible = false; + this._terminateEvent(e); + }, + + _doCancelDragProcessing: function() { + this._cancelDrag(); + + if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) { + if ( this.dragElement.parentNode != null ) { + this.dragElement.parentNode.removeChild(this.dragElement); + } + } + + this._deactivateRegisteredDropZones(); + this.dragElement = null; + this.currentDragObjectVisible = false; + }, + + _placeDraggableInDropZone: function(e) { + var foundDropZone = false; + var n = this.dropZones.length; + for ( var i = 0 ; i < n ; i++ ) { + if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) { + if ( this.dropZones[i].canAccept(this.currentDragObjects) ) { + this.dropZones[i].hideHover(); + this.dropZones[i].accept(this.currentDragObjects); + foundDropZone = true; + break; + } + } + } + + return foundDropZone; + }, + + _cancelDrag: function() { + for ( var i = 0 ; i < this.currentDragObjects.length ; i++ ) + this.currentDragObjects[i].cancelDrag(); + }, + + _endDrag: function() { + for ( var i = 0 ; i < this.currentDragObjects.length ; i++ ) + this.currentDragObjects[i].endDrag(); + }, + + _mousePointInDropZone: function( e, dropZone ) { + + var absoluteRect = dropZone.getAbsoluteRect(); + + return e.clientX > absoluteRect.left && + e.clientX < absoluteRect.right && + e.clientY > absoluteRect.top && + e.clientY < absoluteRect.bottom; + }, + + _addMouseDownHandler: function( aDraggable ) + { + var htmlElement = aDraggable.getMouseDownHTMLElement(); + if ( htmlElement != null ) { + htmlElement.draggable = aDraggable; + this._addMouseDownEvent( htmlElement ); + } + }, + + _activateRegisteredDropZones: function() { + var n = this.dropZones.length; + for ( var i = 0 ; i < n ; i++ ) { + var dropZone = this.dropZones[i]; + if ( dropZone.canAccept(this.currentDragObjects) ) + dropZone.activate(); + } + + this.activatedDropZones = true; + }, + + _deactivateRegisteredDropZones: function() { + var n = this.dropZones.length; + for ( var i = 0 ; i < n ; i++ ) + this.dropZones[i].deactivate(); + this.activatedDropZones = false; + }, + + _addMouseDownEvent: function( htmlElement ) { + if ( typeof document.implementation != "undefined" && + document.implementation.hasFeature("HTML", "1.0") && + document.implementation.hasFeature("Events", "2.0") && + document.implementation.hasFeature("CSS", "2.0") ) { + htmlElement.addEventListener("mousedown", this._mouseDownHandler.bindAsEventListener(this), false); + } + else { + htmlElement.attachEvent( "onmousedown", this._mouseDownHandler.bindAsEventListener(this) ); + } + }, + + _terminateEvent: function(e) { + if ( e.stopPropagation != undefined ) + e.stopPropagation(); + else if ( e.cancelBubble != undefined ) + e.cancelBubble = true; + + if ( e.preventDefault != undefined ) + e.preventDefault(); + else + e.returnValue = false; + }, + + initializeEventHandlers: function() { + if ( typeof document.implementation != "undefined" && + document.implementation.hasFeature("HTML", "1.0") && + document.implementation.hasFeature("Events", "2.0") && + document.implementation.hasFeature("CSS", "2.0") ) { + document.addEventListener("mouseup", this._mouseUpHandler.bindAsEventListener(this), false); + document.addEventListener("mousemove", this._mouseMoveHandler.bindAsEventListener(this), false); + } + else { + document.attachEvent( "onmouseup", this._mouseUpHandler.bindAsEventListener(this) ); + document.attachEvent( "onmousemove", this._mouseMoveHandler.bindAsEventListener(this) ); + } + } +} + +var dndMgr = new Rico.DragAndDrop(); +dndMgr.initializeEventHandlers(); + + +//-------------------- ricoDraggable.js +Rico.Draggable = Class.create(); + +Rico.Draggable.prototype = { + + initialize: function( type, htmlElement ) { + this.type = type; + this.htmlElement = $(htmlElement); + this.selected = false; + }, + + /** + * Returns the HTML element that should have a mouse down event + * added to it in order to initiate a drag operation + * + **/ + getMouseDownHTMLElement: function() { + return this.htmlElement; + }, + + select: function() { + this.selected = true; + + if ( this.showingSelected ) + return; + + var htmlElement = this.getMouseDownHTMLElement(); + + var color = Rico.Color.createColorFromBackground(htmlElement); + color.isBright() ? color.darken(0.033) : color.brighten(0.033); + + this.saveBackground = RicoUtil.getElementsComputedStyle(htmlElement, "backgroundColor", "background-color"); + htmlElement.style.backgroundColor = color.asHex(); + this.showingSelected = true; + }, + + deselect: function() { + this.selected = false; + if ( !this.showingSelected ) + return; + + var htmlElement = this.getMouseDownHTMLElement(); + + htmlElement.style.backgroundColor = this.saveBackground; + this.showingSelected = false; + }, + + isSelected: function() { + return this.selected; + }, + + startDrag: function() { + }, + + cancelDrag: function() { + }, + + endDrag: function() { + }, + + getSingleObjectDragGUI: function() { + return this.htmlElement; + }, + + getMultiObjectDragGUI: function( draggables ) { + return this.htmlElement; + }, + + getDroppedGUI: function() { + return this.htmlElement; + }, + + toString: function() { + return this.type + ":" + this.htmlElement + ":"; + } + +} + + +//-------------------- ricoDropzone.js +Rico.Dropzone = Class.create(); + +Rico.Dropzone.prototype = { + + initialize: function( htmlElement ) { + this.htmlElement = $(htmlElement); + this.absoluteRect = null; + }, + + getHTMLElement: function() { + return this.htmlElement; + }, + + clearPositionCache: function() { + this.absoluteRect = null; + }, + + getAbsoluteRect: function() { + if ( this.absoluteRect == null ) { + var htmlElement = this.getHTMLElement(); + var pos = RicoUtil.toViewportPosition(htmlElement); + + this.absoluteRect = { + top: pos.y, + left: pos.x, + bottom: pos.y + htmlElement.offsetHeight, + right: pos.x + htmlElement.offsetWidth + }; + } + return this.absoluteRect; + }, + + activate: function() { + var htmlElement = this.getHTMLElement(); + if (htmlElement == null || this.showingActive) + return; + + this.showingActive = true; + this.saveBackgroundColor = htmlElement.style.backgroundColor; + + var fallbackColor = "#ffea84"; + var currentColor = Rico.Color.createColorFromBackground(htmlElement); + if ( currentColor == null ) + htmlElement.style.backgroundColor = fallbackColor; + else { + currentColor.isBright() ? currentColor.darken(0.2) : currentColor.brighten(0.2); + htmlElement.style.backgroundColor = currentColor.asHex(); + } + }, + + deactivate: function() { + var htmlElement = this.getHTMLElement(); + if (htmlElement == null || !this.showingActive) + return; + + htmlElement.style.backgroundColor = this.saveBackgroundColor; + this.showingActive = false; + this.saveBackgroundColor = null; + }, + + showHover: function() { + var htmlElement = this.getHTMLElement(); + if ( htmlElement == null || this.showingHover ) + return; + + this.saveBorderWidth = htmlElement.style.borderWidth; + this.saveBorderStyle = htmlElement.style.borderStyle; + this.saveBorderColor = htmlElement.style.borderColor; + + this.showingHover = true; + htmlElement.style.borderWidth = "1px"; + htmlElement.style.borderStyle = "solid"; + //htmlElement.style.borderColor = "#ff9900"; + htmlElement.style.borderColor = "#ffff00"; + }, + + hideHover: function() { + var htmlElement = this.getHTMLElement(); + if ( htmlElement == null || !this.showingHover ) + return; + + htmlElement.style.borderWidth = this.saveBorderWidth; + htmlElement.style.borderStyle = this.saveBorderStyle; + htmlElement.style.borderColor = this.saveBorderColor; + this.showingHover = false; + }, + + canAccept: function(draggableObjects) { + return true; + }, + + accept: function(draggableObjects) { + var htmlElement = this.getHTMLElement(); + if ( htmlElement == null ) + return; + + n = draggableObjects.length; + for ( var i = 0 ; i < n ; i++ ) + { + var theGUI = draggableObjects[i].getDroppedGUI(); + if ( RicoUtil.getElementsComputedStyle( theGUI, "position" ) == "absolute" ) + { + theGUI.style.position = "static"; + theGUI.style.top = ""; + theGUI.style.top = ""; + } + htmlElement.appendChild(theGUI); + } + } +} + + +//-------------------- ricoEffects.js + +/** + * Use the Effect namespace for effects. If using scriptaculous effects + * this will already be defined, otherwise we'll just create an empty + * object for it... + **/ +if ( window.Effect == undefined ) + Effect = {}; + +Effect.SizeAndPosition = Class.create(); +Effect.SizeAndPosition.prototype = { + + initialize: function(element, x, y, w, h, duration, steps, options) { + this.element = $(element); + this.x = x; + this.y = y; + this.w = w; + this.h = h; + this.duration = duration; + this.steps = steps; + this.options = arguments[7] || {}; + + this.sizeAndPosition(); + }, + + sizeAndPosition: function() { + if (this.isFinished()) { + if(this.options.complete) this.options.complete(this); + return; + } + + if (this.timer) + clearTimeout(this.timer); + + var stepDuration = Math.round(this.duration/this.steps) ; + + // Get original values: x,y = top left corner; w,h = width height + var currentX = this.element.offsetLeft; + var currentY = this.element.offsetTop; + var currentW = this.element.offsetWidth; + var currentH = this.element.offsetHeight; + + // If values not set, or zero, we do not modify them, and take original as final as well + this.x = (this.x) ? this.x : currentX; + this.y = (this.y) ? this.y : currentY; + this.w = (this.w) ? this.w : currentW; + this.h = (this.h) ? this.h : currentH; + + // how much do we need to modify our values for each step? + var difX = this.steps > 0 ? (this.x - currentX)/this.steps : 0; + var difY = this.steps > 0 ? (this.y - currentY)/this.steps : 0; + var difW = this.steps > 0 ? (this.w - currentW)/this.steps : 0; + var difH = this.steps > 0 ? (this.h - currentH)/this.steps : 0; + + this.moveBy(difX, difY); + this.resizeBy(difW, difH); + + this.duration -= stepDuration; + this.steps--; + + this.timer = setTimeout(this.sizeAndPosition.bind(this), stepDuration); + }, + + isFinished: function() { + return this.steps <= 0; + }, + + moveBy: function( difX, difY ) { + var currentLeft = this.element.offsetLeft; + var currentTop = this.element.offsetTop; + var intDifX = parseInt(difX); + var intDifY = parseInt(difY); + + var style = this.element.style; + if ( intDifX != 0 ) + style.left = (currentLeft + intDifX) + "px"; + if ( intDifY != 0 ) + style.top = (currentTop + intDifY) + "px"; + }, + + resizeBy: function( difW, difH ) { + var currentWidth = this.element.offsetWidth; + var currentHeight = this.element.offsetHeight; + var intDifW = parseInt(difW); + var intDifH = parseInt(difH); + + var style = this.element.style; + if ( intDifW != 0 ) + style.width = (currentWidth + intDifW) + "px"; + if ( intDifH != 0 ) + style.height = (currentHeight + intDifH) + "px"; + } +} + +Effect.Size = Class.create(); +Effect.Size.prototype = { + + initialize: function(element, w, h, duration, steps, options) { + new Effect.SizeAndPosition(element, null, null, w, h, duration, steps, options); + } +} + +Effect.Position = Class.create(); +Effect.Position.prototype = { + + initialize: function(element, x, y, duration, steps, options) { + new Effect.SizeAndPosition(element, x, y, null, null, duration, steps, options); + } +} + +Effect.Round = Class.create(); +Effect.Round.prototype = { + + initialize: function(tagName, className, options) { + var elements = document.getElementsByTagAndClassName(tagName,className); + for ( var i = 0 ; i < elements.length ; i++ ) + Rico.Corner.round( elements[i], options ); + } +}; + +Effect.FadeTo = Class.create(); +Effect.FadeTo.prototype = { + + initialize: function( element, opacity, duration, steps, options) { + this.element = $(element); + this.opacity = opacity; + this.duration = duration; + this.steps = steps; + this.options = arguments[4] || {}; + this.fadeTo(); + }, + + fadeTo: function() { + if (this.isFinished()) { + if(this.options.complete) this.options.complete(this); + return; + } + + if (this.timer) + clearTimeout(this.timer); + + var stepDuration = Math.round(this.duration/this.steps) ; + var currentOpacity = this.getElementOpacity(); + var delta = this.steps > 0 ? (this.opacity - currentOpacity)/this.steps : 0; + + this.changeOpacityBy(delta); + this.duration -= stepDuration; + this.steps--; + + this.timer = setTimeout(this.fadeTo.bind(this), stepDuration); + }, + + changeOpacityBy: function(v) { + var currentOpacity = this.getElementOpacity(); + var newOpacity = Math.max(0, Math.min(currentOpacity+v, 1)); + this.element.ricoOpacity = newOpacity; + + this.element.style.filter = "alpha(opacity:"+Math.round(newOpacity*100)+")"; + this.element.style.opacity = newOpacity; /*//*/; + }, + + isFinished: function() { + return this.steps <= 0; + }, + + getElementOpacity: function() { + if ( this.element.ricoOpacity == undefined ) { + var opacity; + if ( this.element.currentStyle ) { + opacity = this.element.currentStyle.opacity; + } + else if ( document.defaultView.getComputedStyle != undefined ) { + var computedStyle = document.defaultView.getComputedStyle; + opacity = computedStyle(this.element, null).getPropertyValue('opacity'); + } + + this.element.ricoOpacity = opacity != undefined ? opacity : 1.0; + } + + return parseFloat(this.element.ricoOpacity); + } +} + +Effect.AccordionSize = Class.create(); + +Effect.AccordionSize.prototype = { + + initialize: function(e1, e2, start, end, duration, steps, options) { + this.e1 = $(e1); + this.e2 = $(e2); + this.start = start; + this.end = end; + this.duration = duration; + this.steps = steps; + this.options = arguments[6] || {}; + + this.accordionSize(); + }, + + accordionSize: function() { + + if (this.isFinished()) { + // just in case there are round errors or such... + this.e1.style.height = this.start + "px"; + this.e2.style.height = this.end + "px"; + + if(this.options.complete) + this.options.complete(this); + return; + } + + if (this.timer) + clearTimeout(this.timer); + + var stepDuration = Math.round(this.duration/this.steps) ; + + var diff = this.steps > 0 ? (parseInt(this.e1.offsetHeight) - this.start)/this.steps : 0; + this.resizeBy(diff); + + this.duration -= stepDuration; + this.steps--; + + this.timer = setTimeout(this.accordionSize.bind(this), stepDuration); + }, + + isFinished: function() { + return this.steps <= 0; + }, + + resizeBy: function(diff) { + var h1Height = this.e1.offsetHeight; + var h2Height = this.e2.offsetHeight; + var intDiff = parseInt(diff); + if ( diff != 0 ) { + this.e1.style.height = (h1Height - intDiff) + "px"; + this.e2.style.height = (h2Height + intDiff) + "px"; + } + } + +}; + + +//-------------------- ricoLiveGrid.js + +// Rico.LiveGridMetaData ----------------------------------------------------- + +Rico.LiveGridMetaData = Class.create(); + +Rico.LiveGridMetaData.prototype = { + + initialize: function( pageSize, totalRows, columnCount, options ) { + this.pageSize = pageSize; + this.totalRows = totalRows; + this.setOptions(options); + this.scrollArrowHeight = 16; + this.columnCount = columnCount; + }, + + setOptions: function(options) { + this.options = { + largeBufferSize : 7.0, // 7 pages + nearLimitFactor : 0.2 // 20% of buffer + }.extend(options || {}); + }, + + getPageSize: function() { + return this.pageSize; + }, + + getTotalRows: function() { + return this.totalRows; + }, + + setTotalRows: function(n) { + this.totalRows = n; + }, + + getLargeBufferSize: function() { + return parseInt(this.options.largeBufferSize * this.pageSize); + }, + + getLimitTolerance: function() { + return parseInt(this.getLargeBufferSize() * this.options.nearLimitFactor); + } +}; + +// Rico.LiveGridScroller ----------------------------------------------------- + +Rico.LiveGridScroller = Class.create(); + +Rico.LiveGridScroller.prototype = { + + initialize: function(liveGrid, viewPort) { + this.isIE = navigator.userAgent.toLowerCase().indexOf("msie") >= 0; + this.liveGrid = liveGrid; + this.metaData = liveGrid.metaData; + this.createScrollBar(); + this.scrollTimeout = null; + this.lastScrollPos = 0; + this.viewPort = viewPort; + this.rows = new Array(); + }, + + isUnPlugged: function() { + return this.scrollerDiv.onscroll == null; + }, + + plugin: function() { + this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this); + }, + + unplug: function() { + this.scrollerDiv.onscroll = null; + }, + + sizeIEHeaderHack: function() { + if ( !this.isIE ) return; + var headerTable = $(this.liveGrid.tableId + "_header"); + if ( headerTable ) + headerTable.rows[0].cells[0].style.width = + (headerTable.rows[0].cells[0].offsetWidth + 1) + "px"; + }, + + createScrollBar: function() { + var visibleHeight = this.liveGrid.viewPort.visibleHeight(); + // create the outer div... + this.scrollerDiv = document.createElement("div"); + var scrollerStyle = this.scrollerDiv.style; + scrollerStyle.borderRight = "1px solid #ababab"; // hard coded color!!! + scrollerStyle.position = "relative"; + scrollerStyle.left = this.isIE ? "-6px" : "-3px"; + scrollerStyle.width = "19px"; + scrollerStyle.height = visibleHeight + "px"; + scrollerStyle.overflow = "auto"; + + // create the inner div... + this.heightDiv = document.createElement("div"); + this.heightDiv.style.width = "1px"; + + this.heightDiv.style.height = parseInt(visibleHeight * + this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px" ; + this.scrollerDiv.appendChild(this.heightDiv); + this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this); + + var table = this.liveGrid.table; + table.parentNode.parentNode.insertBefore( this.scrollerDiv, table.parentNode.nextSibling ); + }, + + updateSize: function() { + var table = this.liveGrid.table; + var visibleHeight = this.viewPort.visibleHeight(); + this.heightDiv.style.height = parseInt(visibleHeight * + this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px"; + }, + + rowToPixel: function(rowOffset) { + return (rowOffset / this.metaData.getTotalRows()) * this.heightDiv.offsetHeight + }, + + moveScroll: function(rowOffset) { + this.scrollerDiv.scrollTop = this.rowToPixel(rowOffset); + if ( this.metaData.options.onscroll ) + this.metaData.options.onscroll( this.liveGrid, rowOffset ); + }, + + handleScroll: function() { + if ( this.scrollTimeout ) + clearTimeout( this.scrollTimeout ); + + var contentOffset = parseInt(this.scrollerDiv.scrollTop / this.viewPort.rowHeight); + this.liveGrid.requestContentRefresh(contentOffset); + this.viewPort.scrollTo(this.scrollerDiv.scrollTop); + + if ( this.metaData.options.onscroll ) + this.metaData.options.onscroll( this.liveGrid, contentOffset ); + + this.scrollTimeout = setTimeout( this.scrollIdle.bind(this), 1200 ); + }, + + scrollIdle: function() { + if ( this.metaData.options.onscrollidle ) + this.metaData.options.onscrollidle(); + } +}; + +// Rico.LiveGridBuffer ----------------------------------------------------- + +Rico.LiveGridBuffer = Class.create(); + +Rico.LiveGridBuffer.prototype = { + + initialize: function(metaData, viewPort) { + this.startPos = 0; + this.size = 0; + this.metaData = metaData; + this.rows = new Array(); + this.updateInProgress = false; + this.viewPort = viewPort; + this.maxBufferSize = metaData.getLargeBufferSize() * 2; + this.maxFetchSize = metaData.getLargeBufferSize(); + this.lastOffset = 0; + }, + + getBlankRow: function() { + if (!this.blankRow ) { + this.blankRow = new Array(); + for ( var i=0; i < this.metaData.columnCount ; i++ ) + this.blankRow[i] = " "; + } + return this.blankRow; + }, + + loadRows: function(ajaxResponse) { + var rowsElement = ajaxResponse.getElementsByTagName('rows')[0]; + this.updateUI = rowsElement.getAttribute("update_ui") == "true" + var newRows = new Array() + var trs = rowsElement.getElementsByTagName("tr"); + for ( var i=0 ; i < trs.length; i++ ) { + var row = newRows[i] = new Array(); + var cells = trs[i].getElementsByTagName("td"); + for ( var j=0; j < cells.length ; j++ ) { + var cell = cells[j]; + var convertSpaces = cell.getAttribute("convert_spaces") == "true"; + var cellContent = RicoUtil.getContentAsString(cell); + row[j] = convertSpaces ? this.convertSpaces(cellContent) : cellContent; + if (!row[j]) + row[j] = ' '; + } + } + return newRows; + }, + + update: function(ajaxResponse, start) { + var newRows = this.loadRows(ajaxResponse); + if (this.rows.length == 0) { // initial load + this.rows = newRows; + this.size = this.rows.length; + this.startPos = start; + return; + } + if (start > this.startPos) { //appending + if (this.startPos + this.rows.length < start) { + this.rows = newRows; + this.startPos = start;// + } else { + this.rows = this.rows.concat( newRows.slice(0, newRows.length)); + if (this.rows.length > this.maxBufferSize) { + var fullSize = this.rows.length; + this.rows = this.rows.slice(this.rows.length - this.maxBufferSize, this.rows.length) + this.startPos = this.startPos + (fullSize - this.rows.length); + } + } + } else { //prepending + if (start + newRows.length < this.startPos) { + this.rows = newRows; + } else { + this.rows = newRows.slice(0, this.startPos).concat(this.rows); + if (this.rows.length > this.maxBufferSize) + this.rows = this.rows.slice(0, this.maxBufferSize) + } + this.startPos = start; + } + this.size = this.rows.length; + }, + + clear: function() { + this.rows = new Array(); + this.startPos = 0; + this.size = 0; + }, + + isOverlapping: function(start, size) { + return ((start < this.endPos()) && (this.startPos < start + size)) || (this.endPos() == 0) + }, + + isInRange: function(position) { + return (position >= this.startPos) && (position + this.metaData.getPageSize() <= this.endPos()); + //&& this.size() != 0; + }, + + isNearingTopLimit: function(position) { + return position - this.startPos < this.metaData.getLimitTolerance(); + }, + + endPos: function() { + return this.startPos + this.rows.length; + }, + + isNearingBottomLimit: function(position) { + return this.endPos() - (position + this.metaData.getPageSize()) < this.metaData.getLimitTolerance(); + }, + + isAtTop: function() { + return this.startPos == 0; + }, + + isAtBottom: function() { + return this.endPos() == this.metaData.getTotalRows(); + }, + + isNearingLimit: function(position) { + return ( !this.isAtTop() && this.isNearingTopLimit(position)) || + ( !this.isAtBottom() && this.isNearingBottomLimit(position) ) + }, + + getFetchSize: function(offset) { + var adjustedOffset = this.getFetchOffset(offset); + var adjustedSize = 0; + if (adjustedOffset >= this.startPos) { //apending + var endFetchOffset = this.maxFetchSize + adjustedOffset; + if (endFetchOffset > this.metaData.totalRows) + endFetchOffset = this.metaData.totalRows; + adjustedSize = endFetchOffset - adjustedOffset; + } else {//prepending + var adjustedSize = this.startPos - adjustedOffset; + if (adjustedSize > this.maxFetchSize) + adjustedSize = this.maxFetchSize; + } + return adjustedSize; + }, + + getFetchOffset: function(offset) { + var adjustedOffset = offset; + if (offset > this.startPos) //apending + adjustedOffset = (offset > this.endPos()) ? offset : this.endPos(); + else { //prepending + if (offset + this.maxFetchSize >= this.startPos) { + var adjustedOffset = this.startPos - this.maxFetchSize; + if (adjustedOffset < 0) + adjustedOffset = 0; + } + } + this.lastOffset = adjustedOffset; + return adjustedOffset; + }, + + getRows: function(start, count) { + var begPos = start - this.startPos + var endPos = begPos + count + + // er? need more data... + if ( endPos > this.size ) + endPos = this.size + + var results = new Array() + var index = 0; + for ( var i=begPos ; i < endPos; i++ ) { + results[index++] = this.rows[i] + } + return results + }, + + convertSpaces: function(s) { + return s.split(" ").join(" "); + } + +}; + + +//Rico.GridViewPort -------------------------------------------------- +Rico.GridViewPort = Class.create(); + +Rico.GridViewPort.prototype = { + + initialize: function(table, rowHeight, visibleRows, buffer, liveGrid) { + this.lastDisplayedStartPos = 0; + this.div = table.parentNode; + this.table = table + this.rowHeight = rowHeight; + this.div.style.height = this.rowHeight * visibleRows; + this.div.style.overflow = "hidden"; + this.buffer = buffer; + this.liveGrid = liveGrid; + this.visibleRows = visibleRows + 1; + this.lastPixelOffset = 0; + this.startPos = 0; + }, + + populateRow: function(htmlRow, row) { + for (var j=0; j < row.length; j++) { + htmlRow.cells[j].innerHTML = row[j] + } + }, + + bufferChanged: function() { + this.refreshContents( parseInt(this.lastPixelOffset / this.rowHeight)); + }, + + clearRows: function() { + if (!this.isBlank) { + for (var i=0; i < this.visibleRows; i++) + this.populateRow(this.table.rows[i], this.buffer.getBlankRow()); + this.isBlank = true; + } + }, + + clearContents: function() { + this.clearRows(); + this.scrollTo(0); + this.startPos = 0; + this.lastStartPos = -1; + }, + + refreshContents: function(startPos) { + if (startPos == this.lastRowPos && !this.isPartialBlank && !this.isBlank) { + return; + } + if ((startPos + this.visibleRows < this.buffer.startPos) + || (this.buffer.startPos + this.buffer.size < startPos) + || (this.buffer.size == 0)) { + this.clearRows(); + return; + } + this.isBlank = false; + var viewPrecedesBuffer = this.buffer.startPos > startPos + var contentStartPos = viewPrecedesBuffer ? this.buffer.startPos: startPos; + + var contentEndPos = (this.buffer.startPos + this.buffer.size < startPos + this.visibleRows) + ? this.buffer.startPos + this.buffer.size + : startPos + this.visibleRows; + var rowSize = contentEndPos - contentStartPos; + var rows = this.buffer.getRows(contentStartPos, rowSize ); + var blankSize = this.visibleRows - rowSize; + var blankOffset = viewPrecedesBuffer ? 0: rowSize; + var contentOffset = viewPrecedesBuffer ? blankSize: 0; + + for (var i=0; i < rows.length; i++) {//initialize what we have + this.populateRow(this.table.rows[i + contentOffset], rows[i]); + } + for (var i=0; i < blankSize; i++) {// blank out the rest + this.populateRow(this.table.rows[i + blankOffset], this.buffer.getBlankRow()); + } + this.isPartialBlank = blankSize > 0; + this.lastRowPos = startPos; + }, + + scrollTo: function(pixelOffset) { + if (this.lastPixelOffset == pixelOffset) + return; + + this.refreshContents(parseInt(pixelOffset / this.rowHeight)) + this.div.scrollTop = pixelOffset % this.rowHeight + + this.lastPixelOffset = pixelOffset; + }, + + visibleHeight: function() { + return parseInt(this.div.style.height); + } + +}; + + +Rico.LiveGridRequest = Class.create(); +Rico.LiveGridRequest.prototype = { + initialize: function( requestOffset, options ) { + this.requestOffset = requestOffset; + } +}; + +// Rico.LiveGrid ----------------------------------------------------- + +Rico.LiveGrid = Class.create(); + +Rico.LiveGrid.prototype = { + + initialize: function( tableId, visibleRows, totalRows, url, options ) { + if ( options == null ) + options = {}; + + this.tableId = tableId; + this.table = $(tableId); + var columnCount = this.table.rows[0].cells.length + this.metaData = new Rico.LiveGridMetaData(visibleRows, totalRows, columnCount, options); + this.buffer = new Rico.LiveGridBuffer(this.metaData); + + var rowCount = this.table.rows.length; + this.viewPort = new Rico.GridViewPort(this.table, + this.table.offsetHeight/rowCount, + visibleRows, + this.buffer, this); + this.scroller = new Rico.LiveGridScroller(this,this.viewPort); + + this.additionalParms = options.requestParameters || []; + + options.sortHandler = this.sortHandler.bind(this); + + if ( $(tableId + '_header') ) + this.sort = new Rico.LiveGridSort(tableId + '_header', options) + + this.processingRequest = null; + this.unprocessedRequest = null; + + this.initAjax(url); + if ( options.prefetchBuffer || options.prefetchOffset > 0) { + var offset = 0; + if (options.offset ) { + offset = options.offset; + this.scroller.moveScroll(offset); + this.viewPort.scrollTo(this.scroller.rowToPixel(offset)); + } + if (options.sortCol) { + this.sortCol = options.sortCol; + this.sortDir = options.sortDir; + } + this.requestContentRefresh(offset); + } + }, + + resetContents: function() { + this.scroller.moveScroll(0); + this.buffer.clear(); + this.viewPort.clearContents(); + }, + + sortHandler: function(column) { + this.sortCol = column.name; + this.sortDir = column.currentSort; + + this.resetContents(); + this.requestContentRefresh(0) + }, + + setRequestParams: function() { + this.additionalParms = []; + for ( var i=0 ; i < arguments.length ; i++ ) + this.additionalParms[i] = arguments[i]; + }, + + setTotalRows: function( newTotalRows ) { + this.resetContents(); + this.metaData.setTotalRows(newTotalRows); + this.scroller.updateSize(); + }, + + initAjax: function(url) { + ajaxEngine.registerRequest( this.tableId + '_request', url ); + ajaxEngine.registerAjaxObject( this.tableId + '_updater', this ); + }, + + invokeAjax: function() { + }, + + handleTimedOut: function() { + //server did not respond in 4 seconds... assume that there could have been + //an error or something, and allow requests to be processed again... + this.processingRequest = null; + this.processQueuedRequest(); + }, + + fetchBuffer: function(offset) { + if ( this.buffer.isInRange(offset) && + !this.buffer.isNearingLimit(offset)) { + return; + } + if (this.processingRequest) { + this.unprocessedRequest = new Rico.LiveGridRequest(offset); + return; + } + var bufferStartPos = this.buffer.getFetchOffset(offset); + this.processingRequest = new Rico.LiveGridRequest(offset); + this.processingRequest.bufferOffset = bufferStartPos; + var fetchSize = this.buffer.getFetchSize(offset); + var partialLoaded = false; + var callParms = []; + callParms.push(this.tableId + '_request'); + callParms.push('id=' + this.tableId); + callParms.push('page_size=' + fetchSize); + callParms.push('offset=' + bufferStartPos); + if ( this.sortCol) { + callParms.push('sort_col=' + this.sortCol); + callParms.push('sort_dir=' + this.sortDir); + } + + for( var i=0 ; i < this.additionalParms.length ; i++ ) + callParms.push(this.additionalParms[i]); + ajaxEngine.sendRequest.apply( ajaxEngine, callParms ); + + this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), 20000 ); //todo: make as option + }, + + requestContentRefresh: function(contentOffset) { + this.fetchBuffer(contentOffset); + }, + + ajaxUpdate: function(ajaxResponse) { + try { + clearTimeout( this.timeoutHandler ); + this.buffer.update(ajaxResponse,this.processingRequest.bufferOffset); + this.viewPort.bufferChanged(); + } + catch(err) {} + finally {this.processingRequest = null; } + this.processQueuedRequest(); + }, + + processQueuedRequest: function() { + if (this.unprocessedRequest != null) { + this.requestContentRefresh(this.unprocessedRequest.requestOffset); + this.unprocessedRequest = null + } + } + +}; + + +//-------------------- ricoLiveGridSort.js +Rico.LiveGridSort = Class.create(); + +Rico.LiveGridSort.prototype = { + + initialize: function(headerTableId, options) { + this.headerTableId = headerTableId; + this.headerTable = $(headerTableId); + this.setOptions(options); + this.applySortBehavior(); + + if ( this.options.sortCol ) { + this.setSortUI( this.options.sortCol, this.options.sortDir ); + } + }, + + setSortUI: function( columnName, sortDirection ) { + var cols = this.options.columns; + for ( var i = 0 ; i < cols.length ; i++ ) { + if ( cols[i].name == columnName ) { + this.setColumnSort(i, sortDirection); + break; + } + } + }, + + setOptions: function(options) { + this.options = { + sortAscendImg: 'images/sort_asc.gif', + sortDescendImg: 'images/sort_desc.gif', + imageWidth: 9, + imageHeight: 5, + ajaxSortURLParms: [] + }.extend(options); + + // preload the images... + new Image().src = this.options.sortAscendImg; + new Image().src = this.options.sortDescendImg; + + this.sort = options.sortHandler; + if ( !this.options.columns ) + this.options.columns = this.introspectForColumnInfo(); + else { + // allow client to pass { columns: [ ["a", true], ["b", false] ] } + // and convert to an array of Rico.TableColumn objs... + this.options.columns = this.convertToTableColumns(this.options.columns); + } + }, + + applySortBehavior: function() { + var headerRow = this.headerTable.rows[0]; + var headerCells = headerRow.cells; + for ( var i = 0 ; i < headerCells.length ; i++ ) { + this.addSortBehaviorToColumn( i, headerCells[i] ); + } + }, + + addSortBehaviorToColumn: function( n, cell ) { + if ( this.options.columns[n].isSortable() ) { + cell.id = this.headerTableId + '_' + n; + cell.style.cursor = 'pointer'; + cell.onclick = this.headerCellClicked.bindAsEventListener(this); + cell.innerHTML = cell.innerHTML + '' + + '   '; + } + }, + + // event handler.... + headerCellClicked: function(evt) { + var eventTarget = evt.target ? evt.target : evt.srcElement; + var cellId = eventTarget.id; + var columnNumber = parseInt(cellId.substring( cellId.lastIndexOf('_') + 1 )); + var sortedColumnIndex = this.getSortedColumnIndex(); + if ( sortedColumnIndex != -1 ) { + if ( sortedColumnIndex != columnNumber ) { + this.removeColumnSort(sortedColumnIndex); + this.setColumnSort(columnNumber, Rico.TableColumn.SORT_ASC); + } + else + this.toggleColumnSort(sortedColumnIndex); + } + else + this.setColumnSort(columnNumber, Rico.TableColumn.SORT_ASC); + + if (this.options.sortHandler) { + this.options.sortHandler(this.options.columns[columnNumber]); + } + }, + + removeColumnSort: function(n) { + this.options.columns[n].setUnsorted(); + this.setSortImage(n); + }, + + setColumnSort: function(n, direction) { + this.options.columns[n].setSorted(direction); + this.setSortImage(n); + }, + + toggleColumnSort: function(n) { + this.options.columns[n].toggleSort(); + this.setSortImage(n); + }, + + setSortImage: function(n) { + var sortDirection = this.options.columns[n].getSortDirection(); + + var sortImageSpan = $( this.headerTableId + '_img_' + n ); + if ( sortDirection == Rico.TableColumn.UNSORTED ) + sortImageSpan.innerHTML = '  '; + else if ( sortDirection == Rico.TableColumn.SORT_ASC ) + sortImageSpan.innerHTML = '  '; + else if ( sortDirection == Rico.TableColumn.SORT_DESC ) + sortImageSpan.innerHTML = '  '; + }, + + getSortedColumnIndex: function() { + var cols = this.options.columns; + for ( var i = 0 ; i < cols.length ; i++ ) { + if ( cols[i].isSorted() ) + return i; + } + + return -1; + }, + + introspectForColumnInfo: function() { + var columns = new Array(); + var headerRow = this.headerTable.rows[0]; + var headerCells = headerRow.cells; + for ( var i = 0 ; i < headerCells.length ; i++ ) + columns.push( new Rico.TableColumn( this.deriveColumnNameFromCell(headerCells[i],i), true ) ); + return columns; + }, + + convertToTableColumns: function(cols) { + var columns = new Array(); + for ( var i = 0 ; i < cols.length ; i++ ) + columns.push( new Rico.TableColumn( cols[i][0], cols[i][1] ) ); + }, + + deriveColumnNameFromCell: function(cell,columnNumber) { + var cellContent = cell.innerText != undefined ? cell.innerText : cell.textContent; + return cellContent ? cellContent.toLowerCase().split(' ').join('_') : "col_" + columnNumber; + } +}; + +Rico.TableColumn = Class.create(); + +Rico.TableColumn.UNSORTED = 0; +Rico.TableColumn.SORT_ASC = "ASC"; +Rico.TableColumn.SORT_DESC = "DESC"; + +Rico.TableColumn.prototype = { + initialize: function(name, sortable) { + this.name = name; + this.sortable = sortable; + this.currentSort = Rico.TableColumn.UNSORTED; + }, + + isSortable: function() { + return this.sortable; + }, + + isSorted: function() { + return this.currentSort != Rico.TableColumn.UNSORTED; + }, + + getSortDirection: function() { + return this.currentSort; + }, + + toggleSort: function() { + if ( this.currentSort == Rico.TableColumn.UNSORTED || this.currentSort == Rico.TableColumn.SORT_DESC ) + this.currentSort = Rico.TableColumn.SORT_ASC; + else if ( this.currentSort == Rico.TableColumn.SORT_ASC ) + this.currentSort = Rico.TableColumn.SORT_DESC; + }, + + setUnsorted: function(direction) { + this.setSorted(Rico.TableColumn.UNSORTED); + }, + + setSorted: function(direction) { + // direction must by one of Rico.TableColumn.UNSORTED, .SORT_ASC, or .SET_DESC... + this.currentSort = direction; + } + +}; + + +//-------------------- ricoUtil.js + +var RicoUtil = { + + getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) { + if ( arguments.length == 2 ) + mozillaEquivalentCSS = cssProperty; + + var el = $(htmlElement); + if ( el.currentStyle ) + return el.currentStyle[cssProperty]; + else + return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozillaEquivalentCSS); + }, + + createXmlDocument : function() { + if (document.implementation && document.implementation.createDocument) { + var doc = document.implementation.createDocument("", "", null); + + if (doc.readyState == null) { + doc.readyState = 1; + doc.addEventListener("load", function () { + doc.readyState = 4; + if (typeof doc.onreadystatechange == "function") + doc.onreadystatechange(); + }, false); + } + + return doc; + } + + if (window.ActiveXObject) + return Try.these( + function() { return new ActiveXObject('MSXML2.DomDocument') }, + function() { return new ActiveXObject('Microsoft.DomDocument')}, + function() { return new ActiveXObject('MSXML.DomDocument') }, + function() { return new ActiveXObject('MSXML3.DomDocument') } + ) || false; + + return null; + }, + + getContentAsString: function( parentNode ) { + return parentNode.xml != undefined ? + this._getContentAsStringIE(parentNode) : + this._getContentAsStringMozilla(parentNode); + }, + + _getContentAsStringIE: function(parentNode) { + var contentStr = ""; + for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) + contentStr += parentNode.childNodes[i].xml; + return contentStr; + }, + + _getContentAsStringMozilla: function(parentNode) { + var xmlSerializer = new XMLSerializer(); + var contentStr = ""; + for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) + contentStr += xmlSerializer.serializeToString(parentNode.childNodes[i]); + return contentStr; + }, + + toViewportPosition: function(element) { + return this._toAbsolute(element,true); + }, + + toDocumentPosition: function(element) { + return this._toAbsolute(element,false); + }, + + /** + * Compute the elements position in terms of the window viewport + * so that it can be compared to the position of the mouse (dnd) + * This is additions of all the offsetTop,offsetLeft values up the + * offsetParent hierarchy, ...taking into account any scrollTop, + * scrollLeft values along the way... + * + * IE has a bug reporting a correct offsetLeft of elements within a + * a relatively positioned parent!!! + **/ + _toAbsolute: function(element,accountForDocScroll) { + + if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 ) + return this._toAbsoluteMozilla(element,accountForDocScroll); + + var x = 0; + var y = 0; + var parent = element; + while ( parent ) { + + var borderXOffset = 0; + var borderYOffset = 0; + if ( parent != element ) { + var borderXOffset = parseInt(this.getElementsComputedStyle(parent, "borderLeftWidth" )); + var borderYOffset = parseInt(this.getElementsComputedStyle(parent, "borderTopWidth" )); + borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset; + borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset; + } + + x += parent.offsetLeft - parent.scrollLeft + borderXOffset; + y += parent.offsetTop - parent.scrollTop + borderYOffset; + parent = parent.offsetParent; + } + + if ( accountForDocScroll ) { + x -= this.docScrollLeft(); + y -= this.docScrollTop(); + } + + return { x:x, y:y }; + }, + + /** + * Mozilla did not report all of the parents up the hierarchy via the + * offsetParent property that IE did. So for the calculation of the + * offsets we use the offsetParent property, but for the calculation of + * the scrollTop/scrollLeft adjustments we navigate up via the parentNode + * property instead so as to get the scroll offsets... + * + **/ + _toAbsoluteMozilla: function(element,accountForDocScroll) { + var x = 0; + var y = 0; + var parent = element; + while ( parent ) { + x += parent.offsetLeft; + y += parent.offsetTop; + parent = parent.offsetParent; + } + + parent = element; + while ( parent && + parent != document.body && + parent != document.documentElement ) { + if ( parent.scrollLeft ) + x -= parent.scrollLeft; + if ( parent.scrollTop ) + y -= parent.scrollTop; + parent = parent.parentNode; + } + + if ( accountForDocScroll ) { + x -= this.docScrollLeft(); + y -= this.docScrollTop(); + } + + return { x:x, y:y }; + }, + + docScrollLeft: function() { + if ( window.pageXOffset ) + return window.pageXOffset; + else if ( document.documentElement && document.documentElement.scrollLeft ) + return document.documentElement.scrollLeft; + else if ( document.body ) + return document.body.scrollLeft; + else + return 0; + }, + + docScrollTop: function() { + if ( window.pageYOffset ) + return window.pageYOffset; + else if ( document.documentElement && document.documentElement.scrollTop ) + return document.documentElement.scrollTop; + else if ( document.body ) + return document.body.scrollTop; + else + return 0; + } + +}; diff --git a/activemq-web/src/webapp/js/ricoAjax.js b/activemq-web/src/webapp/js/ricoAjax.js new file mode 100644 index 0000000000..9ba355c985 --- /dev/null +++ b/activemq-web/src/webapp/js/ricoAjax.js @@ -0,0 +1,469 @@ +/** + * + * Copyright 2005 Sabre Airline Solutions + * + * Licensed 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. + **/ + + +//-------------------- rico.js +var Rico = { + Version: '1.1-beta2' +} + +Rico.ArrayExtensions = new Array(); + +if (Object.prototype.extend) { + // in prototype.js... + Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend; +} + +if (Array.prototype.push) { + // in prototype.js... + Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.push; +} + +if (!Array.prototype.remove) { + Array.prototype.remove = function(dx) { + if( isNaN(dx) || dx > this.length ) + return false; + for( var i=0,n=0; i 1 ) + queryString = this._createQueryString(arguments, 1); + + new Ajax.Request(requestURL, this._requestOptions(queryString)); + }, + + sendRequestWithData: function(requestName, xmlDocument) { + var requestURL = this.requestURLS[requestName]; + if ( requestURL == null ) + return; + + var queryString = ""; + if ( arguments.length > 2 ) + queryString = this._createQueryString(arguments, 2); + + new Ajax.Request(requestURL + (requestURL.indexOf("?")<0?"?":"&") + queryString, this._requestOptions(null,xmlDocument)); + }, + + sendRequestAndUpdate: function(requestName,container,options) { + var requestURL = this.requestURLS[requestName]; + if ( requestURL == null ) + return; + + var queryString = ""; + if ( arguments.length > 3 ) + queryString = this._createQueryString(arguments, 3); + + var updaterOptions = this._requestOptions(queryString); + updaterOptions.onComplete = null; + updaterOptions.extend(options); + + new Ajax.Updater(container, requestURL, updaterOptions); + }, + + sendRequestWithDataAndUpdate: function(requestName,xmlDocument,container,options) { + var requestURL = this.requestURLS[requestName]; + if ( requestURL == null ) + return; + + var queryString = ""; + if ( arguments.length > 4 ) + queryString = this._createQueryString(arguments, 4); + + + var updaterOptions = this._requestOptions(queryString,xmlDocument); + updaterOptions.onComplete = null; + updaterOptions.extend(options); + + new Ajax.Updater(container, requestURL + (requestURL.indexOf("?")<0?"?":"&") + queryString, updaterOptions); + }, + + // Private -- not part of intended engine API -------------------------------------------------------------------- + + _requestOptions: function(queryString,xmlDoc) { + var self = this; + + var requestHeaders = ['X-Rico-Version', Rico.Version ]; + var sendMethod = "post" + if ( arguments[1] ) + requestHeaders.push( 'Content-type', 'text/xml' ); + else + sendMethod = "get"; + + return { requestHeaders: requestHeaders, + parameters: queryString, + postBody: arguments[1] ? xmlDoc : null, + method: sendMethod, + onComplete: self._onRequestComplete.bind(self) }; + }, + + _createQueryString: function( theArgs, offset ) { + var queryString = "" + for ( var i = offset ; i < theArgs.length ; i++ ) { + if ( i != offset ) + queryString += "&"; + + var anArg = theArgs[i]; + + if ( anArg.name != undefined && anArg.value != undefined ) { + queryString += anArg.name + "=" + escape(anArg.value); + } + else { + var ePos = anArg.indexOf('='); + var argName = anArg.substring( 0, ePos ); + var argValue = anArg.substring( ePos + 1 ); + queryString += argName + "=" + escape(argValue); + } + } + + return queryString; + }, + + _onRequestComplete : function(request) { + + //!!TODO: error handling infrastructure?? + if (request.status != 200) + return; + + var response = request.responseXML.getElementsByTagName("ajax-response"); + + if (response == null || response.length != 1) + return; + this._processAjaxResponse( response[0].childNodes ); + }, + + _processAjaxResponse: function( xmlResponseElements ) { + for ( var i = 0 ; i < xmlResponseElements.length ; i++ ) { + var responseElement = xmlResponseElements[i]; + + // only process nodes of type element..... + if ( responseElement.nodeType != 1 ) + continue; + + var responseType = responseElement.getAttribute("type"); + var responseId = responseElement.getAttribute("id"); + + if ( responseType == "object" ) + this._processAjaxObjectUpdate( this.ajaxObjects[ responseId ], responseElement ); + else if ( responseType == "element" ) + this._processAjaxElementUpdate( this.ajaxElements[ responseId ], responseElement ); + else + alert('unrecognized AjaxResponse type : ' + responseType ); + } + }, + + _processAjaxObjectUpdate: function( ajaxObject, responseElement ) { + ajaxObject.ajaxUpdate( responseElement ); + }, + + _processAjaxElementUpdate: function( ajaxElement, responseElement ) { + if (ajaxElement) { + ajaxElement.innerHTML = RicoUtil.getContentAsString(responseElement); + } + } + +} + +var ajaxEngine = new Rico.AjaxEngine(); + + +//-------------------- ricoUtil.js + +var RicoUtil = { + + getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) { + if ( arguments.length == 2 ) + mozillaEquivalentCSS = cssProperty; + + var el = $(htmlElement); + if ( el.currentStyle ) + return el.currentStyle[cssProperty]; + else + return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozillaEquivalentCSS); + }, + + createXmlDocument : function() { + if (document.implementation && document.implementation.createDocument) { + var doc = document.implementation.createDocument("", "", null); + + if (doc.readyState == null) { + doc.readyState = 1; + doc.addEventListener("load", function () { + doc.readyState = 4; + if (typeof doc.onreadystatechange == "function") + doc.onreadystatechange(); + }, false); + } + + return doc; + } + + if (window.ActiveXObject) + return Try.these( + function() { return new ActiveXObject('MSXML2.DomDocument') }, + function() { return new ActiveXObject('Microsoft.DomDocument')}, + function() { return new ActiveXObject('MSXML.DomDocument') }, + function() { return new ActiveXObject('MSXML3.DomDocument') } + ) || false; + + return null; + }, + + getContentAsString: function( parentNode ) { + return parentNode.xml != undefined ? + this._getContentAsStringIE(parentNode) : + this._getContentAsStringMozilla(parentNode); + }, + + _getContentAsStringIE: function(parentNode) { + var contentStr = ""; + for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) + contentStr += parentNode.childNodes[i].xml; + return contentStr; + }, + + _getContentAsStringMozilla: function(parentNode) { + var xmlSerializer = new XMLSerializer(); + var contentStr = ""; + for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) + contentStr += xmlSerializer.serializeToString(parentNode.childNodes[i]); + return contentStr; + }, + + toViewportPosition: function(element) { + return this._toAbsolute(element,true); + }, + + toDocumentPosition: function(element) { + return this._toAbsolute(element,false); + }, + + /** + * Compute the elements position in terms of the window viewport + * so that it can be compared to the position of the mouse (dnd) + * This is additions of all the offsetTop,offsetLeft values up the + * offsetParent hierarchy, ...taking into account any scrollTop, + * scrollLeft values along the way... + * + * IE has a bug reporting a correct offsetLeft of elements within a + * a relatively positioned parent!!! + **/ + _toAbsolute: function(element,accountForDocScroll) { + + if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 ) + return this._toAbsoluteMozilla(element,accountForDocScroll); + + var x = 0; + var y = 0; + var parent = element; + while ( parent ) { + + var borderXOffset = 0; + var borderYOffset = 0; + if ( parent != element ) { + var borderXOffset = parseInt(this.getElementsComputedStyle(parent, "borderLeftWidth" )); + var borderYOffset = parseInt(this.getElementsComputedStyle(parent, "borderTopWidth" )); + borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset; + borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset; + } + + x += parent.offsetLeft - parent.scrollLeft + borderXOffset; + y += parent.offsetTop - parent.scrollTop + borderYOffset; + parent = parent.offsetParent; + } + + if ( accountForDocScroll ) { + x -= this.docScrollLeft(); + y -= this.docScrollTop(); + } + + return { x:x, y:y }; + }, + + /** + * Mozilla did not report all of the parents up the hierarchy via the + * offsetParent property that IE did. So for the calculation of the + * offsets we use the offsetParent property, but for the calculation of + * the scrollTop/scrollLeft adjustments we navigate up via the parentNode + * property instead so as to get the scroll offsets... + * + **/ + _toAbsoluteMozilla: function(element,accountForDocScroll) { + var x = 0; + var y = 0; + var parent = element; + while ( parent ) { + x += parent.offsetLeft; + y += parent.offsetTop; + parent = parent.offsetParent; + } + + parent = element; + while ( parent && + parent != document.body && + parent != document.documentElement ) { + if ( parent.scrollLeft ) + x -= parent.scrollLeft; + if ( parent.scrollTop ) + y -= parent.scrollTop; + parent = parent.parentNode; + } + + if ( accountForDocScroll ) { + x -= this.docScrollLeft(); + y -= this.docScrollTop(); + } + + return { x:x, y:y }; + }, + + docScrollLeft: function() { + if ( window.pageXOffset ) + return window.pageXOffset; + else if ( document.documentElement && document.documentElement.scrollLeft ) + return document.documentElement.scrollLeft; + else if ( document.body ) + return document.body.scrollLeft; + else + return 0; + }, + + docScrollTop: function() { + if ( window.pageYOffset ) + return window.pageYOffset; + else if ( document.documentElement && document.documentElement.scrollTop ) + return document.documentElement.scrollTop; + else if ( document.body ) + return document.body.scrollTop; + else + return 0; + } + +}; diff --git a/activemq-web/src/webapp/js/scriptaculous.js b/activemq-web/src/webapp/js/scriptaculous.js new file mode 100644 index 0000000000..af82bc6583 --- /dev/null +++ b/activemq-web/src/webapp/js/scriptaculous.js @@ -0,0 +1,1305 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Element.collectTextNodesIgnoreClass = function(element, ignoreclass) { + var children = $(element).childNodes; + var text = ""; + var classtest = new RegExp("^([^ ]+ )*" + ignoreclass+ "( [^ ]+)*$","i"); + + for (var i = 0; i < children.length; i++) { + if(children[i].nodeType==3) { + text+=children[i].nodeValue; + } else { + if((!children[i].className.match(classtest)) && children[i].hasChildNodes()) + text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass); + } + } + + return text; +} + +Ajax.Autocompleter = Class.create(); +Ajax.Autocompleter.prototype = (new Ajax.Base()).extend({ + initialize: function(element, update, url, options) { + this.element = $(element); + this.update = $(update); + this.has_focus = false; + this.changed = false; + this.active = false; + this.index = 0; + this.entry_count = 0; + this.url = url; + + this.setOptions(options); + this.options.asynchronous = true; + this.options.onComplete = this.onComplete.bind(this) + this.options.frequency = this.options.frequency || 0.4; + this.options.min_chars = this.options.min_chars || 1; + this.options.method = 'post'; + + this.options.onShow = this.options.onShow || + function(element, update){ + if(!update.style.position || update.style.position=='absolute') { + update.style.position = 'absolute'; + var offsets = Position.cumulativeOffset(element); + update.style.left = offsets[0] + 'px'; + update.style.top = (offsets[1] + element.offsetHeight) + 'px'; + update.style.width = element.offsetWidth + 'px'; + } + new Effect.Appear(update,{duration:0.3}); + }; + this.options.onHide = this.options.onHide || + function(element, update){ new Effect.Fade(update,{duration:0.3}) }; + + + if(this.options.indicator) + this.indicator = $(this.options.indicator); + + this.observer = null; + + Element.hide(this.update); + + Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this)); + Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this)); + }, + + show: function() { + if(this.update.style.display=='none') this.options.onShow(this.element, this.update); + if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && this.update.style.position=='absolute') { + new Insertion.After(this.update, + ''); + this.iefix = $(this.update.id+'_iefix'); + } + if(this.iefix) { + Position.clone(this.update, this.iefix); + this.iefix.style.zIndex = 1; + this.update.style.zIndex = 2; + Element.show(this.iefix); + } + }, + + hide: function() { + if(this.update.style.display=='') this.options.onHide(this.element, this.update); + if(this.iefix) Element.hide(this.iefix); + }, + + startIndicator: function() { + if(this.indicator) Element.show(this.indicator); + }, + + stopIndicator: function() { + if(this.indicator) Element.hide(this.indicator); + }, + + onObserverEvent: function() { + this.changed = false; + if(this.element.value.length>=this.options.min_chars) { + this.startIndicator(); + this.options.parameters = this.options.callback ? + this.options.callback(this.element, Form.Element.getValue(this.element)) : + Form.Element.serialize(this.element); + new Ajax.Request(this.url, this.options); + } else { + this.active = false; + this.hide(); + } + }, + + addObservers: function(element) { + Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); + Event.observe(element, "click", this.onClick.bindAsEventListener(this)); + }, + + onComplete: function(request) { + if(!this.changed && this.has_focus) { + this.update.innerHTML = request.responseText; + Element.cleanWhitespace(this.update); + Element.cleanWhitespace(this.update.firstChild); + + if(this.update.firstChild && this.update.firstChild.childNodes) { + this.entry_count = + this.update.firstChild.childNodes.length; + for (var i = 0; i < this.entry_count; i++) { + entry = this.get_entry(i); + entry.autocompleteIndex = i; + this.addObservers(entry); + } + } else { + this.entry_count = 0; + } + + this.stopIndicator(); + + this.index = 0; + this.render(); + } + }, + + onKeyPress: function(event) { + if(this.active) + switch(event.keyCode) { + case Event.KEY_TAB: + case Event.KEY_RETURN: + this.select_entry(); + Event.stop(event); + case Event.KEY_ESC: + this.hide(); + this.active = false; + return; + case Event.KEY_LEFT: + case Event.KEY_RIGHT: + return; + case Event.KEY_UP: + this.mark_previous(); + this.render(); + if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); + return; + case Event.KEY_DOWN: + this.mark_next(); + this.render(); + if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); + return; + } + else + if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN) + return; + + this.changed = true; + this.has_focus = true; + + if(this.observer) clearTimeout(this.observer); + this.observer = + setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); + }, + + onHover: function(event) { + var element = Event.findElement(event, 'LI'); + if(this.index != element.autocompleteIndex) + { + this.index = element.autocompleteIndex; + this.render(); + } + Event.stop(event); + }, + + onClick: function(event) { + var element = Event.findElement(event, 'LI'); + this.index = element.autocompleteIndex; + this.select_entry(); + Event.stop(event); + }, + + onBlur: function(event) { + // needed to make click events working + setTimeout(this.hide.bind(this), 250); + this.has_focus = false; + this.active = false; + }, + + render: function() { + if(this.entry_count > 0) { + for (var i = 0; i < this.entry_count; i++) + this.index==i ? + Element.addClassName(this.get_entry(i),"selected") : + Element.removeClassName(this.get_entry(i),"selected"); + + if(this.has_focus) { + if(this.get_current_entry().scrollIntoView) + this.get_current_entry().scrollIntoView(false); + + this.show(); + this.active = true; + } + } else this.hide(); + }, + + mark_previous: function() { + if(this.index > 0) this.index-- + else this.index = this.entry_count-1; + }, + + mark_next: function() { + if(this.index < this.entry_count-1) this.index++ + else this.index = 0; + }, + + get_entry: function(index) { + return this.update.firstChild.childNodes[index]; + }, + + get_current_entry: function() { + return this.get_entry(this.index); + }, + + select_entry: function() { + this.active = false; + value = Element.collectTextNodesIgnoreClass(this.get_current_entry(), 'informal').unescapeHTML(); + this.element.value = value; + this.element.focus(); + } +});// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// Element.Class part Copyright (c) 2005 by Rick Olson +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Element.Class = { + // Element.toggleClass(element, className) toggles the class being on/off + // Element.toggleClass(element, className1, className2) toggles between both classes, + // defaulting to className1 if neither exist + toggle: function(element, className) { + if(Element.Class.has(element, className)) { + Element.Class.remove(element, className); + if(arguments.length == 3) Element.Class.add(element, arguments[2]); + } else { + Element.Class.add(element, className); + if(arguments.length == 3) Element.Class.remove(element, arguments[2]); + } + }, + + // gets space-delimited classnames of an element as an array + get: function(element) { + element = $(element); + return element.className.split(' '); + }, + + // functions adapted from original functions by Gavin Kistner + remove: function(element) { + element = $(element); + var regEx; + for(var i = 1; i < arguments.length; i++) { + regEx = new RegExp("^" + arguments[i] + "\\b\\s*|\\s*\\b" + arguments[i] + "\\b", 'g'); + element.className = element.className.replace(regEx, '') + } + }, + + add: function(element) { + element = $(element); + for(var i = 1; i < arguments.length; i++) { + Element.Class.remove(element, arguments[i]); + element.className += (element.className.length > 0 ? ' ' : '') + arguments[i]; + } + }, + + // returns true if all given classes exist in said element + has: function(element) { + element = $(element); + if(!element || !element.className) return false; + var regEx; + for(var i = 1; i < arguments.length; i++) { + regEx = new RegExp("\\b" + arguments[i] + "\\b"); + if(!regEx.test(element.className)) return false; + } + return true; + }, + + // expects arrays of strings and/or strings as optional paramters + // Element.Class.has_any(element, ['classA','classB','classC'], 'classD') + has_any: function(element) { + element = $(element); + if(!element || !element.className) return false; + var regEx; + for(var i = 1; i < arguments.length; i++) { + if((typeof arguments[i] == 'object') && + (arguments[i].constructor == Array)) { + for(var j = 0; j < arguments[i].length; j++) { + regEx = new RegExp("\\b" + arguments[i][j] + "\\b"); + if(regEx.test(element.className)) return true; + } + } else { + regEx = new RegExp("\\b" + arguments[i] + "\\b"); + if(regEx.test(element.className)) return true; + } + } + return false; + }, + + childrenWith: function(element, className) { + var children = $(element).getElementsByTagName('*'); + var elements = new Array(); + + for (var i = 0; i < children.length; i++) { + if (Element.Class.has(children[i], className)) { + elements.push(children[i]); + break; + } + } + + return elements; + } +} + +/*--------------------------------------------------------------------------*/ + +var Droppables = { + drops: false, + + add: function(element) { + var element = $(element); + var options = { + greedy: true, + hoverclass: null + }.extend(arguments[1] || {}); + + // cache containers + if(options.containment) { + options._containers = new Array(); + var containment = options.containment; + if((typeof containment == 'object') && + (containment.constructor == Array)) { + for(var i=0; i0) window.scrollBy(0,0); + + Event.stop(event); + } + } +} + +/*--------------------------------------------------------------------------*/ + +SortableObserver = Class.create(); +SortableObserver.prototype = { + initialize: function(element, observer) { + this.element = $(element); + this.observer = observer; + this.lastValue = Sortable.serialize(this.element); + }, + onStart: function() { + this.lastValue = Sortable.serialize(this.element); + }, + onEnd: function() { + if(this.lastValue != Sortable.serialize(this.element)) + this.observer(this.element) + } +} + +Sortable = { + create: function(element) { + var element = $(element); + var options = { + tag: 'li', // assumes li children, override with tag: 'tagname' + overlap: 'vertical', // one of 'vertical', 'horizontal' + constraint: 'vertical', // one of 'vertical', 'horizontal', false + containment: element, // also takes array of elements (or id's); or false + handle: false, // or a CSS class + only: false, + hoverclass: null, + onChange: function() {}, + onUpdate: function() {} + }.extend(arguments[1] || {}); + element.sortable = options; + + // build options for the draggables + var options_for_draggable = { + revert: true, + constraint: options.constraint, + handle: handle }; + if(options.starteffect) + options_for_draggable.starteffect = options.starteffect; + if(options.reverteffect) + options_for_draggable.reverteffect = options.reverteffect; + if(options.endeffect) + options_for_draggable.endeffect = options.endeffect; + if(options.zindex) + options_for_draggable.zindex = options.zindex; + + // build options for the droppables + var options_for_droppable = { + overlap: options.overlap, + containment: options.containment, + hoverclass: options.hoverclass, + onHover: function(element, dropon, overlap) { + if(overlap>0.5) { + if(dropon.previousSibling != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, dropon); + if(dropon.parentNode!=oldParentNode && oldParentNode.sortable) + oldParentNode.sortable.onChange(element); + if(dropon.parentNode.sortable) + dropon.parentNode.sortable.onChange(element); + } + } else { + var nextElement = dropon.nextSibling || null; + if(nextElement != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, nextElement); + if(dropon.parentNode!=oldParentNode && oldParentNode.sortable) + oldParentNode.sortable.onChange(element); + if(dropon.parentNode.sortable) + dropon.parentNode.sortable.onChange(element); + } + } + } + } + + // fix for gecko engine + Element.cleanWhitespace(element); + + // for onupdate + Draggables.addObserver(new SortableObserver(element, options.onUpdate)); + + // make it so + var elements = element.childNodes; + for (var i = 0; i < elements.length; i++) + if(elements[i].tagName && elements[i].tagName==options.tag.toUpperCase() && + (!options.only || (Element.Class.has(elements[i], options.only)))) { + + // handles are per-draggable + var handle = options.handle ? + Element.Class.childrenWith(elements[i], options.handle)[0] : elements[i]; + + new Draggable(elements[i], options_for_draggable.extend({ handle: handle })); + Droppables.add(elements[i], options_for_droppable); + } + + }, + serialize: function(element) { + var element = $(element); + var options = { + tag: element.sortable.tag, + only: element.sortable.only, + name: element.id + }.extend(arguments[1] || {}); + + var items = $(element).childNodes; + var queryComponents = new Array(); + + for(var i=0; i= this.finishOn) { + this.render(this.options.to); + if(this.finish) this.finish(); + if(this.options.afterFinish) this.options.afterFinish(this); + return; + } + pos = (timePos - this.startOn) / (this.finishOn - this.startOn); + frame = Math.round(pos * this.options.fps * this.options.duration); + if(frame > this.currentFrame) { + this.render(pos); + this.currentFrame = frame; + } + this.timeout = setTimeout(this.loop.bind(this), 10); + }, + render: function(pos) { + if(this.options.transition) pos = this.options.transition(pos); + pos = pos * (this.options.to-this.options.from); + pos += this.options.from; + if(this.options.beforeUpdate) this.options.beforeUpdate(this); + if(this.update) this.update(pos); + if(this.options.afterUpdate) this.options.afterUpdate(this); + }, + cancel: function() { + if(this.timeout) clearTimeout(this.timeout); + } +} + +Effect.Parallel = Class.create(); + Effect.Parallel.prototype = (new Effect.Base()).extend({ + initialize: function(effects) { + this.effects = effects || []; + this.start(arguments[1]); + }, + update: function(position) { + for (var i = 0; i < this.effects.length; i++) + this.effects[i].render(position); + }, + finish: function(position) { + for (var i = 0; i < this.effects.length; i++) + if(this.effects[i].finish) this.effects[i].finish(position); + } + }); + +// Internet Explorer caveat: works only on elements the have +// a 'layout', meaning having a given width or height. +// There is no way to safely set this automatically. +Effect.Opacity = Class.create(); +Effect.Opacity.prototype = (new Effect.Base()).extend({ + initialize: function(element) { + this.element = $(element); + options = { + from: 0.0, + to: 1.0 + }.extend(arguments[1] || {}); + this.start(options); + }, + update: function(position) { + this.setOpacity(position); + }, + setOpacity: function(opacity) { + opacity = (opacity == 1) ? 0.99999 : opacity; + this.element.style.opacity = opacity; + this.element.style.filter = "alpha(opacity:"+opacity*100+")"; + } +}); + +Effect.MoveBy = Class.create(); + Effect.MoveBy.prototype = (new Effect.Base()).extend({ + initialize: function(element, toTop, toLeft) { + this.element = $(element); + this.originalTop = parseFloat(this.element.style.top || '0'); + this.originalLeft = parseFloat(this.element.style.left || '0'); + this.toTop = toTop; + this.toLeft = toLeft; + if(this.element.style.position == "") + this.element.style.position = "relative"; + this.start(arguments[3]); + }, + update: function(position) { + topd = this.toTop * position + this.originalTop; + leftd = this.toLeft * position + this.originalLeft; + this.setPosition(topd, leftd); + }, + setPosition: function(topd, leftd) { + this.element.style.top = topd + "px"; + this.element.style.left = leftd + "px"; + } +}); + +Effect.Scale = Class.create(); +Effect.Scale.prototype = (new Effect.Base()).extend({ + initialize: function(element, percent) { + this.element = $(element) + options = { + scaleX: true, + scaleY: true, + scaleContent: true, + scaleFromCenter: false, + scaleMode: 'box', // 'box' or 'contents' or {} with provided values + scaleFrom: 100.0 + }.extend(arguments[2] || {}); + this.originalTop = this.element.offsetTop; + this.originalLeft = this.element.offsetLeft; + if (this.element.style.fontSize=="") this.sizeEm = 1.0; + if (this.element.style.fontSize && this.element.style.fontSize.indexOf("em")>0) + this.sizeEm = parseFloat(this.element.style.fontSize); + this.factor = (percent/100.0) - (options.scaleFrom/100.0); + if(options.scaleMode=='box') { + this.originalHeight = this.element.clientHeight; + this.originalWidth = this.element.clientWidth; + } else + if(options.scaleMode=='contents') { + this.originalHeight = this.element.scrollHeight; + this.originalWidth = this.element.scrollWidth; + } else { + this.originalHeight = options.scaleMode.originalHeight; + this.originalWidth = options.scaleMode.originalWidth; + } + this.start(options); + }, + + update: function(position) { + currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); + if(this.options.scaleContent && this.sizeEm) + this.element.style.fontSize = this.sizeEm*currentScale + "em"; + this.setDimensions( + this.originalWidth * currentScale, + this.originalHeight * currentScale); + }, + + setDimensions: function(width, height) { + if(this.options.scaleX) this.element.style.width = width + 'px'; + if(this.options.scaleY) this.element.style.height = height + 'px'; + if(this.options.scaleFromCenter) { + topd = (height - this.originalHeight)/2; + leftd = (width - this.originalWidth)/2; + if(this.element.style.position=='absolute') { + if(this.options.scaleY) this.element.style.top = this.originalTop-topd + "px"; + if(this.options.scaleX) this.element.style.left = this.originalLeft-leftd + "px"; + } else { + if(this.options.scaleY) this.element.style.top = -topd + "px"; + if(this.options.scaleX) this.element.style.left = -leftd + "px"; + } + } + } +}); + +Effect.Highlight = Class.create(); +Effect.Highlight.prototype = (new Effect.Base()).extend({ + initialize: function(element) { + this.element = $(element); + + // try to parse current background color as default for endcolor + // browser stores this as: "rgb(255, 255, 255)", convert to "#ffffff" format + var endcolor = "#ffffff"; + var current = this.element.style.backgroundColor; + if(current && current.slice(0,4) == "rgb(") { + endcolor = "#"; + var cols = current.slice(4,current.length-1).split(','); + var i=0; do { endcolor += parseInt(cols[i]).toColorPart() } while (++i<3); } + + var options = { + startcolor: "#ffff99", + endcolor: endcolor + }.extend(arguments[1] || {}); + + // init color calculations + this.colors_base = [ + parseInt(options.startcolor.slice(1,3),16), + parseInt(options.startcolor.slice(3,5),16), + parseInt(options.startcolor.slice(5),16) ]; + this.colors_delta = [ + parseInt(options.endcolor.slice(1,3),16)-this.colors_base[0], + parseInt(options.endcolor.slice(3,5),16)-this.colors_base[1], + parseInt(options.endcolor.slice(5),16)-this.colors_base[2] ]; + + this.start(options); + }, + update: function(position) { + var colors = [ + Math.round(this.colors_base[0]+(this.colors_delta[0]*position)), + Math.round(this.colors_base[1]+(this.colors_delta[1]*position)), + Math.round(this.colors_base[2]+(this.colors_delta[2]*position)) ]; + this.element.style.backgroundColor = "#" + + colors[0].toColorPart() + colors[1].toColorPart() + colors[2].toColorPart(); + } +}); + + +/* ------------- prepackaged effects ------------- */ + +Effect.Fade = function(element) { + options = { + from: 1.0, + to: 0.0, + afterFinish: function(effect) + { Element.hide(effect.element); + effect.setOpacity(1); } + }.extend(arguments[1] || {}); + new Effect.Opacity(element,options); +} + +Effect.Appear = function(element) { + options = { + from: 0.0, + to: 1.0, + beforeStart: function(effect) + { effect.setOpacity(0); + Element.show(effect.element); }, + afterUpdate: function(effect) + { Element.show(effect.element); } + }.extend(arguments[1] || {}); + new Effect.Opacity(element,options); +} + +Effect.Puff = function(element) { + new Effect.Parallel( + [ new Effect.Scale(element, 200, { sync: true, scaleFromCenter: true }), + new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0 } ) ], + { duration: 1.0, + afterUpdate: function(effect) + { effect.effects[0].element.style.position = 'absolute'; }, + afterFinish: function(effect) + { Element.hide(effect.effects[0].element); } + } + ); +} + +Effect.BlindUp = function(element) { + $(element)._overflow = $(element).style.overflow || 'visible'; + $(element).style.overflow = 'hidden'; + new Effect.Scale(element, 0, + { scaleContent: false, + scaleX: false, + afterFinish: function(effect) + { + Element.hide(effect.element); + effect.element.style.overflow = effect.element._overflow; + } + }.extend(arguments[1] || {}) + ); +} + +Effect.BlindDown = function(element) { + $(element).style.height = '0px'; + $(element)._overflow = $(element).style.overflow || 'visible'; + $(element).style.overflow = 'hidden'; + Element.show(element); + new Effect.Scale(element, 100, + { scaleContent: false, + scaleX: false, + scaleMode: 'contents', + scaleFrom: 0, + afterFinish: function(effect) { + effect.element.style.overflow = effect.element._overflow; + } + }.extend(arguments[1] || {}) + ); +} + +Effect.SwitchOff = function(element) { + new Effect.Appear(element, + { duration: 0.4, + transition: Effect.Transitions.flicker, + afterFinish: function(effect) + { effect.element.style.overflow = 'hidden'; + new Effect.Scale(effect.element, 1, + { duration: 0.3, scaleFromCenter: true, + scaleX: false, scaleContent: false, + afterUpdate: function(effect) { + if(effect.element.style.position=="") + effect.element.style.position = 'relative'; }, + afterFinish: function(effect) { Element.hide(effect.element); } + } ) + } + } ) +} + +Effect.DropOut = function(element) { + new Effect.Parallel( + [ new Effect.MoveBy(element, 100, 0, { sync: true }), + new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0 } ) ], + { duration: 0.5, + afterFinish: function(effect) + { Element.hide(effect.effects[0].element); } + }); +} + +Effect.Shake = function(element) { + new Effect.MoveBy(element, 0, 20, + { duration: 0.05, afterFinish: function(effect) { + new Effect.MoveBy(effect.element, 0, -40, + { duration: 0.1, afterFinish: function(effect) { + new Effect.MoveBy(effect.element, 0, 40, + { duration: 0.1, afterFinish: function(effect) { + new Effect.MoveBy(effect.element, 0, -40, + { duration: 0.1, afterFinish: function(effect) { + new Effect.MoveBy(effect.element, 0, 40, + { duration: 0.1, afterFinish: function(effect) { + new Effect.MoveBy(effect.element, 0, -20, + { duration: 0.05, afterFinish: function(effect) { + }}) }}) }}) }}) }}) }}); +} + +Effect.SlideDown = function(element) { + $(element)._overflow = $(element).style.overflow || 'visible'; + $(element).style.height = '0px'; + $(element).style.overflow = 'hidden'; + $(element).firstChild.style.position = 'relative'; + Element.show(element); + new Effect.Scale(element, 100, + { scaleContent: false, + scaleX: false, + scaleMode: 'contents', + scaleFrom: 0, + afterUpdate: function(effect) + { effect.element.firstChild.style.bottom = + (effect.originalHeight - effect.element.clientHeight) + 'px'; }, + afterFinish: function(effect) + { effect.element.style.overflow = effect.element._overflow; } + }.extend(arguments[1] || {}) + ); +} + +Effect.SlideUp = function(element) { + $(element)._overflow = $(element).style.overflow || 'visible'; + $(element).style.overflow = 'hidden'; + $(element).firstChild.style.position = 'relative'; + Element.show(element); + new Effect.Scale(element, 0, + { scaleContent: false, + scaleX: false, + afterUpdate: function(effect) + { effect.element.firstChild.style.bottom = + (effect.originalHeight - effect.element.clientHeight) + 'px'; }, + afterFinish: function(effect) + { + Element.hide(effect.element); + effect.element.style.overflow = effect.element._overflow; + } + }.extend(arguments[1] || {}) + ); +} + +Effect.Squish = function(element) { + new Effect.Scale(element, 0, + { afterFinish: function(effect) { Element.hide(effect.element); } }); +} + +Effect.Grow = function(element) { + element = $(element); + var options = arguments[1] || {}; + + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + element.style.overflow = 'hidden'; + Element.show(element); + + var direction = options.direction || 'center'; + var moveTransition = options.moveTransition || Effect.Transitions.sinoidal; + var scaleTransition = options.scaleTransition || Effect.Transitions.sinoidal; + var opacityTransition = options.opacityTransition || Effect.Transitions.full; + + var initialMoveX, initialMoveY; + var moveX, moveY; + + switch (direction) { + case 'top-left': + initialMoveX = initialMoveY = moveX = moveY = 0; + break; + case 'top-right': + initialMoveX = originalWidth; + initialMoveY = moveY = 0; + moveX = -originalWidth; + break; + case 'bottom-left': + initialMoveX = moveX = 0; + initialMoveY = originalHeight; + moveY = -originalHeight; + break; + case 'bottom-right': + initialMoveX = originalWidth; + initialMoveY = originalHeight; + moveX = -originalWidth; + moveY = -originalHeight; + break; + case 'center': + initialMoveX = originalWidth / 2; + initialMoveY = originalHeight / 2; + moveX = -originalWidth / 2; + moveY = -originalHeight / 2; + break; + } + + new Effect.MoveBy(element, initialMoveY, initialMoveX, { + duration: 0.01, + beforeUpdate: function(effect) { $(element).style.height = '0px'; }, + afterFinish: function(effect) { + new Effect.Parallel( + [ new Effect.Opacity(element, { sync: true, to: 1.0, from: 0.0, transition: opacityTransition }), + new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: moveTransition }), + new Effect.Scale(element, 100, { + scaleMode: { originalHeight: originalHeight, originalWidth: originalWidth }, + sync: true, scaleFrom: 0, scaleTo: 100, transition: scaleTransition })], + options); } + }); +} + +Effect.Shrink = function(element) { + element = $(element); + var options = arguments[1] || {}; + + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + element.style.overflow = 'hidden'; + Element.show(element); + + var direction = options.direction || 'center'; + var moveTransition = options.moveTransition || Effect.Transitions.sinoidal; + var scaleTransition = options.scaleTransition || Effect.Transitions.sinoidal; + var opacityTransition = options.opacityTransition || Effect.Transitions.none; + + var moveX, moveY; + + switch (direction) { + case 'top-left': + moveX = moveY = 0; + break; + case 'top-right': + moveX = originalWidth; + moveY = 0; + break; + case 'bottom-left': + moveX = 0; + moveY = originalHeight; + break; + case 'bottom-right': + moveX = originalWidth; + moveY = originalHeight; + break; + case 'center': + moveX = originalWidth / 2; + moveY = originalHeight / 2; + break; + } + + new Effect.Parallel( + [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: opacityTransition }), + new Effect.Scale(element, 0, { sync: true, transition: moveTransition }), + new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: scaleTransition }) ], + options); +} + +Effect.Pulsate = function(element) { + var options = arguments[1] || {}; + var transition = options.transition || Effect.Transitions.sinoidal; + var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) }; + reverser.bind(transition); + new Effect.Opacity(element, + { duration: 3.0, + afterFinish: function(effect) { Element.show(effect.element); } + }.extend(options).extend({transition: reverser})); +} + +Effect.Fold = function(element) { + $(element).style.overflow = 'hidden'; + new Effect.Scale(element, 5, { + scaleContent: false, + scaleTo: 100, + scaleX: false, + afterFinish: function(effect) { + new Effect.Scale(element, 1, { + scaleContent: false, + scaleTo: 0, + scaleY: false, + afterFinish: function(effect) { Element.hide(effect.element) } }); + }}.extend(arguments[1] || {})); +} + +// old: new Effect.ContentZoom(element, percent) +// new: Element.setContentZoom(element, percent) + +Element.setContentZoom = function(element, percent) { + var element = $(element); + element.style.fontSize = (percent/100) + "em"; + if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); +} diff --git a/activemq-web/src/webapp/portfolio/portfolio.html b/activemq-web/src/webapp/portfolio/portfolio.html new file mode 100644 index 0000000000..2afb8cb21f --- /dev/null +++ b/activemq-web/src/webapp/portfolio/portfolio.html @@ -0,0 +1,70 @@ + + +My Portfolio + + + + + + + +

My Portfolio

+ +

This example displays an example stock portfolio. In a real system +this page would be generated dynamically based on the users current +stock portfolio

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
StockDescriptionAmountPriceValueCostP & L
IBMWIBM Stock100019000
MSFTMicrosoft600022000
BEASBEA Stock110012342
SUNWSun Microsystems Inc30007700
+ + diff --git a/activemq-web/src/webapp/portfolio/portfolio.js b/activemq-web/src/webapp/portfolio/portfolio.js new file mode 100644 index 0000000000..19cf51cba6 --- /dev/null +++ b/activemq-web/src/webapp/portfolio/portfolio.js @@ -0,0 +1,100 @@ + +var PollHandler = +{ + ajaxUpdate: function(ajaxResponse) + { + // Poll again for events + ajaxEngine.sendRequest('getEvents'); + + } +}; + +var PriceHandler = +{ + ajaxUpdate: function(ajaxResponse) + { + var priceMessage = firstElement(ajaxResponse) + if (priceMessage != null) { + + var price = parseFloat(priceMessage.getAttribute('bid')) + var symbol = priceMessage.getAttribute('stock') + var movement = priceMessage.getAttribute('movement') + if (movement == null) { + movement = 'up' + } + + //alert('Received price ' + priceMessage + ' for price: ' + price + ' symbol: ' + symbol + ' movement: ' + movement) + + var row = document.getElementById(symbol) + if (row) { + // perform portfolio calculations + var value = asFloat(find(row, 'amount')) * price + var pl = value - asFloat(find(row, 'cost')) + + // now lets update the HTML DOM + find(row, 'price').innerHTML = fixedDigits(price, 2) + find(row, 'value').innerHTML = fixedDigits(value, 2) + find(row, 'pl').innerHTML = fixedDigits(pl, 2) + find(row, 'price').className = movement + find(row, 'pl').className = pl >= 0 ? 'up' : 'down' + } + } + + } +}; + +function initPage() +{ + ajaxEngine.registerRequest('getEvents', '/jms/STOCKS/*?rico=true&id=priceChange'); + + ajaxEngine.registerAjaxObject('poll', PollHandler); + + ajaxEngine.registerAjaxObject('priceChange', PriceHandler); + + ajaxEngine.sendRequest('getEvents'); +} + +Behaviour.addLoadEvent(initPage); + +/// ----------------- +// Original code by Joe Walnes +// ----------------- + +/*** Convenience methods, added as mixins to standard classes (object prototypes) ***/ + +/** + * Return number as fixed number of digits. + */ +function fixedDigits(t, digits) { + return (t.toFixed) ? t.toFixed(digits) : this +} + +/** + * Find direct child of an element, by id. + */ +function find(t, id) { + for (i = 0; i < t.childNodes.length; i++) { + var child = t.childNodes[i] + if (child.id == id) { + return child + } + } + return null +} + +function firstElement(t) { + for (i = 0; i < t.childNodes.length; i++) { + var child = t.childNodes[i] + if (child.nodeType == 1) { + return child + } + } + return null +} + +/** + * Return the text contents of an element as a floating point number. + */ +function asFloat(t) { + return parseFloat(t.innerHTML) +} diff --git a/activemq-web/src/webapp/sandbox/index.html b/activemq-web/src/webapp/sandbox/index.html new file mode 100644 index 0000000000..6ff4772df0 --- /dev/null +++ b/activemq-web/src/webapp/sandbox/index.html @@ -0,0 +1,15 @@ + + +Sandbox + + + + +

Sandbox

+ +

This area contains the old HTML and JavaScript demos. We've now +migrated to OpenRico instead to reuse +an off the shelf Ajax library.

+ + + diff --git a/activemq-web/src/webapp/sandbox/portfolio.html b/activemq-web/src/webapp/sandbox/portfolio.html new file mode 100755 index 0000000000..f304ca813e --- /dev/null +++ b/activemq-web/src/webapp/sandbox/portfolio.html @@ -0,0 +1,77 @@ + + + My Portfolio + + + + + + + + +

My Portfolio

+ +

+ This example displays an example stock portfolio. + In a real system this page would be generated dynamically based on the + users current stock portfolio +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
StockDescriptionAmountPriceValueCostP & L
IBMWIBM Stock100019000
MSFTMicrosoft600022000
BEASBEA Stock110012342
SUNWSun Microsystems Inc30007700
+ +
+ + + + \ No newline at end of file diff --git a/activemq-web/src/webapp/sandbox/portfolio.js b/activemq-web/src/webapp/sandbox/portfolio.js new file mode 100755 index 0000000000..df72dbb9be --- /dev/null +++ b/activemq-web/src/webapp/sandbox/portfolio.js @@ -0,0 +1,32 @@ +// updates the portfolio row for a given message and symbol +function updatePortfolioRow(message, destination) { + + var priceMessage = message.documentElement + + var price = parseFloat(priceMessage.getAttribute('bid')) + var symbol = priceMessage.getAttribute('stock') + var movement = priceMessage.getAttribute('movement') + if (movement == null) { + movement = 'up' + } + + var row = document.getElementById(symbol) + + // perform portfolio calculations + var value = asFloat(find(row, 'amount')) * price + var pl = value - asFloat(find(row, 'cost')) + + // now lets update the HTML DOM + find(row, 'price').innerHTML = fixedDigits(price, 2) + find(row, 'value').innerHTML = fixedDigits(value, 2) + find(row, 'pl').innerHTML = fixedDigits(pl, 2) + find(row, 'price').className = movement + find(row, 'pl').className = pl >= 0 ? 'up' : 'down' +} + +var connection = new Connection("jms/STOCKS/*") + +function subscribe() { + connection.addMessageListener(/^STOCKS\..*$/, updatePortfolioRow) +} + diff --git a/activemq-web/src/webapp/sandbox/util.js b/activemq-web/src/webapp/sandbox/util.js new file mode 100755 index 0000000000..03c0f05671 --- /dev/null +++ b/activemq-web/src/webapp/sandbox/util.js @@ -0,0 +1,35 @@ +/// ----------------- +// Original code by Joe Walnes +// ----------------- + +/*** Convenience methods, added as mixins to standard classes (object prototypes) ***/ + +/** + * Return number as fixed number of digits. + */ +//Number.prototype.fixedDigits = function(digits) { +function fixedDigits(t, digits) { + return (t.toFixed) ? t.toFixed(digits) : this +} + +/** + * Find direct child of an element, by id. + */ +// Element.prototype.find = function(id) { +function find(t, id) { + for (i = 0; i < t.childNodes.length; i++) { + var child = t.childNodes[i] + if (child.id == id) { + return child + } + } + return null +} + +/** + * Return the text contents of an element as a floating point number. + */ +//Element.prototype.asFloat = function() { +function asFloat(t) { + return parseFloat(t.innerHTML) +} diff --git a/activemq-web/src/webapp/sandbox/webmq.js b/activemq-web/src/webapp/sandbox/webmq.js new file mode 100755 index 0000000000..e13eef2f95 --- /dev/null +++ b/activemq-web/src/webapp/sandbox/webmq.js @@ -0,0 +1,132 @@ +// ----------------- +// Original code by Joe Walnes +// ----------------- + +function Connection(webmqUrl, id) { + this.messageListeners = new Array() + this.webmqUrl = webmqUrl + if (id == null) { + // TODO use secure random id generation + id = Math.round(Math.random() * 100000000) + } + this.id = id + // TODO don't start anything until document has finished loading + var http = this.createHttpControl() + this.getNextMessageAndLoop(webmqUrl, id, http) +} + +Connection.prototype.getNextMessageAndLoop = function(webmqUrl, id, http) { + http.open("GET", webmqUrl + "?id=" + id + "&xml=true", true) + var connection = this + http.onreadystatechange = function() { + if (http.readyState == 4) { + var ok + try { + ok = http.status && http.status == 200 + } + catch (e) { + ok = false // sometimes accessing the http.status fields causes errors in firefox. dunno why. -joe + } + if (ok) { + connection.processIncomingMessage(http) + } + // why do we have to create a new instance? + // this is not required on firefox but is on mozilla + //http.abort() + http = connection.createHttpControl() + connection.getNextMessageAndLoop(webmqUrl, id, http) + } + } + http.send(null) +} + +Connection.prototype.sendMessage = function(destination, message) { + // TODO should post via body rather than URL + // TODO specify destination in message + var http = this.createHttpControl() + http.open("POST", this.webmqUrl + "?id=" + this.id + "&body=" + message, true) + http.send(null) +} + +Connection.prototype.processIncomingMessage = function(http) { + var destination = http.getResponseHeader("destination") + var message = http.responseXML + if (message == null) { + message = http.responseText + } + //alert(message.responseText) + for (var j = 0; j < this.messageListeners.length; j++) { + var registration = this.messageListeners[j] + if (registration.matcher(destination)) { + registration.messageListener(message, destination) + } + } +} + +Connection.prototype.addMessageListener = function(matcher, messageListener) { + var wrappedMatcher + if (matcher.constructor == RegExp) { + wrappedMatcher = function(destination) { + return matcher.test(destination) + } + } + else if (matcher.constructor == String) { + wrappedMatcher = function(destination) { + return matcher == destination + } + } + else { + wrappedMatcher = matcher + } + this.messageListeners[this.messageListeners.length] = { matcher: wrappedMatcher, messageListener: messageListener } +} + +Connection.prototype.createHttpControl = function() { + // for details on using XMLHttpRequest see + // http://webfx.eae.net/dhtml/xmlextras/xmlextras.html + try { + if (window.XMLHttpRequest) { + var req = new XMLHttpRequest() + + // some older versions of Moz did not support the readyState property + // and the onreadystate event so we patch it! + if (req.readyState == null) { + req.readyState = 1 + req.addEventListener("load", function () { + req.readyState = 4 + if (typeof req.onreadystatechange == "function") { + req.onreadystatechange() + } + }, false) + } + + return req + } + if (window.ActiveXObject) { + return new ActiveXObject(this.getControlPrefix() + ".XmlHttp") + } + } + catch (ex) {} + // fell through + throw new Error("Your browser does not support XmlHttp objects") +} + +Connection.prototype.getControlPrefix = function() { + if (this.prefix) { + return this.prefix + } + + var prefixes = ["MSXML2", "Microsoft", "MSXML", "MSXML3"] + var o, o2 + for (var i = 0; i < prefixes.length; i++) { + try { + // try to create the objects + o = new ActiveXObject(prefixes[i] + ".XmlHttp") + o2 = new ActiveXObject(prefixes[i] + ".XmlDom") + return this.prefix = prefixes[i] + } + catch (ex) {} + } + throw new Error("Could not find an installed XML parser") +} + diff --git a/activemq-web/src/webapp/send.html b/activemq-web/src/webapp/send.html new file mode 100755 index 0000000000..ef7f011e8d --- /dev/null +++ b/activemq-web/src/webapp/send.html @@ -0,0 +1,27 @@ + + + + Send a JMS Message + + + + +

Send a JMS Message

+ +
+

+ +

+

+ +

+

+ + +

+
+ + + \ No newline at end of file diff --git a/activemq-web/src/webapp/style.css b/activemq-web/src/webapp/style.css new file mode 100755 index 0000000000..dda06b03c7 --- /dev/null +++ b/activemq-web/src/webapp/style.css @@ -0,0 +1,425 @@ + +body th { + font-family: Verdana, Helvetica, Arial, sans-serif; + margin: 0; +} + +div { + line-height: 1.5em; +} + +a { + color: #008800; + text-decoration: none; + font-weight: bold; +} + +table, th, td { + border: none; +} + +.a td { + background: #ddd; + color: #000; + } + +.b td { + background: #efefef; + color: #000; + } + + +.navLink a { + font-weight: normal; +} + +.navLink { + margin-left: 5px; +} + +.navLink:first-line { + margin-left: -5px; +} + + +a:link.selfref, a:visited.selfref { +} + +a:link, a:visited { +} + +a:active, a:hover { + text-decoration: underline; +} + +a.plain:active, a.plain:hover { + text-decoration: none; +} + +.sectionTitle a { + text-decoration: underline; +} +.subsectionTitle a { + text-decoration: underline; +} + +span.highlight { + font-weight: bold; + color: #990000; +} + +#layout { + margin: 0px; + padding: 0px; +} + +#banner { + padding: 8px; +} + +#breadcrumbs { + border-top: 1px solid #009900; + padding-left: 12px; + padding-right: 12px; + padding-top: 2px; + padding-bottom: 2px; + font-size: x-small; +} + +#breadcrumbs td { + font-size: x-small; +} + +#breadcrumbs a { + font-weight: bold; +} + +#layout { + border-top: 1px solid #009900; + padding: 0px; + margin: 0px; + +} + +.navSection { + background-color: #ffffff; + border: 1px solid #999999; + border-top: none; + padding: 0px; + margin-bottom: 8px; + font-size: small; +} + +.navSection a { + font-weight: normal; +} + +.navSectionHead { + border-top: 1px solid #999999; + border-bottom: 1px solid #999999; + color: #555555; + padding: 4px; + margin-left: 0px; + margin-right: 0px; + background-color: #eeeeee; + font-weight: bold; + font-size: x-small; +} + +.navLink { + padding-top: 2px; + padding-bottom: 2px; + padding-left: 14px; + font-size: small; +} + +.section { + padding-bottom: 16px; +} + +.sectionTitle, h1 { + padding: 4px; + border: 1px solid #aaaaaa; + color: #007700; + font-size: x-large; + background-color: #dddddd; +} + +h1 { + margin: 0px; + margin-bottom: .2em; +} + +.subsection { + padding-left: 20px; + margin-top: 10px; + margin-bottom: 10px; +} + +body:first-child { + padding-top: 0px; +} + +.subsectionTitle, h2 { + margin-top: 8px; + margin-bottom: 8px; + padding: 6px; +/* + border-left: 1px solid #999999; + border-bottom: 1px solid #999999; + border: 1px solid #999999; +*/ + border-top: 1px solid #dddddd; + border-bottom: 1px solid #dddddd; + border-left: 2px solid #999999; + border-right: 2px solid #999999; + color: #007700; + background-color: #eeeeee; + font-weight: normal; + font-size: larger; +} + +.sectionTitle a { + font-weight: normal; +} + +.subsectionTitle a { + font-weight: normal; +} + +.subsubsection { + padding-left: 30px; +} + +.subsubsectionTitle, h3, .blogheading { + font-weight: bold; + /*background-color: #eeeeee;*/ + border: 1px solid #cccccc; + padding: .2em; +} + + +p { + line-height: 1.8em; + /*padding-left: 20px;*/ + padding-right: 20px; +} + +blockquote p { + padding-left: 0px; + padding-right: 0px; +} + +ul { + padding-left: 1.5em; +} + +li { + margin-right: 15%; + line-height: 1.6em; +} + +#leftColumn { + border-right: 1px solid #cccccc; + background-color: #ffffff; + padding: 12px; + font-size: small; +} + +#extraColumn { + padding: 12px; +} + +#navBox { +} + +#rightColumn { + padding: 12px; + border-right: 1px solid #cccccc; +} + +#contentBox { + +} + +table.bodyTable, table.wikitable { + margin: 10px; + border-collapse: collapse; + border-spacing: 0pt; + background-color: #eeeeee; +} + +#Content table.grid { + border: 1px solid #bbbbbb; +} + +table.grid { + padding: 0px; + border-collapse: collapse; + border-spacing: 0pt; +} + +table.grid th { + background-color: #eeeeee; + font-size: smaller; + padding: 4px; + border-bottom: 1px solid #bbbbbb; +} + +table.grid td { + font-size: x-small; + border: 1px solid #bbbbbb; + padding: 3px; +} + +table.bodyTable th, table.bodyTable td, table.wikitable th, table.wikitable td { + border: 1px solid #999999; + font-size: smaller; + padding: 4px; +} + + +table.bodyTable th, table.wikitable th { + text-align: left; + background-color: #dddddd; + border: 2px solid #999999; + padding: 4px; +} + +.nobr + white-space: nowrap; +} + +table.bodyTable td { + padding: 4px; +} + +/* +table.bodyTable th, table.wikitable th { + border-bottom: 2px solid #999999; +} + +table.bodyTable tr.a { + background-color: #dedede; +} + +table.bodyTable tr.b { + background-color: #efefef; +} +*/ + +.source, .code { + padding: 12px; + margin: 12px; + border: 1px solid #007700; + border-left: 2px solid #007700; + border-right: 2px solid #007700; + color: #555555; +} + +pre { + padding: 12px; +} + +.java-keyword { + color: #009900; +} + +.java-object { + color: #000099; +} + +.java-quote { + color: #990000; +} + + +.source, .code pre { + margin: 0px; + margin-left: 8px; + padding: 0px; +} + +#footer { + padding-left: 4px; + border-top: 1px solid #999999; + color: #888888; + font-size: x-small; +} + +blockquote { + border-top: 1px solid #bbbbbb; + border-bottom: 1px solid #bbbbbb; + border-left: 3px solid #bbbbbb; + border-right: 3px solid #bbbbbb; + padding: 12px; + margin-left: 10%; + margin-right: 15%; + color: #666666; + background-color: white; + line-height: 1.5em; +} + +input[type="text"] { + margin: 0px; + border: 1px solid #999999; + background-color: #dddddd; +} + +input.required { + margin: 0px; + border: 1px solid #990000; +} + +input { + border: 1px solid #999999; +} + +textarea { + border: 1px solid #999999; +} + +textarea.required { + border: 1px solid #990000; +} + +label { + font-size: smaller; +} + +label.required { + color: #990000; +} + +.searchResults { + color: black; +} + +.searchResults b { + color: #007700; +} + + +.linecomment { color: #bbbbbbb; } +.blockcomment { color: #bbbbbbb; } +.prepro { color: #0000BB; } +.select {} +.quote { color: #770000; } +.category1 { color: #007700; } +.category2 { color: #0000BB; } +.category3 { color: #0000BB; } + + +/* Portfolio demo */ +td.amount, td.price, td.value, td.pl, td.cost { + text-align: right +} + +div.up { + color: black; +} + +div.down { + color: red; +} + diff --git a/assembly/.cvsignore b/assembly/.cvsignore new file mode 100755 index 0000000000..896c38c51d --- /dev/null +++ b/assembly/.cvsignore @@ -0,0 +1,6 @@ +bin +target +.classpath +.project +junit*.properties +derby.log diff --git a/assembly/maven.xml b/assembly/maven.xml new file mode 100755 index 0000000000..3ea04c3ef3 --- /dev/null +++ b/assembly/maven.xml @@ -0,0 +1,749 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +-------------------------------------------------------+ + | C R E A T I N G B I N A R Y D I S T R I B U T I O N | + +-------------------------------------------------------+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${artifact.path} ${dependency.getProperty('optional') == 'true'} + + + + + ${artifact.path} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +-------------------------------------------------------+ + | C R E A T I N G S O U R C E D I S T R I B U T I O N | + +-------------------------------------------------------+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Running an ActiveMQ server with $$config = ${config} + + + + + + + + + + + + + + + + + + + + Running an ActiveMQ server at $$url = ${url} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Profiling an ActiveMQ server with $$config = ${config} + + + + + + + + + + + + + + + + + Profiling an ActiveMQ server at $$url = ${url} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Running an ActiveMQ web server with webapp = ${url} + + + + + + + + + + + + + + + + + + + + + + + + + + + Running consumer against server at $$url = ${url} for subject $$subject = ${subject} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Running producer against server at $$url = ${url} for subject $$subject = ${subject} + + + + + + + + + + + + + + + + + + Using JNDI properties ${jndi} + + + + + + + + + + Running producer using subject $$subject = ${subject} + + + + + + + + + + + + Using JNDI properties ${jndi} + + + + + + + Running consumer using subject $$subject = ${subject} + + + + + + + + + + + + + + + + + + + + + + + + + + + Running topic consumer against server at $$url = ${url} for subject $$subject = ${subject} with $$topic = ${topic} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Running topic producer against server at $$url = ${url} for subject $$subject = ${subject} with $$topic = ${topic} $$messageSize = ${messageSize} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Running topic producer against server at $$url = ${url} for subject $$subject = ${subject} with $$topic = ${topic} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Running ActiveMQ Journal Benchmark + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Running HOWL Journal Benchmark + + + + + + + + + + + + + + + + + + + + + + Running Derby Network Server + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assembly/project.properties b/assembly/project.properties new file mode 100755 index 0000000000..2c9b6c22cf --- /dev/null +++ b/assembly/project.properties @@ -0,0 +1,22 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=jar + +maven.dist.assembly.dir=${maven.build.dir}/${maven.final.name} +maven.dist.bin.archive.dir=${maven.dist.assembly.dir}/bin +maven.dist.src.archive.dir=${maven.dist.assembly.dir}/src +maven.dist.bin.assembly.dir=${maven.dist.assembly.dir}/bin/${maven.final.name} +maven.dist.src.assembly.dir=${maven.dist.assembly.dir}/src/${maven.final.name} +maven.dist.dir=${maven.build.dir}/distributions +maven.dist.tar.executable=tar +maven.dist.gunzip.executable=gunzip + +maven.repo.remote=\ +http://www.ibiblio.org/maven,\ +http://dist.codehaus.org,\ +http://cvs.apache.org/repository,\ +http://www.openejb.org/maven + +maven.jar.mainclass = org.activemq.broker.Main + diff --git a/assembly/project.xml b/assembly/project.xml new file mode 100755 index 0000000000..fa1b3653b1 --- /dev/null +++ b/assembly/project.xml @@ -0,0 +1,332 @@ + + + + 3 + ${basedir}/../etc/project.xml + ActiveMQ :: Assembly + activemq + ActiveMQ Assembly + ActiveMQ Assembly creates an ActiveMQ distribution + + + + + + + + activemq + activemq-core + ${pom.currentVersion} + + true + true + + + + + + + activeio + activeio + ${activeio_version} + + true + true + + + + + + activemq + activemq-core-test + ${pom.currentVersion} + + + + activemq + activemq-ra + ${pom.currentVersion} + rar + + true + + + + + + + + activesoap + activesoap + ${activesoap_version} + jar + + true + + + + + + commons-beanutils + commons-beanutils + ${commons_beanutils_version} + + true + true + true + + + + commons-collections + commons-collections + ${commons_collections_version} + + true + true + true + + + + + commons-httpclient + commons-httpclient + ${commons_httpclient_version} + + true + + + + + + + jetty + servlet-api + ${servlet_api_version} + + + jetty + jetty + ${jetty_version} + + true + + + + + jrms + jrms + ${jrms_version} + + true + + + + + xstream + xstream + ${xstream_version} + + true + + + + + xmlpull + xmlpull + ${xmlpull_version} + + true + + + + + activemq + jmdns + ${jmdns_version} + + true + + + + + activecluster + activecluster + ${activecluster_version} + + true + + + + + xbean + xbean-spring + ${xbean_spring_version} + http://www.gbean.org + + true + + + + + springframework + spring + ${spring_version} + http://www.springframework.org + + true + + + + + + commons-dbcp + commons-dbcp + ${commons_dbcp_version} + + true + + + + commons-pool + commons-pool + ${commons_pool_version} + + true + + + + + + org.apache.derby + derby + ${derby_version} + + true + true + + + + org.apache.derby + derbynet + ${derbynet_version} + + + + xerces + xercesImpl + ${xercesImpl_version} + + + xerces + xmlParserAPIs + ${xercesImpl_version} + + + + + geronimo-spec + geronimo-spec-jsp + ${geronimo_spec_jsp_version} + + false + + + + + + geronimo-spec + geronimo-spec-j2ee-connector + ${geronimo_spec_j2ee_connector_version} + + true + + + + + mx4j + mx4j + ${mx4j_version} + + true + + + + + mx4j + mx4j-remote + ${mx4j_remote_version} + + true + + + + + + + dev@activemq.codehaus.org + src/java + src/test + + + + + + + + src/test + + **/*.properties + **/*.xml + + + + + **/*Test.* + + + + + + + + **/DurableConsumerCloseAndReconnectTest.* + **/QueueConsumerCloseAndReconnectTest.* + **/ChangeSessionDeliveryModeTest.* + **/DeadLetterTest.* + + + + + + src/resources + + **/* + + + + + + + diff --git a/assembly/src/release/.cvsignore b/assembly/src/release/.cvsignore new file mode 100755 index 0000000000..e43b0f9889 --- /dev/null +++ b/assembly/src/release/.cvsignore @@ -0,0 +1 @@ +.DS_Store diff --git a/assembly/src/release/bin/activemq b/assembly/src/release/bin/activemq new file mode 100755 index 0000000000..0936f7a694 --- /dev/null +++ b/assembly/src/release/bin/activemq @@ -0,0 +1,132 @@ +#!/bin/sh + +# ActiveMQ shell script +# +# $Id: activemq,v 1.1.1.1 2005/03/11 21:14:04 jstrachan Exp $ +# +# This script is heavily based on the Ant script +# +# Copyright (c) 2001-2003 The Apache Software Foundation. All rights +# reserved. + +# load system-wide activemq configuration +if [ -f "/etc/activemq.conf" ] ; then + . /etc/activemq.conf +fi + +# provide default values for people who don't use RPMs +if [ -z "$usejikes" ] ; then + usejikes=false; +fi + +# load user activemq configuration +if [ -f "$HOME/.activemqrc" ] ; then + . "$HOME/.activemqrc" +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +case "`uname`" in + CYGWIN*) cygwin=true ;; + Darwin*) darwin=true + if [ -z "$JAVA_HOME" ] ; then + JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home + fi + ;; +esac + +if [ -z "$ACTIVEMQ_HOME" ] ; then + # try to find ACTIVEMQ + if [ -d /opt/activemq ] ; then + ACTIVEMQ_HOME=/opt/activemq + fi + + if [ -d "${HOME}/opt/activemq" ] ; then + ACTIVEMQ_HOME="${HOME}/opt/activemq" + fi + + ## resolve links - $0 may be a link to activemq's home + PRG="$0" + progname=`basename "$0"` + saveddir=`pwd` + + # need this for relative symlinks + dirname_prg=`dirname "$PRG"` + cd "$dirname_prg" + + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '.*/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi + done + + ACTIVEMQ_HOME=`dirname "$PRG"`/.. + + cd "$saveddir" + + # make it fully qualified + ACTIVEMQ_HOME=`cd "$ACTIVEMQ_HOME" && pwd` +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$ACTIVEMQ_HOME" ] && + ACTIVEMQ_HOME=`cygpath --unix "$ACTIVEMQ_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD=`which java 2> /dev/null ` + if [ -z "$JAVACMD" ] ; then + JAVACMD=java + fi + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." + echo " We cannot execute $JAVACMD" + exit 1 +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + ACTIVEMQ_HOME=`cygpath --windows "$ACTIVEMQ_HOME"` + JAVA_HOME=`cygpath --windows "$JAVA_HOME"` + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + CYGHOME=`cygpath --windows "$HOME"` +fi + + +if [ -n "ACTIVEMQ_OPTS" ] ; then + ACTIVEMQ_OPTS="-Xmx512M -Dderby.system.home=../data -Dderby.storage.fileSyncTransactionLog=true" +fi + +# Uncomment to enable YourKit profiling +#ACTIVEMQ_DEBUG_OPTS="-Xrunyjpagent" + +# Uncomment to enable remote debugging +#ACTIVEMQ_DEBUG_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005" + +if [ -n "$CYGHOME" ]; then + exec "$JAVACMD" $ACTIVEMQ_DEBUG_OPTS $ACTIVEMQ_OPTS -classpath "" -Dactivemq.home="${ACTIVEMQ_HOME}" -Dcygwin.user.home="$CYGHOME" -jar ${ACTIVEMQ_HOME}/bin/run.jar $@ +else + exec "$JAVACMD" $ACTIVEMQ_DEBUG_OPTS $ACTIVEMQ_OPTS -classpath "" -Dactivemq.home="${ACTIVEMQ_HOME}" -jar ${ACTIVEMQ_HOME}/bin/run.jar $@ +fi + diff --git a/assembly/src/release/bin/activemq.bat b/assembly/src/release/bin/activemq.bat new file mode 100755 index 0000000000..e728114ad3 --- /dev/null +++ b/assembly/src/release/bin/activemq.bat @@ -0,0 +1,106 @@ +@echo off + +REM ActiveMQ shell script +REM +REM $Id: activemq.bat,v 1.1.1.1 2005/03/11 21:14:04 jstrachan Exp $ +REM +REM This script is heavily based on the Ant script +REM +REM Copyright (c) 2001-2003 The Apache Software Foundation. All rights +REM reserved. + +if exist "%HOME%\activemqrc_pre.bat" call "%HOME%\activemqrc_pre.bat" + +if "%OS%"=="Windows_NT" @setlocal + +rem %~dp0 is expanded pathname of the current script under NT +set DEFAULT_ACTIVEMQ_HOME=%~dp0.. + +if "%ACTIVEMQ_HOME%"=="" set ACTIVEMQ_HOME=%DEFAULT_ACTIVEMQ_HOME% +set DEFAULT_ACTIVEMQ_HOME= + +rem Slurp the command line arguments. This loop allows for an unlimited number +rem of arguments (up to the command line limit, anyway). +set ACTIVEMQ_CMD_LINE_ARGS=%1 +if ""%1""=="""" goto doneStart +shift +:setupArgs +if ""%1""=="""" goto doneStart +set ACTIVEMQ_CMD_LINE_ARGS=%ACTIVEMQ_CMD_LINE_ARGS% %1 +shift +goto setupArgs +rem This label provides a place for the argument list loop to break out +rem and for NT handling to skip to. + +:doneStart +rem find ACTIVEMQ_HOME if it does not exist due to either an invalid value passed +rem by the user or the %0 problem on Windows 9x +if exist "%ACTIVEMQ_HOME%\README.txt" goto checkJava + +rem check for activemq in Program Files on system drive +if not exist "%SystemDrive%\Program Files\activemq" goto checkSystemDrive +set ACTIVEMQ_HOME=%SystemDrive%\Program Files\activemq +goto checkJava + +:checkSystemDrive +rem check for activemq in root directory of system drive +if not exist %SystemDrive%\activemq\README.txt goto checkCDrive +set ACTIVEMQ_HOME=%SystemDrive%\activemq +goto checkJava + +:checkCDrive +rem check for activemq in C:\activemq for Win9X users +if not exist C:\activemq\README.txt goto noAntHome +set ACTIVEMQ_HOME=C:\activemq +goto checkJava + +:noAntHome +echo ACTIVEMQ_HOME is set incorrectly or activemq could not be located. Please set ACTIVEMQ_HOME. +goto end + +:checkJava +set _JAVACMD=%JAVACMD% +set LOCALCLASSPATH=%CLASSPATH% + +set JAVA_EXT_DIRS=%JAVA_HOME%\lib\ext;%ACTIVEMQ_HOME%;%ACTIVEMQ_HOME%\lib;%ACTIVEMQ_HOME%\lib\optional + +if "%JAVA_HOME%" == "" goto noJavaHome +if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome +if "%_JAVACMD%" == "" set _JAVACMD=%JAVA_HOME%\bin\java.exe +goto runAnt + +:noJavaHome +if "%_JAVACMD%" == "" set _JAVACMD=java.exe +echo. +echo Warning: JAVA_HOME environment variable is not set. +echo. + +:runAnt + +if "%ACTIVEMQ_OPTS%" == "" set ACTIVEMQ_OPTS=-Xmx512M -Dderby.system.home="..\data" -Dderby.storage.fileSyncTransactionLog=true + +REM Uncomment to enable YourKit profiling +REM SET ACTIVEMQ_DEBUG_OPTS="-Xrunyjpagent" + +REM Uncomment to enable remote debugging +REM SET ACTIVEMQ_DEBUG_OPTS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 + +set LOCALCLASSPATH=%ACTIVEMQ_HOME%\conf;%LOCALCLASSPATH% + + +"%_JAVACMD%" %ACTIVEMQ_DEBUG_OPTS% %ACTIVEMQ_OPTS% -Djava.ext.dirs="%JAVA_EXT_DIRS%" -classpath "%LOCALCLASSPATH%" -Dactivemq.home="%ACTIVEMQ_HOME%" -jar %ACTIVEMQ_HOME%/bin/run.jar %ACTIVEMQ_ARGS% %ACTIVEMQ_CMD_LINE_ARGS% + + +goto end + + +:end +set LOCALCLASSPATH= +set _JAVACMD= +set ACTIVEMQ_CMD_LINE_ARGS= + +if "%OS%"=="Windows_NT" @endlocal + +:mainEnd +if exist "%HOME%\activemqrc_post.bat" call "%HOME%\activemqrc_post.bat" + diff --git a/assembly/src/release/bin/lcp.bat b/assembly/src/release/bin/lcp.bat new file mode 100755 index 0000000000..b8c1102c2f --- /dev/null +++ b/assembly/src/release/bin/lcp.bat @@ -0,0 +1,16 @@ +REM Copyright (c) 2001-2003 The Apache Software Foundation. All rights +REM reserved. + +set _CLASSPATHCOMPONENT=%1 +if ""%1""=="""" goto gotAllArgs +shift + +:argCheck +if ""%1""=="""" goto gotAllArgs +set _CLASSPATHCOMPONENT=%_CLASSPATHCOMPONENT% %1 +shift +goto argCheck + +:gotAllArgs +set LOCALCLASSPATH=%_CLASSPATHCOMPONENT%;%LOCALCLASSPATH% + diff --git a/assembly/src/release/conf/activemq.xml b/assembly/src/release/conf/activemq.xml new file mode 100755 index 0000000000..7a636182ac --- /dev/null +++ b/assembly/src/release/conf/activemq.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assembly/src/release/conf/log4j.properties b/assembly/src/release/conf/log4j.properties new file mode 100755 index 0000000000..adf3d40765 --- /dev/null +++ b/assembly/src/release/conf/log4j.properties @@ -0,0 +1,26 @@ +# +# The logging properties used during tests.. +# +log4j.rootLogger=INFO, stdout +log4j.logger.org.activemq.spring=WARN +log4j.logger.org.springframework=WARN +log4j.logger.org.xbean.spring=WARN + +# When debugging or reporting problems to the ActiveMQ team, +# comment out the above lines and uncomment the next. +#log4j.rootLogger=DEBUG, out, stdout + +# CONSOLE appender not used by default +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%-5p %-30.30c{1} - %m%n +log4j.appender.stdout.threshold=INFO + +# File appender +log4j.appender.out=org.apache.log4j.RollingFileAppender +log4j.appender.out.file=../data/activemq.log +log4j.appender.out.maxFileSize=1024KB +log4j.appender.out.maxBackupIndex=5 +log4j.appender.out.append=true +log4j.appender.out.layout=org.apache.log4j.PatternLayout +log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n diff --git a/assembly/src/release/docs/index.html b/assembly/src/release/docs/index.html new file mode 100755 index 0000000000..1f218b8404 --- /dev/null +++ b/assembly/src/release/docs/index.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/assembly/src/release/example/build.xml b/assembly/src/release/example/build.xml new file mode 100755 index 0000000000..b862ec359c --- /dev/null +++ b/assembly/src/release/example/build.xml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This script requires Ant 1.6 or higher + + usage: + ant -help display ant help screen + ant help display this message + ant clean delete the built directory + + ant consumer creates a consumer which waits until a specific number of messages have been received + ant producer creates a producer publishing a number of messages + + ant war creates a WAR deployment unit of the ActiveMQ Broker + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Running consumer against server at $$url = ${url} for subject $$subject = ${subject} + + + + + + + + + + + + + + + + + Running producer against server at $$url = ${url} for subject $$subject = ${subject} + + + + + + + + + + + + + + + + + diff --git a/assembly/src/release/example/conf/activemq-stomp.xml b/assembly/src/release/example/conf/activemq-stomp.xml new file mode 100644 index 0000000000..0ffb686ade --- /dev/null +++ b/assembly/src/release/example/conf/activemq-stomp.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.apache.derby.jdbc.EmbeddedDriver + + + + jdbc:derby:derbydb;create=true + + + + + + + + + true + + + + + + + com.mysql.jdbc.Driver + + + jdbc:mysql://localhost/activemq + + + myname + + + mypassword + + + true + + + + + + diff --git a/assembly/src/release/example/conf/activemq.dtd b/assembly/src/release/example/conf/activemq.dtd new file mode 100644 index 0000000000..3dbffeb1da --- /dev/null +++ b/assembly/src/release/example/conf/activemq.dtd @@ -0,0 +1,751 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assembly/src/release/example/conf/activemq.xml b/assembly/src/release/example/conf/activemq.xml new file mode 100644 index 0000000000..aac4e482b3 --- /dev/null +++ b/assembly/src/release/example/conf/activemq.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assembly/src/release/example/conf/log4j.properties b/assembly/src/release/example/conf/log4j.properties new file mode 100644 index 0000000000..7c7e87259e --- /dev/null +++ b/assembly/src/release/example/conf/log4j.properties @@ -0,0 +1,22 @@ +# +# The logging properties used by the standalone ActiveMQ broker +# +log4j.rootLogger=INFO, stdout, logfile + +# CONSOLE appender +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss} %-5p %m%n + +# Log File appender +log4j.appender.logfile=org.apache.log4j.FileAppender +log4j.appender.logfile.layout=org.apache.log4j.PatternLayout +log4j.appender.logfile.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +log4j.appender.logfile.file=activemq.log +log4j.appender.logfile.append=true + +# +# You can change logger levels here. +# +log4j.logger.org.activemq=INFO +log4j.logger.org.activemq.spring=WARN diff --git a/assembly/src/release/example/conf/resin-web.xml b/assembly/src/release/example/conf/resin-web.xml new file mode 100644 index 0000000000..9bfb109c1e --- /dev/null +++ b/assembly/src/release/example/conf/resin-web.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/assembly/src/release/example/conf/web.xml b/assembly/src/release/example/conf/web.xml new file mode 100644 index 0000000000..8af18a8aa9 --- /dev/null +++ b/assembly/src/release/example/conf/web.xml @@ -0,0 +1,24 @@ + + + + + + + ActiveMQ Message Broker Web Application + + + ActiveMQ web application to deploy the Broker in a servlet engine. + + + + brokerURI + /WEB-INF/activemq.xml + + + + org.activemq.web. + + + diff --git a/assembly/src/release/example/src/ConsumerTool.java b/assembly/src/release/example/src/ConsumerTool.java new file mode 100755 index 0000000000..fef3a9bf05 --- /dev/null +++ b/assembly/src/release/example/src/ConsumerTool.java @@ -0,0 +1,179 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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. + * + **/ + +import javax.jms.Connection; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; +import java.io.IOException; + +/** + * A simple tool for consuming messages + * + * @version $Revision: 1.1.1.1 $ + */ +public class ConsumerTool extends ToolSupport implements MessageListener, ExceptionListener { + + protected int count = 0; + protected int dumpCount = 10; + protected boolean verbose = true; + protected int maxiumMessages = 0; + private boolean pauseBeforeShutdown; + private boolean running; + private Session session; + private long sleepTime=0; + + + public static void main(String[] args) { + ConsumerTool tool = new ConsumerTool(); + if (args.length > 0) { + tool.url = args[0]; + } + if (args.length > 1) { + tool.topic = args[1].equalsIgnoreCase("true"); + } + if (args.length > 2) { + tool.subject = args[2]; + } + if (args.length > 3) { + tool.durable = args[3].equalsIgnoreCase("true"); + } + if (args.length > 4) { + tool.maxiumMessages = Integer.parseInt(args[4]); + } + if (args.length > 5) { + tool.clientID = args[5]; + } + if (args.length > 6) { + tool.transacted = "true".equals(args[6]); + } + if (args.length > 7) { + tool.sleepTime = Long.parseLong(args[7]); + } + + tool.run(); + } + + public void run() { + try { + running = true; + + System.out.println("Connecting to URL: " + url); + System.out.println("Consuming " + (topic ? "topic" : "queue") + ": " + subject); + System.out.println("Using " + (durable ? "durable" : "non-durable") + " subscription"); + + Connection connection = createConnection(); + connection.setExceptionListener(this); + session = createSession(connection); + MessageConsumer consumer = null; + if (durable && topic) { + consumer = session.createDurableSubscriber((Topic) destination, consumerName); + } + else { + consumer = session.createConsumer(destination); + } + if (maxiumMessages <= 0) { + consumer.setMessageListener(this); + } + + if (maxiumMessages > 0) { + consumeMessagesAndClose(connection, session, consumer); + } + } + catch (Exception e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + + public void onMessage(Message message) { + try { + if (message instanceof TextMessage) { + TextMessage txtMsg = (TextMessage) message; + if (verbose) { + + String msg = txtMsg.getText(); + if (msg.length() > 50) { + msg = msg.substring(0, 50) + "..."; + } + + System.out.println("Received: " + msg); + } + } + else { + if (verbose) { + System.out.println("Received: " + message); + } + } + if(transacted) { + session.commit(); + } + /* + if (++count % dumpCount == 0) { + dumpStats(connection); + } + */ + } + catch (JMSException e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } finally { + if( sleepTime> 0 ) { + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + } + } + } + } + + synchronized public void onException(JMSException ex) { + System.out.println("JMS Exception occured. Shutting down client."); + running=false; + } + + synchronized boolean isRunning() { + return running; + } + + protected void consumeMessagesAndClose(Connection connection, Session session, MessageConsumer consumer) throws JMSException, IOException { + System.out.println("We are about to wait until we consume: " + maxiumMessages + " message(s) then we will shutdown"); + + for (int i = 0; i < maxiumMessages && isRunning(); ) { + Message message = consumer.receive(1000); + if( message!=null ) { + i++; + onMessage(message); + } + } + System.out.println("Closing connection"); + consumer.close(); + session.close(); + connection.close(); + if (pauseBeforeShutdown) { + System.out.println("Press return to shut down"); + System.in.read(); + } + } + +} \ No newline at end of file diff --git a/assembly/src/release/example/src/ProducerAndConsumerTool.java b/assembly/src/release/example/src/ProducerAndConsumerTool.java new file mode 100644 index 0000000000..f58f352844 --- /dev/null +++ b/assembly/src/release/example/src/ProducerAndConsumerTool.java @@ -0,0 +1,77 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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. + * + **/ + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; +import java.io.IOException; + +/** + * A simple tool for producing and consuming messages + * + * @version $Revision: 1.1.1.1 $ + */ +public class ProducerAndConsumerTool extends ConsumerTool implements MessageListener { + + public static void main(String[] args) { + ProducerAndConsumerTool tool = new ProducerAndConsumerTool(); + if (args.length > 0) { + tool.url = args[0]; + } + else { + tool.url = "vm://localhost"; + } + if (args.length > 1) { + tool.topic = args[1].equalsIgnoreCase("true"); + } + if (args.length > 2) { + tool.subject = args[2]; + } + if (args.length > 3) { + tool.durable = args[3].equalsIgnoreCase("true"); + } + if (args.length > 4) { + tool.maxiumMessages = Integer.parseInt(args[4]); + } + if (args.length > 5) { + tool.clientID = args[5]; + } + tool.run(); + } + + public void run() { + super.run(); + + // now lets publish some messages + ProducerTool tool = new ProducerTool(); + tool.url = this.url; + tool.topic = this.topic; + tool.subject = this.subject; + tool.durable = this.durable; + tool.clientID = this.clientID; + + tool.run(); + } + + +} \ No newline at end of file diff --git a/assembly/src/release/example/src/ProducerTool.java b/assembly/src/release/example/src/ProducerTool.java new file mode 100755 index 0000000000..dfce879a2a --- /dev/null +++ b/assembly/src/release/example/src/ProducerTool.java @@ -0,0 +1,158 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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. + * + **/ + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import java.util.Date; + +/** + * A simple tool for publishing messages + * + * @version $Revision: 1.2 $ + */ +public class ProducerTool extends ToolSupport { + + protected int messageCount = 10; + protected long sleepTime = 0L; + protected boolean verbose = true; + protected int messageSize = 255; + private long timeToLive; + + public static void main(String[] args) { + runTool(args, new ProducerTool()); + } + + protected static void runTool(String[] args, ProducerTool tool) { + tool.clientID = null; + if (args.length > 0) { + tool.url = args[0]; + } + if (args.length > 1) { + tool.topic = args[1].equalsIgnoreCase("true"); + } + if (args.length > 2) { + tool.subject = args[2]; + } + if (args.length > 3) { + tool.durable = args[3].equalsIgnoreCase("true"); + } + if (args.length > 4) { + tool.messageCount = Integer.parseInt(args[4]); + } + if (args.length > 5) { + tool.messageSize = Integer.parseInt(args[5]); + } + if (args.length > 6) { + if( ! "null".equals(args[6]) ) { + tool.clientID = args[6]; + } + } + if (args.length > 7) { + tool.timeToLive = Long.parseLong(args[7]); + } + if (args.length > 8) { + tool.sleepTime = Long.parseLong(args[8]); + } + if (args.length > 9) { + tool.transacted = "true".equals(args[9]); + } + tool.run(); + } + + public void run() { + try { + System.out.println("Connecting to URL: " + url); + System.out.println("Publishing a Message with size " + messageSize + " to " + (topic ? "topic" : "queue") + ": " + subject); + System.out.println("Using " + (durable ? "durable" : "non-durable") + " publishing"); + System.out.println("Sleeping between publish "+sleepTime+" ms"); + if( timeToLive!=0 ) { + System.out.println("Messages time to live "+timeToLive+" ms"); + } + Connection connection = createConnection(); + Session session = createSession(connection); + MessageProducer producer = createProducer(session); + sendLoop(session, producer); + + System.out.println("Done."); + close(connection, session); + } + catch (Exception e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + + protected MessageProducer createProducer(Session session) throws JMSException { + MessageProducer producer = session.createProducer(destination); + if (durable) { + producer.setDeliveryMode(DeliveryMode.PERSISTENT); + } + else { + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + } + if( timeToLive!=0 ) + producer.setTimeToLive(timeToLive); + return producer; + } + + protected void sendLoop(Session session, MessageProducer producer) throws Exception { + + for (int i = 0; i < messageCount || messageCount==0 ; i++) { + + + TextMessage message = session.createTextMessage(createMessageText(i)); + + if (verbose) { + String msg = message.getText(); + if (msg.length() > 50) { + msg = msg.substring(0, 50) + "..."; + } + System.out.println("Sending message: " + msg); + } + + producer.send(message); + if(transacted) { + session.commit(); + } + + Thread.sleep(sleepTime); + + } + + } + + /** + * @param i + * @return + */ + private String createMessageText(int index) { + StringBuffer buffer = new StringBuffer(messageSize); + buffer.append("Message: " + index + " sent at: " + new Date()); + if (buffer.length() > messageSize) { + return buffer.substring(0, messageSize); + } + for (int i = buffer.length(); i < messageSize; i++) { + buffer.append(' '); + } + return buffer.toString(); + } +} diff --git a/assembly/src/release/example/src/ToolSupport.java b/assembly/src/release/example/src/ToolSupport.java new file mode 100755 index 0000000000..918e1ab744 --- /dev/null +++ b/assembly/src/release/example/src/ToolSupport.java @@ -0,0 +1,86 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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. + * + **/ + +import org.activemq.ActiveMQConnection; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.util.IndentPrinter; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Session; + +/** + * Abstract base class useful for implementation inheritence + * + * @version $Revision: 1.2 $ + */ +public class ToolSupport { + + + protected Destination destination; + protected String subject = "TOOL.DEFAULT"; + protected boolean topic = false; + protected String user = ActiveMQConnection.DEFAULT_USER; + protected String pwd = ActiveMQConnection.DEFAULT_PASSWORD; + protected String url = ActiveMQConnection.DEFAULT_BROKER_URL; + protected boolean transacted = false; + protected boolean durable = false; + protected String clientID; + protected int ackMode = Session.AUTO_ACKNOWLEDGE; + protected String consumerName = "James"; + + + protected Session createSession(Connection connection) throws Exception { + Session session = connection.createSession(transacted, ackMode); + if (topic) { + destination = session.createTopic(subject); + } + else { + destination = session.createQueue(subject); + } + return session; + } + + protected Connection createConnection() throws JMSException, Exception { + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user, pwd, url); + Connection connection = connectionFactory.createConnection(); + if (durable && clientID!=null) { + connection.setClientID(clientID); + } + connection.start(); + return connection; + } + + protected void close(Connection connection, Session session) throws JMSException { + // lets dump the stats + dumpStats(connection); + + if (session != null) { + session.close(); + } + if (connection != null) { + connection.close(); + } + } + + protected void dumpStats(Connection connection) { + ActiveMQConnection c = (ActiveMQConnection) connection; + c.getConnectionStats().dump(new IndentPrinter()); + } +} diff --git a/assembly/src/release/userGuide.html b/assembly/src/release/userGuide.html new file mode 100755 index 0000000000..5cb2f3a237 --- /dev/null +++ b/assembly/src/release/userGuide.html @@ -0,0 +1,96 @@ + + + + Getting Started + + + +

Getting Started
+

+ +

+Welcome to ActiveMQ. This document gives you a quick overview of how to get started trying out ActiveMQ. +

+ +

Running the broker

+ +

+From the binary distribution you can run the ActiveMQ server pretty easily via the bin/activemq command. +e.g. from a shell type +

+
+    cd bin
+    activemq
+
+ +

+The ActiveMQ broker should now have started +

+ +

Running the example programs

+ +

+To run the demo producer/consumer tools, you need Java and Ant installed. +

+ +

Installing Ant

+ + +

+The example programs depend on Ant being installed. There are more detailed +instructions on the Ant website - but essentially you need to follow these steps. +

+
    +
  • +JAVA_HOME must point to your JDK installation, such that $JAVA_HOME/bin contains the java executable and $JAVA_HOME/lib contains some jar files. +
  • +
  • +ANT_HOME must point to where you installed Apache Ant (http://ant.apache.org) +
  • +
  • +then you need to add $JAVA_HOME/bin and $ANT_HOME/bin to your PATH variable. +
  • +
+ +

+To test this is all working, try typing +

+ +
+    java -version
+    cd example
+    ant -version
+
+ +

Running the example producer and consumer

+ +

+Once you've got Java and Ant installed correctly you should be able to run +

+ +
+    cd example
+    ant consumer
+
+ +

+and in another shell +

+ +
+    cd example
+    ant producer
+
+ +

+you can specify exact URLs to connect to via command like arguments. e.g. +

+ +
+    ant producer -Durl=tcp://somehost:port
+
+ + + + + diff --git a/assembly/src/release/var/activemq.log b/assembly/src/release/var/activemq.log new file mode 100755 index 0000000000..e69de29bb2 diff --git a/assembly/src/sample-conf/default.xml b/assembly/src/sample-conf/default.xml new file mode 100755 index 0000000000..3fc79dcd8f --- /dev/null +++ b/assembly/src/sample-conf/default.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + org.apache.derby.jdbc.EmbeddedDriver + + + + jdbc:derby:target/data/derbydb;create=true + + + + + + + + + true + + + + + + diff --git a/assembly/src/sample-conf/derby-jdbc-example.xml b/assembly/src/sample-conf/derby-jdbc-example.xml new file mode 100755 index 0000000000..50eef2b9bc --- /dev/null +++ b/assembly/src/sample-conf/derby-jdbc-example.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.apache.derby.jdbc.EmbeddedDriver + + + + jdbc:derby:target/data/derbydb;create=true + + + + + + + + + true + + + + diff --git a/assembly/src/sample-conf/derbynet-jdbc-example.xml b/assembly/src/sample-conf/derbynet-jdbc-example.xml new file mode 100755 index 0000000000..c3d8f8040f --- /dev/null +++ b/assembly/src/sample-conf/derbynet-jdbc-example.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + com.ibm.db2.jcc.DB2Driver + + + + jdbc:derby:net://localhost/target/data/derbydb;create=true + + + na + + + na + + + true + + + + diff --git a/assembly/src/sample-conf/jdbc-example.xml b/assembly/src/sample-conf/jdbc-example.xml new file mode 100755 index 0000000000..2b24cbcea2 --- /dev/null +++ b/assembly/src/sample-conf/jdbc-example.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assembly/src/sample-conf/jgroups/default.xml b/assembly/src/sample-conf/jgroups/default.xml new file mode 100755 index 0000000000..5058099e36 --- /dev/null +++ b/assembly/src/sample-conf/jgroups/default.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assembly/src/sample-conf/journal-example.xml b/assembly/src/sample-conf/journal-example.xml new file mode 100755 index 0000000000..e1e58e0044 --- /dev/null +++ b/assembly/src/sample-conf/journal-example.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + target/test-data/testJournalConfig/journal + + + + + + + + + + + diff --git a/assembly/src/sample-conf/journaledjdbc-example.xml b/assembly/src/sample-conf/journaledjdbc-example.xml new file mode 100644 index 0000000000..6a086990c1 --- /dev/null +++ b/assembly/src/sample-conf/journaledjdbc-example.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assembly/src/sample-conf/memory-example.xml b/assembly/src/sample-conf/memory-example.xml new file mode 100644 index 0000000000..2cf0d07b2c --- /dev/null +++ b/assembly/src/sample-conf/memory-example.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assembly/src/sample-conf/peer.xml b/assembly/src/sample-conf/peer.xml new file mode 100755 index 0000000000..557141ccc1 --- /dev/null +++ b/assembly/src/sample-conf/peer.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/assembly/src/sample-conf/vm-example.xml b/assembly/src/sample-conf/vm-example.xml new file mode 100755 index 0000000000..343f9cdba9 --- /dev/null +++ b/assembly/src/sample-conf/vm-example.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assembly/src/test/log4j.properties b/assembly/src/test/log4j.properties new file mode 100755 index 0000000000..c9e5e0f06e --- /dev/null +++ b/assembly/src/test/log4j.properties @@ -0,0 +1,19 @@ +# +# The logging properties used during tests.. +# +log4j.rootLogger=INFO, out + +log4j.logger.org.activemq.spring=WARN +log4j.logger.org.activemq=INFO + +# CONSOLE appender not used by default +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n + +# File appender +log4j.appender.out=org.apache.log4j.FileAppender +log4j.appender.out.layout=org.apache.log4j.PatternLayout +log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +log4j.appender.out.file=target/test-reports/activemq-test.log +log4j.appender.out.append=true diff --git a/assembly/src/test/org/activemq/benchmark/BenchmarkSupport.java b/assembly/src/test/org/activemq/benchmark/BenchmarkSupport.java new file mode 100755 index 0000000000..ecf63113dd --- /dev/null +++ b/assembly/src/test/org/activemq/benchmark/BenchmarkSupport.java @@ -0,0 +1,243 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.benchmark; + +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.util.IdGenerator; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Session; + +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; + +/** + * Abstract base class for some simple benchmark tools + * + * @author James Strachan + * @version $Revision$ + */ +public class BenchmarkSupport { + + protected int connectionCount = 1; + protected int batch = 1000; + protected Destination destination; + protected boolean embeddedBroker = false; + private boolean topic = true; + private boolean durable = false; + + private ActiveMQConnectionFactory factory; + private String url; + protected String[] subjects; + private long time = System.currentTimeMillis(); + private int counter; + private List resources = new ArrayList(); + private NumberFormat formatter = NumberFormat.getInstance(); + private AtomicInteger connectionCounter = new AtomicInteger(0); + private IdGenerator idGenerator = new IdGenerator(); + + public BenchmarkSupport() { + } + + public void start() { + System.out.println("Using: " + connectionCount + " connection(s)"); + subjects = new String[connectionCount]; + for (int i = 0; i < connectionCount; i++) { + subjects[i] = "BENCHMARK.FEED" + i; + } + if (useTimerLoop()) { + Thread timer = new Thread() { + public void run() { + timerLoop(); + } + }; + timer.start(); + } + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public boolean isTopic() { + return topic; + } + + public void setTopic(boolean topic) { + this.topic = topic; + } + + public ActiveMQConnectionFactory getFactory() { + return factory; + } + + public void setFactory(ActiveMQConnectionFactory factory) { + this.factory = factory; + } + + public void setSubject(String subject) { + connectionCount = 1; + subjects = new String[]{subject}; + } + + public boolean isDurable() { + return durable; + } + + public void setDurable(boolean durable) { + this.durable = durable; + } + + public boolean isEmbeddedBroker() { + return embeddedBroker; + } + + public void setEmbeddedBroker(boolean embeddedBroker) { + this.embeddedBroker = embeddedBroker; + } + + public int getConnectionCount() { + return connectionCount; + } + + public void setConnectionCount(int connectionCount) { + this.connectionCount = connectionCount; + } + + protected Session createSession() throws JMSException { + if (factory == null) { + factory = createFactory(); + } + Connection connection = factory.createConnection(); + int value = connectionCounter.incrementAndGet(); + System.out.println("Created connection: " + value + " = " + connection); + if (durable) { + connection.setClientID(idGenerator.generateId()); + } + addResource(connection); + connection.start(); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + addResource(session); + return session; + } + + protected ActiveMQConnectionFactory createFactory() { + ActiveMQConnectionFactory answer = new ActiveMQConnectionFactory(getUrl()); + if (embeddedBroker) { + answer.setUseEmbeddedBroker(true); + } + return answer; + } + + protected synchronized void count(int count) { + counter += count; + /* + if (counter > batch) { + counter = 0; + long current = System.currentTimeMillis(); + double end = current - time; + end /= 1000; + time = current; + + System.out.println("Processed " + batch + " messages in " + end + " (secs)"); + } + */ + } + + protected synchronized int resetCount() { + int answer = counter; + counter = 0; + return answer; + } + + + protected void timerLoop() { + int times = 0; + int total = 0; + int dumpVmStatsFrequency = 10; + Runtime runtime = Runtime.getRuntime(); + + while (true) { + try { + Thread.sleep(1000); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + int processed = resetCount(); + double average = 0; + if (processed > 0) { + total += processed; + times++; + } + if (times > 0) { + average = total / times; + } + + long oldtime = time; + time = System.currentTimeMillis(); + + double diff = time - oldtime; + + System.out.println(getClass().getName() + " Processed: " + processed + " messages this second. Average: " + average); + + if ((times % dumpVmStatsFrequency) == 0 && times != 0) { + System.out.println("Used memory: " + asMemoryString(runtime.totalMemory() - runtime.freeMemory()) + + " Free memory: " + asMemoryString(runtime.freeMemory()) + + " Total memory: " + asMemoryString(runtime.totalMemory()) + + " Max memory: " + asMemoryString(runtime.maxMemory())); + } + + } + } + + protected String asMemoryString(long value) { + return formatter.format(value / 1024) + " K"; + } + + protected boolean useTimerLoop() { + return true; + } + + protected Destination createDestination(Session session, String subject) throws JMSException { + if (topic) { + return session.createTopic(subject); + } + else { + return session.createQueue(subject); + } + } + + protected void addResource(Object resource) { + resources.add(resource); + } + + protected static boolean parseBoolean(String text) { + return text.equalsIgnoreCase("true"); + } +} diff --git a/assembly/src/test/org/activemq/benchmark/Consumer.java b/assembly/src/test/org/activemq/benchmark/Consumer.java new file mode 100755 index 0000000000..51e40f449f --- /dev/null +++ b/assembly/src/test/org/activemq/benchmark/Consumer.java @@ -0,0 +1,112 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.benchmark; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; + +/** + * @author James Strachan + * @version $Revision$ + */ +public class Consumer extends BenchmarkSupport implements MessageListener { + + public static void main(String[] args) { + Consumer tool = new Consumer(); + if (args.length > 0) { + tool.setUrl(args[0]); + } + if (args.length > 1) { + tool.setTopic(parseBoolean(args[1])); + } + if (args.length > 2) { + tool.setSubject(args[2]); + } + if (args.length > 3) { + tool.setDurable(parseBoolean(args[3])); + } + if (args.length > 4) { + tool.setConnectionCount(Integer.parseInt(args[4])); + } + + try { + tool.run(); + } + catch (Exception e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + + public Consumer() { + } + + public void run() throws JMSException { + start(); + subscribe(); + } + + protected void subscribe() throws JMSException { + for (int i = 0; i < subjects.length; i++) { + subscribe(subjects[i]); + } + } + + protected void subscribe(String subject) throws JMSException { + Session session = createSession(); + + Destination destination = createDestination(session, subject); + + System.out.println("Consuming on : " + destination + " of type: " + destination.getClass().getName()); + + MessageConsumer consumer = null; + if (isDurable() && isTopic()) { + consumer = session.createDurableSubscriber((Topic) destination, getClass().getName()); + } + else { + consumer = session.createConsumer(destination); + } + consumer.setMessageListener(this); + addResource(consumer); + } + + public void onMessage(Message message) { + try { + TextMessage textMessage = (TextMessage) message; + + // lets force the content to be deserialized + String text = textMessage.getText(); + count(1); + + // lets count the messages + + //message.acknowledge(); + } + catch (JMSException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +} diff --git a/assembly/src/test/org/activemq/benchmark/Producer.java b/assembly/src/test/org/activemq/benchmark/Producer.java new file mode 100755 index 0000000000..37c81f3e23 --- /dev/null +++ b/assembly/src/test/org/activemq/benchmark/Producer.java @@ -0,0 +1,183 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.benchmark; + +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.Session; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +/** + * @author James Strachan + * @version $Revision$ + */ +public class Producer extends BenchmarkSupport { + + int loops = -1; + int loopSize = 1000; + private int messageSize = 1000; + + public static void main(String[] args) { + Producer tool = new Producer(); + if (args.length > 0) { + tool.setUrl(args[0]); + } + if (args.length > 1) { + tool.setTopic(parseBoolean(args[1])); + } + if (args.length > 2) { + tool.setSubject(args[2]); + } + if (args.length > 3) { + tool.setDurable(parseBoolean(args[3])); + } + if (args.length > 4) { + tool.setMessageSize(Integer.parseInt(args[4])); + } + if (args.length > 5) { + tool.setConnectionCount(Integer.parseInt(args[5])); + } + try { + tool.run(); + } + catch (Exception e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + + public Producer() { + } + + public void run() throws Exception { + start(); + publish(); + } + + // Properties + //------------------------------------------------------------------------- + public int getMessageSize() { + return messageSize; + } + + public void setMessageSize(int messageSize) { + this.messageSize = messageSize; + } + + public int getLoopSize() { + return loopSize; + } + + public void setLoopSize(int loopSize) { + this.loopSize = loopSize; + } + + // Implementation methods + //------------------------------------------------------------------------- + + protected void publish() throws Exception { + final String text = getMessage(); + + System.out.println("Publishing to: " + subjects.length + " subject(s)"); + + for (int i = 0; i < subjects.length; i++) { + final String subject = subjects[i]; + Thread thread = new Thread() { + public void run() { + try { + publish(text, subject); + } + catch (JMSException e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + }; + thread.start(); + } + + } + + protected String getMessage() { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < messageSize; i++) { + char ch = 'X'; + buffer.append(ch); + } + return buffer.toString(); + } + + protected void publish(String text, String subject) throws JMSException { + Session session = createSession(); + + Destination destination = createDestination(session, subject); + + MessageProducer publisher = session.createProducer(destination); + if (isDurable()) { + publisher.setDeliveryMode(DeliveryMode.PERSISTENT); + } + else { + publisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + } + + System.out.println("Starting publisher on : " + destination + " of type: " + destination.getClass().getName()); + System.out.println("Message length: " + text.length()); + + if (loops <= 0) { + while (true) { + publishLoop(session, publisher, text); + } + } + else { + for (int i = 0; i < loops; i++) { + publishLoop(session, publisher, text); + } + } + } + + protected void publishLoop(Session session, MessageProducer publisher, String text) throws JMSException { + for (int i = 0; i < loopSize; i++) { + Message message = session.createTextMessage(text); + + publisher.send(message); + count(1); + } + } + + protected String loadFile(String file) throws IOException { + System.out.println("Loading file: " + file); + + StringBuffer buffer = new StringBuffer(); + BufferedReader in = new BufferedReader(new FileReader(file)); + while (true) { + String line = in.readLine(); + if (line == null) { + break; + } + buffer.append(line); + buffer.append(File.separator); + } + return buffer.toString(); + } +} diff --git a/assembly/src/test/org/activemq/benchmark/ProducerConsumer.java b/assembly/src/test/org/activemq/benchmark/ProducerConsumer.java new file mode 100755 index 0000000000..1b5374f903 --- /dev/null +++ b/assembly/src/test/org/activemq/benchmark/ProducerConsumer.java @@ -0,0 +1,83 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.benchmark; + + +/** + * @author James Strachan + * @version $Revision$ + */ +public class ProducerConsumer extends Producer { + + private Consumer consumer = new Consumer(); + + public static void main(String[] args) { + ProducerConsumer tool = new ProducerConsumer(); + if (args.length > 0) { + tool.setUrl(args[0]); + } + if (args.length > 1) { + tool.setTopic(parseBoolean(args[1])); + } + if (args.length > 2) { + tool.setSubject(args[2]); + } + if (args.length > 3) { + tool.setDurable(Boolean.getBoolean(args[3])); + } + if (args.length > 4) { + tool.setConnectionCount(Integer.parseInt(args[4])); + } + try { + tool.run(); + } + catch (Exception e) { + System.out.println("Caught: " + e); + e.printStackTrace(); + } + } + + public ProducerConsumer() { + } + + public void run() throws Exception { + consumer.start(); + consumer.subscribe(); + start(); + publish(); + } + + public void setTopic(boolean topic) { + super.setTopic(topic); + consumer.setTopic(topic); + } + + public void setSubject(String subject) { + super.setSubject(subject); + consumer.setSubject(subject); + } + + public void setUrl(String url) { + super.setUrl(url); + consumer.setUrl(url); + } + + protected boolean useTimerLoop() { + return false; + } +} diff --git a/assembly/src/test/org/activemq/config/BrokerXmlConfigFromJNDITest.java b/assembly/src/test/org/activemq/config/BrokerXmlConfigFromJNDITest.java new file mode 100755 index 0000000000..d1c75beed9 --- /dev/null +++ b/assembly/src/test/org/activemq/config/BrokerXmlConfigFromJNDITest.java @@ -0,0 +1,61 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.config; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.test.JmsTopicSendReceiveWithTwoConnectionsTest; + +import javax.naming.InitialContext; +import javax.naming.Context; + +import java.io.File; +import java.util.Hashtable; + +/** + * @version $Revision: 1.2 $ + */ +public class BrokerXmlConfigFromJNDITest extends JmsTopicSendReceiveWithTwoConnectionsTest { + protected ActiveMQConnectionFactory createConnectionFactory() throws Exception { + // START SNIPPET: example + + + System.err.print(System.getProperties()); + + // we could put these properties into a jndi.properties + // on the classpath instead + Hashtable properties = new Hashtable(); + properties.put("java.naming.factory.initial", "org.activemq.jndi.ActiveMQInitialContextFactory"); + properties.put("useEmbeddedBroker", Boolean.TRUE); + + // configure the embedded broker using an XML config file + // which is either a URL or a resource on the classpath + + File f = new File(System.getProperty("basedir", ".")); + f = new File(f, "src/sample-conf/default.xml"); + + properties.put("brokerXmlConfig", "file:"+f.getPath()); + properties.put(Context.PROVIDER_URL, "vm://localhost"); + + InitialContext context = new InitialContext(properties); + ActiveMQConnectionFactory connectionFactory = (ActiveMQConnectionFactory) context.lookup("ConnectionFactory"); + + // END SNIPPET: example + return connectionFactory; + } + +} diff --git a/assembly/src/test/org/activemq/config/BrokerXmlConfigTest.java b/assembly/src/test/org/activemq/config/BrokerXmlConfigTest.java new file mode 100755 index 0000000000..4fb95b481d --- /dev/null +++ b/assembly/src/test/org/activemq/config/BrokerXmlConfigTest.java @@ -0,0 +1,52 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.config; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.test.JmsTopicSendReceiveWithTwoConnectionsTest; + +import java.net.URI; + +/** + * @version $Revision: 1.2 $ + */ +public class BrokerXmlConfigTest extends JmsTopicSendReceiveWithTwoConnectionsTest { + protected ActiveMQConnectionFactory createConnectionFactory() throws Exception { + // START SNIPPET: bean + + // configure the connection factory using + // normal Java Bean property methods + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(); + connectionFactory.setUseEmbeddedBroker(true); + + // configure the embedded broker using an XML config file + // which is either a URL or a resource on the classpath + + // TODO ... + + //connectionFactory.setBrokerXmlConfig("file:src/sample-conf/default.xml"); + + // you only need to configure the broker URL if you wish to change the + // default connection mechanism, which in this test case we do + connectionFactory.setBrokerURL("vm://localhost"); + + // END SNIPPET: bean + return connectionFactory; + } + +} diff --git a/assembly/src/test/org/activemq/config/ConfigTest.java b/assembly/src/test/org/activemq/config/ConfigTest.java new file mode 100755 index 0000000000..0ac3a9e00c --- /dev/null +++ b/assembly/src/test/org/activemq/config/ConfigTest.java @@ -0,0 +1,380 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.config; + +import org.activemq.broker.BrokerService; +import org.activemq.broker.TransportConnector; +import org.activemq.broker.region.policy.*; +import org.activemq.xbean.BrokerFactoryBean; +import org.activemq.transport.activeio.ActiveIOTransportServer; +import org.activemq.transport.tcp.TcpTransportServer; +import org.activemq.command.ActiveMQTopic; +import org.activemq.openwire.OpenWireFormat; +import org.activemq.store.PersistenceAdapter; +import org.activemq.store.PersistenceAdapterFactory; +import org.activemq.store.DefaultPersistenceAdapterFactory; +import org.activemq.store.memory.MemoryPersistenceAdapter; +import org.activemq.store.jdbc.JDBCPersistenceAdapter; +import org.activemq.store.journal.JournalPersistenceAdapter; +import org.activemq.memory.UsageManager; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.activeio.command.WireFormat; +import org.activeio.command.DefaultWireFormat; +import org.apache.derby.jdbc.EmbeddedDataSource; + +import java.io.File; +import java.util.List; + +import junit.framework.TestCase; + +/** + * @version $Revision: 1.2 $ + */ +public class ConfigTest extends TestCase { + protected static final String JOURNAL_ROOT = "target/test-data/"; + protected static final String DERBY_ROOT = "target/test-data/"; + protected static final String CONF_ROOT = "src/sample-conf/"; + + /* + * IMPORTANT NOTE: Assertions checking for the existence of the derby directory will fail if the first derby + * directory is not created under target/test-data/. The test in unable to change the derby + * root directory for succeeding creation. It uses the first created directory as the root. + */ + + /* + * This tests configuring the different broker properties using xbeans-spring + */ + public void testBrokerConfig() throws Exception { + ActiveMQTopic dest; + BrokerService broker; + + File journalFile = new File(JOURNAL_ROOT); + recursiveDelete(journalFile); + + // Create broker from resource + System.out.print("Creating broker... "); + broker = createBroker("org/activemq/config/example.xml"); + System.out.println("Success"); + + try { + // Check broker configuration + System.out.print("Checking broker configurations... "); + assertEquals("Broker Config Error (brokerName)", "brokerConfigTest", broker.getBrokerName()); + assertEquals("Broker Config Error (populateJMSXUserID)", false, broker.isPopulateJMSXUserID()); + assertEquals("Broker Config Error (useLoggingForShutdownErrors)", true, broker.isUseLoggingForShutdownErrors()); + assertEquals("Broker Config Error (useJmx)", true, broker.isUseJmx()); + assertEquals("Broker Config Error (persistent)", false, broker.isPersistent()); + assertEquals("Broker Config Error (useShutdownHook)", false, broker.isUseShutdownHook()); + assertEquals("Broker Config Error (deleteAllMessagesOnStartup)", true, broker.isDeleteAllMessagesOnStartup()); + System.out.println("Success"); + + // Check specific vm transport + System.out.print("Checking vm connector... "); + assertEquals("Should have a specific VM Connector", "vm://javacoola", broker.getVmConnectorURI().toString()); + System.out.println("Success"); + + // Check transport connectors list + System.out.print("Checking transport connectors... "); + List connectors = broker.getTransportConnectors(); + assertTrue("Should have created at least 4 connectors", (connectors.size() >= 4)); + assertTrue ("1st connector should be TcpTransportServer", ((TransportConnector)connectors.get(0)).getServer() instanceof TcpTransportServer); + assertTrue ("2nd connector should be TcpTransportServer", ((TransportConnector)connectors.get(1)).getServer() instanceof TcpTransportServer); + assertTrue ("3rd connector should be TcpTransportServer", ((TransportConnector)connectors.get(2)).getServer() instanceof TcpTransportServer); + assertTrue ("4th connector should be ActiveIOTransportServer", ((TransportConnector)connectors.get(3)).getServer() instanceof ActiveIOTransportServer); + + // Check spring configured transport server (last transport connector only) + ActiveIOTransportServer myTransportServer = (ActiveIOTransportServer)((TransportConnector)connectors.get(3)).getServer(); + assertEquals("URI should be ssl", "ssl://localhost:61634", myTransportServer.getConnectURI().toString()); + assertEquals("Error transport server config (stopTimeout)", 5000, myTransportServer.getStopTimeout()); + + // Check spring configured wire format factory + WireFormat myWireFormat = myTransportServer.getWireFormatFactory().createWireFormat(); + assertTrue("WireFormat should be OpenWireFormat", myWireFormat instanceof OpenWireFormat); + assertEquals("WireFormat Config Error (stackTraceEnabled)", false, ((OpenWireFormat)myWireFormat).isStackTraceEnabled()); + assertEquals("WireFormat Config Error (tcpNoDelayEnabled)", true, ((OpenWireFormat)myWireFormat).isTcpNoDelayEnabled()); + assertEquals("WireFormat Config Error (cacheEnabled)", false, ((OpenWireFormat)myWireFormat).isCacheEnabled()); + System.out.println("Success"); + + // Check network connectors + System.out.print("Checking network connectors... "); + List networkConnectors = broker.getNetworkConnectors(); + assertEquals("Should have a single network connector", 1, networkConnectors.size()); + System.out.println("Success"); + + // Check dispatch policy configuration + System.out.print("Checking dispatch policies... "); + + dest = new ActiveMQTopic("Topic.SimpleDispatch"); + assertTrue("Should have a simple dispatch policy for " + dest.getTopicName(), + broker.getDestinationPolicy().getEntryFor(dest).getDispatchPolicy() instanceof SimpleDispatchPolicy); + + dest = new ActiveMQTopic("Topic.RoundRobinDispatch"); + assertTrue("Should have a round robin dispatch policy for " + dest.getTopicName(), + broker.getDestinationPolicy().getEntryFor(dest).getDispatchPolicy() instanceof RoundRobinDispatchPolicy); + + dest = new ActiveMQTopic("Topic.StrictOrderDispatch"); + assertTrue("Should have a strict order dispatch policy for " + dest.getTopicName(), + broker.getDestinationPolicy().getEntryFor(dest).getDispatchPolicy() instanceof StrictOrderDispatchPolicy); + System.out.println("Success"); + + // Check subscription policy configuration + System.out.print("Checking subscription recovery policies... "); + SubscriptionRecoveryPolicy subsPolicy; + + dest = new ActiveMQTopic("Topic.FixedSizedSubs"); + subsPolicy = broker.getDestinationPolicy().getEntryFor(dest).getSubscriptionRecoveryPolicy(); + assertTrue("Should have a fixed sized subscription recovery policy for " + dest.getTopicName(), + subsPolicy instanceof FixedSizedSubscriptionRecoveryPolicy); + assertEquals("FixedSizedSubsPolicy Config Error (maximumSize)", 2000000, + ((FixedSizedSubscriptionRecoveryPolicy)subsPolicy).getMaximumSize()); + assertEquals("FixedSizedSubsPolicy Config Error (useSharedBuffer)", false, + ((FixedSizedSubscriptionRecoveryPolicy)subsPolicy).isUseSharedBuffer()); + + dest = new ActiveMQTopic("Topic.LastImageSubs"); + subsPolicy = broker.getDestinationPolicy().getEntryFor(dest).getSubscriptionRecoveryPolicy(); + assertTrue("Should have a last image subscription recovery policy for " + dest.getTopicName(), + subsPolicy instanceof LastImageSubscriptionRecoveryPolicy); + + dest = new ActiveMQTopic("Topic.NoSubs"); + subsPolicy = broker.getDestinationPolicy().getEntryFor(dest).getSubscriptionRecoveryPolicy(); + assertTrue("Should have no subscription recovery policy for " + dest.getTopicName(), + subsPolicy instanceof NoSubscriptionRecoveryPolicy); + + dest = new ActiveMQTopic("Topic.TimedSubs"); + subsPolicy = broker.getDestinationPolicy().getEntryFor(dest).getSubscriptionRecoveryPolicy(); + assertTrue("Should have a timed subscription recovery policy for " + dest.getTopicName(), + subsPolicy instanceof TimedSubscriptionRecoveryPolicy); + assertEquals("TimedSubsPolicy Config Error (recoverDuration)", 25000, + ((TimedSubscriptionRecoveryPolicy)subsPolicy).getRecoverDuration()); + System.out.println("Success"); + + // Check usage manager + System.out.print("Checking memory manager configurations... "); + UsageManager memMgr = broker.getMemoryManager(); + assertTrue("Should have a memory manager", memMgr != null); + assertEquals("UsageManager Config Error (limit)", 200000, memMgr.getLimit()); + assertEquals("UsageManager Config Error (percentUsageMinDelta)", 20, memMgr.getPercentUsageMinDelta()); + System.out.println("Success"); + + // Check persistence factory configurations + System.out.print("Checking persistence adapter factory settings... "); + PersistenceAdapterFactory factory = broker.getPersistenceFactory(); + assertTrue("Should have a default persistence factory", factory instanceof DefaultPersistenceAdapterFactory); + assertEquals("PersistenceFactory Config Error (journalLogFileSize)", 32768, + ((DefaultPersistenceAdapterFactory)factory).getJournalLogFileSize()); + assertEquals("PersistenceFactory Config Error (journalLogFiles)", 4, + ((DefaultPersistenceAdapterFactory)factory).getJournalLogFiles()); + assertEquals("PersistenceFactory Config Error (useJournal)", true, + ((DefaultPersistenceAdapterFactory)factory).isUseJournal()); + assertEquals("PersistenceFactory Config Error (dataDirectory)", journalFile.getAbsolutePath(), + ((DefaultPersistenceAdapterFactory)factory).getDataDirectory().getAbsolutePath()); + System.out.println("Success"); + + // Check journal configurations + System.out.print("Checking if persistence adapter was succesfully created... "); + assertTrue("Should have created a journal persistence adapter", broker.getPersistenceAdapter() instanceof JournalPersistenceAdapter); + System.out.println("Success"); + } finally { + if (broker != null) { + broker.stop(); + } + } + } + + /* + * This tests creating a journal persistence adapter using xbeans-spring + */ + public void testJournalConfig() throws Exception { + System.out.print("Checking journal persistence adapter configuration... "); + + File journalFile = new File(JOURNAL_ROOT + "testJournalConfig/journal"); + recursiveDelete(journalFile); + + File derbyFile = new File(DERBY_ROOT + "testJournalConfig/derbydb"); + recursiveDelete(derbyFile); + + BrokerService broker; + broker = createBroker(new FileSystemResource(CONF_ROOT + "journal-example.xml")); + try { + assertEquals("Broker Config Error (brokerName)", "brokerJournalConfigTest", broker.getBrokerName()); + + PersistenceAdapter adapter = broker.getPersistenceAdapter(); + + assertTrue("Should have created a journal persistence adapter", adapter instanceof JournalPersistenceAdapter); + assertTrue("Should have created a derby directory at " + derbyFile.getAbsolutePath(), derbyFile.exists()); + assertTrue("Should have created a journal directory at " + journalFile.getAbsolutePath(), journalFile.exists()); + + System.out.println("Success"); + } finally { + if (broker != null) { + broker.stop(); + } + } + } + + /* + * This tests creating a jdbc persistence adapter using xbeans-spring + */ + public void testJdbcConfig() throws Exception { + System.out.print("Checking jdbc persistence adapter configuration... "); + + File derbyFile = new File(DERBY_ROOT + "testJdbcConfig"); + recursiveDelete(derbyFile); + + BrokerService broker; + broker = createBroker(new FileSystemResource(CONF_ROOT + "jdbc-example.xml")); + try { + assertEquals("Broker Config Error (brokerName)", "brokerJdbcConfigTest", broker.getBrokerName()); + + PersistenceAdapter adapter = broker.getPersistenceAdapter(); + + assertTrue("Should have created a jdbc persistence adapter", adapter instanceof JDBCPersistenceAdapter); + assertEquals("JDBC Adapter Config Error (cleanupPeriod)", 60000, + ((JDBCPersistenceAdapter)adapter).getCleanupPeriod()); + assertTrue("Should have created an EmbeddedDataSource", + ((JDBCPersistenceAdapter)adapter).getDataSource() instanceof EmbeddedDataSource); + assertTrue("Should have created a DefaultWireFormat", + ((JDBCPersistenceAdapter)adapter).getWireFormat() instanceof DefaultWireFormat); + assertTrue("Should have created a derby directory at " + derbyFile.getAbsolutePath(), derbyFile.exists()); + + System.out.println("Success"); + } finally { + if (broker != null) { + broker.stop(); + } + } + } + + /* + * This tests creating a journal persistence adapter using the persistence adapter factory bean + */ + public void testJournaledJDBCConfig() throws Exception { + System.out.print("Checking journaled JDBC persistence adapter configuration... "); + + File journalFile = new File(JOURNAL_ROOT + "testJournaledJDBCConfig"); + recursiveDelete(journalFile); + + File derbyFile = new File(DERBY_ROOT + "derbydb"); // Default derby name + recursiveDelete(derbyFile); + + BrokerService broker; + broker = createBroker(new FileSystemResource(CONF_ROOT + "journaledjdbc-example.xml")); + try { + assertEquals("Broker Config Error (brokerName)", "brokerJournaledJDBCConfigTest", broker.getBrokerName()); + + PersistenceAdapter adapter = broker.getPersistenceAdapter(); + + assertTrue("Should have created a journal persistence adapter", adapter instanceof JournalPersistenceAdapter); + assertTrue("Should have created a derby directory at " + derbyFile.getAbsolutePath(), derbyFile.exists()); + assertTrue("Should have created a journal directory at " + journalFile.getAbsolutePath(), journalFile.exists()); + + System.out.println("Success"); + } finally { + if (broker != null) { + broker.stop(); + } + } + } + + /* + * This tests creating a memory persistence adapter using xbeans-spring + */ + public void testMemoryConfig() throws Exception { + System.out.print("Checking memory persistence adapter configuration... "); + + File journalFile = new File(JOURNAL_ROOT + "testMemoryConfig"); + recursiveDelete(journalFile); + + File derbyFile = new File(DERBY_ROOT + "testMemoryConfig"); + recursiveDelete(derbyFile); + + BrokerService broker; + broker = createBroker(new FileSystemResource(CONF_ROOT + "memory-example.xml")); + + try { + assertEquals("Broker Config Error (brokerName)", "brokerMemoryConfigTest", broker.getBrokerName()); + + PersistenceAdapter adapter = broker.getPersistenceAdapter(); + + assertTrue("Should have created a memory persistence adapter", adapter instanceof MemoryPersistenceAdapter); + assertTrue("Should have not created a derby directory at " + derbyFile.getAbsolutePath(), !derbyFile.exists()); + assertTrue("Should have not created a journal directory at " + journalFile.getAbsolutePath(), !journalFile.exists()); + + System.out.println("Success"); + } finally { + if (broker != null) { + broker.stop(); + } + } + + } + + public void testXmlConfigHelper() throws Exception { + BrokerService broker; + + broker = createBroker(new FileSystemResource(CONF_ROOT + "memory-example.xml")); + try { + assertEquals("Broker Config Error (brokerName)", "brokerMemoryConfigTest", broker.getBrokerName()); + } finally { + if (broker != null) { + broker.stop(); + } + } + + broker = createBroker("org/activemq/config/config.xml"); + try { + assertEquals("Broker Config Error (brokerName)", "brokerXmlConfigHelper", broker.getBrokerName()); + } finally { + if (broker != null) { + broker.stop(); + } + } + } + + /* + * TODO: Create additional tests for forwarding bridges + */ + + protected static void recursiveDelete(File file) { + if( file.isDirectory() ) { + File[] files = file.listFiles(); + for (int i = 0; i < files.length; i++) { + recursiveDelete(files[i]); + } + } + file.delete(); + } + + protected BrokerService createBroker(String resource) throws Exception { + return createBroker(new ClassPathResource(resource)); + } + + protected BrokerService createBroker(Resource resource) throws Exception { + BrokerFactoryBean factory = new BrokerFactoryBean(resource); + factory.afterPropertiesSet(); + + BrokerService broker = factory.getBroker(); + + assertTrue("Should have a broker!", broker != null); + + broker.start(); + return broker; + } +} diff --git a/assembly/src/test/org/activemq/config/config.xml b/assembly/src/test/org/activemq/config/config.xml new file mode 100755 index 0000000000..b368fccde4 --- /dev/null +++ b/assembly/src/test/org/activemq/config/config.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assembly/src/test/org/activemq/config/example.xml b/assembly/src/test/org/activemq/config/example.xml new file mode 100755 index 0000000000..ca8934060b --- /dev/null +++ b/assembly/src/test/org/activemq/config/example.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ssl://localhost:61634 + + + + + + + + + + + + + + + + + + + diff --git a/assembly/src/test/org/activemq/config/spring-test.xml b/assembly/src/test/org/activemq/config/spring-test.xml new file mode 100755 index 0000000000..f2823e63d3 --- /dev/null +++ b/assembly/src/test/org/activemq/config/spring-test.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + vm://localhost + + + + \ No newline at end of file diff --git a/assembly/src/test/org/activemq/transport/QueueClusterTest.java b/assembly/src/test/org/activemq/transport/QueueClusterTest.java new file mode 100755 index 0000000000..b8426893da --- /dev/null +++ b/assembly/src/test/org/activemq/transport/QueueClusterTest.java @@ -0,0 +1,37 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport; + + + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class QueueClusterTest extends TopicClusterTest { + + + protected void setUp() throws Exception{ + topic = false; + super.setUp(); + } + + protected int expectedReceiveCount(){ + return MESSAGE_COUNT * NUMBER_IN_CLUSTER; + } + +} diff --git a/assembly/src/test/org/activemq/transport/TopicClusterTest.java b/assembly/src/test/org/activemq/transport/TopicClusterTest.java new file mode 100755 index 0000000000..75c45ccb0c --- /dev/null +++ b/assembly/src/test/org/activemq/transport/TopicClusterTest.java @@ -0,0 +1,182 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.broker.BrokerService; +import org.activemq.broker.TransportConnector; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTextMessage; +import org.activemq.command.ActiveMQTopic; +import org.activemq.network.NetworkConnector; +import org.activemq.transport.discovery.rendezvous.RendezvousDiscoveryAgent; +import org.activemq.util.ServiceStopper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class TopicClusterTest extends TestCase implements MessageListener { + protected Log log = LogFactory.getLog(getClass()); + protected Destination destination; + protected boolean topic = true; + protected AtomicInteger receivedMessageCount = new AtomicInteger(0); + protected static int MESSAGE_COUNT = 50; + protected static int NUMBER_IN_CLUSTER = 3; + protected int deliveryMode = DeliveryMode.NON_PERSISTENT; + protected MessageProducer[] producers; + protected Connection[] connections; + protected List services = new ArrayList(); + + protected void setUp() throws Exception { + connections = new Connection[NUMBER_IN_CLUSTER]; + producers = new MessageProducer[NUMBER_IN_CLUSTER]; + Destination destination = createDestination(); + int portStart = 50000; + String root = System.getProperty("activemq.store.dir"); + if (root == null) { + root = "target/store"; + } + try { + for (int i = 0;i < NUMBER_IN_CLUSTER;i++) { + + System.setProperty("activemq.store.dir", root + "_broker_" + i); + connections[i] = createConnection("broker-" + i); + connections[i].setClientID("ClusterTest" + i); + connections[i].start(); + Session session = connections[i].createSession(false, Session.AUTO_ACKNOWLEDGE); + producers[i] = session.createProducer(destination); + producers[i].setDeliveryMode(deliveryMode); + MessageConsumer consumer = createMessageConsumer(session,destination); + consumer.setMessageListener(this); + + } + System.out.println("Sleeping to ensure cluster is fully connected"); + Thread.sleep(5000); + } finally { + System.setProperty("activemq.store.dir", root); + } + } + + protected void tearDown() throws Exception { + if (connections != null) { + for (int i = 0;i < connections.length;i++) { + connections[i].close(); + } + } + ServiceStopper stopper = new ServiceStopper(); + stopper.stopServices(services); + } + + protected MessageConsumer createMessageConsumer(Session session, Destination destination) throws JMSException{ + return session.createConsumer(destination); + } + + protected ActiveMQConnectionFactory createGenericClusterFactory(String brokerName) throws Exception { + BrokerService container = new BrokerService(); + container.setBrokerName(brokerName); + + String url = "tcp://localhost:0"; + TransportConnector connector = container.addConnector(url); + connector.setDiscoveryUri(new URI("multicast://default")); + container.addNetworkConnector("multicast://default"); + container.start(); + + services.add(container); + + return new ActiveMQConnectionFactory("vm://"+brokerName); + } + + protected int expectedReceiveCount() { + return MESSAGE_COUNT * NUMBER_IN_CLUSTER * NUMBER_IN_CLUSTER; + } + + protected Connection createConnection(String name) throws Exception { + return createGenericClusterFactory(name).createConnection(); + } + + protected Destination createDestination() { + return createDestination(getClass().getName()); + } + + protected Destination createDestination(String name) { + if (topic) { + return new ActiveMQTopic(name); + } + else { + return new ActiveMQQueue(name); + } + } + + + /** + * @param msg + */ + public void onMessage(Message msg) { + //System.out.println("GOT: " + msg); + receivedMessageCount.incrementAndGet(); + synchronized (receivedMessageCount) { + if (receivedMessageCount.get() >= expectedReceiveCount()) { + receivedMessageCount.notify(); + } + } + } + + /** + * @throws Exception + */ + public void testSendReceive() throws Exception { + for (int i = 0;i < MESSAGE_COUNT;i++) { + TextMessage textMessage = new ActiveMQTextMessage(); + textMessage.setText("MSG-NO:" + i); + for (int x = 0;x < producers.length;x++) { + producers[x].send(textMessage); + } + } + synchronized (receivedMessageCount) { + if (receivedMessageCount.get() < expectedReceiveCount()) { + receivedMessageCount.wait(20000); + } + } + //sleep a little - to check we don't get too many messages + Thread.sleep(2000); + System.err.println("GOT: " + receivedMessageCount.get()); + assertEquals("Expected message count not correct", expectedReceiveCount(), receivedMessageCount.get()); + } + +} \ No newline at end of file diff --git a/assembly/src/test/org/activemq/usecases/ChangeSentMessageTest.java b/assembly/src/test/org/activemq/usecases/ChangeSentMessageTest.java new file mode 100755 index 0000000000..5a5ca38c9b --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/ChangeSentMessageTest.java @@ -0,0 +1,64 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; +import java.util.HashMap; +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Session; + +import org.activemq.test.TestSupport; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class ChangeSentMessageTest extends TestSupport { + private static final int COUNT = 200; + private static final String VALUE_NAME = "value"; + + /** + * test Object messages can be changed after sending with no side-affects + * @throws Exception + */ + public void testDoChangeSentMessage() throws Exception { + Destination destination = createDestination("test-"+ChangeSentMessageTest.class.getName()); + Connection connection = createConnection(); + connection.start(); + Session consumerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageConsumer consumer = consumerSession.createConsumer(destination); + Session publisherSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = publisherSession.createProducer(destination); + HashMap map = new HashMap(); + ObjectMessage message = publisherSession.createObjectMessage(); + for (int i = 0;i < COUNT;i++) { + map.put(VALUE_NAME, new Integer(i)); + message.setObject(map); + producer.send(message); + assertTrue(message.getObject()==map); + } + for (int i = 0;i < COUNT;i++) { + ObjectMessage msg = (ObjectMessage) consumer.receive(); + HashMap receivedMap = (HashMap) msg.getObject(); + Integer intValue = (Integer) receivedMap.get(VALUE_NAME); + assertTrue(intValue.intValue() == i); + } + } +} \ No newline at end of file diff --git a/assembly/src/test/org/activemq/usecases/ChangeSessionDeliveryModeTest.java b/assembly/src/test/org/activemq/usecases/ChangeSessionDeliveryModeTest.java new file mode 100755 index 0000000000..24625592f0 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/ChangeSessionDeliveryModeTest.java @@ -0,0 +1,67 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; +import java.util.HashMap; +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.IllegalStateException; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Session; + +import org.activemq.test.TestSupport; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class ChangeSessionDeliveryModeTest extends TestSupport implements MessageListener { + private static final int COUNT = 200; + private static final String VALUE_NAME = "value"; + + /** + * test following condition- which are defined by JMS Spec 1.1: MessageConsumers cannot use a MessageListener and + * receive() from the same session + * + * @throws Exception + */ + public void testDoChangeSessionDeliveryMode() throws Exception { + Destination destination = createDestination("foo.bar"); + Connection connection = createConnection(); + connection.start(); + Session consumerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageConsumer consumer1 = consumerSession.createConsumer(destination); + consumer1.setMessageListener(this); + JMSException jmsEx = null; + MessageConsumer consumer2 = consumerSession.createConsumer(destination); + try { + consumer2.receive(10); + } + catch (JMSException e) { + jmsEx = e; + } + assertTrue(jmsEx != null && jmsEx instanceof IllegalStateException); + } + + public void onMessage(Message msg) { + } +} \ No newline at end of file diff --git a/assembly/src/test/org/activemq/usecases/CompositeConsumeTest.java b/assembly/src/test/org/activemq/usecases/CompositeConsumeTest.java new file mode 100755 index 0000000000..e29b989155 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/CompositeConsumeTest.java @@ -0,0 +1,71 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import org.activemq.command.ActiveMQTopic; +import org.activemq.test.JmsTopicSendReceiveWithTwoConnectionsTest; + +import javax.jms.Destination; +import javax.jms.Message; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class CompositeConsumeTest extends JmsTopicSendReceiveWithTwoConnectionsTest { + + public void testSendReceive() throws Exception { + messages.clear(); + + Destination[] destinations = getDestinations(); + int destIdx = 0; + + for (int i = 0; i < data.length; i++) { + Message message = session.createTextMessage(data[i]); + + if (verbose) { + System.out.println("About to send a message: " + message + " with text: " + data[i]); + } + + producer.send(destinations[destIdx], message); + + if (++destIdx >= destinations.length) { + destIdx = 0; + } + } + + assertMessagesAreReceived(); + } + + /** + * Returns the subscription subject + */ + protected String getSubject() { + return getPrefix() + "FOO.BAR," + getPrefix() + "FOO.X.Y," + getPrefix() + "BAR.>"; + } + + /** + * Returns the destinations on which we publish + */ + protected Destination[] getDestinations() { + return new Destination[]{new ActiveMQTopic(getPrefix() + "FOO.BAR"), new ActiveMQTopic(getPrefix() + "BAR.WHATNOT.XYZ"), new ActiveMQTopic(getPrefix() + "FOO.X.Y")}; + } + + protected String getPrefix() { + return super.getSubject() + "."; + } +} diff --git a/assembly/src/test/org/activemq/usecases/CompositePublishTest.java b/assembly/src/test/org/activemq/usecases/CompositePublishTest.java new file mode 100755 index 0000000000..23bc4c6177 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/CompositePublishTest.java @@ -0,0 +1,143 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.command.ActiveMQTopic; +import org.activemq.test.JmsSendReceiveTestSupport; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Session; +import java.util.List; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class CompositePublishTest extends JmsSendReceiveTestSupport { + + protected Connection sendConnection; + protected Connection receiveConnection; + protected Session receiveSession; + protected MessageConsumer[] consumers; + protected List[] messageLists; + + protected void setUp() throws Exception { + super.setUp(); + + connectionFactory = createConnectionFactory(); + + sendConnection = createConnection(); + sendConnection.start(); + + receiveConnection = createConnection(); + receiveConnection.start(); + + System.out.println("Created sendConnection: " + sendConnection); + System.out.println("Created receiveConnection: " + receiveConnection); + + session = sendConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + receiveSession = receiveConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + System.out.println("Created sendSession: " + session); + System.out.println("Created receiveSession: " + receiveSession); + + producer = session.createProducer(null); + + System.out.println("Created producer: " + producer); + + if (topic) { + consumerDestination = session.createTopic(getConsumerSubject()); + producerDestination = session.createTopic(getProducerSubject()); + } + else { + consumerDestination = session.createQueue(getConsumerSubject()); + producerDestination = session.createQueue(getProducerSubject()); + } + + System.out.println("Created consumer destination: " + consumerDestination + " of type: " + consumerDestination.getClass()); + System.out.println("Created producer destination: " + producerDestination + " of type: " + producerDestination.getClass()); + + Destination[] destinations = getDestinations(); + consumers = new MessageConsumer[destinations.length]; + messageLists = new List[destinations.length]; + for (int i = 0; i < destinations.length; i++) { + Destination dest = destinations[i]; + messageLists[i] = createConcurrentList(); + consumers[i] = receiveSession.createConsumer(dest); + consumers[i].setMessageListener(createMessageListener(i, messageLists[i])); + } + + + System.out.println("Started connections"); + } + + protected MessageListener createMessageListener(int i, final List messageList) { + return new MessageListener() { + public void onMessage(Message message) { + consumeMessage(message, messageList); + } + }; + } + + /** + * Returns the subject on which we publish + */ + protected String getSubject() { + return getPrefix() + "FOO.BAR," + getPrefix() + "FOO.X.Y"; + } + + /** + * Returns the destinations to which we consume + */ + protected Destination[] getDestinations() { + return new Destination[]{new ActiveMQTopic(getPrefix() + "FOO.BAR"), new ActiveMQTopic(getPrefix() + "FOO.*"), new ActiveMQTopic(getPrefix() + "FOO.X.Y")}; + } + + protected String getPrefix() { + return super.getSubject() + "."; + } + + protected void assertMessagesAreReceived() throws JMSException { + waitForMessagesToBeDelivered(); + + for (int i = 0, size = messageLists.length; i < size; i++) { + System.out.println("Message list: " + i + " contains: " + messageLists[i].size() + " message(s)"); + } + + for (int i = 0, size = messageLists.length; i < size; i++) { + assertMessagesReceivedAreValid(messageLists[i]); + } + } + + protected ActiveMQConnectionFactory createConnectionFactory() { + return new ActiveMQConnectionFactory("vm://localhost"); + } + + protected void tearDown() throws Exception { + session.close(); + receiveSession.close(); + + sendConnection.close(); + receiveConnection.close(); + } +} diff --git a/assembly/src/test/org/activemq/usecases/ConsumeQueuePrefetchTest.java b/assembly/src/test/org/activemq/usecases/ConsumeQueuePrefetchTest.java new file mode 100755 index 0000000000..700a10c501 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/ConsumeQueuePrefetchTest.java @@ -0,0 +1,43 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import javax.jms.JMSException; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class ConsumeQueuePrefetchTest extends ConsumeTopicPrefetchTest { + + /** + * TODO disabled failing test cases until we fix queue dispatching + */ + public void testSendDoublePrefetchSize() throws JMSException { + } + + /** + * TODO disabled failing test cases until we fix queue dispatching + */ + public void testSendPrefetchSizePlusOne() throws JMSException { + } + + protected void setUp() throws Exception { + topic = false; + super.setUp(); + } +} diff --git a/assembly/src/test/org/activemq/usecases/ConsumeTopicPrefetchTest.java b/assembly/src/test/org/activemq/usecases/ConsumeTopicPrefetchTest.java new file mode 100755 index 0000000000..ad4f57ffd3 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/ConsumeTopicPrefetchTest.java @@ -0,0 +1,88 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.TextMessage; + +import org.activemq.ActiveMQConnection; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class ConsumeTopicPrefetchTest extends ProducerConsumerTestSupport { + + protected int prefetchSize = 100; + protected String[] messageTexts; + protected long consumerTimeout = 10000L; + + public void testSendPrefetchSize() throws JMSException { + testWithMessageCount(prefetchSize); + } + + public void testSendDoublePrefetchSize() throws JMSException { + testWithMessageCount(prefetchSize * 2); + } + + public void testSendPrefetchSizePlusOne() throws JMSException { + testWithMessageCount(prefetchSize + 1); + } + + protected void testWithMessageCount(int messageCount) throws JMSException { + makeMessages(messageCount); + + System.out.println("About to send and receive: " + messageCount + " on destination: " + destination + + " of type: " + destination.getClass().getName()); + + for (int i = 0; i < messageCount; i++) { + Message message = session.createTextMessage(messageTexts[i]); + producer.send(message); + } + + // lets consume them in two fetch batches + for (int i = 0; i < messageCount; i++) { + consumeMessge(i); + } + } + + protected Connection createConnection() throws Exception { + ActiveMQConnection connection = (ActiveMQConnection) super.createConnection(); + connection.getPrefetchPolicy().setQueuePrefetch(prefetchSize); + connection.getPrefetchPolicy().setTopicPrefetch(prefetchSize); + return connection; + } + + protected void consumeMessge(int i) throws JMSException { + Message message = consumer.receive(consumerTimeout); + assertTrue("Should have received a message by now for message: " + i, message != null); + assertTrue("Should be a TextMessage: " + message, message instanceof TextMessage); + TextMessage textMessage = (TextMessage) message; + assertEquals("Message content", messageTexts[i], textMessage.getText()); + } + + + protected void makeMessages(int messageCount) { + messageTexts = new String[messageCount]; + for (int i = 0; i < messageCount; i++) { + messageTexts[i] = "Message for test: + " + getName() + " = " + i; + } + } + +} diff --git a/assembly/src/test/org/activemq/usecases/CreateLotsOfTemporaryQueuesTest.java b/assembly/src/test/org/activemq/usecases/CreateLotsOfTemporaryQueuesTest.java new file mode 100644 index 0000000000..249830a7f9 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/CreateLotsOfTemporaryQueuesTest.java @@ -0,0 +1,72 @@ +/** + * + * Copyright 2005 LogicBlaze, Inc. http://www.logicblaze.com + * + * Licensed 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.activemq.usecases; + +import org.activemq.EmbeddedBrokerTestSupport; + +import javax.jms.Connection; +import javax.jms.Session; +import javax.jms.TemporaryQueue; + +import junit.framework.Test; +import junit.framework.TestSuite; +import junit.textui.TestRunner; + +/** + * + * @version $Revision: 1.1 $ + */ +public class CreateLotsOfTemporaryQueuesTest extends EmbeddedBrokerTestSupport { + + private static int numberToCreate = 500; + private static long sleep = 20; + + + public static void main(String[] args) { + configure(args); + TestRunner.run(suite()); + } + + public static Test suite() { + return new TestSuite(CreateLotsOfTemporaryQueuesTest.class); + } + + public void testCreateLotsOfTemporaryQueues() throws Exception { + System.out.println("Creating " + numberToCreate + " temporary queue(s)"); + + Connection connection = createConnection(); + connection.start(); + Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE); + for (int i = 0; i < numberToCreate; i++) { + if (i % 1000 == 0) { + System.out.println("attempt " + i); + } + TemporaryQueue temporaryQueue = session.createTemporaryQueue(); + temporaryQueue.delete(); + Thread.sleep(sleep ); + } + System.out.println("Created " + numberToCreate + " temporary queue(s)"); + connection.close(); + } + + public static void configure(String[] args) { + if (args.length > 0) { + numberToCreate = Integer.parseInt(args[0]); + } + } +} diff --git a/assembly/src/test/org/activemq/usecases/CreateTemporaryQueueBeforeStartTest.java b/assembly/src/test/org/activemq/usecases/CreateTemporaryQueueBeforeStartTest.java new file mode 100755 index 0000000000..1dacd8700c --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/CreateTemporaryQueueBeforeStartTest.java @@ -0,0 +1,133 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.broker.BrokerService; + +import javax.jms.Connection; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueReceiver; +import javax.jms.QueueSender; +import javax.jms.QueueSession; +import javax.jms.Session; +import javax.jms.Topic; + +import junit.framework.TestCase; + +/** + * @author Peter Henning + * @version $Revision: 1.1.1.1 $ + */ +public class CreateTemporaryQueueBeforeStartTest extends TestCase { + protected String bindAddress = "tcp://localhost:61621"; + private Connection connection; + private BrokerService broker = new BrokerService(); + + public void testCreateTemporaryQueue() throws Exception { + connection = createConnection(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createTemporaryQueue(); + assertTrue("No queue created!", queue != null); + Topic topic = session.createTemporaryTopic(); + assertTrue("No topic created!", topic != null); + } + + public void testTryToReproduceNullPointerBug() throws Exception { + String url = bindAddress; + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(url); + QueueConnection queueConnection = factory.createQueueConnection(); + this.connection = queueConnection; + QueueSession session = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + QueueSender sender = session.createSender(null); //Unidentified + Queue receiverQueue = session.createTemporaryQueue(); + QueueReceiver receiver = session.createReceiver(receiverQueue); + queueConnection.start(); + } + + public void testTemporaryQueueConsumer() throws Exception { + final int NUMBER = 20; + final AtomicInteger count = new AtomicInteger(0); + for (int i = 0;i < NUMBER;i++) { + Thread thread = new Thread(new Runnable() { + public void run() { + try { + QueueConnection connection = createConnection(); + QueueSession session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createTemporaryQueue(); + QueueReceiver consumer = session.createReceiver(queue); + connection.start(); + + + if (count.incrementAndGet() >= NUMBER){ + synchronized(count){ + count.notify(); + } + } + } + catch (Exception ex) { + ex.printStackTrace(); + } + } + }); + thread.start(); + } + int maxWaitTime = 20000; + synchronized (count) { + long waitTime = maxWaitTime; + long start = System.currentTimeMillis(); + while (count.get() < NUMBER) { + if (waitTime <= 0) { + break; + } + else { + count.wait(waitTime); + waitTime = maxWaitTime - (System.currentTimeMillis() - start); + } + } + } + assertTrue("Unexpected count: " + count, count.get() == NUMBER); + } + + protected QueueConnection createConnection() throws Exception { + ActiveMQConnectionFactory factory = createConnectionFactory(); + return factory.createQueueConnection(); + } + + protected ActiveMQConnectionFactory createConnectionFactory() throws Exception { + return new ActiveMQConnectionFactory(bindAddress); + } + + protected void setUp() throws Exception { + broker.setPersistent(false); + broker.addConnector(bindAddress); + broker.start(); + super.setUp(); + } + + protected void tearDown() throws Exception { + if (connection != null) { + connection.close(); + } + broker.stop(); + super.tearDown(); + } +} \ No newline at end of file diff --git a/assembly/src/test/org/activemq/usecases/DeadLetterTest.java b/assembly/src/test/org/activemq/usecases/DeadLetterTest.java new file mode 100755 index 0000000000..b154720a7b --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/DeadLetterTest.java @@ -0,0 +1,127 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import org.activemq.TestSupport; +import org.activemq.command.ActiveMQDestination; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.Topic; + + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class DeadLetterTest extends TestSupport { + + private static final int MESSAGE_COUNT = 10; + private static final long TIME_TO_LIVE = 250; + private Connection connection; + private Session session; + private MessageConsumer consumer; + private MessageProducer producer; + private ActiveMQDestination destination; + private int deliveryMode = DeliveryMode.PERSISTENT; + private boolean durableSubscriber = false; + + protected void setUp() throws Exception{ + super.setUp(); + connection = createConnection(); + connection.setClientID(toString()); + + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + } + + protected void tearDown() throws Exception { + connection.close(); + } + + protected void doTest() throws Exception{ + destination = (ActiveMQDestination) createDestination(getClass().getName()); + producer = session.createProducer(destination); + producer.setDeliveryMode(deliveryMode); + producer.setTimeToLive(TIME_TO_LIVE); + + // TODO FIXME + + /* + DeadLetterPolicy dlq = new DeadLetterPolicy(); + String dlqName = dlq.getDeadLetterNameFromDestination(destination); + */ + String dlqName = "FOO"; + + Destination dlqDestination = session.createQueue(dlqName); + MessageConsumer dlqConsumer = session.createConsumer(dlqDestination); + + if (durableSubscriber){ + consumer = session.createDurableSubscriber((Topic)destination, destination.toString()); + }else { + consumer = session.createConsumer(destination); + } + + for (int i =0; i < MESSAGE_COUNT;i++){ + Message message = session.createTextMessage("msg: " + i); + producer.send(message); + } + Thread.sleep(TIME_TO_LIVE * 2); + connection.start(); + + + for (int i =0; i < MESSAGE_COUNT; i++){ + Message msg = consumer.receive(10); + assertNull("Should be null message", msg); + } + + for (int i =0; i < MESSAGE_COUNT; i++){ + Message msg = dlqConsumer.receive(1000); + assertNotNull("Should be message", msg); + } + + + } + + + + public void testDurableTopicMessageExpiration() throws Exception{ + super.topic = true; + deliveryMode = DeliveryMode.PERSISTENT; + durableSubscriber = true; + doTest(); + } + + public void testTransientQueueMessageExpiration() throws Exception{ + super.topic = false; + deliveryMode = DeliveryMode.NON_PERSISTENT; + durableSubscriber = false; + doTest(); + } + + public void testDurableQueueMessageExpiration() throws Exception{ + super.topic = false; + deliveryMode = DeliveryMode.PERSISTENT; + durableSubscriber = false; + doTest(); + } + +} diff --git a/assembly/src/test/org/activemq/usecases/DurableConsumerCloseAndReconnectTest.java b/assembly/src/test/org/activemq/usecases/DurableConsumerCloseAndReconnectTest.java new file mode 100755 index 0000000000..012b3626d0 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/DurableConsumerCloseAndReconnectTest.java @@ -0,0 +1,168 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import org.activemq.test.TestSupport; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.Topic; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class DurableConsumerCloseAndReconnectTest extends TestSupport { + protected static final long RECEIVE_TIMEOUT = 5000L; + + private Connection connection; + private Session session; + private MessageConsumer consumer; + private MessageProducer producer; + private Destination destination; + + public void testCreateDurableConsumerCloseThenReconnect() throws Exception { + // force the server to stay up across both connection tests + Connection dummyConnection = createConnection(); + + consumeMessagesDeliveredWhileConsumerClosed(); + + dummyConnection.close(); + + // now lets try again without one connection open + consumeMessagesDeliveredWhileConsumerClosed(); + } + + protected void consumeMessagesDeliveredWhileConsumerClosed() throws Exception { + makeConsumer(); + closeConsumer(); + + publish(); + + // wait a few moments for the close to really occur + Thread.sleep(1000); + + makeConsumer(); + + Message message = consumer.receive(RECEIVE_TIMEOUT); + assertTrue("Should have received a message!", message != null); + + closeConsumer(); + + System.out.println("Now lets create the consumer again and because we didn't ack, we should get it again"); + makeConsumer(); + + message = consumer.receive(RECEIVE_TIMEOUT); + assertTrue("Should have received a message!", message != null); + message.acknowledge(); + + closeConsumer(); + + System.out.println("Now lets create the consumer again and because we didn't ack, we should get it again"); + makeConsumer(); + + message = consumer.receive(2000); + assertTrue("Should have no more messages left!", message == null); + + closeConsumer(); + + System.out.println("Lets publish one more message now"); + publish(); + + makeConsumer(); + message = consumer.receive(RECEIVE_TIMEOUT); + assertTrue("Should have received a message!", message != null); + message.acknowledge(); + + closeConsumer(); + } + + protected void publish() throws Exception { + connection = createConnection(); + connection.start(); + + session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + destination = createDestination(); + + producer = session.createProducer(destination); + producer.setDeliveryMode(DeliveryMode.PERSISTENT); + + producer.send(session.createTextMessage("This is a test")); + + producer.close(); + producer = null; + closeSession(); + } + + protected Destination createDestination() throws JMSException { + if (isTopic()) { + return session.createTopic(getSubject()); + } + else { + return session.createQueue(getSubject()); + } + } + + protected boolean isTopic() { + return true; + } + + protected void closeConsumer() throws JMSException { + consumer.close(); + consumer = null; + closeSession(); + } + + protected void closeSession() throws JMSException { + session.close(); + session = null; + connection.close(); + connection = null; + } + + protected void makeConsumer() throws Exception { + String durableName = getName(); + String clientID = getSubject(); + System.out.println("Creating a durable subscribe for clientID: " + clientID + " and durable name: " + durableName); + createSession(clientID); + consumer = createConsumer(durableName); + } + + private MessageConsumer createConsumer(String durableName) throws JMSException { + if (destination instanceof Topic) { + return session.createDurableSubscriber((Topic) destination, durableName); + } + else { + return session.createConsumer(destination); + } + } + + protected void createSession(String clientID) throws Exception { + connection = createConnection(); + connection.setClientID(clientID); + connection.start(); + + session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + destination = createDestination(); + } +} diff --git a/assembly/src/test/org/activemq/usecases/DurableSubscriptionTestSupport.java b/assembly/src/test/org/activemq/usecases/DurableSubscriptionTestSupport.java new file mode 100755 index 0000000000..1f6a82007b --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/DurableSubscriptionTestSupport.java @@ -0,0 +1,377 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicSubscriber; + +import org.activemq.TestSupport; +import org.activemq.broker.BrokerService; +import org.activemq.store.PersistenceAdapter; + +/** + * @version $Revision: 1.1.1.1 $ + */ +abstract public class DurableSubscriptionTestSupport extends TestSupport { + + private Connection connection; + private Session session; + private TopicSubscriber consumer; + private MessageProducer producer; + private BrokerService broker; + private boolean init; + + protected Connection createConnection() throws Exception { + Connection rc = super.createConnection(); + rc.setClientID(getName()); + return rc; + } + + protected void setUp() throws Exception { + createBroker(); + super.setUp(); + } + protected void tearDown() throws Exception { + destroyBroker(); + super.tearDown(); + } + protected void restartBroker() throws Exception { + destroyBroker(); + createRestartedBroker(); // retain stored messages + } + private void createBroker() throws Exception { + try { + broker = new BrokerService(); + broker.setBrokerName("durable_broker"); + broker.setDeleteAllMessagesOnStartup(true); + broker.setPersistenceAdapter(createPersistenceAdapter()); + broker.setPersistent(true); + broker.addConnector("vm://localhost"); + init = true; + + broker.start(); + } catch (Exception e) { + e.printStackTrace(); + } + + connection = createConnection(); + } + + private void createRestartedBroker() throws Exception { + try { + broker = new BrokerService(); + broker.setBrokerName("durable_broker"); + broker.setDeleteAllMessagesOnStartup(false); + broker.setPersistenceAdapter(createPersistenceAdapter()); + broker.setPersistent(true); + broker.addConnector("vm://localhost"); + broker.start(); + + } catch (Exception e) { + e.printStackTrace(); + } + + connection = createConnection(); + } + private void destroyBroker() throws Exception { + if( connection != null ) + connection.close(); + if( broker!=null ) + broker.stop(); + } + + abstract protected PersistenceAdapter createPersistenceAdapter() throws Exception; + + + public void testUnsubscribeSubscription() throws Exception { + session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + Topic topic = session.createTopic("TestTopic"); + consumer = session.createDurableSubscriber(topic, "sub1"); + producer = session.createProducer(topic); + producer.setDeliveryMode(DeliveryMode.PERSISTENT); + connection.start(); + + // Make sure it works when the durable sub is active. + producer.send(session.createTextMessage("Msg:1")); + assertTextMessageEquals("Msg:1", consumer.receive(5000)); + + // Deactivate the sub. + consumer.close(); + // Send a new message. + producer.send(session.createTextMessage("Msg:2")); + session.unsubscribe("sub1"); + + // Reopen the connection. + connection.close(); + connection = createConnection(); + session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + producer = session.createProducer(topic); + connection.start(); + + // Activate the sub. + consumer = session.createDurableSubscriber(topic, "sub1"); + producer.send(session.createTextMessage("Msg:3")); + + // Try to get the message. + assertTextMessageEquals("Msg:3", consumer.receive(5000)); + } + + public void testInactiveDurableSubscriptionTwoConnections() throws Exception { + session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + Topic topic = session.createTopic("TestTopic"); + consumer = session.createDurableSubscriber(topic, "sub1"); + producer = session.createProducer(topic); + producer.setDeliveryMode(DeliveryMode.PERSISTENT); + connection.start(); + + // Make sure it works when the durable sub is active. + producer.send(session.createTextMessage("Msg:1")); + assertTextMessageEquals("Msg:1", consumer.receive(5000)); + + // Deactivate the sub. + consumer.close(); + + // Send a new message. + producer.send(session.createTextMessage("Msg:2")); + + // Reopen the connection. + connection.close(); + connection = createConnection(); + session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + connection.start(); + + // Activate the sub. + consumer = session.createDurableSubscriber(topic, "sub1"); + + // Try to get the message. + assertTextMessageEquals("Msg:2", consumer.receive(5000)); + } + + public void testInactiveDurableSubscriptionBrokerRestart() throws Exception { + session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + Topic topic = session.createTopic("TestTopic"); + consumer = session.createDurableSubscriber(topic, "sub1"); + producer = session.createProducer(topic); + producer.setDeliveryMode(DeliveryMode.PERSISTENT); + connection.start(); + + // Make sure it works when the durable sub is active. + producer.send(session.createTextMessage("Msg:1")); + assertTextMessageEquals("Msg:1", consumer.receive(5000)); + + // Deactivate the sub. + consumer.close(); + + // Send a new message. + producer.send(session.createTextMessage("Msg:2")); + + // Reopen the connection. + restartBroker(); + session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + connection.start(); + + // Activate the sub. + consumer = session.createDurableSubscriber(topic, "sub1"); + + // Try to get the message. + assertTextMessageEquals("Msg:2", consumer.receive(5000)); + assertNull(consumer.receive(5000)); + } + + public void testDurableSubscriptionPersistsPastBrokerRestart() throws Exception { + + // Create the durable sub. + connection.start(); + session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + + // Ensure that consumer will receive messages sent before it was created + Topic topic = session.createTopic("TestTopic?consumer.retroactive=true"); + consumer = session.createDurableSubscriber(topic, "sub1"); + + // Restart the broker. + restartBroker(); + + // Reconnection + connection.start(); + session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + producer = session.createProducer(topic); + producer.setDeliveryMode(DeliveryMode.PERSISTENT); + + // Make sure it works when the durable sub is active. + producer.send(session.createTextMessage("Msg:1")); + + // Activate the sub. + consumer = session.createDurableSubscriber(topic, "sub1"); + + // Send a new message. + producer.send(session.createTextMessage("Msg:2")); + + + // Try to get the message. + assertTextMessageEquals("Msg:1", consumer.receive(5000)); + assertTextMessageEquals("Msg:2", consumer.receive(5000)); + + assertNull(consumer.receive(5000)); + } + + public void testInactiveDurableSubscriptionOneConnection() throws Exception { + session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + Topic topic = session.createTopic("TestTopic"); + consumer = session.createDurableSubscriber(topic, "sub1"); + producer = session.createProducer(topic); + producer.setDeliveryMode(DeliveryMode.PERSISTENT); + connection.start(); + + // Make sure it works when the durable sub is active. + producer.send(session.createTextMessage("Msg:1")); + assertTextMessageEquals("Msg:1", consumer.receive(5000)); + + // Deactivate the sub. + consumer.close(); + + // Send a new message. + producer.send(session.createTextMessage("Msg:2")); + + // Activate the sub. + consumer = session.createDurableSubscriber(topic, "sub1"); + + // Try to get the message. + assertTextMessageEquals("Msg:2", consumer.receive(5000)); + } + + public void xtestSelectorChange() throws Exception { + session = connection.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); + Topic topic = session.createTopic("TestTopic"); + consumer = session.createDurableSubscriber(topic, "sub1", "color='red'", false); + producer = session.createProducer(topic); + producer.setDeliveryMode(DeliveryMode.PERSISTENT); + connection.start(); + + // Make sure it works when the durable sub is active. + TextMessage msg = session.createTextMessage(); + msg.setText("Msg:1"); + msg.setStringProperty("color", "blue"); + producer.send(msg); + msg.setText("Msg:2"); + msg.setStringProperty("color", "red"); + producer.send(msg); + + assertTextMessageEquals("Msg:2", consumer.receive(5000)); + + // Change the subscription + consumer.close(); + consumer = session.createDurableSubscriber(topic, "sub1", "color='blue'", false); + + // Send a new message. + msg.setText("Msg:3"); + msg.setStringProperty("color", "red"); + producer.send(msg); + msg.setText("Msg:4"); + msg.setStringProperty("color", "blue"); + producer.send(msg); + + // Try to get the message. + assertTextMessageEquals("Msg:4", consumer.receive(5000)); + } + + + public void testDurableSubWorksInNewSession() throws JMSException { + + // Create the consumer. + connection.start(); + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Topic topic = session.createTopic("topic-"+getName()); + MessageConsumer consumer = session.createDurableSubscriber(topic, "sub1"); + // Drain any messages that may allready be in the sub + while( consumer.receive(1000)!=null ) + ; + + // See if the durable sub works in a new session. + session.close(); + session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Send a Message that should be added to the durable sub. + MessageProducer producer = createProducer(session, topic); + producer.send(session.createTextMessage("Message 1")); + + // Activate the durable sub now. And receive the message. + consumer = session.createDurableSubscriber(topic, "sub1"); + Message msg = consumer.receive(1000); + assertNotNull(msg); + assertEquals( "Message 1", ((TextMessage)msg).getText() ); + + } + + + public void testDurableSubWorksInNewConnection() throws Exception { + + // Create the consumer. + connection.start(); + Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + Topic topic = session.createTopic("topic-"+getName()); + MessageConsumer consumer = session.createDurableSubscriber(topic, "sub1"); + // Drain any messages that may allready be in the sub + while( consumer.receive(1000)!=null ) + ; + + // See if the durable sub works in a new connection. + // The embeded broker shutsdown when his connections are closed. + // So we open the new connection before the old one is closed. + Connection t = createConnection(); + connection.close(); + connection = t; + connection.start(); + session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + + // Send a Message that should be added to the durable sub. + MessageProducer producer = createProducer(session, topic); + producer.send(session.createTextMessage("Message 1")); + + // Activate the durable sub now. And receive the message. + consumer = session.createDurableSubscriber(topic, "sub1"); + Message msg = consumer.receive(1000); + assertNotNull(msg); + assertEquals( "Message 1", ((TextMessage)msg).getText() ); + + } + + private MessageProducer createProducer(Session session, Destination queue) throws JMSException { + MessageProducer producer = session.createProducer(queue); + producer.setDeliveryMode(getDeliveryMode()); + return producer; + } + + protected int getDeliveryMode() { + return DeliveryMode.PERSISTENT; + } + private void assertTextMessageEquals(String string, Message message) throws JMSException { + assertNotNull("Message was null", message); + assertTrue("Message is not a TextMessage", message instanceof TextMessage); + assertEquals(string, ((TextMessage)message).getText()); + } + +} diff --git a/assembly/src/test/org/activemq/usecases/ExceptionListenerTest.java b/assembly/src/test/org/activemq/usecases/ExceptionListenerTest.java new file mode 100755 index 0000000000..282f99db5c --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/ExceptionListenerTest.java @@ -0,0 +1,92 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import javax.jms.ExceptionListener; +import javax.jms.JMSException; + +import junit.framework.TestCase; + +/** + * @author Oliver Belikan + * @version $Revision: 1.1.1.1 $ + */ +public class ExceptionListenerTest extends TestCase implements ExceptionListener { + boolean isException = false; + + + public ExceptionListenerTest(String arg) { + super(arg); + } + + + public void testOnException() throws Exception { + /* TODO not sure yet if this is a valid test + + System.setProperty("activemq.persistenceAdapter", + "org.activemq.store.vm.VMPersistenceAdapter"); + // configuration of container and all protocolls + BrokerContainerImpl container = new + BrokerContainerImpl("DefaultBroker"); + BrokerConnectorImpl connector = new + BrokerConnectorImpl(container, + "vm://localhost", new DefaultWireFormat()); + container.start(); + + ActiveMQConnectionFactory factory = new + ActiveMQConnectionFactory("vm://localhost"); + factory.start(); + + Connection connection = factory.createConnection(); + connection.setExceptionListener(this); + connection.start(); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination destination = session.createTopic(getClass().getName()); + MessageProducer producer = session.createProducer(destination); + + try { + Thread.currentThread().sleep(1000); + } + catch (Exception e) { + } + + container.stop(); + + // now lets try send + try { + producer.send(session.createTextMessage("This will never get anywhere")); + } + catch (JMSException e) { + System.out.println("Caught: " + e); + } + + try { + Thread.currentThread().sleep(1000); + } + catch (Exception e) { + } + + assertTrue("Should have received an exception", isException); + */ + } + + + public void onException(JMSException e) { + isException = true; + } +} diff --git a/assembly/src/test/org/activemq/usecases/JDBCDurableSubscriptionTest.java b/assembly/src/test/org/activemq/usecases/JDBCDurableSubscriptionTest.java new file mode 100755 index 0000000000..495598aa19 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/JDBCDurableSubscriptionTest.java @@ -0,0 +1,40 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import java.io.File; +import java.io.IOException; + +import org.activemq.store.DefaultPersistenceAdapterFactory; +import org.activemq.store.PersistenceAdapter; +import org.activemq.store.jdbc.JDBCPersistenceAdapter; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class JDBCDurableSubscriptionTest extends DurableSubscriptionTestSupport { + + protected PersistenceAdapter createPersistenceAdapter() throws IOException { + File dataDir = new File("target/test-data/durableJDBC"); + DefaultPersistenceAdapterFactory factory = new DefaultPersistenceAdapterFactory(); + factory.setDataDirectory(dataDir); + factory.setUseJournal(false); + return factory.createPersistenceAdapter(); + } + +} diff --git a/assembly/src/test/org/activemq/usecases/JournalDurableSubscriptionTest.java b/assembly/src/test/org/activemq/usecases/JournalDurableSubscriptionTest.java new file mode 100755 index 0000000000..71f3d3ddb2 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/JournalDurableSubscriptionTest.java @@ -0,0 +1,41 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import java.io.File; +import java.io.IOException; + +import org.activemq.store.DefaultPersistenceAdapterFactory; +import org.activemq.store.PersistenceAdapter; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class JournalDurableSubscriptionTest extends DurableSubscriptionTestSupport { + + private boolean init; + + protected PersistenceAdapter createPersistenceAdapter() throws IOException { + File dataDir = new File("target/test-data/durableJournal"); + DefaultPersistenceAdapterFactory factory = new DefaultPersistenceAdapterFactory(); + factory.setDataDirectory(dataDir); + factory.setUseJournal(true); + factory.setJournalLogFileSize(1024*64); + return factory.createPersistenceAdapter(); + } +} diff --git a/assembly/src/test/org/activemq/usecases/ProducerConsumerTestSupport.java b/assembly/src/test/org/activemq/usecases/ProducerConsumerTestSupport.java new file mode 100755 index 0000000000..edf6cb8e1c --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/ProducerConsumerTestSupport.java @@ -0,0 +1,57 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; + + +/** + * Base class for simple test cases using a single connection, session + * producer and consumer + * + * @version $Revision: 1.1.1.1 $ + */ +public class ProducerConsumerTestSupport extends TestSupport { + protected Connection connection; + protected Session session; + protected MessageProducer producer; + protected MessageConsumer consumer; + protected Destination destination; + + protected void setUp() throws Exception { + super.setUp(); + connection = createConnection(); + session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + destination = this.createDestination(getSubject()); + producer = session.createProducer(destination); + consumer = session.createConsumer(destination); + connection.start(); + } + + protected void tearDown() throws Exception { + consumer.close(); + producer.close(); + session.close(); + connection.close(); + super.tearDown(); + } +} diff --git a/assembly/src/test/org/activemq/usecases/PublishOnDurableTopicConsumedMessageTest.java b/assembly/src/test/org/activemq/usecases/PublishOnDurableTopicConsumedMessageTest.java new file mode 100755 index 0000000000..98f3b43e99 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/PublishOnDurableTopicConsumedMessageTest.java @@ -0,0 +1,29 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class PublishOnDurableTopicConsumedMessageTest extends PublishOnTopicConsumedMessageTest { + + protected void setUp() throws Exception { + this.durable = true; + super.setUp(); + } +} diff --git a/assembly/src/test/org/activemq/usecases/PublishOnQueueConsumedMessageTest.java b/assembly/src/test/org/activemq/usecases/PublishOnQueueConsumedMessageTest.java new file mode 100755 index 0000000000..fbb16577bb --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/PublishOnQueueConsumedMessageTest.java @@ -0,0 +1,29 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class PublishOnQueueConsumedMessageTest extends PublishOnTopicConsumedMessageTest { + + protected void setUp() throws Exception { + topic = false; + super.setUp(); + } +} diff --git a/assembly/src/test/org/activemq/usecases/PublishOnTemporaryQueueConsumedMessageTest.java b/assembly/src/test/org/activemq/usecases/PublishOnTemporaryQueueConsumedMessageTest.java new file mode 100755 index 0000000000..99a2205422 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/PublishOnTemporaryQueueConsumedMessageTest.java @@ -0,0 +1,32 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import javax.jms.DeliveryMode; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class PublishOnTemporaryQueueConsumedMessageTest extends PublishOnTopicConsumedMessageTest { + + protected void setUp() throws Exception { + topic = false; + deliveryMode = DeliveryMode.NON_PERSISTENT; + super.setUp(); + } +} diff --git a/assembly/src/test/org/activemq/usecases/PublishOnTopicConsumedMessageTest.java b/assembly/src/test/org/activemq/usecases/PublishOnTopicConsumedMessageTest.java new file mode 100755 index 0000000000..f82e1d3ba1 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/PublishOnTopicConsumedMessageTest.java @@ -0,0 +1,66 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import org.activemq.test.JmsTopicSendReceiveWithTwoConnectionsTest; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class PublishOnTopicConsumedMessageTest extends JmsTopicSendReceiveWithTwoConnectionsTest { + private MessageProducer replyProducer; + + + public synchronized void onMessage(Message message) { + + // lets resend the message somewhere else + try { + Message msgCopy = (Message)((org.activemq.command.Message)message).copy(); + replyProducer.send(msgCopy); + + //System.out.println("Sending reply: " + message); + super.onMessage(message); + } + catch (JMSException e) { + System.out.println("Failed to send message: " + e); + e.printStackTrace(); + } + } + + protected void setUp() throws Exception { + super.setUp(); + + Destination replyDestination = null; + + if (topic) { + replyDestination = receiveSession.createTopic("REPLY." + getSubject()); + } + else { + replyDestination = receiveSession.createQueue("REPLY." + getSubject()); + } + + replyProducer = receiveSession.createProducer(replyDestination); + System.out.println("Created replyProducer: " + replyProducer); + + } +} diff --git a/assembly/src/test/org/activemq/usecases/QueueConsumerCloseAndReconnectTest.java b/assembly/src/test/org/activemq/usecases/QueueConsumerCloseAndReconnectTest.java new file mode 100755 index 0000000000..976d9ad3b0 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/QueueConsumerCloseAndReconnectTest.java @@ -0,0 +1,27 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class QueueConsumerCloseAndReconnectTest extends DurableConsumerCloseAndReconnectTest { + protected boolean isTopic() { + return false; + } +} diff --git a/assembly/src/test/org/activemq/usecases/QueueDuplicatesTest.java b/assembly/src/test/org/activemq/usecases/QueueDuplicatesTest.java new file mode 100755 index 0000000000..2431d4be55 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/QueueDuplicatesTest.java @@ -0,0 +1,147 @@ +/** + * Copyright 2004 Protique Ltd Licensed 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.activemq.usecases; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.net.URI; +import java.net.URISyntaxException; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import junit.framework.TestCase; +import org.activemq.ActiveMQConnectionFactory; + +public class QueueDuplicatesTest extends TestCase { + private static DateFormat formatter = new SimpleDateFormat("HH:mm:ss SSS"); + private String brokerUrl; + private String subject; + private Connection brokerConnection; + + public QueueDuplicatesTest(String name) { + super(name); + } + + protected void setUp() throws Exception { + String peerUrl = "peer://localhost:6099"; + + subject = this.getClass().getName(); + + ActiveMQConnectionFactory fac = createFactory(peerUrl); + brokerConnection = fac.createConnection(); + brokerConnection.start(); + } + + protected void tearDown() throws Exception { + if (brokerConnection != null) { + brokerConnection.close(); + } + } + + public void testDuplicates() { + try { + // Get Session + Session session = createSession(brokerConnection); + // create consumer + Destination dest = session.createQueue(subject); + MessageConsumer consumer = session.createConsumer(dest); + // subscribe to queue + consumer.setMessageListener(new SimpleConsumer()); + // create producer + Thread sendingThread = new SendingThread(brokerUrl, subject); + // start producer + sendingThread.start(); + // wait about 5 seconds + Thread.sleep(5000); + // unsubscribe consumer + consumer.close(); + // wait another 5 seconds + Thread.sleep(5000); + // create new consumer + consumer = session.createConsumer(dest); + // subscribe to queue + consumer.setMessageListener(new SimpleConsumer()); + // sleep a little while longer + Thread.sleep(15000); + session.close(); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + Session createSession(Connection peerConnection) throws JMSException { + // Connect using peer to peer connection + Session session = peerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); + return session; + } + + private ActiveMQConnectionFactory createFactory(String brokerUrl) { + ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory(); + cf.setBrokerURL(brokerUrl); + + return cf; + } + private class SendingThread extends Thread { + private String brokerUrl; + private String subject; + + SendingThread(String brokerUrl, String subject) { + this.brokerUrl = brokerUrl; + this.subject = subject; + setDaemon(false); + } + + public void run() { + try { + Session session = createSession(brokerConnection); + Destination dest = session.createQueue(subject); + MessageProducer producer = session.createProducer(dest); + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + for (int i = 0;i < 20;i++) { + String txt = "Text Message: " + i; + TextMessage msg = session.createTextMessage(txt); + producer.send(msg); + System.out.println(formatter.format(new Date()) + " Sent ==> " + msg + " to " + subject); + Thread.sleep(1000); + } + session.close(); + } + catch (Exception e) { + e.printStackTrace(); + } + } + } + private static class SimpleConsumer implements MessageListener { + private Map msgs = new HashMap(); + + public void onMessage(Message message) { + System.out.println(formatter.format(new Date()) + " SimpleConsumer Message Received: " + message); + try { + String id = message.getJMSMessageID(); + assertNull("Message is duplicate: " + id, msgs.get(id)); + msgs.put(id, message); + } + catch (Exception e) { + e.printStackTrace(); + } + } + } +} \ No newline at end of file diff --git a/assembly/src/test/org/activemq/usecases/QueueRedeliverTest.java b/assembly/src/test/org/activemq/usecases/QueueRedeliverTest.java new file mode 100755 index 0000000000..467cbbd4f2 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/QueueRedeliverTest.java @@ -0,0 +1,31 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class QueueRedeliverTest extends TopicRedeliverTest { + + protected void setUp() throws Exception{ + super.setUp(); + topic = false; + } + +} diff --git a/assembly/src/test/org/activemq/usecases/ReliableReconnectTest.java b/assembly/src/test/org/activemq/usecases/ReliableReconnectTest.java new file mode 100755 index 0000000000..ca77b70c7c --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/ReliableReconnectTest.java @@ -0,0 +1,182 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; +import java.util.HashMap; +import java.net.URI; +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueReceiver; +import javax.jms.QueueSender; +import javax.jms.QueueSession; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; +import org.activemq.ActiveMQConnection; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.broker.BrokerService; +import org.activemq.broker.BrokerFactory; +import org.activemq.broker.Broker; +import org.activemq.test.TestSupport; +import org.activemq.util.IdGenerator; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean; +import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class ReliableReconnectTest extends TestSupport { + private static final int RECEIVE_TIMEOUT = 10000; + protected static final int MESSAGE_COUNT = 100; + protected static final String DEFAULT_BROKER_URL = "vm://localhost"; + private IdGenerator idGen = new IdGenerator(); + protected int deliveryMode = DeliveryMode.PERSISTENT; + protected String consumerClientId; + protected Destination destination; + protected AtomicBoolean closeBroker = new AtomicBoolean(false); + protected AtomicInteger messagesReceived = new AtomicInteger(0); + protected BrokerService broker; + protected int firstBatch = MESSAGE_COUNT/10; + + public ReliableReconnectTest() { + } + + public ReliableReconnectTest(String n) { + super(n); + } + + protected void setUp() throws Exception { + consumerClientId = idGen.generateId(); + super.setUp(); + topic = true; + destination = createDestination(getClass().getName()); + } + + public ActiveMQConnectionFactory getConnectionFactory() throws Exception { + String url = "failover://" + DEFAULT_BROKER_URL; + return new ActiveMQConnectionFactory(url); + } + + protected void startBroker() throws JMSException { + try { + broker = BrokerFactory.createBroker(new URI("broker://()/localhost")); + broker.addConnector(DEFAULT_BROKER_URL); + broker.start(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + protected Connection createConsumerConnection() throws Exception { + Connection consumerConnection = getConnectionFactory().createConnection(); + consumerConnection.setClientID(consumerClientId); + consumerConnection.start(); + return consumerConnection; + } + + protected MessageConsumer createConsumer(Connection con) throws Exception { + Session s = con.createSession(false, Session.AUTO_ACKNOWLEDGE); + return s.createDurableSubscriber((Topic) destination, "TestFred"); + } + + protected void spawnConsumer() { + Thread thread = new Thread(new Runnable() { + public void run() { + try { + Connection consumerConnection = createConsumerConnection(); + MessageConsumer consumer = createConsumer(consumerConnection); + //consume some messages + + for (int i = 0;i < firstBatch;i++) { + Message msg = consumer.receive(RECEIVE_TIMEOUT); + if (msg != null) { + //System.out.println("GOT: " + msg); + messagesReceived.incrementAndGet(); + } + } + synchronized (closeBroker) { + closeBroker.set(true); + closeBroker.notify(); + } + Thread.sleep(2000); + for (int i = firstBatch;i < MESSAGE_COUNT;i++) { + Message msg = consumer.receive(RECEIVE_TIMEOUT); + //System.out.println("GOT: " + msg); + if (msg != null) { + messagesReceived.incrementAndGet(); + } + } + consumerConnection.close(); + synchronized (messagesReceived) { + messagesReceived.notify(); + } + } + catch (Throwable e) { + e.printStackTrace(); + } + } + }); + thread.start(); + } + + public void testReconnect() throws Exception { + startBroker(); + //register an interest as a durable subscriber + Connection consumerConnection = createConsumerConnection(); + createConsumer(consumerConnection); + consumerConnection.close(); + //send some messages ... + Connection connection = createConnection(); + connection.setClientID(idGen.generateId()); + connection.start(); + Session producerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(destination); + TextMessage msg = producerSession.createTextMessage(); + for (int i = 0;i < MESSAGE_COUNT;i++) { + msg.setText("msg: " + i); + producer.send(msg); + } + connection.close(); + spawnConsumer(); + synchronized (closeBroker) { + if (!closeBroker.get()) { + closeBroker.wait(); + } + } + System.err.println("Stopping broker"); + broker.stop(); + startBroker(); + System.err.println("Started Broker again"); + synchronized (messagesReceived) { + if (messagesReceived.get() < MESSAGE_COUNT) { + messagesReceived.wait(60000); + } + } + //assertTrue(messagesReceived.get() == MESSAGE_COUNT); + int count = messagesReceived.get(); + assertTrue("Not enough messages received: " + count, count > firstBatch); + } +} \ No newline at end of file diff --git a/assembly/src/test/org/activemq/usecases/StartAndStopBrokerTest.java b/assembly/src/test/org/activemq/usecases/StartAndStopBrokerTest.java new file mode 100755 index 0000000000..3edbd89bae --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/StartAndStopBrokerTest.java @@ -0,0 +1,79 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import junit.framework.TestCase; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.broker.BrokerService; +import org.activemq.broker.BrokerFactory; +import org.activemq.broker.TransportConnector; + +import javax.jms.JMSException; +import java.net.URI; + +/** + * @author Oliver Belikan + * @version $Revision: 1.1.1.1 $ + */ +public class StartAndStopBrokerTest extends TestCase { + public void testStartupShutdown() throws Exception { + // This systemproperty is used if we dont want to + // have persistence messages as a default + System.setProperty("activemq.persistenceAdapter", + "org.activemq.store.vm.VMPersistenceAdapter"); + + // configuration of container and all protocolls + BrokerService broker = createBroker(); + + // start a client + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:9100"); + factory.createConnection(); + + // stop activemq broker + broker.stop(); + + // start activemq broker again + broker = createBroker(); + + // start a client again + factory = new ActiveMQConnectionFactory("tcp://localhost:9100"); + factory.createConnection(); + + // stop activemq broker + broker.stop(); + + } + + protected BrokerService createBroker() throws JMSException { + BrokerService broker = null; + + try { + broker = BrokerFactory.createBroker(new URI("broker://()/localhost")); + broker.setBrokerName("DefaultBroker"); + broker.addConnector("tcp://localhost:9100"); + broker.setUseShutdownHook(false); + + broker.start(); + } catch (Exception e) { + e.printStackTrace(); + } + + return broker; + } + +} diff --git a/assembly/src/test/org/activemq/usecases/SubscribeClosePublishThenConsumeTest.java b/assembly/src/test/org/activemq/usecases/SubscribeClosePublishThenConsumeTest.java new file mode 100755 index 0000000000..83692edc84 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/SubscribeClosePublishThenConsumeTest.java @@ -0,0 +1,107 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.test.TestSupport; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicSubscriber; + +/** + * @author Paul Smith + * @version $Revision: 1.1.1.1 $ + */ +public class SubscribeClosePublishThenConsumeTest extends TestSupport { + + public void testDurableTopic() throws Exception { + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://locahost"); + connectionFactory.setUseEmbeddedBroker(true); + + String topicName = "TestTopic"; + String clientID = getName(); + String subscriberName = "MySubscriber:"+System.currentTimeMillis(); + + Connection connection = connectionFactory.createConnection(); + connection.setClientID(clientID); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Topic topic = session.createTopic(topicName); + + // this should register a durable subscriber, we then close it to + // test that we get messages from the producer later on + TopicSubscriber subscriber = session.createDurableSubscriber(topic, subscriberName); + connection.start(); + + topic = null; + subscriber.close(); + subscriber = null; + session.close(); + session = null; + + // Create the new connection before closing to avoid the broker shutting down. + // now create a new Connection, Session & Producer, send some messages & then close + Connection t = connectionFactory.createConnection(); + connection.close(); + connection = t; + + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + topic = session.createTopic(topicName); + MessageProducer producer = session.createProducer(topic); + producer.setDeliveryMode(DeliveryMode.PERSISTENT); + TextMessage textMessage = session.createTextMessage("Hello World"); + producer.send(textMessage); + textMessage = null; + + topic = null; + session.close(); + session = null; + + // Now (re)register the Durable subscriber, setup a listener and wait for messages that should + // have been published by the previous producer + t = connectionFactory.createConnection(); + connection.close(); + connection = t; + + connection.setClientID(clientID); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + topic = session.createTopic(topicName); + + subscriber = session.createDurableSubscriber(topic, subscriberName); + connection.start(); + + log.info("Started connection - now about to try receive the textMessage"); + + long time = System.currentTimeMillis(); + Message message = subscriber.receive(15000L); + long elapsed = System.currentTimeMillis() - time; + + log.info("Waited for: " + elapsed + " millis"); + + assertNotNull("Should have received the message we published by now", message); + assertTrue("should be text textMessage", message instanceof TextMessage); + textMessage = (TextMessage) message; + assertEquals("Hello World", textMessage.getText()); + } +} diff --git a/assembly/src/test/org/activemq/usecases/TestSupport.java b/assembly/src/test/org/activemq/usecases/TestSupport.java new file mode 100755 index 0000000000..763eb5318e --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/TestSupport.java @@ -0,0 +1,150 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.TextMessage; + +import junit.framework.TestCase; + + +/** + * Useful base class for unit test cases + * + * @version $Revision: 1.1.1.1 $ + */ +public class TestSupport extends TestCase { + protected Log log = LogFactory.getLog(getClass()); + protected ActiveMQConnectionFactory connectionFactory; + protected boolean topic = true; + + public TestSupport() { + super(); + } + + public TestSupport(String name) { + super(name); + } + + protected ActiveMQMessage createMessage() { + return new ActiveMQMessage(); + } + + protected Destination createDestination(String subject) { + if (topic) { + return new ActiveMQTopic(subject); + } + else { + return new ActiveMQQueue(subject); + } + } + + protected void assertTextMessagesEqual(Message[] firstSet, Message[] secondSet) throws JMSException { + assertTextMessagesEqual("", firstSet, secondSet); + } + /** + * @param messsage + * @param firstSet + * @param secondSet + */ + protected void assertTextMessagesEqual(String messsage, Message[] firstSet, Message[] secondSet) throws JMSException { + assertEquals("Message count does not match: " + messsage, firstSet.length, secondSet.length); + for (int i = 0; i < secondSet.length; i++) { + TextMessage m1 = (TextMessage) firstSet[i]; + TextMessage m2 = (TextMessage) secondSet[i]; + assertTextMessageEqual("Message " + (i + 1) + " did not match : ", m1,m2); + } + } + + protected void assertEquals(TextMessage m1, TextMessage m2) throws JMSException { + assertEquals("", m1, m2); + } + + /** + * @param message + * @param firstSet + * @param secondSet + */ + protected void assertTextMessageEqual(String message, TextMessage m1, TextMessage m2) throws JMSException { + assertFalse(message + ": expected {" + m1 + "}, but was {" + m2 + "}", m1 == null ^ m2 == null); + if( m1 == null ) + return; + assertEquals(message, m1.getText(), m2.getText()); + } + + protected void assertEquals(Message m1, Message m2) throws JMSException { + assertEquals("", m1, m2); + } + /** + * @param message + * @param firstSet + * @param secondSet + */ + protected void assertEquals(String message, Message m1, Message m2) throws JMSException { + assertFalse(message + ": expected {" + m1 + "}, but was {" + m2 + "}", m1 == null ^ m2 == null); + if( m1 == null ) + return; + assertTrue(message + ": expected {" + m1 + "}, but was {" + m2 + "}", m1.getClass()==m2.getClass()); + if( m1 instanceof TextMessage ) { + assertTextMessageEqual(message, (TextMessage)m1, (TextMessage)m2); + } else { + assertEquals(message, m1, m2); + } + } + + protected ActiveMQConnectionFactory createConnectionFactory() throws Exception { + return new ActiveMQConnectionFactory("vm://localhost"); + } + + /** + * Factory method to create a new connection + */ + protected Connection createConnection() throws Exception { + return getConnectionFactory().createConnection(); + } + + public ActiveMQConnectionFactory getConnectionFactory() throws Exception { + if (connectionFactory == null) { + connectionFactory = createConnectionFactory(); + assertTrue("Should have created a connection factory!", connectionFactory != null); + } + return connectionFactory; + } + + protected String getConsumerSubject() { + return getSubject(); + } + + protected String getProducerSubject() { + return getSubject(); + } + + protected String getSubject() { + return getClass().getName() + "." + getName(); + } +} diff --git a/assembly/src/test/org/activemq/usecases/TopicRedeliverTest.java b/assembly/src/test/org/activemq/usecases/TopicRedeliverTest.java new file mode 100755 index 0000000000..5853df2d24 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/TopicRedeliverTest.java @@ -0,0 +1,224 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.jms.Topic; + +import org.activemq.test.TestSupport; +import org.activemq.util.IdGenerator; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class TopicRedeliverTest extends TestSupport { + + private static final int RECEIVE_TIMEOUT = 10000; + private IdGenerator idGen = new IdGenerator(); + protected int deliveryMode = DeliveryMode.PERSISTENT; + public TopicRedeliverTest(){ + } + + public TopicRedeliverTest(String n){ + super(n); + } + + protected void setup() throws Exception{ + super.setUp(); + topic = true; + } + + + /** + * test messages are acknowledged and recovered properly + * @throws Exception + */ + public void testClientAcknowledge() throws Exception { + Destination destination = createDestination(getClass().getName()); + Connection connection = createConnection(); + connection.setClientID(idGen.generateId()); + connection.start(); + Session consumerSession = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); + MessageConsumer consumer = consumerSession.createConsumer(destination); + Session producerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(destination); + producer.setDeliveryMode(deliveryMode); + + //send some messages + + TextMessage sent1 = producerSession.createTextMessage(); + sent1.setText("msg1"); + producer.send(sent1); + + TextMessage sent2 = producerSession.createTextMessage(); + sent1.setText("msg2"); + producer.send(sent2); + + TextMessage sent3 = producerSession.createTextMessage(); + sent1.setText("msg3"); + producer.send(sent3); + + Message rec1 = consumer.receive(RECEIVE_TIMEOUT); + Message rec2 = consumer.receive(RECEIVE_TIMEOUT); + Message rec3 = consumer.receive(RECEIVE_TIMEOUT); + + //ack rec2 + rec2.acknowledge(); + + TextMessage sent4 = producerSession.createTextMessage(); + sent4.setText("msg4"); + producer.send(sent4); + + Message rec4 = consumer.receive(RECEIVE_TIMEOUT); + assertTrue(rec4.equals(sent4)); + consumerSession.recover(); + rec4 = consumer.receive(RECEIVE_TIMEOUT); + assertTrue(rec4.equals(sent4)); + assertTrue(rec4.getJMSRedelivered()); + rec4.acknowledge(); + connection.close(); + + } + + /** + * Test redelivered flag is set on rollbacked transactions + * @throws Exception + */ + public void testRedilveredFlagSetOnRollback() throws Exception { + Destination destination = createDestination(getClass().getName()); + Connection connection = createConnection(); + connection.setClientID(idGen.generateId()); + connection.start(); + Session consumerSession = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE); + MessageConsumer consumer = null; + if (topic){ + consumer = consumerSession.createDurableSubscriber((Topic)destination, "TESTRED"); + }else{ + consumer = consumerSession.createConsumer(destination); + } + Session producerSession = connection.createSession(true, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(destination); + producer.setDeliveryMode(deliveryMode); + + TextMessage sentMsg = producerSession.createTextMessage(); + sentMsg.setText("msg1"); + producer.send(sentMsg); + producerSession.commit(); + + Message recMsg = consumer.receive(RECEIVE_TIMEOUT); + assertTrue(recMsg.getJMSRedelivered() == false); + recMsg = consumer.receive(RECEIVE_TIMEOUT); + consumerSession.rollback(); + recMsg = consumer.receive(RECEIVE_TIMEOUT); + assertTrue(recMsg.getJMSRedelivered()); + consumerSession.commit(); + assertTrue(recMsg.equals(sentMsg)); + assertTrue(recMsg.getJMSRedelivered()); + connection.close(); + } + + + /** + * Check a session is rollbacked on a Session close(); + * @throws Exception + */ + + public void XtestTransactionRollbackOnSessionClose() throws Exception { + Destination destination = createDestination(getClass().getName()); + Connection connection = createConnection(); + connection.setClientID(idGen.generateId()); + connection.start(); + Session consumerSession = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE); + MessageConsumer consumer = null; + if (topic){ + consumer = consumerSession.createDurableSubscriber((Topic)destination, "TESTRED"); + }else{ + consumer = consumerSession.createConsumer(destination); + } + Session producerSession = connection.createSession(true, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(destination); + producer.setDeliveryMode(deliveryMode); + + TextMessage sentMsg = producerSession.createTextMessage(); + sentMsg.setText("msg1"); + producer.send(sentMsg); + + producerSession.commit(); + + Message recMsg = consumer.receive(RECEIVE_TIMEOUT); + assertTrue(recMsg.getJMSRedelivered() == false); + consumerSession.close(); + consumerSession = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE); + consumer = consumerSession.createConsumer(destination); + + recMsg = consumer.receive(RECEIVE_TIMEOUT); + consumerSession.commit(); + assertTrue(recMsg.equals(sentMsg)); + connection.close(); + } + + /** + * check messages are actuallly sent on a tx rollback + * @throws Exception + */ + + public void testTransactionRollbackOnSend() throws Exception { + Destination destination = createDestination(getClass().getName()); + Connection connection = createConnection(); + connection.setClientID(idGen.generateId()); + connection.start(); + Session consumerSession = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE); + MessageConsumer consumer = consumerSession.createConsumer(destination); + Session producerSession = connection.createSession(true, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = producerSession.createProducer(destination); + producer.setDeliveryMode(deliveryMode); + + TextMessage sentMsg = producerSession.createTextMessage(); + sentMsg.setText("msg1"); + producer.send(sentMsg); + producerSession.commit(); + + Message recMsg = consumer.receive(RECEIVE_TIMEOUT); + consumerSession.commit(); + assertTrue(recMsg.equals(sentMsg)); + + sentMsg = producerSession.createTextMessage(); + sentMsg.setText("msg2"); + producer.send(sentMsg); + producerSession.rollback(); + + sentMsg = producerSession.createTextMessage(); + sentMsg.setText("msg3"); + producer.send(sentMsg); + producerSession.commit(); + + recMsg = consumer.receive(RECEIVE_TIMEOUT); + assertTrue(recMsg.equals(sentMsg)); + consumerSession.commit(); + + connection.close(); + } + +} diff --git a/assembly/src/test/org/activemq/usecases/TransactionRollbackOrderTest.java b/assembly/src/test/org/activemq/usecases/TransactionRollbackOrderTest.java new file mode 100644 index 0000000000..cc57039656 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/TransactionRollbackOrderTest.java @@ -0,0 +1,163 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; +import junit.framework.TestCase; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.command.ActiveMQQueue; +import org.activemq.command.ActiveMQTopic; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import java.util.ArrayList; +import java.util.List; + + +/** + * Test case for AMQ-268 + * + * @author Paul Smith + * @version $Revision: 1.1 $ + */ +public final class TransactionRollbackOrderTest extends TestCase { + + private volatile String receivedText; + + private Session producerSession; + private Session consumerSession; + private Destination queue; + + private MessageProducer producer; + private MessageConsumer consumer; + private Connection connection; + private CountDownLatch latch = new CountDownLatch(1); + private int NUM_MESSAGES = 5; + private List msgSent = new ArrayList(); + private List msgCommitted = new ArrayList(); + private List msgRolledBack = new ArrayList(); + private List msgRedelivered = new ArrayList(); + + public void testTransaction() throws Exception { + + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false"); + + factory.setUseEmbeddedBroker(true); + connection = factory.createConnection(); + queue = new ActiveMQQueue(getClass().getName() + "." + getName()); + + producerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + consumerSession = connection.createSession(true, 0); + + producer = producerSession.createProducer(queue); + + consumer = consumerSession.createConsumer(queue); + consumer.setMessageListener(new MessageListener() { + + int msgCount = 0; + int msgCommittedCount = 0; + + public void onMessage(Message m) { + try { + msgCount++; + TextMessage tm = (TextMessage) m; + receivedText = tm.getText(); + + if (tm.getJMSRedelivered()) { + msgRedelivered.add(receivedText); + } + + System.out.println("consumer received message: " + receivedText + (tm.getJMSRedelivered() ? " ** Redelivered **" : "")); + if (msgCount == 3) { + msgRolledBack.add(receivedText); + consumerSession.rollback(); + System.out.println("[msg: " + receivedText + "] ** rolled back **"); + } + else { + msgCommittedCount++; + msgCommitted.add(receivedText); + consumerSession.commit(); + System.out.println("[msg: " + receivedText + "] committed transaction "); + } + if (msgCommittedCount == NUM_MESSAGES) { + latch.countDown(); + } + } + catch (JMSException e) { + try { + consumerSession.rollback(); + System.out.println("rolled back transaction"); + } + catch (JMSException e1) { + System.out.println(e1); + e1.printStackTrace(); + } + System.out.println(e); + e.printStackTrace(); + } + } + }); + connection.start(); + + TextMessage tm = null; + try { + for (int i = 1; i <= NUM_MESSAGES; i++) { + tm = producerSession.createTextMessage(); + tm.setText("Hello " + i); + msgSent.add(tm.getText()); + producer.send(tm); + System.out.println("producer sent message: " + tm.getText()); + } + } + catch (JMSException e) { + e.printStackTrace(); + } + + System.out.println("Waiting for latch"); + latch.await(); + + assertEquals(1, msgRolledBack.size()); + assertEquals(1, msgRedelivered.size()); + + System.out.println("msg RolledBack = " + msgRolledBack.get(0)); + System.out.println("msg Redelivered = " + msgRedelivered.get(0)); + + assertEquals(msgRolledBack.get(0), msgRedelivered.get(0)); + + assertEquals(NUM_MESSAGES, msgSent.size()); + assertEquals(NUM_MESSAGES, msgCommitted.size()); + + assertEquals(msgSent, msgCommitted); + + } + + protected void tearDown() throws Exception { + if (connection != null) { + System.out.println("Closing the connection"); + connection.close(); + } + super.tearDown(); + } +} \ No newline at end of file diff --git a/assembly/src/test/org/activemq/usecases/TransactionTest.java b/assembly/src/test/org/activemq/usecases/TransactionTest.java new file mode 100755 index 0000000000..b033a69b26 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/TransactionTest.java @@ -0,0 +1,120 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import junit.framework.TestCase; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.command.ActiveMQQueue; + +import javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import java.util.Date; + +import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch; + +/** + * @author pragmasoft + * @version $Revision: 1.1.1.1 $ + */ +public final class TransactionTest extends TestCase { + + private volatile String receivedText; + + private Session producerSession; + private Session consumerSession; + private Destination queue; + + private MessageProducer producer; + private MessageConsumer consumer; + private Connection connection; + private CountDownLatch latch = new CountDownLatch(1); + + public void testTransaction() throws Exception { + + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://localhost"); + + factory.setUseEmbeddedBroker(true); + connection = factory.createConnection(); + queue = new ActiveMQQueue(getClass().getName() + "." + getName()); + + producerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + consumerSession = connection.createSession(true, 0); + + producer = producerSession.createProducer(queue); + + consumer = consumerSession.createConsumer(queue); + consumer.setMessageListener(new MessageListener() { + + public void onMessage(Message m) { + try { + TextMessage tm = (TextMessage) m; + receivedText = tm.getText(); + latch.countDown(); + + System.out.println("consumer received message :" + receivedText); + consumerSession.commit(); + System.out.println("committed transaction"); + } + catch (JMSException e) { + try { + consumerSession.rollback(); + System.out.println("rolled back transaction"); + } + catch (JMSException e1) { + System.out.println(e1); + e1.printStackTrace(); + } + System.out.println(e); + e.printStackTrace(); + } + } + }); + + connection.start(); + + TextMessage tm = null; + try { + tm = producerSession.createTextMessage(); + tm.setText("Hello, " + new Date()); + producer.send(tm); + System.out.println("producer sent message :" + tm.getText()); + } + catch (JMSException e) { + e.printStackTrace(); + } + + System.out.println("Waiting for latch"); + latch.await(); + + System.out.println("test completed, destination=" + receivedText); + } + + protected void tearDown() throws Exception { + if (connection != null) { + connection.close(); + } + super.tearDown(); + } +} \ No newline at end of file diff --git a/assembly/src/test/org/activemq/usecases/TransientQueueRedeliverTest.java b/assembly/src/test/org/activemq/usecases/TransientQueueRedeliverTest.java new file mode 100755 index 0000000000..f9f6126235 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/TransientQueueRedeliverTest.java @@ -0,0 +1,34 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import javax.jms.DeliveryMode; + + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class TransientQueueRedeliverTest extends TopicRedeliverTest { + + protected void setUp() throws Exception{ + super.setUp(); + topic = false; + deliveryMode = DeliveryMode.NON_PERSISTENT; + } + +} diff --git a/assembly/src/test/org/activemq/usecases/receiver-activecluster.xml b/assembly/src/test/org/activemq/usecases/receiver-activecluster.xml new file mode 100755 index 0000000000..d3918251bc --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/receiver-activecluster.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assembly/src/test/org/activemq/usecases/receiver-discovery.xml b/assembly/src/test/org/activemq/usecases/receiver-discovery.xml new file mode 100644 index 0000000000..330f42bb58 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/receiver-discovery.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assembly/src/test/org/activemq/usecases/receiver-http.xml b/assembly/src/test/org/activemq/usecases/receiver-http.xml new file mode 100644 index 0000000000..d1cc778349 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/receiver-http.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assembly/src/test/org/activemq/usecases/receiver-zeroconf.xml b/assembly/src/test/org/activemq/usecases/receiver-zeroconf.xml new file mode 100755 index 0000000000..fe1e7c41fd --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/receiver-zeroconf.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assembly/src/test/org/activemq/usecases/receiver.xml b/assembly/src/test/org/activemq/usecases/receiver.xml new file mode 100755 index 0000000000..784224f974 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/receiver.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assembly/src/test/org/activemq/usecases/sender-activecluster.xml b/assembly/src/test/org/activemq/usecases/sender-activecluster.xml new file mode 100755 index 0000000000..a0545d3e2c --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/sender-activecluster.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assembly/src/test/org/activemq/usecases/sender-discovery.xml b/assembly/src/test/org/activemq/usecases/sender-discovery.xml new file mode 100644 index 0000000000..cf9fc8b1e2 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/sender-discovery.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assembly/src/test/org/activemq/usecases/sender-http.xml b/assembly/src/test/org/activemq/usecases/sender-http.xml new file mode 100644 index 0000000000..8998f34818 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/sender-http.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assembly/src/test/org/activemq/usecases/sender-zeroconf.xml b/assembly/src/test/org/activemq/usecases/sender-zeroconf.xml new file mode 100755 index 0000000000..596e043771 --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/sender-zeroconf.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assembly/src/test/org/activemq/usecases/sender.xml b/assembly/src/test/org/activemq/usecases/sender.xml new file mode 100755 index 0000000000..c0002093ce --- /dev/null +++ b/assembly/src/test/org/activemq/usecases/sender.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/build.properties b/build.properties new file mode 100644 index 0000000000..6589467867 --- /dev/null +++ b/build.properties @@ -0,0 +1,5 @@ +maven.site.deploy.method=ssh +#pom.siteAddress=http://activemq.codehaus.org/ +#pom.siteDirectory=c:\deployment\ + +#maven.xdoc.distributionUrl=http://dist.codehaus.org/activemq/distributions/ diff --git a/etc/checkstyle.xml b/etc/checkstyle.xml new file mode 100755 index 0000000000..6820c66819 --- /dev/null +++ b/etc/checkstyle.xml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/etc/maven.xml b/etc/maven.xml new file mode 100755 index 0000000000..75d2183049 --- /dev/null +++ b/etc/maven.xml @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOTICE: Skipping tests; they seem to have passed already + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/etc/project.properties b/etc/project.properties new file mode 100755 index 0000000000..843e235566 --- /dev/null +++ b/etc/project.properties @@ -0,0 +1,193 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.repo.remote=\ +http://dist.codehaus.org,\ +http://www.ibiblio.org/maven,\ +http://cvs.apache.org/repository + +maven.compile.source=1.4 +maven.compile.target=1.4 +maven.compile.deprecation=true +maven.compile.debug=true +maven.compile.optimize=true + +maven.docs.src=${basedir}/xdocs + +# ------------------------------------------------------------------- +# Testing Properties +# ------------------------------------------------------------------- +maven.test.source=1.4 +maven.test.target=1.4 +maven.junit.sysproperties = activemq.store.dir derby.system.home derby.storage.fileSyncTransactionLog java.security.auth.login.config +maven.junit.fork = true +maven.junit.jvmargs=-Xmx160m + +activemq.store.dir = target/MessageStore +activemq.persistenceAdapterFactory = org.activemq.broker.impl.DefaultPersistenceAdapterFactory +derby.system.home = target/derby +derby.storage.fileSyncTransactionLog=true +java.security.auth.login.config=src/test/resources/login.config + +# ------------------------------------------------------------------- +# Javadoc Properties +# ------------------------------------------------------------------- +maven.javadoc.source=1.4 +maven.javadoc.links=\ +http://java.sun.com/j2se/1.4.1/docs/api/,\ +http://java.sun.com/j2ee/1.4/docs/api/,\ +http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent,\ +http://jakarta.apache.org/commons/logging/apidocs + +maven.javadoc.additionalparam = -linksource + +# ------------------------------------------------------------------- +# Checkstyle Properties +# ------------------------------------------------------------------- +maven.checkstyle.properties = ${basedir}/checkstyle.xml +maven.checkstyle.header.file = ${basedir}/LICENSE.txt + +# ------------------------------------------------------------------- +# codehaus theme +# ------------------------------------------------------------------- +maven.xdoc.theme.url=http://codehaus.org/codehaus-style.css + +maven.artifact.legacy=true +maven.repo.central=dist.codehaus.org +maven.repo.central.directory=/dist +maven.remote.group=activemq + +# ------------------------------------------------------------------- +# dependency versions +# ------------------------------------------------------------------- +#maven.jar.override=on + + +geronimo_spec_j2ee_version=1.4-rc4 +concurrent_version=1.3.4 +commons_logging_version=1.0.3 +log4j_version=1.2.8 +junit_version=3.8.1 +jmock_version=1.0.1 +jmock_cglib_version=1.0.1 + + +commons_beanutils_version=1.6.1 +javacc_version=2.1 +activecluster_version=1.1-SNAPSHOT +activeio_version=2.2-SNAPSHOT +drools_core_version=2.0-beta-13 +xerces_version=2.6.2 +xml_parser_apis_version=2.2.1 + + + +axis_version=1.2-RC1 + + +geronimo_kernel_version=1.0-SNAPSHOT +geronimo_system_version=1.0-SNAPSHOT +xmlbeans_version=1.0-DEV + + +spring_version=1.2.4 + +xbean_spring_version=2.0-SNAPSHOT + + +berkeleydb_version=1.5.1 + + +berkeleydb_native_version=4.2 + + +howl_logger_version=0.1.8 + + + +commons_dbcp_version=1.2 +commons_pool_version=1.2 + +axion_version=1.0-M3-dev +commons_collections_version=2.1 +commons_primitives_version=20041207.202534 +regexp_version=1.3 + +hsqldb_version=1.7.2.2 + +derby_version=10.1.1.0 +derbynet_version=10.1.1.0 + + +jdbm_version=0.20-dev + + +emberio_version=0.3-alpha +emberio_version=0.3-alpha + + +geronimo_remoting_version=1.0-SNAPSHOT +geronimo_network_version=1.0-SNAPSHOT +geronimo_core_version=1.0-SNAPSHOT + + +commons_httpclient_version=2.0.1 +servlet_api_version=2.5-6.0.0beta6 +jetty_version=6.0.0beta6 +tomcat_version=5.0.28 +xercesImpl_version=2.6.2 + + +jgroups_version=2.2.5 + + +jrms_version=1.1 + + +p2psockets_core_version=1.1.2 +jxta_version=2.0 + + +xstream_version=1.1.2 +xmlpull_version=1.1.3.4d_b4_min + + +jmdns_version=1.0-RC2 + + +maven_itest_plugin_version=1.0 +geronimo_deployment_plugin_version=1.0-SNAPSHOT +openejb_core_version=2.0-SNAPSHOT +geronimo_security_version=1.0-SNAPSHOT +cglib_version=2.0 +cglib_full_version=2.0 +commons_jelly_tags_velocity_version=SNAPSHOT +velocity_version=1.4-rc1 + + +geronimo_spec_jms_version=1.1-rc4 +geronimo_spec_jta_version=1.0.1B-rc4 +geronimo_spec_j2ee_management_version=1.0-rc4 +geronimo_spec_j2ee_jacc_version=1.0-rc4 +geronimo_spec_ejb_version=2.1-rc4 +geronimo_spec_j2ee_connector_version=1.5-rc4 +geronimo_spec_jsp_version=2.0-rc4 + + +activesoap_version=1.0-SNAPSHOT + +mx4j_version=2.1.1 +mx4j_remote_version=2.1.1 + + +antlr_version=2.7.2 +apacheds_version=0.9.2 +asn1_version=0.3.2 +commons_io_version=1.0 +commons_lang_version=2.0 +kerberos_common_version=0.5 +kerberos_protocols_version=0.5 +ldap_protocols_version=0.9.2 +mina_version=0.7.3 +oro_version=2.0.8 +slf4j_version=1.0-beta7 diff --git a/etc/project.xml b/etc/project.xml new file mode 100755 index 0000000000..df760572a0 --- /dev/null +++ b/etc/project.xml @@ -0,0 +1,569 @@ + + + + + + 3 + + ActiveMQ + activemq + activemq + 4.0-SNAPSHOT + + + LogicBlaze, Inc. + http://logicblaze.com + http://logicblaze.com/images/logo.jpg + + + + 2004 + + org.activemq + + + Core JMS Client API + org.activemq:org.activemq.message + + + JMS Broker and Container + org.activemq.broker:org.activemq.broker.* + + + JCA Managed Connections, Resource Adapters and Management Statistics + org.activemq.ra:org.activemq.management + + + JNDI support + org.activemq.jndi + + + Message filter and router + org.activemq.filter:org.activemq.filter.*:org.activemq.selector + + + Web Connector for REST API and Streamlets support + org.activemq.web + + + Web Service and Apache Axis support + org.activemq.axis + + + Spring support + org.activemq.spring + + + Geronimo / GBean support + org.activemq.gbean + + + Security strategies and implementations + org.activemq.security + + + Transport and WireFormat strategies and implementations + org.activemq.transport:org.activemq.transport.* + + + Message persistence strategies and implementations + org.activemq.store:org.activemq.store.* + + + Journal strategies, implementations and adapters + org.activemq.journal:org.activemq.journal.* + + + Core router services + org.activemq.service:org.activemq.service.* + + + Utilities + org.activemq.capacity:org.activemq.io.util:org.activemq.util + + + + + ActiveMQ is a message broker and a JMS 1.1 provider + + activemq + + + ActiveMQ is an open source message broker and JMS 1.1 provider + + + http://activemq.org/ + http://jira.codehaus.org + + activemq.org + /home/projects/activemq/public_html/maven/${pom.artifactId} + dist.codehaus.org + /dist + + + scm:subversion:svn+ssh://svn.activemq.org/home/projects/activemq/scm/trunk/activemq + scm:subversion:svn+ssh://svn.activemq.org/home/projects/activemq/scm/trunk/activemq + http://svn.activemq.codehaus.org/trunk/activemq/ + + + + + ActiveMQ Developer List + dev-subscribe@activemq.codehaus.org + dev-unsubscribe@activemq.codehaus.org + + http://dir.gmane.org/gmane.comp.java.activemq.devel + + + ActiveMQ User List + user-subscribe@activemq.codehaus.org + user-unsubscribe@activemq.codehaus.org + + http://dir.gmane.org/gmane.comp.java.activemq.user + + + ActiveMQ SCM List + scm-subscribe@activemq.codehaus.org + scm-unsubscribe@activemq.codehaus.org + http://archive.activemq.codehaus.org/scm/ + + + + + + trunk + + + + + + 3.1-SNAPSHOT + 3.1-SNAPSHOT + trunk + + + 3.0 + 3.0 + ACTIVEMQ_3_0 + + + 2.2 + 2.2 + ACTIVEMQ_2_2 + + + 2.1 + 2.1 + ACTIVEMQ_2_1 + + + 2.0 + 2.0 + ACTIVEMQ_2_0 + + + 1.6-SNAPSHOT + 1.6-SNAPSHOT + ACTIVEMQ_1_BRANCH + + + 1.5 + 1.5 + ACTIVEMQ_1_5 + + + 1.4 + 1.4 + ACTIVEMQ_1_4 + + + 1.3 + 1.3 + ACTIVEMQ_1_3 + + + 1.2 + 1.2 + ACTIVEMQ_1_2 + + + 1.1-G1M3 + 1.1-G1M3 + GERONIMO_1_0_M3 + + + 1.1 + 1.1 + ACTIVEMQ_1_1 + + + 1.0 + 1.0 + ACTIVEMQ_1_0 + + + 1.0-beta-1 + 1.0-beta-1 + 1.0-beta-1 + + + + + + + + James Strachan + jstrachan + jstrachan@protique.com + Protique + + + Hiram Chirino + chirino + hiram@protique.com + Protique + + + Geir Magnusson Jr. + geir + + + + + Joe Walnes + joe + + + + + David Jencks + djencks + + + + + Dain Sundstrom + dain + + + + + Alan D. Cabrera + maguro + + + + + Aaron Mulder + ammulder + ammulder@alumni.princeton.edu + + + + Michael Gaffney + mgaffney + mike@gaffney.cc + Panacya Inc. + + + Dennis Cook + dcook + dcook@codehaus.org + + + Darwin Flores + dflores + dflores@logicblaze.com + + + Fritz Oconer + foconer + foconer@logicblaze.com + + + Jonas Lim + jlim + jlim@logicblaze.com + + + Joseph Gapuz + jgapuz + jgapuz@logicblaze.com + + + Merwin Yap + myap + myap@logicblaze.com + + + Patrick Villacorta + pvillacorta + pvillacorta@logicblaze.com + + + + + + + Charles Anthony + + + + Brian Guan + + + + Niklas Gustavsson + + + + Mike Perham + + + + Oliver Belikan + + + + Paul Smith + + + + Ross Mason + + + + Thomas Heller + + + + Peter Henning + + + + Mats Henricson + + + + Leo Pechersky + + + + Li Ma + + + + Mark Bucayan + + + + Neil Clayton + + + + + + + + + + + + commons-logging + commons-logging + http://jakarta.apache.org/commons/logging/ + ${commons_logging_version} + + true + true + true + true + + + + + + + geronimo-spec + geronimo-spec-jms + ${geronimo_spec_jms_version} + + true + false + true + true + + + + + + geronimo-spec + geronimo-spec-jta + ${geronimo_spec_jta_version} + + false + false + false + + + + + geronimo-spec + geronimo-spec-j2ee-management + ${geronimo_spec_j2ee_management_version} + + true + true + true + true + + + + + geronimo-spec + geronimo-spec-j2ee-jacc + ${geronimo_spec_j2ee_jacc_version} + + false + true + true + + + + + backport-util-concurrent + backport-util-concurrent + 2.0_01_pd + + true + true + true + true + + + + + log4j + log4j + ${log4j_version} + + true + + + + + junit + junit + ${junit_version} + + + + + + dev@activemq.codehaus.org + src/java + src/test + + + + + + + + src/test + + **/*.properties + **/*.xml + + + + + **/*Test.* + + + + + + src/conf + + **/* + + + + ${basedir}/target/xmlbeans + + schema/**/*.xsb + + + + + + + + maven-jdepend-plugin + + + + maven-javadoc-plugin + maven-jxr-plugin + maven-junit-report-plugin + maven-pmd-plugin + + + + diff --git a/jmeter/.cvsignore b/jmeter/.cvsignore new file mode 100755 index 0000000000..5a2facdffc --- /dev/null +++ b/jmeter/.cvsignore @@ -0,0 +1,9 @@ +target +.classpath +.project +*.iws +*.ipr +*.iml +build.properties + +.DS_Store diff --git a/jmeter/maven.xml b/jmeter/maven.xml new file mode 100755 index 0000000000..5776445efb --- /dev/null +++ b/jmeter/maven.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +-----------------------------------------------------+ + | Cleaning: ${basedir}\target + +-----------------------------------------------------+ + + + + + diff --git a/jmeter/project.properties b/jmeter/project.properties new file mode 100755 index 0000000000..2456d074bd --- /dev/null +++ b/jmeter/project.properties @@ -0,0 +1,90 @@ +# ------------------------------------------------------------------------ +# Copyright 2004 Protique Ltd +# +# Licensed 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. +# ------------------------------------------------------------------------ + + +# change this property to point to your jmeter home folder +jmeter.home = ${maven.build.dir}/jmeter + +# change this property to point to your jmeter home folder +activemq.jmeter.distribution.name = activemq-jmeter + +# ------------------------------------------------------------------------ +# M A V E N P R O P E R T I E S +# ------------------------------------------------------------------------ +maven.final.name=APacheJMeter_activemq +maven.repo.remote=http://activemq.codehaus.org/~jlim/maven, http://www.ibiblio.org/maven, http://dist.codehaus.org, http://cvs.apache.org/repository + + +# ------------------------------------------------------------------------ +# M A V E N J A R O V E R R I D E +# ------------------------------------------------------------------------ +maven.jar.override = on + + +# ------------------------------------------------------------------------ +# J A R S V E R S I O N N U M B ER +# ------------------------------------------------------------------------ +ApacheJMeter_tcp_version=1.0 +ApacheJMeter_monitors_version=1.0 +ApacheJMeter_mail_version=1.0 +ApacheJMeter_ldap_version=1.0 +ApacheJMeter_jms_version=1.0 +ApacheJMeter_jdbc_version=1.0 +ApacheJMeter_java_version=1.0 +ApacheJMeter_http_version=1.0 +ApacheJMeter_functions_version=1.0 +ApacheJMeter_ftp_version=1.0 +ApacheJMeter_core_version=1.1 +ApacheJMeter_components_version=1.0 +ApacheJMeter_version=1.0 + +avalon_framework_version=4.1.4 +batik_awt_util_version=1.0 +commons_collections_version=2.1 +commons_httpclient_version=2.0 +commons_logging_version=1.0.3 +concurrent_version=1.3.4 +excalibur_datasource_version=1.1.1 +excalibur_instrument_version=1.0 +excalibur_logger_version=1.1 +excalibur_pool_version=1.2 +geronimo_spec_j2ee_version=1.4-rc3 +htmlparser_version=1.0 +jakarta_oro_version=2.0.8 +jdom_version=1.0 +jorphan_version=1.0 +js_version=1.0 +junit_version=3.8.1 +logkit_version=1.2 +soap_version=2.3.1 +Tidy_version=1.0 +velocity_version=1.4-dev +xalan_version=1.0 +xercesImpl_version=1.0 +xml_apis_version=1.0 +xpp3_min_version=1.1.3.4.I +xstream_version=1.1 +manta_version=1.0 +log4j_version=1.2.8 +mx4j_version=2.0.1 +antlr_version=2.7.2 + + +# ------------------------------------------------------------------------ +# Jars set explicity by path. +# ------------------------------------------------------------------------ +# maven.jar.jorphan = ${maven.repo.local}/jorphan/jars/jorphan-1.0.jar +# maven.jar.ApacheJMeter_core = ${maven.repo.local}/jmeter/jars/ApacheJMeter_core-1.1.jar diff --git a/jmeter/project.xml b/jmeter/project.xml new file mode 100755 index 0000000000..21d0a39eff --- /dev/null +++ b/jmeter/project.xml @@ -0,0 +1,334 @@ + + + + 3 + ${basedir}/../etc/project.xml + + ActiveMQ :: Jmeter + JMeter_sampler + ActiveMQ Assembly + ActiveMQ Jmeter creates an ActiveMQ sampler for Jmeter + + org.activemq + + + src/java + src/test + + + + + + activemq + activemq-core + ${pom.currentVersion} + + + + jorphan + jorphan + ${jorphan_version} + + + + + activeio + activeio + ${activeio_version} + + + + + org.apache.derby + derby + ${derby_version} + + + + + jmeter + ApacheJMeter_components + ${ApacheJMeter_components_version} + + + + jmeter + ApacheJMeter_core + ${ApacheJMeter_core_version} + + + + jmeter + ApacheJMeter_ftp + ${ApacheJMeter_ftp_version} + + + + jmeter + ApacheJMeter_functions + ${ApacheJMeter_functions_version} + + + + jmeter + ApacheJMeter_http + ${ApacheJMeter_http_version} + + + + jmeter + ApacheJMeter_java + ${ApacheJMeter_java_version} + + + + jmeter + ApacheJMeter_jdbc + ${ApacheJMeter_jdbc_version} + + + + jmeter + ApacheJMeter_jms + ${ApacheJMeter_jms_version} + + + + jmeter + ApacheJMeter_ldap + ${ApacheJMeter_ldap_version} + + + + jmeter + ApacheJMeter_mail + ${ApacheJMeter_mail_version} + + + + jmeter + ApacheJMeter_monitors + ${ApacheJMeter_monitors_version} + + + + jmeter + ApacheJMeter_tcp + ${ApacheJMeter_tcp_version} + + + + jmeter + ApacheJMeter + ${ApacheJMeter_version} + + + + ant + 1.5 + + + + + avalon-framework + avalon-framework + ${avalon_framework_version} + + + + batik + batik-awt-util + ${batik_awt_util_version} + + + + commons-collections + commons-collections + ${commons_collections_version} + + + + commons-httpclient + commons-httpclient + ${commons_httpclient_version} + + + + commons-logging + commons-logging + ${commons_logging_version} + + + + concurrent + concurrent + ${concurrent_version} + + + + excalibur-datasource + excalibur-datasource + ${excalibur_datasource_version} + + + + excalibur-instrument + excalibur-instrument + ${excalibur_instrument_version} + + + + excalibur-logger + excalibur-logger + ${excalibur_logger_version} + + + + excalibur-pool + excalibur-pool + ${excalibur_pool_version} + + + + geronimo-spec + geronimo-spec-j2ee + ${geronimo_spec_j2ee_version} + + + + htmlparser + htmlparser + ${htmlparser_version} + + + + oro + jakarta-oro + ${jakarta_oro_version} + + + + jdom + jdom + ${jdom_version} + + + + js + js + ${js_version} + + + + junit + junit + ${junit_version} + + + + logkit + logkit + ${logkit_version} + + + + soap + soap + ${soap_version} + + + + tidy + Tidy + ${Tidy_version} + + + + velocity + velocity + ${velocity_version} + + + + xalan + xalan + ${xalan_version} + + + + xerces + xercesImpl + ${xercesImpl_version} + + + + xml-apis + xml-apis + ${xml_apis_version} + + + + xpp3 + xpp3_min + ${xpp3_min_version} + + + + xstream + xstream + ${xstream_version} + + + + log4j + log4j + ${log4j_version} + + + + mx4j + mx4j + ${mx4j_version} + + + + antlr + antlr + ${antlr_version} + + + + manta + manta + ${manta_version} + + + + + + src/java + src/test/java + + + + + + src/test/resources + + **/*.properties + **/*.xml + + + + + **/*Test.* + + + + **/NoDestinationErrorTest.* + + + + + + diff --git a/jmeter/resource/BeanShellAssertion.bshrc b/jmeter/resource/BeanShellAssertion.bshrc new file mode 100644 index 0000000000..b987486e04 --- /dev/null +++ b/jmeter/resource/BeanShellAssertion.bshrc @@ -0,0 +1,25 @@ +// Sample BeanShell Assertion initialisation file + +//print("Initialisation started"); + +import org.apache.jmeter.util.JMeterUtils; + +i = j = k = 0; // for counters + +getprop(p){// get a JMeter property + return JMeterUtils.getPropDefault(p,""); +} + +getprop(p,d){// get a JMeter property with default + return JMeterUtils.getPropDefault(p,d); +} + +setprop(p,v){// set a JMeter property + JMeterUtils.getJMeterProperties().setProperty(p, v); +} + +// Assertions can use the following methods on the Response object: +// SampleResult.setStopThread(true) +// SampleResult.setStopTest(true) + +//print("Initialisation complete"); diff --git a/jmeter/resource/BeanShellFunction.bshrc b/jmeter/resource/BeanShellFunction.bshrc new file mode 100644 index 0000000000..797b911106 --- /dev/null +++ b/jmeter/resource/BeanShellFunction.bshrc @@ -0,0 +1,30 @@ +// Sample BeanShell Function initialisation file + +//print("Initialisation started"); + +import org.apache.jmeter.util.JMeterUtils; + +i = j = k = 0; // for counters + +getprop(p){// get a JMeter property + return JMeterUtils.getPropDefault(p,""); +} + +getprop(p,d){// get a JMeter property with default + return JMeterUtils.getPropDefault(p,d); +} + +setprop(p,v){// set a JMeter property + JMeterUtils.getJMeterProperties().setProperty(p, v); +} + +// Define routines to stop the test or the current thread +stopTest(){// Stop the JMeter test + org.apache.jmeter.engine.StandardJMeterEngine.stopEngine(); +} + +stopThread(){// Stop current JMeter thread + org.apache.jmeter.engine.StandardJMeterEngine.stopThread(Thread.currentThread().getName()); +} + +//print("Initialisation complete"); diff --git a/jmeter/resource/BeanShellSampler.bshrc b/jmeter/resource/BeanShellSampler.bshrc new file mode 100644 index 0000000000..4bd46d0a6a --- /dev/null +++ b/jmeter/resource/BeanShellSampler.bshrc @@ -0,0 +1,30 @@ +// Sample BeanShell Sampler initialisation file + +//print("Initialisation started"); + +import org.apache.jmeter.util.JMeterUtils; + +i = j = k = 0; // for counters + +getprop(p){// get a JMeter property + return JMeterUtils.getPropDefault(p,""); +} + +getprop(p,d){// get a JMeter property with default + return JMeterUtils.getPropDefault(p,d); +} + +setprop(p,v){// set a JMeter property + JMeterUtils.getJMeterProperties().setProperty(p, v); +} + +// Define routines to stop the test or a thread +stopEngine(){// Stop the JMeter test + org.apache.jmeter.engine.StandardJMeterEngine.stopEngine(); +} + +stopThread(t){// Stop a JMeter thread + org.apache.jmeter.engine.StandardJMeterEngine.stopThread(t); +} + +//print("Initialisation complete"); diff --git a/jmeter/resource/LICENSE(XPP3).txt b/jmeter/resource/LICENSE(XPP3).txt new file mode 100644 index 0000000000..4f7a695aac --- /dev/null +++ b/jmeter/resource/LICENSE(XPP3).txt @@ -0,0 +1,46 @@ +Indiana University Extreme! Lab Software License + +Version 1.1.1 + +Copyright (c) 2002 Extreme! Lab, Indiana University. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + +3. The end-user documentation included with the redistribution, if any, + must include the following acknowledgment: + + "This product includes software developed by the Indiana University + Extreme! Lab (http://www.extreme.indiana.edu/)." + +Alternately, this acknowledgment may appear in the software itself, +if and wherever such third-party acknowledgments normally appear. + +4. The names "Indiana Univeristy" and "Indiana Univeristy Extreme! Lab" +must not be used to endorse or promote products derived from this +software without prior written permission. For written permission, +please contact http://www.extreme.indiana.edu/. + +5. Products derived from this software may not use "Indiana Univeristy" +name nor may "Indiana Univeristy" appear in their name, without prior +written permission of the Indiana University. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHORS, COPYRIGHT HOLDERS OR ITS CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/jmeter/resource/LICENSE(XStream).txt b/jmeter/resource/LICENSE(XStream).txt new file mode 100644 index 0000000000..fe86383b43 --- /dev/null +++ b/jmeter/resource/LICENSE(XStream).txt @@ -0,0 +1,25 @@ +Copyright (c) 2003-2004, Joe Walnes +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the distribution. + +Neither the name of XStream nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. \ No newline at end of file diff --git a/jmeter/resource/LICENSE(jdom).txt b/jmeter/resource/LICENSE(jdom).txt new file mode 100644 index 0000000000..71426a36a5 --- /dev/null +++ b/jmeter/resource/LICENSE(jdom).txt @@ -0,0 +1,56 @@ +/*-- + + $Id: LICENSE(jdom).txt,v 1.1 2004/02/10 00:59:07 sebb Exp $ + + Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the disclaimer that follows + these conditions in the documentation and/or other materials + provided with the distribution. + + 3. The name "JDOM" must not be used to endorse or promote products + derived from this software without prior written permission. For + written permission, please contact . + + 4. Products derived from this software may not be called "JDOM", nor + may "JDOM" appear in their name, without prior written permission + from the JDOM Project Management . + + In addition, we request (but do not require) that you include in the + end-user documentation provided with the redistribution and/or in the + software itself an acknowledgement equivalent to the following: + "This product includes software developed by the + JDOM Project (http://www.jdom.org/)." + Alternatively, the acknowledgment may be graphical using the logos + available at http://www.jdom.org/images/logos. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + This software consists of voluntary contributions made by many + individuals on behalf of the JDOM Project and was originally + created by Jason Hunter and + Brett McLaughlin . For more information + on the JDOM Project, please see . + + */ + diff --git a/jmeter/resource/LICENSE(jtidy).txt b/jmeter/resource/LICENSE(jtidy).txt new file mode 100644 index 0000000000..c373900cc4 --- /dev/null +++ b/jmeter/resource/LICENSE(jtidy).txt @@ -0,0 +1,49 @@ + Java HTML Tidy - JTidy + HTML parser and pretty printer + + Copyright (c) 1998-2000 World Wide Web Consortium (Massachusetts + Institute of Technology, Institut National de Recherche en + Informatique et en Automatique, Keio University). All Rights + Reserved. + + Contributing Author(s): + + Dave Raggett + Andy Quick (translation to Java) + Gary L Peskin (Java development) + Sami Lempinen (release management) + + The contributing author(s) would like to thank all those who + helped with testing, bug fixes, and patience. This wouldn't + have been possible without all of you. + + COPYRIGHT NOTICE: + + This software and documentation is provided "as is," and + the copyright holders and contributing author(s) make no + representations or warranties, express or implied, including + but not limited to, warranties of merchantability or fitness + for any particular purpose or that the use of the software or + documentation will not infringe any third party patents, + copyrights, trademarks or other rights. + + The copyright holders and contributing author(s) will not be + liable for any direct, indirect, special or consequential damages + arising out of any use of the software or documentation, even if + advised of the possibility of such damage. + + Permission is hereby granted to use, copy, modify, and distribute + this source code, or portions hereof, documentation and executables, + for any purpose, without fee, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented. + 2. Altered versions must be plainly marked as such and must + not be misrepresented as being the original source. + 3. This Copyright notice may not be removed or altered from any + source or altered source distribution. + + The copyright holders and contributing author(s) specifically + permit, without fee, and encourage the use of this source code + as a component for supporting the Hypertext Markup Language in + commercial products. If you use this source code in a product, + acknowledgment is not required but would be appreciated. diff --git a/jmeter/resource/TestPlans/Queue_Persistent_P10_S10_T1.jmx b/jmeter/resource/TestPlans/Queue_Persistent_P10_S10_T1.jmx new file mode 100644 index 0000000000..d0042be24b --- /dev/null +++ b/jmeter/resource/TestPlans/Queue_Persistent_P10_S10_T1.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 10 + 1 + false + org.activemq.sampler.Producer + false + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 1 + org.activemq.sampler.Consumer + 10 + false + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/TestPlans/Queue_Persistent_P10_S10_T10.jmx b/jmeter/resource/TestPlans/Queue_Persistent_P10_S10_T10.jmx new file mode 100644 index 0000000000..ec51b95003 --- /dev/null +++ b/jmeter/resource/TestPlans/Queue_Persistent_P10_S10_T10.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 10 + 10 + false + org.activemq.sampler.Producer + false + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 10 + org.activemq.sampler.Consumer + 10 + false + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/TestPlans/Queue_Persistent_P1_S1_T1.jmx b/jmeter/resource/TestPlans/Queue_Persistent_P1_S1_T1.jmx new file mode 100644 index 0000000000..ecb9dd8c95 --- /dev/null +++ b/jmeter/resource/TestPlans/Queue_Persistent_P1_S1_T1.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 1 + 1 + false + org.activemq.sampler.Producer + false + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 1 + org.activemq.sampler.Consumer + 1 + false + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/TestPlans/Topic_NonPersistent_Durable_P10_S10_T1.jmx b/jmeter/resource/TestPlans/Topic_NonPersistent_Durable_P10_S10_T1.jmx new file mode 100644 index 0000000000..0511661425 --- /dev/null +++ b/jmeter/resource/TestPlans/Topic_NonPersistent_Durable_P10_S10_T1.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 10 + 1 + false + org.activemq.sampler.Producer + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 1 + org.activemq.sampler.Consumer + 10 + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/TestPlans/Topic_NonPersistent_Durable_P10_S10_T10.jmx b/jmeter/resource/TestPlans/Topic_NonPersistent_Durable_P10_S10_T10.jmx new file mode 100644 index 0000000000..3371401384 --- /dev/null +++ b/jmeter/resource/TestPlans/Topic_NonPersistent_Durable_P10_S10_T10.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 10 + 10 + false + org.activemq.sampler.Producer + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 10 + org.activemq.sampler.Consumer + 10 + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/TestPlans/Topic_NonPersistent_Durable_P1_S1_T1.jmx b/jmeter/resource/TestPlans/Topic_NonPersistent_Durable_P1_S1_T1.jmx new file mode 100644 index 0000000000..dfd6c9f174 --- /dev/null +++ b/jmeter/resource/TestPlans/Topic_NonPersistent_Durable_P1_S1_T1.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 1 + 1 + false + org.activemq.sampler.Producer + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 1 + org.activemq.sampler.Consumer + 1 + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/TestPlans/Topic_NonPersistent_NonDurable_P10_S10_T1.jmx b/jmeter/resource/TestPlans/Topic_NonPersistent_NonDurable_P10_S10_T1.jmx new file mode 100644 index 0000000000..778093ec56 --- /dev/null +++ b/jmeter/resource/TestPlans/Topic_NonPersistent_NonDurable_P10_S10_T1.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 10 + 1 + false + org.activemq.sampler.Producer + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 1 + org.activemq.sampler.Consumer + 10 + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/TestPlans/Topic_NonPersistent_NonDurable_P10_S10_T10.jmx b/jmeter/resource/TestPlans/Topic_NonPersistent_NonDurable_P10_S10_T10.jmx new file mode 100644 index 0000000000..5ecb8e16c3 --- /dev/null +++ b/jmeter/resource/TestPlans/Topic_NonPersistent_NonDurable_P10_S10_T10.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 10 + 10 + false + org.activemq.sampler.Producer + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 10 + org.activemq.sampler.Consumer + 10 + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/TestPlans/Topic_NonPersistent_NonDurable_P1_S1_T1.jmx b/jmeter/resource/TestPlans/Topic_NonPersistent_NonDurable_P1_S1_T1.jmx new file mode 100644 index 0000000000..36c4502f14 --- /dev/null +++ b/jmeter/resource/TestPlans/Topic_NonPersistent_NonDurable_P1_S1_T1.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 1 + 1 + false + org.activemq.sampler.Producer + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 1 + org.activemq.sampler.Consumer + 1 + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/TestPlans/Topic_Persistent_Durable_P10_S10_T1.jmx b/jmeter/resource/TestPlans/Topic_Persistent_Durable_P10_S10_T1.jmx new file mode 100644 index 0000000000..27b946e526 --- /dev/null +++ b/jmeter/resource/TestPlans/Topic_Persistent_Durable_P10_S10_T1.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 10 + 1 + false + org.activemq.sampler.Producer + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 1 + org.activemq.sampler.Consumer + 10 + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/TestPlans/Topic_Persistent_Durable_P10_S10_T10.jmx b/jmeter/resource/TestPlans/Topic_Persistent_Durable_P10_S10_T10.jmx new file mode 100644 index 0000000000..80d5ac51ce --- /dev/null +++ b/jmeter/resource/TestPlans/Topic_Persistent_Durable_P10_S10_T10.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 10 + 10 + false + org.activemq.sampler.Producer + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 10 + org.activemq.sampler.Consumer + 10 + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/TestPlans/Topic_Persistent_Durable_P1_S1_T1.jmx b/jmeter/resource/TestPlans/Topic_Persistent_Durable_P1_S1_T1.jmx new file mode 100644 index 0000000000..dbfa915815 --- /dev/null +++ b/jmeter/resource/TestPlans/Topic_Persistent_Durable_P1_S1_T1.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 1 + 1 + false + org.activemq.sampler.Producer + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 1 + org.activemq.sampler.Consumer + 1 + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/TestPlans/Topic_Persistent_NonDurable_P10_S10_T1.jmx b/jmeter/resource/TestPlans/Topic_Persistent_NonDurable_P10_S10_T1.jmx new file mode 100644 index 0000000000..8d6fbc30c7 --- /dev/null +++ b/jmeter/resource/TestPlans/Topic_Persistent_NonDurable_P10_S10_T1.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 10 + 1 + false + org.activemq.sampler.Producer + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 1 + org.activemq.sampler.Consumer + 10 + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/TestPlans/Topic_Persistent_NonDurable_P10_S10_T10.jmx b/jmeter/resource/TestPlans/Topic_Persistent_NonDurable_P10_S10_T10.jmx new file mode 100644 index 0000000000..4333b49a5c --- /dev/null +++ b/jmeter/resource/TestPlans/Topic_Persistent_NonDurable_P10_S10_T10.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 10 + 10 + false + org.activemq.sampler.Producer + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 10 + org.activemq.sampler.Consumer + 10 + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/TestPlans/Topic_Persistent_NonDurable_P1_S1_T1.jmx b/jmeter/resource/TestPlans/Topic_Persistent_NonDurable_P1_S1_T1.jmx new file mode 100644 index 0000000000..c4a5fa4e28 --- /dev/null +++ b/jmeter/resource/TestPlans/Topic_Persistent_NonDurable_P1_S1_T1.jmx @@ -0,0 +1,157 @@ + + + + + org.apache.jmeter.config.gui.ArgumentsPanel + org.apache.jmeter.config.Arguments + + User+Defined+Variables + true + + org.apache.jmeter.control.gui.TestPlanGui + false + org.apache.jmeter.testelement.TestPlan + Test+Plan + false + true + + + + + 1114501707000 + org.apache.jmeter.threads.ThreadGroup + + + true + 1 + false + org.apache.jmeter.threads.gui.ThreadGroupGui + + org.apache.jmeter.control.gui.LoopControlPanel + 1 + org.apache.jmeter.control.LoopController + Loop+Controller + true + false + + Thread+Group + 1114501707000 + continue + 1 + + + + true + + 1 + 1 + false + org.activemq.sampler.Producer + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + 1024 + org.activemq.sampler.control.gui.ProducerSamplerGui + 1 + Producer+Sampler + true + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ProducerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Producer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + false + 1 + org.activemq.sampler.Consumer + 1 + true + + true + tcp%3A%2F%2Flocalhost%3A61616 + org.activemq.sampler.control.gui.ConsumerSamplerGui + 1 + Consumer+Sampler + false + ActiveMQ+Server + 5 + + + + org.apache.jmeter.visualizers.ConsumerTableVisualizer + org.apache.jmeter.reporters.ResultCollector + View+Consumer+Results + + + + true + true + true + + true + true + true + true + true + true + true + false + true + true + false + true + true + false + 0 + , + true + + saveConfig + + true + + false + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/jar_usage.txt b/jmeter/resource/jar_usage.txt new file mode 100644 index 0000000000..988464ac56 --- /dev/null +++ b/jmeter/resource/jar_usage.txt @@ -0,0 +1,75 @@ +Which jars are used by which modules? +Version: $Revision: 1.6 $ $Date: 2005/03/18 15:26:59 $ +Version: $Revision: 1.6 $ $Date: 2005/03/18 15:26:59 $ + +avalon-framework-4.1.4 +- LogKit (used by HttpClient ?) +- Configuration (ResultCollector, SaveService, SampleResult, TestElementSaver) + +commons-collections +- ListenerNotifier +- Anakia + +commons-httpclient-2.0 +- httpclient + +commons-logging +- httpclient + +excalibur-compatibility +- CLI in JMeter.java + +excalibur-i18n-1.1 + +excalibur-logger-1.1 +- httpclient? + +(htmlparser) +- http: parsing html + +jakarta-oro +- regular expressions: various + +jdom-b9 +- XMLAssertion, JMeterTest ONLY +jdom-b9 +- Anakia + +(jorphan) + +js (Rhino) +- javascript function + +junit +- unit tests + +logkit-1.2 +- logging +- Anakia + +soap +- WebServiceSampler ONLY + +Tidy +- http: various modules for parsing html +- org.xml.sax - various +- XPathUtil (XPath assertion) + +velocity-1.4 +- Anakia (create documentation) Not used by JMeter runtime + +xalan ++org.apache.xalan|xml|xpath + +xercesimpl ++org.apache.html.dom|org.apache.wml|org.apache.xerces|org.apache.xml.serialize ++org.w3c.dom.html|ls + +xml-apis ++javax.xml ++org.w3c.dom ++org.xml.sax + +xml-batik +- org.apache.batik.ext.awt.image.codec|org.apache.batik.ext.awt.image.codec.tiff +The x* jars are used for XML handling (not needed for JDK1.4) \ No newline at end of file diff --git a/jmeter/resource/jmeter b/jmeter/resource/jmeter new file mode 100644 index 0000000000..ef466d981f --- /dev/null +++ b/jmeter/resource/jmeter @@ -0,0 +1,80 @@ +#! /bin/sh + +## $Id: jmeter,v 1.27 2005/03/18 15:26:54 mstover1 Exp $ +## Copyright 2001-2004 The Apache Software Foundation +## +## Licensed 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. + +# The following should be reasonably good values for most tests running +# on Sun JVMs. Following is the analysis on which it is based. If it's total +# gibberish to you, please study my article at +# http://www.atg.com/portal/myatg/developer?paf_dm=full&paf_gear_id=1100010&detailArticle=true&id=9606 +# +# JMeter objects can generally be grouped into three life-length groups: +# +# - Per-sample objects (results, DOMs,...). An awful lot of those. +# Life length of milliseconds to a few seconds. +# +# - Per-run objects (threads, listener data structures,...). Not that many +# of those unless we use the table or tree listeners on heavy runs. +# Life length of minutes to several hours, from creation to start of next run. +# +# - Per-work-session objects (test plans, GUIs,...). +# Life length: for the life of the JVM. + +# This is the base heap size -- you may increase or decrease it to fit your +# system's memory availablity: +HEAP="-Xms256m -Xmx256m" + +# There's an awful lot of per-sample objects allocated during test run, so we +# need a large eden to avoid too frequent scavenges -- you'll need to tune this +# down proportionally if you reduce the HEAP values above: +NEW="-XX:NewSize=128m -XX:MaxNewSize=128m" + +# This ratio and target have been proven OK in tests with a specially high +# amount of per-sample objects (the HtmlParserHTMLParser tests): +# SURVIVOR="-XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=50%" + +# Think about it: trying to keep per-run objects in tenuring definitely +# represents a cost, but where's the benefit? They won't disappear before +# the test is over, and at that point we will no longer care about performance. +# +# So we will have JMeter do an explicit Full GC before starting a test run, +# but then we won't make any effort (or spend any CPU) to keep objects +# in tenuring longer than the life of per-sample objects -- which is hopefully +# shorter than the period between two scavenges): +# +TENURING="-XX:MaxTenuringThreshold=2" + +# This evacuation ratio is OK (see the comments for SURVIVOR) during test +# runs -- no so sure about operations that bring a lot of long-lived information into +# memory in a short period of time, such as loading tests or listener data files. +# Increase it if you experience OutOfMemory problems during those operations +# without having gone through a lot of Full GC-ing just before the OOM: +# EVACUATION="-XX:MaxLiveObjectEvacuationRatio=20%" + +# Avoid the RMI-induced Full GCs to run too frequently -- once every ten minutes +# should be more than enough: +RMIGC="-Dsun.rmi.dgc.client.gcInterval=600000 -Dsun.rmi.dgc.server.gcInterval=600000" + +# PermSize is a scam. Leave it like this: +PERM="-XX:PermSize=64m -XX:MaxPermSize=64m" + +# Finally, some tracing to help in case things go astray: +DEBUG="-verbose:gc -XX:+PrintTenuringDistribution" + +SERVER="-server" + +ARGS="$SERVER $HEAP $NEW $SURVIVOR $TENURING $EVACUATION $RMIGC $PERM $DEBUG" + +java -server -jar `dirname $0`/ApacheJMeter.jar "$@" diff --git a/jmeter/resource/jmeter-n.bat b/jmeter/resource/jmeter-n.bat new file mode 100644 index 0000000000..f716a51d67 --- /dev/null +++ b/jmeter/resource/jmeter-n.bat @@ -0,0 +1,53 @@ +@echo off + +rem $Id: jmeter-n.bat,v 1.3 2005/03/18 15:26:54 mstover1 Exp $ +rem Copyright 2001-2004 The Apache Software Foundation +rem +rem Licensed under the Apache License, Version 2.0 (the "License"); +rem you may not use this file except in compliance with the License. +rem You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, software +rem distributed under the License is distributed on an "AS IS" BASIS, +rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +rem See the License for the specific language governing permissions and +rem limitations under the License. + +rem ============================================ +rem Non-GUI version of JMETER.BAT (WinNT/2K only) +rem +rem Drop a JMX file on this batch script, and it +rem will run it in non-GUI mode, with a log file +rem formed from the input file name but with the +rem extension .jtl +rem +rem Only the first parameter is used. +rem Only works for Win2k. +rem +rem ============================================ + +if "%OS%"=="Windows_NT" goto WinNT +echo "Sorry, this command file requires Windows NT/ 2000 / XP" +pause +goto END +:WinNT + +rem Change to directory containing this file, which must be in bin +echo Changing to JMeter home directory +cd /D %~dp0 + +rem Check file is supplied +if a == a%1 goto winNT2 +rem Check it has extension .jmx +if a%~x1 == a.jmx goto winNT3 +:winNT2 +echo Please supply a script name with the extension .jmx +pause +goto :EOF +:winNT3 + +jmeter -n -t %1 -l %~dpn1.jtl + +:END \ No newline at end of file diff --git a/jmeter/resource/jmeter-server b/jmeter/resource/jmeter-server new file mode 100644 index 0000000000..564f5d61a3 --- /dev/null +++ b/jmeter/resource/jmeter-server @@ -0,0 +1,25 @@ +#!/bin/sh + +## $Id: jmeter-server,v 1.11 2004/02/16 13:34:10 sebb Exp $ +## Copyright 2001-2004 The Apache Software Foundation +## +## Licensed 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. + +set OLDCLASSPATH=$CLASSPATH +export OLDCLASSPATH +set CLASSPATH=`dirname $0`/../lib/ext/ApacheJMeter_core.jar:`dirname $0`/../lib/jorphan.jar:`dirname $0`/../lib/logkit-1.2.jar +export CLASSPATH +rmiregistry & +set CLASSPATH=$OLDCLASSPATH +export CLASSPATH +`dirname $0`/jmeter -s "$@" diff --git a/jmeter/resource/jmeter-server.bat b/jmeter/resource/jmeter-server.bat new file mode 100644 index 0000000000..28c485a750 --- /dev/null +++ b/jmeter/resource/jmeter-server.bat @@ -0,0 +1,78 @@ +@echo off + +rem $Id: jmeter-server.bat,v 1.11 2005/03/18 15:26:54 mstover1 Exp $ +rem Copyright 2001-2004 The Apache Software Foundation +rem +rem Licensed under the Apache License, Version 2.0 (the "License"); +rem you may not use this file except in compliance with the License. +rem You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, software +rem distributed under the License is distributed on an "AS IS" BASIS, +rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +rem See the License for the specific language governing permissions and +rem limitations under the License. + +REM Protect environment against changes if possible: +if "%OS%"=="Windows_NT" setlocal + +rem Need to check if we are using the 4NT shell... +rem [Does that support the ~ constructs?] +if "%eval[2+2]" == "4" goto winNT1 +if exist jmeter-server.bat goto winNT1 +echo Changing to JMeter home directory +cd /D %~dp0 +:winNT1 + +if exist %JMETER_HOME%\lib\ext\ApacheJMeter_core.jar goto setCP +echo Could not find ApacheJmeter_core.jar ... +REM Try to work out JMETER_HOME +echo ... Trying JMETER_HOME=.. +set JMETER_HOME=.. +if exist %JMETER_HOME%\lib\ext\ApacheJMeter_core.jar goto setCP +echo ... trying JMETER_HOME=. +set JMETER_HOME=. +if exist %JMETER_HOME%\lib\ext\ApacheJMeter_core.jar goto setCP +echo Cannot determine JMETER_HOME ! +goto exit + +:setCP +echo Found ApacheJMeter_core.jar +set CLASSPATH=%JMETER_HOME%\lib\ext\ApacheJMeter_core.jar;%JMETER_HOME%\lib\jorphan.jar;%JMETER_HOME%\lib\logkit-1.2.jar +START rmiregistry + +if not "%OS%"=="Windows_NT" goto win9xStart +:winNTStart + +rem Need to check if we are using the 4NT shell... +if "%eval[2+2]" == "4" goto setup4NT + +rem On NT/2K grab all arguments at once +set JMETER_CMD_LINE_ARGS=%* +goto doneStart + +:setup4NT +set JMETER_CMD_LINE_ARGS=%$ +goto doneStart + +:win9xStart +rem Slurp the command line arguments. This loop allows for an unlimited number of +rem agruments (up to the command line limit, anyway). + +set JMETER_CMD_LINE_ARGS= + +:setupArgs +if %1a==a goto doneStart +set JMETER_CMD_LINE_ARGS=%JMETER_CMD_LINE_ARGS% %1 +shift +goto setupArgs + +:doneStart +rem This label provides a place for the argument list loop to break out +rem and for NT handling to skip to. + +jmeter -s %JMETER_CMD_LINE_ARGS% + +:exit \ No newline at end of file diff --git a/jmeter/resource/jmeter-t.bat b/jmeter/resource/jmeter-t.bat new file mode 100644 index 0000000000..d5566bea63 --- /dev/null +++ b/jmeter/resource/jmeter-t.bat @@ -0,0 +1,77 @@ +@echo off +rem $Id: jmeter-t.bat,v 1.3 2005/03/18 15:26:54 mstover1 Exp $ +rem Copyright 2001-2004 The Apache Software Foundation +rem +rem Licensed under the Apache License, Version 2.0 (the "License"); +rem you may not use this file except in compliance with the License. +rem You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, software +rem distributed under the License is distributed on an "AS IS" BASIS, +rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +rem See the License for the specific language governing permissions and +rem limitations under the License. + +rem ============================================ +rem +rem Drop a JMX file on this batch script, and it +rem will load it in the GUI. +rem +rem Only the first parameter is used. +rem Only works for Win2k. +rem +rem ============================================ + + +if "%OS%"=="Windows_NT" goto WinNT +echo "Sorry, this command file requires Windows NT/ 2000" +goto END +:WinNT + +rem change to the directory in which this script resides, i.e. bin +cd %~dp0 + +jmeter -t %1 + +:END@echo off + +rem $Id: jmeter-t.bat,v 1.3 2005/03/18 15:26:54 mstover1 Exp $ +rem Copyright 2001-2004 The Apache Software Foundation +rem +rem Licensed under the Apache License, Version 2.0 (the "License"); +rem you may not use this file except in compliance with the License. +rem You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, software +rem distributed under the License is distributed on an "AS IS" BASIS, +rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +rem See the License for the specific language governing permissions and +rem limitations under the License. + +rem ============================================ +rem +rem Drop a JMX file on this batch script, and it +rem will load it in the GUI. +rem +rem Only the first parameter is used. +rem Only works for Win2k. +rem +rem ============================================ + + +if "%OS%"=="Windows_NT" goto WinNT +echo "Sorry, this command file requires Windows NT/ 2000 / XP" +pause +goto END +:WinNT + +rem change to the directory in which this script resides, i.e. bin +cd /D %~dp0 + +jmeter -t %1 + +:END \ No newline at end of file diff --git a/jmeter/resource/jmeter.bat b/jmeter/resource/jmeter.bat new file mode 100644 index 0000000000..00ec82059a --- /dev/null +++ b/jmeter/resource/jmeter.bat @@ -0,0 +1,83 @@ +@echo off + +rem $Id: jmeter.bat,v 1.30 2005/03/18 15:26:54 mstover1 Exp $ +rem Copyright 2001-2004 The Apache Software Foundation +rem +rem Licensed under the Apache License, Version 2.0 (the "License"); +rem you may not use this file except in compliance with the License. +rem You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, software +rem distributed under the License is distributed on an "AS IS" BASIS, +rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +rem See the License for the specific language governing permissions and +rem limitations under the License. + +if .%JM_LAUNCH% == . set JM_LAUNCH=java.exe + +if not "%OS%"=="Windows_NT" goto win9xStart +:winNTStart +@setlocal + +rem Need to check if we are using the 4NT shell... +if "%eval[2+2]" == "4" goto setup4NT + +if exist jmeter.bat goto winNT1 +echo Changing to JMeter home directory +cd /D %~dp0 + +:winNT1 +rem On NT/2K grab all arguments at once +set JMETER_CMD_LINE_ARGS=%* +goto doneStart + +:setup4NT +set JMETER_CMD_LINE_ARGS=%$ +goto doneStart + +:win9xStart +rem Slurp the command line arguments. This loop allows for an unlimited number of +rem agruments (up to the command line limit, anyway). + +set JMETER_CMD_LINE_ARGS= + +:setupArgs +if %1a==a goto doneStart +set JMETER_CMD_LINE_ARGS=%JMETER_CMD_LINE_ARGS% %1 +shift +goto setupArgs + +:doneStart +rem This label provides a place for the argument list loop to break out +rem and for NT handling to skip to. + +rem See the unix startup file for the rationale of the following parameters, +rem including some tuning recommendations +set HEAP=-Xms256m -Xmx256m +set NEW=-XX:NewSize=128m -XX:MaxNewSize=128m +set SURVIVOR=-XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=50% +set TENURING=-XX:MaxTenuringThreshold=2 +set EVACUATION=-XX:MaxLiveObjectEvacuationRatio=20% +set RMIGC=-Dsun.rmi.dgc.client.gcInterval=600000 -Dsun.rmi.dgc.server.gcInterval=600000 +set PERM=-XX:PermSize=64m -XX:MaxPermSize=64m +set DEBUG=-verbose:gc -XX:+PrintTenuringDistribution + +rem Additional settings that might help improve GUI performance on some platforms +rem See: http://java.sun.com/products/java-media/2D/perf_graphics.html + +set DDRAW= +rem Setting this flag to true turns off DirectDraw usage, which sometimes helps to get rid of a lot of rendering problems on Win32. +rem set DDRAW=%DDRAW% -Dsun.java2d.noddraw=true + +rem Setting this flag to false turns off DirectDraw offscreen surfaces acceleration by forcing all createVolatileImage calls to become createImage calls, and disables hidden acceleration performed on surfaces created with createImage . +rem set DDRAW=%DDRAW% -Dsun.java2d.ddoffscreen=false + +rem Setting this flag to true enables hardware-accelerated scaling. +rem set DDRAW=%DDRAW% -Dsun.java2d.ddscale=true + +rem Collect the settings defined above +set ARGS=%HEAP% %NEW% %SURVIVOR% %TENURING% %EVACUATION% %RMIGC% %PERM% %DEBUG% %DDRAW% + +%JM_START% %JM_LAUNCH% %JVM_ARGS% %ARGS% -jar ApacheJMeter.jar %JMETER_CMD_LINE_ARGS% diff --git a/jmeter/resource/jmeter.properties b/jmeter/resource/jmeter.properties new file mode 100644 index 0000000000..365cf2de48 --- /dev/null +++ b/jmeter/resource/jmeter.properties @@ -0,0 +1,348 @@ +################################################################################ +# Apache JMeter Property file +################################################################################ + +## $Id: jmeter.properties,v 1.116 2005/04/02 23:17:56 sebb Exp $ +## Copyright 2001-2004 The Apache Software Foundation +## +## Licensed 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. + + +#Preferred GUI language. Comment out to use the JVM default locale's language. +#language=de + +# Netscape HTTP Cookie file +cookies=cookies + +# Authorization +authorization=authorization + +#Working directory +user.dir=. + +# XML Reader(Parser) - Must implement SAX 2 specs +xml.parser=org.apache.xerces.parsers.SAXParser + +#Classname of the ssl provider to be used (to enable testing of https urls) +#And the package name where Stream Handlers can be found +#These provided defaults can be uncommented, and they will work if you are using +#Sun's JSSE implementation. + +ssl.provider=com.sun.net.ssl.internal.ssl.Provider +#ssl.provider=iaik.security.jsse.provider.IAIKJSSEProvider +ssl.pkgs=com.sun.net.ssl.internal.www.protocol + +#The location of the truststore (trusted certificates) and keystore ( if other than the default. +#you can uncomment this and change the path to the correct location. +#javax.net.ssl.trustStore=/path/to/cacerts +#javax.net.ssl.keyStore=/path/to/keystore + +#The password to your keystore +#javax.net.ssl.keyStorePassword=password + +#Alternative protocol of the ssl provider for IAIK JCE + iSaSiLk +#You can also override IAIK specific Providers as well. + +#ssl.pkgs=iaik.protocol +#ssl.provider=com.mycompany.security.provider.SmartProvider +#iaik.provider=com.mycompany.security.provider.SmartSSLProvider + +#Flag for whether to output debug messages to System.err +#To enable it, set the value to "all" Note, for it to work with +#JSSE, it needs to be done from the Java command (i.e. -Djavax.net.debug=all) +javax.net.debug=all + +#Classname of the Swing default UI +#Installed Look and Feel classes on Windows are: +#  Metal   = javax.swing.plaf.metal.MetalLookAndFeel +#  Motif   = com.sun.java.swing.plaf.motif.MotifLookAndFeel +#  Windows = com.sun.java.swing.plaf.windows.WindowsLookAndFeel +jmeter.laf=javax.swing.plaf.metal.MetalLookAndFeel + +#icons -> moved to program code +#timer.tree.icon=timer.gif +#listener.tree.icon=ear.gif +#bench.tree.icon=clipboard.gif +#thread.tree.icon=thread.gif +#control.tree.icon=knob.gif +#plan.tree.icon=beaker.gif +#config.tree.icon=leafnode.gif + +# Remote Hosts - comma delimited +remote_hosts=127.0.0.1 +#remote_hosts:localhost:1099,localhost:2010 + +# RMI port to be used by the server +#server_port=1099 + +#Components to not display in JMeter GUI +not_in_menu=Remote Method Configuration,JNDI Configuration,JNDI Lookup Configuration,JNDI Request,Default Controller,org.apache.jmeter.control.DynamicController, org.apache.jmeter.protocol.http.control.Cookie,org.apache.jmeter.protocol.http.control.Authorization,org.apache.jmeter.config.LoginConfig,Header,org.apache.jmeter.protocol.http.config.MultipartUrlConfig + +#--------------------------------------------------------------------------- +# Logging Configuration +#--------------------------------------------------------------------------- + +# Note: JMeter uses Avalon LogKit + +# Logging Format +# see http://avalon.apache.org/logkit/api/org/apache/log/format/PatternFormatter.html +# +# Default format: +#log_format=%{time:yyyy/MM/dd HH:mm:ss} %5.5{priority} - %{category}: %{message} %{throwable} +# \n is automatically added to the end of the string +# +# Predefined formats in the JMeter LoggingManager: +#log_format_type=default +#log_format_type=thread_prefix +#log_format_type=thread_suffix +# default is as above +# thread_prefix adds the thread name as a prefix to the category +# thread_suffix adds the thread name as a suffix to the category +# Note that thread name is not included by default, as it requires extra processing. +# +# To change the logging format, define either log_format_type or log_format +# If both are defined, the type takes precedence +# Note that these properties cannot be defined using the -J or -D JMeter +# command-line flags, as the format will have already been determined by then +# However, they can be defined as JVM properties + +#Logging levels for the logging categories in JMeter. Correct values are FATAL_ERROR, ERROR, WARN, INFO, and DEBUG +# To set the log level for a package or individual class, use: +# log_level.[package_name].[classname]=[PRIORITY_LEVEL] +# But omit "org.apache" from the package name. The classname is optional. Further examples below. + +log_level.jmeter=INFO +log_level.jmeter.junit=DEBUG +#log_level.jmeter.control=DEBUG +#log_level.jmeter.testbeans=DEBUG +#log_level.jmeter.engine=DEBUG +#log_level.jmeter.threads=DEBUG +#log_level.jmeter.gui=WARN +#log_level.jmeter.testelement=DEBUG +#log_level.jmeter.util=WARN +#log_level.jmeter.util.classfinder=WARN +#log_level.jmeter.test=DEBUG +#log_level.jmeter.protocol.http=DEBUG +#log_level.jmeter.protocol.ftp=WARN +#log_level.jmeter.protocol.jdbc=DEBUG +#log_level.jmeter.protocol.java=WARN +#log_level.jmeter.testelements.property=DEBUG +log_level.jorphan=INFO + + +#Log file for log messages. +# You can specify a different log file for different categories via: +# log_file.[category]=[filename] +# category is equivalent to the package/class names described above + +# Combined log file (for jmeter and jorphan) +log_file=jmeter.log +# To redirect logging to standard output, try the following: +# (it will probably report an error, but output will be to stdout) +#log_file= + +# Or define separate logs if required: +#log_file.jorphan=jorphan.log +#log_file.jmeter=jmeter.log + +#--------------------------------------------------------------------------- +# HTTPClient configuration +#--------------------------------------------------------------------------- + +# set the socket timeout +#httpclient.timeout=0 + +# Set the http version (defaults to 1.1) +#httpclient.version=1.0 + +# Sample logging levels for HttpClient +# Note that full category names are used, i.e. must include the org.apache. +# Info level produces no output: +#log_level.org.apache.commons.logging=debug +# Might be useful: +#org.apache.commons.httpclient.Authenticator=trace + +# wire debug produces a lot of output; consider using separate file: +#log_level.httpclient.wire=debug +#log_file.httpclient=httpclient.log + +# Further logging configuration +# Excalibur logging provides the facility to configure logging using +# configuration files written in XML. This allows for such features as +# log file rotation which are not supported directly by JMeter. +# +# If such a file specified, it will be applied to the current logging +# hierarchy when that has been created. +# +#log_config=logkit.xml + +#--------------------------------------------------------------------------- +# Results file configuration +#--------------------------------------------------------------------------- + +# This section helps determine how result data will be saved. +# The commented out values are the defaults. + +# legitimate values: xml, csv, db. Only xml and csv are currently supported. +jmeter.save.saveservice.output_format=xml + + +# true when field should be saved; false otherwise + +# assertion_results_failure_message only affects CSV output +#jmeter.save.saveservice.assertion_results_failure_message=true +#jmeter.save.saveservice.data_type=true +#jmeter.save.saveservice.label=true +#jmeter.save.saveservice.response_code=true +#jmeter.save.saveservice.response_data=false +#jmeter.save.saveservice.response_message=true +#jmeter.save.saveservice.successful=true +#jmeter.save.saveservice.thread_name=true +#jmeter.save.saveservice.time=true + +# legitimate values: none, ms, or a format suitable for SimpleDateFormat +#jmeter.save.saveservice.timestamp_format=ms +#jmeter.save.saveservice.timestamp_format=MM/dd/yy HH:mm:ss + +# legitimate values: none, first, all +#jmeter.save.saveservice.assertion_results=none + +# For use with Comma-separated value (CSV) files or other formats +# where the fields' values are separated by specified delimiters. +#jmeter.save.saveservice.default_delimiter=, +#jmeter.save.saveservice.print_field_names=true + +# File that holds a record of name changes for backward compatibility issues +upgrade_properties=/bin/upgrade.properties + +# If the proxy detects a gap of at least 1s (default) between HTTP requests, +# it assumes that the user has clicked a new URL +#proxy.pause=1000 + +# Add numeric prefix to Sampler names (default false) +#proxy.number.requests=true +# +# Define the HTML parser to be used. +# Default parser: +#htmlParser.className=org.apache.jmeter.protocol.http.parser.HtmlParserHTMLParser +# Other parsers: +#htmlParser.className=org.apache.jmeter.protocol.http.parser.JTidyHTMLParser +#htmlParser.className=org.apache.jmeter.protocol.http.parser.RegexpHTMLParser + +# Put the start time stamp in logs instead of the end +#sampleresult.timestamp.start=true + +# The encoding to be used if none is provided (default ISO-8859-1) +#sampleresult.default.encoding=ISO-8859-1 + +# Remote batching support +# default is Standard, which returns each sample +# Hold retains samples until end of test (may need lots of memory) +# Batch returns samples in batches +# hold_samples was originally defined as a separate property, +# but can now also be defined using remote.mode +#mode=Standard +#mode=Batch +#mode=Hold +#hold_samples=true +#num_sample_threshold=100 +#time_threshold=60000 + +# Turn expert mode on/off: expert mode will show expert-mode beans and properties +#jmeter.expertMode=true + +# To set the Monitor Health Visualiser buffer size, enter the desired value +# monitor.buffer.size=800 + +#TCP sampler +# The default handler class +#tcp.handler=TCPClientImpl +# +# eolByte = byte value for end of line +#tcp.eolByte=0 +# +# status.prefix and suffix = strings that enclose the status response code +#tcp.status.prefix=Status= +#tcp.status.suffix=. +# +# status.properties = property file to convert codes to messages +#tcp.status.properties=mytestfiles/tcpstatus.properties + +# Summariser settings +# +# Define the following property to automatically start a summariser with that name +#summariser.name=summary +# +# interval between summaries (in seconds) default 3 minutes +#summariser.interval=180 +# +# Write messages to log file +#summariser.log=true +# +# Write messages to System.out +#summariser.out=true + +# BeanShell Server properties +# +# Define the port number as non-zero to start the server on that port +#beanshell.server.port=0 +# +# Define the server initialisation file +#beanshell.server.file=initial.bsh +# +# Define the intialisation files for BeanShell Sampler and Function elements +#beanshell.sampler.init=BeanShellSampler.bshrc +#beanshell.function.init=BeanShellFunction.bshrc +#beanshell.assertion.init=etc + +#TestBeanGui +# +#propertyEditorSearchPath=null + +#JMeterUtils +# +#search_paths=null + +# MailerModel settings +# +# Number of successful samples before a message is sent +#mailer.successlimit=2 +# +# Number of failed samples before a message is sent +#mailer.failurelimit=2 + +# CSVRead delimiter setting (default ",") +# Make sure that there are no trailing spaces or tabs after the delimiter +# characters, or these will be included in the list of valid delimiters +#csvread.delimiter=, +#csvread.delimiter=; +#csvread.delimiter=! +#csvread.delimiter=~ +# The following line has a tab after the = +#csvread.delimiter= + +# CookieManager behaviour - should cookies with null/empty values be deleted? +# Default is true. Use false to revert to original behaviour +#CookieManager.delete_null_cookies=true + +# (2.0.3) JMeterThread behaviour has been changed to set the started flag before +# the controllers are initialised. This is so controllers can access variables earlier. +# In case this causes problems, the previous behaviour can be restored by uncommenting +# the following line. +#jmeterthread.startearlier=false +#Producer TCP sampler +# The default handler class +#tcp.prod.handler=ProducerClientImpl +# +# eolByte = byte value for end of line +#tcp.prod.eolByte=0 diff --git a/jmeter/resource/jmeter.properties.merge b/jmeter/resource/jmeter.properties.merge new file mode 100755 index 0000000000..9c5758e745 --- /dev/null +++ b/jmeter/resource/jmeter.properties.merge @@ -0,0 +1,6 @@ +#Producer TCP sampler +# The default handler class +#tcp.prod.handler=ProducerClientImpl +# +# eolByte = byte value for end of line +#tcp.prod.eolByte=0 diff --git a/jmeter/resource/jmetertest.properties b/jmeter/resource/jmetertest.properties new file mode 100644 index 0000000000..d3fd0e59ea --- /dev/null +++ b/jmeter/resource/jmetertest.properties @@ -0,0 +1,166 @@ +################################################################################ +# Apache JMeter Property file +################################################################################ + + +## $Id: jmetertest.properties,v 1.7 2005/03/19 00:32:45 sebb Exp $ +## Copyright 2001-2004 The Apache Software Foundation +## +## Licensed 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. + +#Preferred GUI language. Comment out to use the JVM default locale's language. +#language=de + +#Paths to search for classes (";" must be the separator) +#search_paths=null + +# Netscape HTTP Cookie file +cookies=cookies + +# Authorization +authorization=authorization + +#Working directory +user.dir=. + +# XML Reader(Parser) - Must implement SAX 2 specs +xml.parser=org.apache.xerces.parsers.SAXParser + +#Classname of the ssl provider to be used (to enable testing of https urls) +#And the package name where Stream Handlers can be found +#These provided defaults can be uncommented, and they will work if you are using +#Sun's JSSE implementation. + +ssl.provider=com.sun.net.ssl.internal.ssl.Provider +#ssl.provider=iaik.security.jsse.provider.IAIKJSSEProvider +ssl.pkgs=com.sun.net.ssl.internal.www.protocol + +#The location of the truststore (trusted certificates) and keystore ( if other than the default. +#you can uncomment this and change the path to the correct location. +#javax.net.ssl.trustStore=/path/to/cacerts +#javax.net.ssl.keyStore=/path/to/keystore + +#The password to your keystore +#javax.net.ssl.keyStorePassword=password + +#Alternative protocol of the ssl provider for IAIK JCE + iSaSiLk +#You can also override IAIK specific Providers as well. + +#ssl.pkgs=iaik.protocol +#ssl.provider=com.mycompany.security.provider.SmartProvider +#iaik.provider=com.mycompany.security.provider.SmartSSLProvider + +#Flag for whether to output debug messages to System.err +#To enable it, set the value to "all" Note, for it to work with +#JSSE, it needs to be done from the Java command (i.e. -Djavax.net.debug=all) +javax.net.debug=all + +#Classname of the Swing default UI +#Installed Look and Feel classes on Windows are: +#  Metal   = javax.swing.plaf.metal.MetalLookAndFeel +#  Motif   = com.sun.java.swing.plaf.motif.MotifLookAndFeel +#  Windows = com.sun.java.swing.plaf.windows.WindowsLookAndFeel +## Let LAF be picked up from default +## (otherwise can cause problems for Eclipse JUnit GUI mode) +#jmeter.laf=javax.swing.plaf.metal.MetalLookAndFeel +#jmeter.laf=com.sun.java.swing.plaf.motif.MotifLookAndFeel + +#icons -> moved to program code +#timer.tree.icon=timer.gif +#listener.tree.icon=ear.gif +#bench.tree.icon=clipboard.gif +#thread.tree.icon=thread.gif +#control.tree.icon=knob.gif +#plan.tree.icon=beaker.gif +#config.tree.icon=leafnode.gif + +# Remote Hosts - comma delimited +remote_hosts=127.0.0.1 + +#Components to not display in JMeter GUI +not_in_menu=Remote Method Configuration,JNDI Configuration,JNDI Lookup Configuration,JNDI Request,Default Controller,org.apache.jmeter.control.DynamicController, org.apache.jmeter.protocol.http.control.Cookie,org.apache.jmeter.protocol.http.control.Authorization,org.apache.jmeter.config.LoginConfig,Header,org.apache.jmeter.protocol.http.config.MultipartUrlConfig + +#Logging levels for the logging categories in JMeter. Correct values are FATAL_ERROR, ERROR, WARN, INFO, and DEBUG +# To set the log level for a package or individual class, use: +# log_level.[package_name].[classname]=[PRIORITY_LEVEL] +# But omit "org.apache" from the package name. The classname is optional. Further examples below. + +log_level.jmeter=INFO +#log_level.jmeter.junit=DEBUG +#log_level.jmeter.engine=WARN +#log_level.jmeter.gui=WARN +#log_level.jmeter.testelement=DEBUG +#log_level.jmeter.util=WARN +#log_level.jmeter.util.classfinder=WARN +#log_level.jmeter.test=DEBUG +#log_level.jmeter.protocol.http=DEBUG +#log_level.jmeter.protocol.ftp=WARN +#log_level.jmeter.protocol.jdbc=WARN +#log_level.jmeter.protocol.java=WARN +#log_level.jmeter.testelements.property=DEBUG +log_level.jorphan=INFO + +#Log file for log messages. +# You can specify a different log file for different categories via: +# log_file.[category]=[filename] +# category is equivalent to the package/class names described above + +# Combined log file (for jmeter and jorphan) +log_file=jmeter-test.log + +# Or define separate logs if required: +#log_file.jorphan=jorphan.log +#log_file.jmeter=jmeter.log + + +#--------------------------------------------------------------------------- +# Results file configuration +#--------------------------------------------------------------------------- + +# For testing, output is changed to CSV and variable fields +# (timestamp and elased) are suppressed + +# This section helps determine how result data will be saved. +# The commented out values are the defaults. + +# legitimate values: xml, csv, db. Only xml and csv are currently supported. +jmeter.save.saveservice.output_format=csv + + +# true when field should be saved; false otherwise + +# assertion_results_failure_message only affects CSV output +#jmeter.save.saveservice.assertion_results_failure_message=true +#jmeter.save.saveservice.data_type=true +#jmeter.save.saveservice.label=true +#jmeter.save.saveservice.response_code=true +#jmeter.save.saveservice.response_data=false +#jmeter.save.saveservice.response_message=true +#jmeter.save.saveservice.successful=true +#jmeter.save.saveservice.thread_name=true +jmeter.save.saveservice.time=false + +# legitimate values: none, ms, or a format suitable for SimpleDateFormat +jmeter.save.saveservice.timestamp_format=none +#jmeter.save.saveservice.timestamp_format=MM/dd/yy HH:mm:ss + +# legitimate values: none, first, all +#jmeter.save.saveservice.assertion_results=none + +# For use with Comma-separated value (CSV) files or other formats +# where the fields' values are separated by specified delimiters. +#jmeter.save.saveservice.default_delimiter=, +#jmeter.save.saveservice.print_field_names=true + +# File that holds a record of name changes for backward compatibility issues +upgrade_properties=/bin/upgrade.properties diff --git a/jmeter/resource/jmeterw.bat b/jmeter/resource/jmeterw.bat new file mode 100644 index 0000000000..154f0d843e --- /dev/null +++ b/jmeter/resource/jmeterw.bat @@ -0,0 +1,23 @@ +@echo off +rem Run JMeter using javaw + +rem $Id: jmeterw.bat,v 1.4 2005/03/18 15:26:54 mstover1 Exp $ +rem Copyright 2001-2004 The Apache Software Foundation +rem +rem Licensed under the Apache License, Version 2.0 (the "License"); +rem you may not use this file except in compliance with the License. +rem You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, software +rem distributed under the License is distributed on an "AS IS" BASIS, +rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +rem See the License for the specific language governing permissions and +rem limitations under the License. + +set JM_START=start +set JM_LAUNCH=javaw.exe +call jmeter %* +set JM_START= +set JM_LAUNCH= \ No newline at end of file diff --git a/jmeter/resource/js_jar_license(mozilla).html b/jmeter/resource/js_jar_license(mozilla).html new file mode 100644 index 0000000000..4ac162b230 --- /dev/null +++ b/jmeter/resource/js_jar_license(mozilla).html @@ -0,0 +1,789 @@ + + + + + + + Mozilla Public License version 1.1 + + + + + + + +
MOZILLA PUBLIC LICENSE + +
Version 1.1 + +

+ +


+ + + +

1. Definitions. + +

    1.0.1. "Commercial Use" means distribution or otherwise making + +the Covered Code available to a third party. + +

    1.1. ''Contributor'' means each entity that creates or contributes + +to the creation of Modifications. + +

    1.2. ''Contributor Version'' means the combination of the Original + +Code, prior Modifications used by a Contributor, and the Modifications + +made by that particular Contributor. + +

    1.3. ''Covered Code'' means the Original Code or Modifications + +or the combination of the Original Code and Modifications, in each case + +including portions thereof. + +

    1.4. ''Electronic Distribution Mechanism'' means a mechanism + +generally accepted in the software development community for the electronic + +transfer of data. + +

    1.5. ''Executable'' means Covered Code in any form other than + +Source Code. + +

    1.6. ''Initial Developer'' means the individual or entity identified + +as the Initial Developer in the Source Code notice required by Exhibit + +A. + +

    1.7. ''Larger Work'' means a work which combines Covered Code + +or portions thereof with code not governed by the terms of this License. + +

    1.8. ''License'' means this document. + +

    1.8.1. "Licensable" means having the right to grant, to the maximum + +extent possible, whether at the time of the initial grant or subsequently + +acquired, any and all of the rights conveyed herein. + +

    1.9. ''Modifications'' means any addition to or deletion from + +the substance or structure of either the Original Code or any previous + +Modifications. When Covered Code is released as a series of files, a Modification + +is: + +

      A. Any addition to or deletion from the contents of a file containing + +Original Code or previous Modifications. + +

      B. Any new file that contains any part of the Original Code or + +previous Modifications. + +
       

    + +1.10. ''Original Code'' means Source Code of computer software code + +which is described in the Source Code notice required by Exhibit A + +as Original Code, and which, at the time of its release under this License + +is not already Covered Code governed by this License. + +

    1.10.1. "Patent Claims" means any patent claim(s), now owned + +or hereafter acquired, including without limitation,  method, process, + +and apparatus claims, in any patent Licensable by grantor. + +

    1.11. ''Source Code'' means the preferred form of the Covered + +Code for making modifications to it, including all modules it contains, + +plus any associated interface definition files, scripts used to control + +compilation and installation of an Executable, or source code differential + +comparisons against either the Original Code or another well known, available + +Covered Code of the Contributor's choice. The Source Code can be in a compressed + +or archival form, provided the appropriate decompression or de-archiving + +software is widely available for no charge. + +

    1.12. "You'' (or "Your")  means an individual or a legal + +entity exercising rights under, and complying with all of the terms of, + +this License or a future version of this License issued under Section 6.1. + +For legal entities, "You'' includes any entity which controls, is controlled + +by, or is under common control with You. For purposes of this definition, + +"control'' means (a) the power, direct or indirect, to cause the direction + +or management of such entity, whether by contract or otherwise, or (b) + +ownership of more than fifty percent (50%) of the outstanding shares or + +beneficial ownership of such entity.

+ +2. Source Code License. + +
    2.1. The Initial Developer Grant. + +
    The Initial Developer hereby grants You a world-wide, royalty-free, + +non-exclusive license, subject to third party intellectual property claims: + +
      (a)  under intellectual property rights (other than + +patent or trademark) Licensable by Initial Developer to use, reproduce, + +modify, display, perform, sublicense and distribute the Original Code (or + +portions thereof) with or without Modifications, and/or as part of a Larger + +Work; and + +

      (b) under Patents Claims infringed by the making, using or selling + +of Original Code, to make, have made, use, practice, sell, and offer for + +sale, and/or otherwise dispose of the Original Code (or portions thereof). + +

        + +
           
        + +
      + +(c) the licenses granted in this Section 2.1(a) and (b) are effective + +on the date Initial Developer first distributes Original Code under the + +terms of this License. + +

      (d) Notwithstanding Section 2.1(b) above, no patent license is + +granted: 1) for code that You delete from the Original Code; 2) separate + +from the Original Code;  or 3) for infringements caused by: i) the + +modification of the Original Code or ii) the combination of the Original + +Code with other software or devices. + +
       

    + +2.2. Contributor Grant. + +
    Subject to third party intellectual property claims, each Contributor + +hereby grants You a world-wide, royalty-free, non-exclusive license + +
        + +
      (a)  under intellectual property rights (other than + +patent or trademark) Licensable by Contributor, to use, reproduce, modify, + +display, perform, sublicense and distribute the Modifications created by + +such Contributor (or portions thereof) either on an unmodified basis, with + +other Modifications, as Covered Code and/or as part of a Larger Work; and + +

      (b) under Patent Claims infringed by the making, using, or selling + +of  Modifications made by that Contributor either alone and/or in + +combination with its Contributor Version (or portions of such combination), + +to make, use, sell, offer for sale, have made, and/or otherwise dispose + +of: 1) Modifications made by that Contributor (or portions thereof); and + +2) the combination of  Modifications made by that Contributor with + +its Contributor Version (or portions of such combination). + +

      (c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective + +on the date Contributor first makes Commercial Use of the Covered Code. + +

      (d)    Notwithstanding Section 2.2(b) above, no + +patent license is granted: 1) for any code that Contributor has deleted + +from the Contributor Version; 2)  separate from the Contributor Version;  + +3)  for infringements caused by: i) third party modifications of Contributor + +Version or ii)  the combination of Modifications made by that Contributor + +with other software  (except as part of the Contributor Version) or + +other devices; or 4) under Patent Claims infringed by Covered Code in the + +absence of Modifications made by that Contributor.

    + +
+ + + +


3. Distribution Obligations. + +

    3.1. Application of License. + +
    The Modifications which You create or to which You contribute are governed + +by the terms of this License, including without limitation Section 2.2. + +The Source Code version of Covered Code may be distributed only under the + +terms of this License or a future version of this License released under + +Section 6.1, and You must include a copy of this License with every + +copy of the Source Code You distribute. You may not offer or impose any + +terms on any Source Code version that alters or restricts the applicable + +version of this License or the recipients' rights hereunder. However, You + +may include an additional document offering the additional rights described + +in Section 3.5. + +

    3.2. Availability of Source Code. + +
    Any Modification which You create or to which You contribute must be + +made available in Source Code form under the terms of this License either + +on the same media as an Executable version or via an accepted Electronic + +Distribution Mechanism to anyone to whom you made an Executable version + +available; and if made available via Electronic Distribution Mechanism, + +must remain available for at least twelve (12) months after the date it + +initially became available, or at least six (6) months after a subsequent + +version of that particular Modification has been made available to such + +recipients. You are responsible for ensuring that the Source Code version + +remains available even if the Electronic Distribution Mechanism is maintained + +by a third party. + +

    3.3. Description of Modifications. + +
    You must cause all Covered Code to which You contribute to contain + +a file documenting the changes You made to create that Covered Code and + +the date of any change. You must include a prominent statement that the + +Modification is derived, directly or indirectly, from Original Code provided + +by the Initial Developer and including the name of the Initial Developer + +in (a) the Source Code, and (b) in any notice in an Executable version + +or related documentation in which You describe the origin or ownership + +of the Covered Code. + +

    3.4. Intellectual Property Matters + +

      (a) Third Party Claims. + +
      If Contributor has knowledge that a license under a third party's intellectual + +property rights is required to exercise the rights granted by such Contributor + +under Sections 2.1 or 2.2, Contributor must include a text file with the + +Source Code distribution titled "LEGAL'' which describes the claim and + +the party making the claim in sufficient detail that a recipient will know + +whom to contact. If Contributor obtains such knowledge after the Modification + +is made available as described in Section 3.2, Contributor shall promptly + +modify the LEGAL file in all copies Contributor makes available thereafter + +and shall take other steps (such as notifying appropriate mailing lists + +or newsgroups) reasonably calculated to inform those who received the Covered + +Code that new knowledge has been obtained. + +

      (b) Contributor APIs. + +
      If Contributor's Modifications include an application programming interface + +and Contributor has knowledge of patent licenses which are reasonably necessary + +to implement that API, Contributor must also include this information in + +the LEGAL file. + +
       

    + +          (c)    + +Representations. + +
      Contributor represents that, except as disclosed pursuant to Section + +3.4(a) above, Contributor believes that Contributor's Modifications are + +Contributor's original creation(s) and/or Contributor has sufficient rights + +to grant the rights conveyed by this License.
    + + + +


    3.5. Required Notices. + +
    You must duplicate the notice in Exhibit A in each file of the + +Source Code.  If it is not possible to put such notice in a particular + +Source Code file due to its structure, then You must include such notice + +in a location (such as a relevant directory) where a user would be likely + +to look for such a notice.  If You created one or more Modification(s) + +You may add your name as a Contributor to the notice described in Exhibit + +A.  You must also duplicate this License in any documentation + +for the Source Code where You describe recipients' rights or ownership + +rights relating to Covered Code.  You may choose to offer, and to + +charge a fee for, warranty, support, indemnity or liability obligations + +to one or more recipients of Covered Code. However, You may do so only + +on Your own behalf, and not on behalf of the Initial Developer or any Contributor. + +You must make it absolutely clear than any such warranty, support, indemnity + +or liability obligation is offered by You alone, and You hereby agree to + +indemnify the Initial Developer and every Contributor for any liability + +incurred by the Initial Developer or such Contributor as a result of warranty, + +support, indemnity or liability terms You offer. + +

    3.6. Distribution of Executable Versions. + +
    You may distribute Covered Code in Executable form only if the requirements + +of Section 3.1-3.5 have been met for that Covered Code, and if You + +include a notice stating that the Source Code version of the Covered Code + +is available under the terms of this License, including a description of + +how and where You have fulfilled the obligations of Section 3.2. + +The notice must be conspicuously included in any notice in an Executable + +version, related documentation or collateral in which You describe recipients' + +rights relating to the Covered Code. You may distribute the Executable + +version of Covered Code or ownership rights under a license of Your choice, + +which may contain terms different from this License, provided that You + +are in compliance with the terms of this License and that the license for + +the Executable version does not attempt to limit or alter the recipient's + +rights in the Source Code version from the rights set forth in this License. + +If You distribute the Executable version under a different license You + +must make it absolutely clear that any terms which differ from this License + +are offered by You alone, not by the Initial Developer or any Contributor. + +You hereby agree to indemnify the Initial Developer and every Contributor + +for any liability incurred by the Initial Developer or such Contributor + +as a result of any such terms You offer. + +

    3.7. Larger Works. + +
    You may create a Larger Work by combining Covered Code with other code + +not governed by the terms of this License and distribute the Larger Work + +as a single product. In such a case, You must make sure the requirements + +of this License are fulfilled for the Covered Code.

+ +4. Inability to Comply Due to Statute or Regulation. + +
    If it is impossible for You to comply with any of the terms of this + +License with respect to some or all of the Covered Code due to statute, + +judicial order, or regulation then You must: (a) comply with the terms + +of this License to the maximum extent possible; and (b) describe the limitations + +and the code they affect. Such description must be included in the LEGAL + +file described in Section 3.4 and must be included with all distributions + +of the Source Code. Except to the extent prohibited by statute or regulation, + +such description must be sufficiently detailed for a recipient of ordinary + +skill to be able to understand it.
+ +5. Application of this License. + +
    This License applies to code to which the Initial Developer has attached + +the notice in Exhibit A and to related Covered Code.
+ +6. Versions of the License. + +
    6.1. New Versions. + +
    Netscape Communications Corporation (''Netscape'') may publish revised + +and/or new versions of the License from time to time. Each version will + +be given a distinguishing version number. + +

    6.2. Effect of New Versions. + +
    Once Covered Code has been published under a particular version of + +the License, You may always continue to use it under the terms of that + +version. You may also choose to use such Covered Code under the terms of + +any subsequent version of the License published by Netscape. No one other + +than Netscape has the right to modify the terms applicable to Covered Code + +created under this License. + +

    6.3. Derivative Works. + +
    If You create or use a modified version of this License (which you + +may only do in order to apply it to code which is not already Covered Code + +governed by this License), You must (a) rename Your license so that the + +phrases ''Mozilla'', ''MOZILLAPL'', ''MOZPL'', ''Netscape'', "MPL", ''NPL'' + +or any confusingly similar phrase do not appear in your license (except + +to note that your license differs from this License) and (b) otherwise + +make it clear that Your version of the license contains terms which differ + +from the Mozilla Public License and Netscape Public License. (Filling in + +the name of the Initial Developer, Original Code or Contributor in the + +notice described in Exhibit A shall not of themselves be deemed + +to be modifications of this License.)

+ +7. DISCLAIMER OF WARRANTY. + +
    COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS'' BASIS, WITHOUT + +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, + +WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT + +FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY + +AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE + +PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER + +CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. + +THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. + +NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+ +8. TERMINATION. + +
    8.1.  This License and the rights granted hereunder will + +terminate automatically if You fail to comply with terms herein and fail + +to cure such breach within 30 days of becoming aware of the breach. All + +sublicenses to the Covered Code which are properly granted shall survive + +any termination of this License. Provisions which, by their nature, must + +remain in effect beyond the termination of this License shall survive. + +

    8.2.  If You initiate litigation by asserting a patent infringement + +claim (excluding declatory judgment actions) against Initial Developer + +or a Contributor (the Initial Developer or Contributor against whom You + +file such action is referred to as "Participant")  alleging that: + +

    (a)  such Participant's Contributor Version directly or + +indirectly infringes any patent, then any and all rights granted by such + +Participant to You under Sections 2.1 and/or 2.2 of this License shall, + +upon 60 days notice from Participant terminate prospectively, unless if + +within 60 days after receipt of notice You either: (i)  agree in writing + +to pay Participant a mutually agreeable reasonable royalty for Your past + +and future use of Modifications made by such Participant, or (ii) withdraw + +Your litigation claim with respect to the Contributor Version against such + +Participant.  If within 60 days of notice, a reasonable royalty and + +payment arrangement are not mutually agreed upon in writing by the parties + +or the litigation claim is not withdrawn, the rights granted by Participant + +to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration + +of the 60 day notice period specified above. + +

    (b)  any software, hardware, or device, other than such + +Participant's Contributor Version, directly or indirectly infringes any + +patent, then any rights granted to You by such Participant under Sections + +2.1(b) and 2.2(b) are revoked effective as of the date You first made, + +used, sold, distributed, or had made, Modifications made by that Participant. + +

    8.3.  If You assert a patent infringement claim against + +Participant alleging that such Participant's Contributor Version directly + +or indirectly infringes any patent where such claim is resolved (such as + +by license or settlement) prior to the initiation of patent infringement + +litigation, then the reasonable value of the licenses granted by such Participant + +under Sections 2.1 or 2.2 shall be taken into account in determining the + +amount or value of any payment or license. + +

    8.4.  In the event of termination under Sections 8.1 or + +8.2 above,  all end user license agreements (excluding distributors + +and resellers) which have been validly granted by You or any distributor + +hereunder prior to termination shall survive termination.

+ +9. LIMITATION OF LIABILITY. + +
    UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING + +NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, + +ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER + +OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, + +INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + +LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE + +OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN + +IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. + +THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR + +PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE + +LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION + +OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION + +AND LIMITATION MAY NOT APPLY TO YOU.
+ +10. U.S. GOVERNMENT END USERS. + +
    The Covered Code is a ''commercial item,'' as that term is defined + +in 48 C.F.R. 2.101 (Oct. 1995), consisting of ''commercial computer software'' + +and ''commercial computer software documentation,'' as such terms are used + +in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and + +48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government + +End Users acquire Covered Code with only those rights set forth herein.
+ +11. MISCELLANEOUS. + +
    This License represents the complete agreement concerning subject matter + +hereof. If any provision of this License is held to be unenforceable, such + +provision shall be reformed only to the extent necessary to make it enforceable. + +This License shall be governed by California law provisions (except to + +the extent applicable law, if any, provides otherwise), excluding its conflict-of-law + +provisions. With respect to disputes in which at least one party is a citizen + +of, or an entity chartered or registered to do business in the United States + +of America, any litigation relating to this License shall be subject to + +the jurisdiction of the Federal Courts of the Northern District of California, + +with venue lying in Santa Clara County, California, with the losing party + +responsible for costs, including without limitation, court costs and reasonable + +attorneys' fees and expenses. The application of the United Nations Convention + +on Contracts for the International Sale of Goods is expressly excluded. + +Any law or regulation which provides that the language of a contract shall + +be construed against the drafter shall not apply to this License.
+ +12. RESPONSIBILITY FOR CLAIMS. + +
    As between Initial Developer and the Contributors, each party is responsible + +for claims and damages arising, directly or indirectly, out of its utilization + +of rights under this License and You agree to work with Initial Developer + +and Contributors to distribute such responsibility on an equitable basis. + +Nothing herein is intended or shall be deemed to constitute any admission + +of liability.
+ +13. MULTIPLE-LICENSED CODE. + +
    Initial Developer may designate portions of the Covered Code as “Multiple-Licensed”.  + +“Multiple-Licensed” means that the Initial Developer permits you to utilize + +portions of the Covered Code under Your choice of the MPL or the alternative + +licenses, if any, specified by the Initial Developer in the file described + +in Exhibit A.
+ + + +


EXHIBIT A -Mozilla Public License. + +

    ``The contents of this file are subject to the Mozilla Public License + +Version 1.1 (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.mozilla.org/MPL/ + +

    Software distributed under the License is distributed on an "AS IS" + +basis, WITHOUT WARRANTY OF + +
    ANY KIND, either express or implied. See the License for the specific + +language governing rights and + +
    limitations under the License. + +

    The Original Code is ______________________________________. + +

    The Initial Developer of the Original Code is ________________________. + +Portions created by + +
     ______________________ are Copyright (C) ______ _______________________. + +All Rights + +
    Reserved. + +

    Contributor(s): ______________________________________. + +

    Alternatively, the contents of this file may be used under the terms + +of the _____ license (the  “[___] License”), in which case the provisions + +of [______] License are applicable  instead of those above.  + +If you wish to allow use of your version of this file only under the terms + +of the [____] License and not to allow others to use your version of this + +file under the MPL, indicate your decision by deleting  the provisions + +above and replace  them with the notice and other provisions required + +by the [___] License.  If you do not delete the provisions above, + +a recipient may use your version of this file under either the MPL or the + +[___] License." + +

    [NOTE: The text of this Exhibit A may differ slightly from the text + +of the notices in the Source Code files of the Original Code. You should + +use the text of this Exhibit A rather than the text found in the Original + +Code Source Code for Your Modifications.] + +

    + +

\ No newline at end of file diff --git a/jmeter/resource/junit_license.html b/jmeter/resource/junit_license.html new file mode 100644 index 0000000000..b4a70f7cfb --- /dev/null +++ b/jmeter/resource/junit_license.html @@ -0,0 +1,46 @@ + + + + + + + License Agreement + + +Permission to reproduce and create derivative works from +the Software ("Software Derivative Works") is hereby granted to you under +the copyrights of  Kent Beck and Erich Gamma.  Kent Beck and +Erich Gamma also grants you the right to distribute the Software and Software +Derivative Works. +

Kent Beck and Erich Gamma licenses the Software to you on an "AS IS" +basis, without warranty of any kind.  Kent Beck and Erich Gamma HEREBY +EXPRESSLY DISCLAIMS ALL WARRANTIES OR CONDITIONS, EITHER EXPRESS OR IMPLIED, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS OF +MERCHANTABILITY, NON INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE.  +You are solely responsible for determining the appropriateness of using +the Software and assume all risks associated with the use and distribution +of this Software, including but not limited to the risks of program errors, +damage to or loss of data, programs or equipment, and unavailability or +interruption of operations.  KENT BECK AND ERICH GAMMA WILL NOT BE +LIABLE FOR ANY DIRECT DAMAGES OR FOR ANY SPECIAL, INCIDENTAL, OR INDIRECT +DAMAGES OR FOR ANY ECONOMIC CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS +OR SAVINGS), EVEN IF KENT BECK AND ERICH GAMMA HAD BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE.  Kent Beck and Erich Gamma will not be +liable for the loss of, or damage to, your records or data, or any damages +claimed by you based on a third party claim. +

You agree to distribute the Software and any Software Derivatives under +a license agreement that: 1) is sufficient to notify all licensees of the +Software and Software Derivatives that Kent Beck and Erich Gamma assumes +no liability for any claim that may arise regarding the Software or Software +Derivatives, and 2) that disclaims all warranties, both express and implied, +from Kent Beck and Erich Gamma regarding the Software and Software Derivatives.  +(If you include this Agreement with any distribution of the Software and +Software Derivatives you will have met this requirement).  You agree +that you will not delete any copyright notices in the Software. +

This Agreement is the exclusive statement of your rights in the Software +as provided by Kent Beck and Erich Gamma.  Except for the licenses +granted to you in the second paragraph above, no other licenses are granted +hereunder, by estoppel, implication or otherwise. +
  + + diff --git a/jmeter/resource/log4j.conf b/jmeter/resource/log4j.conf new file mode 100644 index 0000000000..8464d844d4 --- /dev/null +++ b/jmeter/resource/log4j.conf @@ -0,0 +1,38 @@ + +## $Id: log4j.conf,v 1.5 2004/02/16 13:34:10 sebb Exp $ +## Copyright 2001-2004 The Apache Software Foundation +## +## Licensed 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. + +# Set appender specific options + +log4j.appender.Root_Appender=org.apache.log4j.RollingFileAppender +log4j.appender.Root_Appender.File=root.log +log4j.appender.Root_Appender.Append=true +log4j.appender.Root_Appender.MaxBackupIndex=0 +log4j.appender.Root_Appender.layout=org.apache.log4j.PatternLayout +log4j.appender.Root_Appender.layout.ConversionPattern=%-5p %d{MM/dd, hh:mm:ss} %-20.30c %m%n + +log4j.appender.File_Appender=org.apache.log4j.RollingFileAppender +log4j.appender.File_Appender.File=extra.log +log4j.appender.File_Appender.Append=false +log4j.appender.File_Appender.layout=org.apache.log4j.PatternLayout +log4j.appender.File_Appender.layout.ConversionPattern=%r %d{MM/dd, hh:mm:ss} %-5p %-50.50c %m%n + +log4j.appender.SystemOut_Appender=org.apache.log4j.ConsoleAppender +log4j.appender.SystemOut_Appender.layout=org.apache.log4j.SimpleLayout + + +# Set the appenders for the categories +log4j.rootCategory=INFO,Root_Appender +#log4j.configDebug \ No newline at end of file diff --git a/jmeter/resource/logkit.xml b/jmeter/resource/logkit.xml new file mode 100644 index 0000000000..0109b8516e --- /dev/null +++ b/jmeter/resource/logkit.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + false + format.log + +AT: %{time:yyyy/MM/dd HH:mm:ss} PRI: %5.5{priority} CAT: %{category} TEXT: %{message} EX: %{throwable}\n + + + + + + false + prefix + + 1000000 + + + + + + + false + my_log + + 1000000 + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/messages.properties b/jmeter/resource/messages.properties new file mode 100755 index 0000000000..d5146f046e --- /dev/null +++ b/jmeter/resource/messages.properties @@ -0,0 +1,51 @@ +producer_sample_title=Producer Sampler +consumer_sample_title=Consumer Sampler +producer_config_title=Producer Sampler Config +consumer_config_title=Consumer Sampler Config +table_visualizer_sample=Sample Time(sec) +table_visualizer_processed=Processed Messages +table_visualizer_average=Average +view_prod_results_in_table=View Producer Results +view_cons_results_in_table=View Consumer Results +form_delivery_mode=Delivery Mode +form_persistent=Persistent +form_non_persistent=Non-Persistent +form_durable=Durable +form_non_durable=Non-durable +form_topic=Topic +form_queue=Queue +form_msg_size=Message Size (bytes) +form_no_producer=No. of Producer +form_no_consumer=No. of Consumer +form_no_subject=No. of Subject +form_duration=Duration (mins) +form_ramp_up=Ramp Up (mins) +form_url=Server URL +graph_results_average=Average (msg/sec) +graph_results_total_msgs=Total Messages +messaging_domain=Messaging domain +subject_name=TOOL.DEFAULT +form_mq_servers=Message Queue Servers +activemq_server=ActiveMQ Server +sonicmq_server=SonicMQ Server +tibcomq_server=TibcoMQ Server +jbossmq_server=JBossMQ Server +openjms_server=OpenJMS Server +joram_server=Joram Server +mantaray_server=Mantaray +form_msg_interval=Message Interval +form_default_msg_interval=Default +form_custom_msg_interval=Custom +form_custom_msg_interval_value=Interval Value (mins) +form_msg_transaction_type=Transaction Type +form_transacted=Transacted +form_non_transacted=Non-transacted +form_batch_size=Batch Size +filename_error_message=Please specify a filename. +file_saving_error_title=Saving sampler data +file_loading_error_title=Loading Samper data +file_loading_error=There was an error in loading the sampler data. +joram_connection_factory=cf +joram_username=root +joram_password=root +joram_naming_port=16400 \ No newline at end of file diff --git a/jmeter/resource/saveservice.properties b/jmeter/resource/saveservice.properties new file mode 100644 index 0000000000..687f1eb585 --- /dev/null +++ b/jmeter/resource/saveservice.properties @@ -0,0 +1,90 @@ +#--------------------------------------------------------- +# SAVESERVICE PROPERTIES +# +# N.B. To ensure backward compatibility, please do not +# change or delete any entries that have been used. +# New entries can be added as necessary. +# +# Note that keys starting with an underscore are special, +# and are not used as aliases. +# +# version number of this file (automatically generated by CVS) +_version=$Revision: 1.5 $ +# +#--------------------------------------------------------- +# +# The following properties are used to create aliases +# +AccessLogSampler=org.apache.jmeter.protocol.http.sampler.AccessLogSampler +AnchorModifier=org.apache.jmeter.protocol.http.modifier.AnchorModifier +AuthManager=org.apache.jmeter.protocol.http.control.AuthManager +BeanShellSampler=org.apache.jmeter.protocol.java.sampler.BeanShellSampler +ConfigTestElement=org.apache.jmeter.config.ConfigTestElement +ConstantThroughputTimer=org.apache.jmeter.timers.ConstantThroughputTimer +ConstantTimer=org.apache.jmeter.timers.ConstantTimer +CookieManager=org.apache.jmeter.protocol.http.control.CookieManager +CounterConfig=org.apache.jmeter.modifiers.CounterConfig +DurationAssertion=org.apache.jmeter.assertions.DurationAssertion +FloatProperty=org.apache.jmeter.testelement.property.FloatProperty +FTPSampler=org.apache.jmeter.protocol.ftp.sampler.FTPSampler +GaussianRandomTimer=org.apache.jmeter.timers.GaussianRandomTimer +GenericController=org.apache.jmeter.control.GenericController +HeaderManager=org.apache.jmeter.protocol.http.control.HeaderManager +HTTPSampler=org.apache.jmeter.protocol.http.sampler.HTTPSampler +IfController=org.apache.jmeter.control.IfController +InterleaveControl=org.apache.jmeter.control.InterleaveControl +JavaConfig=org.apache.jmeter.protocol.java.config.JavaConfig +JavaSampler=org.apache.jmeter.protocol.java.sampler.JavaSampler +JDBCSampler=org.apache.jmeter.protocol.jdbc.sampler.JDBCSampler +LDAPSampler=org.apache.jmeter.protocol.ldap.sampler.LDAPSampler +LoopController=org.apache.jmeter.control.LoopController +ModuleController=org.apache.jmeter.control.ModuleController +OnceOnlyController=org.apache.jmeter.control.OnceOnlyController +ParamModifier=org.apache.jmeter.protocol.http.modifier.ParamModifier +RandomController=org.apache.jmeter.control.RandomController +RecordingController=org.apache.jmeter.protocol.http.control.RecordingController +RegexExtractor=org.apache.jmeter.extractor.RegexExtractor +ResponseAssertion=org.apache.jmeter.assertions.ResponseAssertion +ResultCollector=org.apache.jmeter.reporters.ResultCollector +SizeAssertion=org.apache.jmeter.assertions.SizeAssertion +SoapSampler=org.apache.jmeter.protocol.http.sampler.SoapSampler +TestPlan=org.apache.jmeter.testelement.TestPlan +ThreadGroup=org.apache.jmeter.threads.ThreadGroup +ThroughputController=org.apache.jmeter.control.ThroughputController +UniformRandomTimer=org.apache.jmeter.timers.UniformRandomTimer +URLRewritingModifier=org.apache.jmeter.protocol.http.modifier.URLRewritingModifier +UserParameterModifier=org.apache.jmeter.protocol.http.modifier.UserParameterModifier +UserParameters=org.apache.jmeter.modifiers.UserParameters +WebServiceSampler=org.apache.jmeter.protocol.http.sampler.WebServiceSampler +XMLAssertion=org.apache.jmeter.assertions.XMLAssertion +stringProp=org.apache.jmeter.testelement.property.StringProperty +intProp=org.apache.jmeter.testelement.property.IntegerProperty +longProp=org.apache.jmeter.testelement.property.LongProperty +collectionProp=org.apache.jmeter.testelement.property.CollectionProperty +mapProp=org.apache.jmeter.testelement.property.MapProperty +elementProp=org.apache.jmeter.testelement.property.TestElementProperty +boolProp=org.apache.jmeter.testelement.property.BooleanProperty +hashTree=org.apache.jorphan.collections.ListedHashTree +jmeterTestPlan=org.apache.jmeter.save.ScriptWrapper +sample=org.apache.jmeter.samplers.SampleResult +httpSample=org.apache.jmeter.protocol.http.sampler.HTTPSampleResult +testResults=org.apache.jmeter.save.TestResultWrapper +assertionResult=org.apache.jmeter.assertions.AssertionResult +monitorStats=org.apache.jmeter.visualizers.MonitorStats +# +# Converters to register. Must start line with '_' +# If the converter is a collection of subitems, set equal to "collection" +# If the converter needs to know the class mappings but is not a collection of +# subitems, set it equal to "mapping" +_org.apache.jmeter.save.converters.StringPropertyConverter= +_org.apache.jmeter.save.converters.BooleanPropertyConverter= +_org.apache.jmeter.save.converters.IntegerPropertyConverter= +_org.apache.jmeter.save.converters.LongPropertyConverter= +_org.apache.jmeter.save.converters.TestElementConverter=collection +_org.apache.jmeter.save.converters.MultiPropertyConverter=collection +_org.apache.jmeter.save.converters.TestElementPropertyConverter=collection +_org.apache.jmeter.save.converters.HashTreeConverter=collection +_org.apache.jmeter.save.ScriptWrapperConverter=mapping +_org.apache.jmeter.save.converters.SampleResultConverter=collection +_org.apache.jmeter.save.converters.TestResultWrapperConverter=collection +_org.apache.jmeter.protocol.http.util.HTTPResultConverter=collection \ No newline at end of file diff --git a/jmeter/resource/upgrade.properties b/jmeter/resource/upgrade.properties new file mode 100644 index 0000000000..bcc8e68cb5 --- /dev/null +++ b/jmeter/resource/upgrade.properties @@ -0,0 +1,38 @@ +# Class, property and value upgrade equivalences. + +## $Id: upgrade.properties,v 1.6 2004/05/19 20:15:42 mstover1 Exp $ +## Copyright 2001-2004 The Apache Software Foundation +## +## Licensed 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. + +# +# Format is as follows -- +# for renamed test element & GUI classes: +# old.class.Name=new.class.Name +# for renamed properties: +# old.class.Name/Old.propertyName=newPropertyName +# for renamed values: +# old.class.Name.old.propertyName/oldValue=newValue +# + +org.apache.jmeter.protocol.http.config.gui.UrlConfigGui=org.apache.jmeter.protocol.http.config.gui.HttpDefaultsGui +org.apache.jmeter.assertions.Assertion=org.apache.jmeter.assertions.ResponseAssertion +org.apache.jmeter.protocol.http.sampler.HTTPSamplerFull=org.apache.jmeter.protocol.http.sampler.HTTPSampler +org.apache.jmeter.control.gui.RecordController=org.apache.jmeter.protocol.http.control.gui.RecordController + +org.apache.jmeter.timers.gui.ConstantThroughputTimerGui=org.apache.jmeter.testbeans.gui.TestBeanGUI +org.apache.jmeter.timers.ConstantThroughputTimer/ConstantThroughputTimer.throughput=throughput + +org.apache.jmeter.protocol.jdbc.control.gui.JdbcTestSampleGui=org.apache.jmeter.testbeans.gui.TestBeanGUI +org.apache.jmeter.protocol.jdbc.sampler.JDBCSampler/JDBCSampler.query=query +org.apache.jmeter.protocol.jdbc.sampler.JDBCSampler.JDBCSampler.dataSource/NULL= \ No newline at end of file diff --git a/jmeter/resource/users.dtd b/jmeter/resource/users.dtd new file mode 100644 index 0000000000..41c29f7096 --- /dev/null +++ b/jmeter/resource/users.dtd @@ -0,0 +1,23 @@ + + + + + + + + \ No newline at end of file diff --git a/jmeter/resource/users.xml b/jmeter/resource/users.xml new file mode 100644 index 0000000000..15de7be199 --- /dev/null +++ b/jmeter/resource/users.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + username + dduck + + + password + quack + + + + + username + mmouse + + + password + squeak + + + + + username + bbunney + + + password + carrot + + + manager + yes + + + + + + diff --git a/jmeter/src/java/org/activemq/sampler/Consumer.java b/jmeter/src/java/org/activemq/sampler/Consumer.java new file mode 100755 index 0000000000..9037fe07d9 --- /dev/null +++ b/jmeter/src/java/org/activemq/sampler/Consumer.java @@ -0,0 +1,403 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.sampler; + +import org.activemq.util.IdGenerator; +import org.activemq.util.connection.ServerConnectionFactory; +import org.apache.jmeter.samplers.Entry; +import org.apache.jmeter.samplers.SampleResult; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.jorphan.logging.LoggingManager; +import org.apache.log.Logger; + +import org.mr.MantaAgent; + +import javax.jms.Connection; +import javax.jms.Session; +import javax.jms.JMSException; +import javax.jms.Topic; +import javax.jms.Queue; +import javax.jms.TopicSession; +import javax.jms.QueueSession; +import javax.jms.Destination; +import javax.jms.MessageListener; +import javax.jms.QueueReceiver; +import javax.jms.MessageConsumer; +import javax.jms.Message; +import javax.jms.TextMessage; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Properties; + +public class Consumer extends Sampler implements MessageListener { + + public static int counter; + + private static final Logger log = LoggingManager.getLoggerForClass(); + + // Otherwise, the response is scanned for these strings + private static final String STATUS_PREFIX = JMeterUtils.getPropDefault("tcp.status.prefix", ""); + private static final String STATUS_SUFFIX = JMeterUtils.getPropDefault("tcp.status.suffix", ""); + + private static final String STATUS_PROPERTIES = JMeterUtils.getPropDefault("tcp.status.properties", ""); + private static final Properties statusProps = new Properties(); + + private int batchCounter = 0; + + static { + log.info("Protocol Handler name=" + getClassname()); + log.info("Status prefix=" + STATUS_PREFIX); + log.info("Status suffix=" + STATUS_SUFFIX); + log.info("Status properties=" + STATUS_PROPERTIES); + + if (STATUS_PROPERTIES.length() > 0) { + File f = new File(STATUS_PROPERTIES); + + try { + statusProps.load(new FileInputStream(f)); + log.info("Successfully loaded properties"); + } catch (FileNotFoundException e) { + log.error("Property file not found"); + } catch (IOException e) { + log.error("Property file error " + e.toString()); + } + } + } + + /** + * Constructor for ConsumerSampler object. + */ + public Consumer() { + log.debug("Created " + this); + protocolHandler = getProtocol(); + log.debug("Using Protocol Handler: " + protocolHandler.getClass().getName()); + } + + /** + * Increments the int variable. + * + * @param count - variable incremented. + */ + private synchronized void count(int count) { + counter += count; + } + + /** + * @return the current number of messages sent. + */ + public static synchronized int resetCount() { + int answer = counter; + counter = 0; + return answer; + } + + + /** + * Subscribes the subject. + * + * @throws JMSException + */ + protected void subscribe() throws JMSException { + for (int i = 0; i < getNoConsumer(); i++) { + String subject = subjects[i % getNoSubject()]; + subscribe(subject); + } + } + + /** + * Subscribes the message. + * + * @param subject - subject to be subscribed. + * @throws JMSException + */ + protected void subscribe(String subject) throws JMSException { + Connection connection = ServerConnectionFactory.createConnectionFactory(this.getURL(), + this.getMQServer(), + this.getTopic(), + this.getEmbeddedBroker()); + + if (this.getDurable()) { + if ((ServerConnectionFactory.JORAM_SERVER.equals(this.getMQServer())) || + (ServerConnectionFactory.MANTARAY_SERVER.equals(this.getMQServer()))) { + //Id set by server + + } else { + IdGenerator idGenerator = new IdGenerator(); + connection.setClientID(idGenerator.generateId()); + } + } + + //start connection before receiving messages. + connection.start(); + + Session session = ServerConnectionFactory.createSession(connection, + this.getTransacted(), + this.getMQServer(), + this.getTopic()); + + Destination destination = ServerConnectionFactory.createDestination(session, + subject, + this.getURL(), + this.getMQServer(), + this.getTopic()); + + MessageConsumer consumer = null; + + if (ServerConnectionFactory.OPENJMS_SERVER.equals(this.getMQServer())) { + if (this.getTopic()) { + Topic topic = (Topic) destination; + + if (this.getDurable()) { + consumer = ((TopicSession) session).createDurableSubscriber(topic, + getClass().getName()); + } else { + consumer = ((TopicSession) session).createSubscriber(topic); + } + + } else { + Queue queue = ((QueueSession) session).createQueue(subject); + QueueReceiver receiver = ((QueueSession) session).createReceiver(queue); + consumer = (MessageConsumer) receiver; + } + } else if (ServerConnectionFactory.MANTARAY_SERVER.equals(this.getMQServer())) { + if (this.getTopic()) { + Topic topic = (Topic) destination; + + if (this.getDurable()) { + consumer = ((TopicSession) session).createDurableSubscriber(topic, + MantaAgent.getInstance().getAgentName()); + } else { + consumer = ((TopicSession) session).createSubscriber(topic); + } + + } else { + Queue queue = ((QueueSession) session).createQueue(subject); + QueueReceiver receiver = ((QueueSession) session).createReceiver(queue); + consumer = (MessageConsumer) receiver; + } + } else { + if (this.getDurable() && this.getTopic()) { + consumer = session.createDurableSubscriber((Topic) destination, getClass().getName()); + } else { + consumer = session.createConsumer(destination); + } + } + + this.setSession(session); + consumer.setMessageListener(this); + addResource(consumer); + } + + /** + * Processes the received message. + * + * @param message - message received by the listener. + */ + public void onMessage(Message message) { + try { + TextMessage textMessage = (TextMessage) message; + Session session; + + // lets force the content to be deserialized + String text = textMessage.getText(); + count(1); + + if (this.getTransacted()) { + batchCounter++; + if (batchCounter == this.getBatchSize()) { + batchCounter = 0; + session = this.getSession(); + session.commit(); + } + } + } catch (JMSException e) { + log.error("Unable to force deserialize the content ", e); + } + } + + /** + * Runs and subscribes to messages. + * + * @throws JMSException + */ + public void run() throws JMSException { + start(); + subscribe(); + } + + /** + * Retrieves the sample as SampleResult object. There are times that this + * is ignored. + * + * @param e - Entry object. + * @return Returns the sample result. + */ + public SampleResult sample(Entry e) {// Entry tends to be ignored ... + SampleResult res = new SampleResult(); + res.setSampleLabel(getName()); + res.setSamplerData(getURL()); + res.sampleStart(); + + try { + this.run(); + } catch (JMSException ex) { + log.error("Error running consumer ", ex); + res.setResponseCode("500"); + res.setResponseMessage(ex.toString()); + } + + //Calculate response time + res.sampleEnd(); + + // Set if we were successful or not + res.setSuccessful(true); + + return res; + } + + /** + * Starts an instance of the Consumer tool. + */ + public static void main(String args[]) { + System.out.println("##########################################"); + System.out.println(" Consumer * start *"); + System.out.println("##########################################"); + + Consumer cons = new Consumer(); + + if (args.length == 0) { + displayToolParameters(); + } + + if (args.length > 0) { + String mqServer = args[0]; + + if (mqServer.equalsIgnoreCase("SONICMQ")) { + cons.setMQServer(ServerConnectionFactory.SONICMQ_SERVER); + } else if (mqServer.equalsIgnoreCase("TIBCOMQ")) { + cons.setMQServer(ServerConnectionFactory.TIBCOMQ_SERVER); + } else if (mqServer.equalsIgnoreCase("JBOSSMQ")) { + cons.setMQServer(ServerConnectionFactory.JBOSSMQ_SERVER); + } else if (mqServer.equalsIgnoreCase("OPENJMS")) { + cons.setMQServer(ServerConnectionFactory.OPENJMS_SERVER); + } else if (mqServer.equalsIgnoreCase("JORAM")) { + cons.setMQServer(ServerConnectionFactory.JORAM_SERVER); + } else if (mqServer.equalsIgnoreCase("MANTARAY")) { + cons.setMQServer(ServerConnectionFactory.MANTARAY_SERVER); + } else if (mqServer.equalsIgnoreCase("ACTIVEMQ")) { + //Run with the default broker + } else { + System.out.print("Please enter a valid mq server: [ "); + System.out.print("SONICMQ | "); + System.out.print("TIBCOMQ | "); + System.out.print("JBOSSMQ | "); + System.out.print("OPENJMS | "); + System.out.print("JORAM | "); + System.out.print("MANTARAY |"); + System.out.println("ACTIVEMQ ]"); + return; + } + + } + + if (args.length > 1) { + cons.setURL(args[1]); + } + + if (args.length > 2) { + cons.setDuration(args[2]); + } + + if (args.length > 3) { + cons.setRampUp(args[3]); + } + + if (args.length > 4) { + cons.setNoConsumer(args[4]); + } + + if (args.length > 5) { + cons.setNoSubject(args[5]); + } + + if (args.length > 6) { + cons.setDurable(args[6]); + } + + if (args.length > 7) { + cons.setTopic(args[7]); + } + + if (args.length > 8) { + cons.setTransacted(args[8]); + + if (cons.getTransacted()) { + if (args.length > 9) { + cons.setBatchSize(args[9]); + } else { + displayToolParameters(); + System.out.println("Please specify the batch size."); + return; + } + } + } + System.out.println("Runnning Consumer tool with the following parameters:"); + System.out.println("Server=" + cons.getMQServer()); + System.out.println("URL=" + cons.getURL()); + System.out.println("Duration=" + cons.getDuration()); + System.out.println("Ramp up=" + cons.getRampUp()); + System.out.println("No consumer=" + cons.getNoConsumer()); + System.out.println("No Subject=" + cons.getNoSubject()); + System.out.println("Is Durable=" + cons.getDurable()); + System.out.println("Is Topic=" + cons.getTopic()); + System.out.println("Is Transacted=" + cons.getTransacted()); + System.out.println("Batch size=" + cons.getBatchSize()); + + try { + cons.run(); + } catch (Exception e) { + System.out.println("Excception e=" + e); + + } + + System.out.println("##########################################"); + System.out.println(" Consumer * end *"); + System.out.println("##########################################"); + + } + + /** + * Prints to the console the Consumer tool parameters. + */ + private static void displayToolParameters() { + System.out.println(" Consumer tool usage: "); + System.out.print("[Message Queue Server] "); + System.out.print("[URL] "); + System.out.print("[Duration] "); + System.out.print("[Ramp up] "); + System.out.print("[No. of consumer] "); + System.out.print("[No. of subject] "); + System.out.print("[Delivery mode] "); + System.out.print("[Is topic] "); + System.out.print("[Is transacted] "); + System.out.print("[Batch size] "); + } +} \ No newline at end of file diff --git a/jmeter/src/java/org/activemq/sampler/ConsumerSysTest.java b/jmeter/src/java/org/activemq/sampler/ConsumerSysTest.java new file mode 100644 index 0000000000..e9eaebb763 --- /dev/null +++ b/jmeter/src/java/org/activemq/sampler/ConsumerSysTest.java @@ -0,0 +1,322 @@ +package org.activemq.sampler; + +import org.apache.log.Logger; +import org.apache.jorphan.logging.LoggingManager; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.jmeter.samplers.SampleResult; +import org.apache.jmeter.samplers.Entry; +import org.activemq.util.connection.ServerConnectionFactory; +import org.activemq.util.IdGenerator; +import org.activemq.command.ActiveMQMessage; + +import javax.jms.*; +import java.util.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap; + + + +public class ConsumerSysTest extends Sampler implements MessageListener { + + private static final Logger log = LoggingManager.getLoggerForClass(); + + // Otherwise, the response is scanned for these strings + private static final String STATUS_PREFIX = JMeterUtils.getPropDefault("tcp.status.prefix", ""); + private static final String STATUS_SUFFIX = JMeterUtils.getPropDefault("tcp.status.suffix", ""); + + private static final String STATUS_PROPERTIES = JMeterUtils.getPropDefault("tcp.status.properties", ""); + private static final Properties statusProps = new Properties(); + private static int msgCounter; + + public static int noOfMessages; + public static int ConsumerCount; + public static Map ProducerMap = Collections.synchronizedMap(new ConcurrentHashMap()); + public static Map CopyProducerMap = Collections.synchronizedMap(new ConcurrentHashMap()); + public static boolean destination; + public static boolean resetMap = false; + + private MessageConsumer consumer = null; + + static { + log.info("Protocol Handler name=" + getClassname()); + log.info("Status prefix=" + STATUS_PREFIX); + log.info("Status suffix=" + STATUS_SUFFIX); + log.info("Status properties=" + STATUS_PROPERTIES); + + if (STATUS_PROPERTIES.length() > 0) { + File f = new File(STATUS_PROPERTIES); + + try { + statusProps.load(new FileInputStream(f)); + log.info("Successfully loaded properties"); + } catch (FileNotFoundException e) { + log.error("Property file not found"); + } catch (IOException e) { + log.error("Property file error " + e.toString()); + } + } + } + + /** + * Constructor for ConsumerSampler object. + */ + public ConsumerSysTest() { + log.debug("Created " + this); + protocolHandler = getProtocol(); + log.debug("Using Protocol Handler: " + protocolHandler.getClass().getName()); + } + + /** + * Subscribe to the config message. + * + * @throws JMSException + */ + protected void suscribeConfigMessage() throws JMSException { + boolean topic = false; + + Connection connection = ServerConnectionFactory.createConnectionFactory(this.getURL(), + ACTIVEMQ_SERVER, + topic, + this.getEmbeddedBroker()); + + // Start connection before receiving messages. + connection.start(); + + Session session = ServerConnectionFactory.createSession(connection, + TRANSACTED_FALSE, + ACTIVEMQ_SERVER, + topic); + + Destination destination = ServerConnectionFactory.createDestination(session, + CONFIG_SUBJECT, + this.getURL(), + ACTIVEMQ_SERVER, + topic); + + MessageConsumer consumer = null; + consumer = session.createConsumer(destination); + Message message = consumer.receive(); + + TextMessage txtMsg = (TextMessage) message; + String configMsg = txtMsg.getText(); + + noOfMessages = Integer.parseInt(configMsg.substring(configMsg.indexOf("#")+1, configMsg.lastIndexOf("#"))); + + ServerConnectionFactory.close(connection, session); + } + + /** + * Create the subscriber/s then subscribe. + * + * @throws JMSException + */ + protected void subscribe() throws JMSException { + String subjects[] = getSubjects(); + + for (int i = 0; i < this.getNoConsumer(); i++) { + String subject = subjects[i % getNoSubject()]; + subscribe(subject); + } + ConsumerCount = getNoConsumer(); + } + + /** + * Subscribe to the subject. + * + * @param subject + * @throws JMSException + */ + protected void subscribe(String subject) throws JMSException { + destination(this.getTopic()); + Connection connection = ServerConnectionFactory.createConnectionFactory(this.getURL(), + ACTIVEMQ_SERVER, + this.getTopic(), + this.getEmbeddedBroker()); + + if (this.getDurable()) { + IdGenerator idGenerator = new IdGenerator(); + connection.setClientID(idGenerator.generateId()); + } + + // Start connection before receiving messages. + connection.start(); + + Session session = ServerConnectionFactory.createSession(connection, + TRANSACTED_FALSE, + ACTIVEMQ_SERVER, + this.getTopic()); + + Destination destination = ServerConnectionFactory.createDestination(session, + subject, + this.getURL(), + ACTIVEMQ_SERVER, + this.getTopic()); + + + if (this.getDurable() && this.getTopic()) { + consumer = session.createDurableSubscriber((Topic) destination, getClass().getName()); + } else { + consumer = session.createConsumer(destination); + } + + consumer.setMessageListener(this); + addResource(consumer); + } + + /** + * Create the publisher then send the confirmation message. + * + * @throws JMSException + */ + protected void publishConfirmMessage() throws JMSException { + MessageProducer publisher = null; + String text = PUBLISH_MSG; + Connection connection = ServerConnectionFactory.createConnectionFactory(this.getURL(), + ACTIVEMQ_SERVER, + this.getTopic(), + this.getEmbeddedBroker()); + if (this.getDurable()) { + IdGenerator idGenerator = new IdGenerator(); + connection.setClientID(idGenerator.generateId()); + } + + Session session = ServerConnectionFactory.createSession(connection, + this.getTransacted(), + ACTIVEMQ_SERVER, + this.getTopic()); + + Destination destination = ServerConnectionFactory.createDestination(session, + CONFIRM_SUBJECT, + this.getURL(), + ACTIVEMQ_SERVER, + this.getTopic()); + + publisher = session.createProducer(destination); + + if (getDurable()) { + publisher.setDeliveryMode(DeliveryMode.PERSISTENT); + } else { + publisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + } + + publishConfirmMessage(connection, session, publisher, text); + } + + /** + * publish the confirmation message. + * + * @param connection + * @param session + * @param publisher + * @param text + * @throws JMSException + */ + protected void publishConfirmMessage(Connection connection, Session session, MessageProducer publisher, String text) + throws JMSException { + + Message message = session.createTextMessage(text); + publisher.send(message); + + // Close the connection and session after sending the config message + ServerConnectionFactory.close(connection, session); + } + + /** + * Runs and subscribes to messages. + * + * @throws JMSException + */ + public void run() throws JMSException { + + // Receives the config message + suscribeConfigMessage(); + + // Create subscriber + subscribe(); + + // Publish confirm messages + publishConfirmMessage(); + } + + /** + * Retrieves the sample as SampleResult object. There are times that this + * is ignored. + * + * @param e - Entry object. + * @return Returns the sample result. + */ + public SampleResult sample(Entry e) {// Entry tends to be ignored ... + SampleResult res = new SampleResult(); + res.setSampleLabel(getName()); + res.setSamplerData(getURL()); + res.sampleStart(); + + try { + this.run(); + } catch (JMSException ex) { + log.error("Error running consumer ", ex); + res.setResponseCode("500"); + res.setResponseMessage(ex.toString()); + } + + // Calculate response time + res.sampleEnd(); + + // Set if we were successful or not + res.setSuccessful(true); + + return res; + } + + public void onMessage(Message message) { + try { + ActiveMQMessage amsg = (ActiveMQMessage) message; + TextMessage textMessage = (TextMessage) message; + + StringBuffer sb = new StringBuffer(); + sb.append(textMessage.getText()); + sb.append("#"); + sb.append(amsg.getJMSMessageID()); + + addToMap(sb.toString()); + + } catch (JMSException e) { + log.error("Unable to force deserialize the content", e); + } + } + + /** + * + * @param text Add the message to a Producer hash map. + */ + private synchronized void addToMap(String text) { + msgCounter++; + String strMsgCounter = String.valueOf(msgCounter); + ProducerMap.put(strMsgCounter, text); + } + + /** + * + * @return Resets the Producer map. + */ + public synchronized Map resetProducerMap() { + Map copy = Collections.synchronizedMap(new ConcurrentHashMap(ProducerMap)); + ProducerMap.clear(); + msgCounter = 0; + return copy; + } + + /** + * + * @param dest + */ + private void destination(boolean dest) { + destination = dest; + } + + +} \ No newline at end of file diff --git a/jmeter/src/java/org/activemq/sampler/Producer.java b/jmeter/src/java/org/activemq/sampler/Producer.java new file mode 100755 index 0000000000..44423fca18 --- /dev/null +++ b/jmeter/src/java/org/activemq/sampler/Producer.java @@ -0,0 +1,561 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.sampler; + +import org.apache.jmeter.engine.event.LoopIterationEvent; +import org.apache.jmeter.samplers.Entry; +import org.apache.jmeter.samplers.SampleResult; +import org.apache.jmeter.testelement.TestListener; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.jorphan.logging.LoggingManager; +import org.apache.log.Logger; +import org.activemq.util.connection.ServerConnectionFactory; +import org.activemq.util.IdGenerator; + +import javax.jms.Connection; +import javax.jms.Session; +import javax.jms.JMSException; +import javax.jms.Topic; +import javax.jms.Queue; +import javax.jms.TopicSession; +import javax.jms.QueueSession; +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.TopicPublisher; +import javax.jms.QueueSender; +import javax.jms.MessageProducer; +import javax.jms.DeliveryMode; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Properties; +import java.util.Timer; +import java.util.TimerTask; + +/** + * A sampler which understands Tcp requests. + */ +public class Producer extends Sampler implements TestListener { + + public static int counter; + + private static final Logger log = LoggingManager.getLoggerForClass(); + + // Otherwise, the response is scanned for these strings + private static final String STATUS_PREFIX = JMeterUtils.getPropDefault("tcp.status.prefix", ""); + private static final String STATUS_SUFFIX = JMeterUtils.getPropDefault("tcp.status.suffix", ""); + private static final String STATUS_PROPERTIES = JMeterUtils.getPropDefault("tcp.status.properties", ""); + + private static final Properties statusProps = new Properties(); + private static final long INSECONDS = 60; + private static final long MSGINTERVALINSECS = 60; + + private Timer timerPublish; + private Timer timerPublishLoop; + + private int batchCounter = 0; + + static { + log.info("Protocol Handler name=" + getClassname()); + log.info("Status prefix=" + STATUS_PREFIX); + log.info("Status suffix=" + STATUS_SUFFIX); + log.info("Status properties=" + STATUS_PROPERTIES); + + if (STATUS_PROPERTIES.length() > 0) { + File f = new File(STATUS_PROPERTIES); + + try { + statusProps.load(new FileInputStream(f)); + log.info("Successfully loaded properties"); + //haveStatusProps = true; + } catch (FileNotFoundException e) { + log.info("Property file not found"); + } catch (IOException e) { + log.info("Property file error " + e.toString()); + } + } + } + + /** + * Constructor for ProducerSampler object. + */ + public Producer() { + log.debug("Created " + this); + protocolHandler = this.getProtocol(); //from superclass sampler. + log.debug("Using Protocol Handler: " + protocolHandler.getClass().getName()); + } + + /** + * Increments the int variable. + * + * @param count - variable incremented. + */ + protected synchronized void count(int count) { + counter += count; + } + + /** + * @return Returns the message. + */ + protected String getMessage() { + StringBuffer buffer = new StringBuffer(); + + for (int i = 0; i < getMsgSize(); i++) { + char ch = 'X'; + buffer.append(ch); + } + + return buffer.toString(); + } + + /** + * Retrieves the message then sends it via tcp. + * + * @throws Exception + */ + protected void publish() throws Exception { + long threadRampUp = 0; + + if (getNoProducer() > 0) { + threadRampUp = (long) ((double) (getRampUp() * INSECONDS) / ((double) getNoProducer()) * 1000); + } + + timerPublish = new Timer(); + timerPublish.scheduleAtFixedRate(new newThread(), 0, threadRampUp); + } + + /** + * Sends the information from the client via tcp. + * + * @param text - message that is sent. + * @param subject - subject of the message to be sent. + * @throws JMSException + */ + protected void publish(String text, String subject) throws JMSException { + Destination destination = null; + Session session = null; + MessageProducer publisher = null; + Connection connection = null; + + connection = ServerConnectionFactory.createConnectionFactory(this.getURL(), + this.getMQServer(), + this.getTopic(), + this.getEmbeddedBroker()); + + if (this.getDurable()) { + if ((ServerConnectionFactory.JORAM_SERVER.equals(this.getMQServer()))|| + (ServerConnectionFactory.MANTARAY_SERVER.equals(this.getMQServer()))) { + //Id set be server + + } else { + IdGenerator idGenerator = new IdGenerator(); + connection.setClientID(idGenerator.generateId()); + } + } + + session = ServerConnectionFactory.createSession(connection, + this.getTransacted(), + this.getMQServer(), + this.getTopic()); + + destination = ServerConnectionFactory.createDestination(session, + subject, + this.getURL(), + this.getMQServer(), + this.getTopic()); + + if ((ServerConnectionFactory.OPENJMS_SERVER.equals(this.getMQServer()))|| + (ServerConnectionFactory.MANTARAY_SERVER.equals(this.getMQServer()))) { + if (this.getTopic()){ + connection.start(); + TopicPublisher topicPublisher = ((TopicSession)session).createPublisher((Topic)destination); + publisher = topicPublisher; + + } else { + connection.start(); + QueueSender queuePublisher = ((QueueSession)session).createSender((Queue)destination); + publisher = queuePublisher; + + } + + } else { + publisher = session.createProducer(destination); + } + + long msgIntervalInMins = this.getMsgInterval(); + long msgIntervalInSecs = msgIntervalInMins * INSECONDS; + + if (msgIntervalInSecs < 0) { + msgIntervalInSecs = MSGINTERVALINSECS; + } + + if (getDurable()) { + publisher.setDeliveryMode(DeliveryMode.PERSISTENT); + } else { + publisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + } + + if (this.getDefMsgInterval()) { + while (!stopThread) { + publishLoop(session, publisher, text); + } + ServerConnectionFactory.close(connection, session); + + } else { + // set the session, publisher and connection. + this.setSession(session); + this.setPublisher(publisher); + this.setConnection(connection); + + timerPublishLoop = new Timer(); + timerPublishLoop.scheduleAtFixedRate(new publish(), 0, msgIntervalInSecs * 1000); + + } + } + + /** + * Sends a message through MessageProducer object. + * + * @param session - Session oject. + * @param publisher - MessageProducer object. + * @param text - text that is used to create Message object. + * @throws JMSException + */ + protected void publishLoop(Session session, MessageProducer publisher, String text) throws JMSException { + if (ServerConnectionFactory.OPENJMS_SERVER.equals(this.getMQServer())) { + if (publisher instanceof TopicPublisher) { + Message message = ((TopicSession)session).createTextMessage(text); + ((TopicPublisher)publisher).publish(message); + } else if (publisher instanceof QueueSender){ + Message message = ((QueueSession)session).createTextMessage(text); + ((QueueSender)publisher).send(message); + } + } else { + Message message = session.createTextMessage(text); + publisher.send(message); + } + + if (this.getTransacted()) { + batchCounter++; + + if (batchCounter == this.getBatchSize()) { + batchCounter = 0; + session.commit(); + } + } + + count(1); + } + + /** + * @return the current number of messages sent. + */ + public static synchronized int resetCount() { + int answer = counter; + counter = 0; + return answer; + } + + /** + * Runs and publish the message. + * + * @throws Exception + */ + public void run() throws Exception { + + start(); + publish(); + } + + /** + * Retrieves the sample as SampleResult object. There are times that this + * is ignored. + * + * @param e - Entry object. + * @return Returns the sample result. + */ + public SampleResult sample(Entry e) { // Entry tends to be ignored ... + SampleResult res = new SampleResult(); + res.setSampleLabel(getName()); + res.setSamplerData(getURL()); + res.sampleStart(); + + try { + //run the benchmark tool code + this.run(); + } catch (Exception ex) { + log.debug("Error running producer ", ex); + res.setResponseCode("500"); + res.setResponseMessage(ex.toString()); + } + + //Calculate response time + res.sampleEnd(); + + // Set if we were successful or not + res.setSuccessful(true); + + return res; + } + + /** + * Logs the end of the test. This is called only once per + * class. + */ + public void testEnded() { + log.debug(this + " test ended"); + } + + /** + * Logs the host at the end of the test. + * + * @param host - the host to be logged. + */ + public void testEnded(String host) { + log.debug(this + " test ended on " + host); + } + + /** + * Logs the start of the test. This is called only once + * per class. + */ + public void testStarted() { + log.debug(this + " test started"); + } + + /** + * Logs the host at the start of the test. * + * @param host - the host to be logged. + */ + public void testStarted(String host) { + log.debug(this + " test started on " + host); + } + + /** + * Logs the iteration event. + * + * @param event + */ + public void testIterationStart(LoopIterationEvent event) { + log.debug(this + " test iteration start on " + event.getIteration()); + } + + /** + * Creates thread for publishing messages. + */ + class newThread extends TimerTask { + final String text = getMessage(); + int numberOfProducer = getNoProducer(); + int counter = 0; + + public void run() { + if (counter < numberOfProducer) { + final String subject = subjects[counter % getNoSubject()]; + + counter++; + + Thread thread = new Thread() { + public void run() { + try { + if (stopThread) { + return; + } else { + publish(text, subject); + } + } catch (JMSException e) { + log.error("Error publishing message ", e); + } + } + }; + + thread.start(); + + } else { + timerPublish.cancel(); + } + } + } + + /** + * Starts the publish loop timer. + */ + class publish extends TimerTask { + public void run() { + try { + if (!stopThread) { + publishLoop(getSession(), getPublisher(), getMessage()); + } else { + ServerConnectionFactory.close(getConnection(), getSession()); + timerPublishLoop.cancel(); + } + } catch(JMSException e) { + log.error("Could not publish "+e); + } + } + } + + /** + * Starts an instance of the Producer tool. + */ + public static void main(String[] args) { + System.out.println("##########################################"); + System.out.println(" Producer * start *"); + System.out.println("##########################################"); + + Producer prod = new Producer(); + + if (args.length == 0 ){ + displayToolParameters(); + } + + if (args.length > 0){ + String mqServer = args[0]; + + if (mqServer.equalsIgnoreCase("SONICMQ")){ + prod.setMQServer(ServerConnectionFactory.SONICMQ_SERVER); + } else if (mqServer.equalsIgnoreCase("TIBCOMQ")) { + prod.setMQServer(ServerConnectionFactory.TIBCOMQ_SERVER); + } else if (mqServer.equalsIgnoreCase("JBOSSMQ")) { + prod.setMQServer(ServerConnectionFactory.JBOSSMQ_SERVER); + } else if (mqServer.equalsIgnoreCase("OPENJMS")) { + prod.setMQServer(ServerConnectionFactory.OPENJMS_SERVER); + } else if (mqServer.equalsIgnoreCase("JORAM")) { + prod.setMQServer(ServerConnectionFactory.JORAM_SERVER); + } else if (mqServer.equalsIgnoreCase("MANTARAY")) { + prod.setMQServer(ServerConnectionFactory.MANTARAY_SERVER); + } else if (mqServer.equalsIgnoreCase("ACTIVEMQ")) { + //Run with the default broker + } else { + System.out.print("Please enter a valid server: [ "); + System.out.print("SONICMQ | " ); + System.out.print("TIBCOMQ | " ); + System.out.print("JBOSSMQ | " ); + System.out.print("OPENJMS | " ); + System.out.print("JORAM |"); + System.out.print("MANTARAY |"); + System.out.println("ACTIVEMQ ]"); + } + + } + + if (args.length > 1) { + prod.setURL(args[1]); + } else { + System.out.println("Please specify the URL."); + } + + if (args.length > 2) { + prod.setDuration(args[2]); + } + + if (args.length > 3) { + prod.setRampUp(args[3]); + } + + if (args.length > 4) { + prod.setNoProducer(args[4]); + } + + if (args.length > 5) { + prod.setNoSubject(args[5]); + } + + if (args.length > 6) { + prod.setMsgSize(args[6]); + } + + if (args.length > 7) { + prod.setDurable(args[7]); + } + + if (args.length > 8) { + prod.setTopic(args[8]); + } + + if (args.length > 9) { + prod.setTransacted(args[9]); + + if (args.length > 10) { + prod.setBatchSize(args[10]); + } else { + displayToolParameters(); + System.out.println("Please specify the batch size."); + return; + } + } + + if (args.length > 11) { + prod.setDefMsgInterval(args[11]); + if (!prod.getDefMsgInterval()) { + if (args.length > 12) { + prod.setMsgInterval(args[12]); + } else { + displayToolParameters(); + System.out.println("Please specify the message interval."); + return; + } + } + } + + prod.setDefMsgInterval("true"); + + System.out.println("Runnning Consumer tool with the following parameters:"); + System.out.println("Server=" + prod.getMQServer()); + System.out.println("URL="+prod.getURL()); + System.out.println("Duration="+prod.getDuration()); + System.out.println("Ramp up="+prod.getRampUp()); + System.out.println("No. Producer="+prod.getNoProducer()); + System.out.println("No. Subject="+prod.getNoSubject()); + System.out.println("Msg Size="+prod.getMsgSize()); + System.out.println("Is Durable="+prod.getDurable()); + System.out.println("Is Topic="+prod.getTopic()); + System.out.println("Is Transacted="+prod.getTransacted()); + + try { + prod.run(); + } catch (Exception e){ + System.out.println("Excception e="+e); + + } + System.out.println("##########################################"); + System.out.println("Producer * end *"); + System.out.println("##########################################"); + } + + /** + * Prints to the console the Producer tool parameters. + */ + private static void displayToolParameters(){ + System.out.println("Producer tool usage: "); + System.out.print("[Message Queue Server] "); + System.out.print("[URL] "); + System.out.print("[Duration] "); + System.out.print("[Ramp up] "); + System.out.print("[No. of producer] "); + System.out.print("[No. of subject] "); + System.out.print("[Message size] "); + System.out.print("[Delivery mode] "); + System.out.print("[Is topic] "); + System.out.print("[Is transacted] "); + System.out.print("[Batch size] "); + System.out.print("[Has Message interval] "); + System.out.println("[Message interval] "); + } +} \ No newline at end of file diff --git a/jmeter/src/java/org/activemq/sampler/ProducerSysTest.java b/jmeter/src/java/org/activemq/sampler/ProducerSysTest.java new file mode 100644 index 0000000000..449e917ea1 --- /dev/null +++ b/jmeter/src/java/org/activemq/sampler/ProducerSysTest.java @@ -0,0 +1,312 @@ +package org.activemq.sampler; + +import org.apache.jmeter.testelement.TestListener; +import org.apache.jmeter.engine.event.LoopIterationEvent; +import org.apache.jmeter.samplers.SampleResult; +import org.apache.jmeter.samplers.Entry; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.log.Logger; +import org.apache.jorphan.logging.LoggingManager; + +import org.activemq.util.connection.ServerConnectionFactory; +import org.activemq.util.IdGenerator; + +import javax.jms.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Properties; + +public class ProducerSysTest extends Sampler implements MessageListener { + + private static final Logger log = LoggingManager.getLoggerForClass(); + + // Otherwise, the response is scanned for these strings + private static final String STATUS_PREFIX = JMeterUtils.getPropDefault("tcp.status.prefix", ""); + private static final String STATUS_SUFFIX = JMeterUtils.getPropDefault("tcp.status.suffix", ""); + private static final String STATUS_PROPERTIES = JMeterUtils.getPropDefault("tcp.status.properties", ""); + + private static final Properties statusProps = new Properties(); + + static { + log.info("Protocol Handler name=" + getClassname()); + log.info("Status prefix=" + STATUS_PREFIX); + log.info("Status suffix=" + STATUS_SUFFIX); + log.info("Status properties=" + STATUS_PROPERTIES); + + if (STATUS_PROPERTIES.length() > 0) { + File f = new File(STATUS_PROPERTIES); + + try { + statusProps.load(new FileInputStream(f)); + log.info("Successfully loaded properties"); + //haveStatusProps = true; + } catch (FileNotFoundException e) { + log.info("Property file not found"); + } catch (IOException e) { + log.info("Property file error " + e.toString()); + } + } + } + + /** + * Constructor for ProducerSysTest Sampler object. + */ + public ProducerSysTest() { + log.debug("Created " + this); + protocolHandler = this.getProtocol(); //from superclass sampler. + log.debug("Using Protocol Handler: " + protocolHandler.getClass().getName()); + } + + /** + * Sends the config message for validating the test messages. + * + * @throws Exception + */ + protected void publishConfigMessage() throws Exception { + String text = getConfigMessage(); + boolean topic = false; + + Connection connection = ServerConnectionFactory.createConnectionFactory(this.getURL(), + ACTIVEMQ_SERVER, + topic, + this.getEmbeddedBroker()); + + Session session = ServerConnectionFactory.createSession(connection, + TRANSACTED_FALSE, + ACTIVEMQ_SERVER, + topic); + + Destination destination = ServerConnectionFactory.createDestination(session, + CONFIG_SUBJECT, + this.getURL(), + ACTIVEMQ_SERVER, + topic); + + MessageProducer publisher = session.createProducer(destination); + + publisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + + publishConfigMessage(connection, session, publisher, text); + } + + /** + * Sends the config information. + * + * @param connection + * @param session + * @param publisher + * @param text + * @throws JMSException + */ + protected void publishConfigMessage(Connection connection, Session session, MessageProducer publisher, String text) + throws JMSException { + + Message message = session.createTextMessage(text); + publisher.send(message); + + // Close the connection and session after sending the config message + ServerConnectionFactory.close(connection, session); + } + + /** + * Creates the publishers then publish the test messages. + * + * @throws JMSException + */ + protected void publish() throws JMSException { + String subjects[] = getSubjects(); + + for( int i=0;i -1) { + w.write(buffer, 0, x); + if ((eolByte != 0) && (buffer[x - 1] == eolByte)) { + break; + } + } + + } catch (InterruptedIOException e) { + // drop out to handle buffer + } catch (IOException e) { + log.warn("Read error:" + e); + return ""; + } + + // do we need to close byte array (or flush it?) + log.debug("Read: " + w.size() + "\n" + w.toString()); + + return w.toString(); + } + + /** + * Non-functional implementation of the write method of + * the implementated class. + * + * @param os - OutputStream object. + * @param is - InputStream. + */ + public void write(OutputStream os, InputStream is) { + + // TODO Auto-generated method stub + return; + } + + /** + * @return Returns the eolByte instance variable. + */ + public byte getEolByte() { + + return eolByte; + } + + /** + * Sets value to eolByte instance variable. + * + * @param eolByte - The eolByte to set.. + */ + public void setEolByte(byte eolByte) { + + this.eolByte = eolByte; + } +} diff --git a/jmeter/src/java/org/activemq/sampler/config/SamplerConfig.java b/jmeter/src/java/org/activemq/sampler/config/SamplerConfig.java new file mode 100755 index 0000000000..c13b004855 --- /dev/null +++ b/jmeter/src/java/org/activemq/sampler/config/SamplerConfig.java @@ -0,0 +1,84 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.sampler.config; + +import org.apache.jmeter.config.ConfigTestElement; +import org.activemq.sampler.Sampler; + +import java.io.Serializable; + +/** + * Producer configuration bean. + */ +public class SamplerConfig extends ConfigTestElement implements Serializable { + + /** + * Default constructor. + */ + public SamplerConfig() { + } + + /** + * Sets the producer sampler filename property. + * + * @param newFilename + */ + public void setFilename(String newFilename) { + this.setProperty(Sampler.FILENAME, newFilename); + } + + /** + * Returns the producer sampler filename property. + * + * @return producer sampler filename + */ + public String getFilename() { + return getPropertyAsString(Sampler.FILENAME); + } + + /** + * Returns the producer sampler url property. + * + * @return url + */ + public String getLabel() { + return (this.getUrl()); + } + + /** + * Sets the producer sampler url property. + * + * @param newUrl + */ + public void setUrl(String newUrl) { + this.setProperty(Sampler.URL, newUrl); + } + + /** + * Returns the producer sampler url property. + * + * @return producer url + */ + public String getUrl() { + return getPropertyAsString(Sampler.URL); + } + + + +} diff --git a/jmeter/src/java/org/activemq/sampler/config/gui/ConsumerConfigGui.java b/jmeter/src/java/org/activemq/sampler/config/gui/ConsumerConfigGui.java new file mode 100755 index 0000000000..a6d56bad57 --- /dev/null +++ b/jmeter/src/java/org/activemq/sampler/config/gui/ConsumerConfigGui.java @@ -0,0 +1,568 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.sampler.config.gui; + +import org.apache.jmeter.config.ConfigTestElement; +import org.apache.jmeter.config.gui.AbstractConfigGui; +import org.apache.jmeter.gui.util.VerticalPanel; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.jorphan.util.JOrphanUtils; +import org.activemq.sampler.Consumer; + + +import javax.swing.JTextField; +import javax.swing.JComboBox; +import javax.swing.JRadioButton; +import javax.swing.JPanel; +import javax.swing.JLabel; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Consumer configuration gui bean. + */ +public class ConsumerConfigGui extends AbstractConfigGui { + + //private final static String FILENAME = "filename"; + private final static String URL = "url"; + private final static String DURATION = "duration"; + private final static String RAMP_UP = "ramp_up"; + private final static String DURABLE = "durable"; + private final static String NONDURABLE = "nondurable"; + private final static String TOPIC = "topic"; + private final static String QUEUE = "queue"; + private final static String NOCONSUMER = "noconsumer"; + private final static String NOSUBJECT = "nosubject"; + private final static String CONSUMER_CONFIG_TITLE = "consumer_config_title"; + private final static String MQSERVER = "mqserver"; + private static final String ACTIVEMQ_SERVER = JMeterUtils.getResString("activemq_server"); + private static final String SONICMQ_SERVER = JMeterUtils.getResString("sonicmq_server"); + private static final String TIBCOMQ_SERVER = JMeterUtils.getResString("tibcomq_server"); + private static final String JBOSSMQ_SERVER = JMeterUtils.getResString("jbossmq_server"); + private static final String OPENJMS_SERVER = JMeterUtils.getResString("openjms_server"); + private static final String JORAM_SERVER = JMeterUtils.getResString("joram_server"); + private static final String MANTARAY_SERVER = JMeterUtils.getResString("mantaray_server"); + private static final String TRANSACTED = "transacted"; + private static final String NONTRANSACTED = "nontransacted"; + private static final String BATCHSIZE = "batchsize"; + + private JTextField setURL; + private JTextField setDuration; + private JTextField setRampUp; + private JTextField setNoConsumer; + private JTextField setNoSubject; + + private JRadioButton setDurable; + private JRadioButton setNonDurable; + private JRadioButton setTopic; + private JRadioButton setQueue; + private JComboBox setMQServer; + private JRadioButton setTransacted; + private JRadioButton setNonTransacted; + private JTextField setBatchSize; + + private boolean displayName = true; + + /** + * Default constructor. + */ + public ConsumerConfigGui() { + this(true); + } + + /** + * Constructor. + * + * @param displayName - whether to display the name of the consumer. + */ + public ConsumerConfigGui(boolean displayName) { + this.displayName = displayName; + init(); + } + + /** + * Returns the consumer configuration title. + * + * @return consumer configuration title + */ + public String getLabelResource() { + return CONSUMER_CONFIG_TITLE; + } + + /** + * Configures the ConsumerConfigGui bean. + * + * @param element - consumer sampler properties. + */ + public void configure(TestElement element) { + super.configure(element); + + setURL.setText(element.getPropertyAsString(Consumer.URL)); + setDuration.setText(element.getPropertyAsString(Consumer.DURATION)); + setRampUp.setText(element.getPropertyAsString(Consumer.RAMP_UP)); + + if (element.getProperty(Consumer.DURABLE) == null) { + setDurable.setSelected(false); + setNonDurable.setSelected(true); + } else { + if (element.getPropertyAsBoolean(Consumer.DURABLE)) { + setDurable.setSelected(true); + setNonDurable.setSelected(false); + } else { + setDurable.setSelected(false); + setNonDurable.setSelected(true); + } + } + + if (element.getProperty(Consumer.TOPIC) == null) { + setTopic.setSelected(true); + setQueue.setSelected(false); + } else { + if (element.getPropertyAsBoolean(Consumer.TOPIC)) { + setTopic.setSelected(true); + setQueue.setSelected(false); + } else { + setTopic.setSelected(false); + setQueue.setSelected(true); + } + } + + setNoConsumer.setText(element.getPropertyAsString(Consumer.NOCONSUMER)); + setNoSubject.setText(element.getPropertyAsString(Consumer.NOSUBJECT)); + setMQServer.setSelectedItem(element.getPropertyAsString(Consumer.MQSERVER)); + if (element.getProperty(Consumer.TRANSACTED) == null) { + setTransacted.setSelected(false); + setNonTransacted.setSelected(true); + setBatchSize.setEnabled(false); + } else { + if (element.getPropertyAsBoolean(Consumer.TRANSACTED)) { + setTransacted.setSelected(true); + setNonTransacted.setSelected(false); + setBatchSize.setEnabled(true); + setBatchSize.setText(element.getPropertyAsString(Consumer.BATCHSIZE)); + } else { + setTransacted.setSelected(false); + setNonTransacted.setSelected(true); + setBatchSize.setEnabled(false); + setBatchSize.setText(""); + } + } + } + + /** + * Creates a test element. + * + * @return element + */ + public TestElement createTestElement() { + ConfigTestElement element = new ConfigTestElement(); + modifyTestElement(element); + + return element; + } + + /** + * Sets the consumer sampler properties to the test element. + * + * @param element + */ + public void modifyTestElement(TestElement element) { + configureTestElement(element); + + element.setProperty(Consumer.URL, setURL.getText()); + element.setProperty(Consumer.DURATION, setDuration.getText()); + element.setProperty(Consumer.RAMP_UP, setRampUp.getText()); + element.setProperty(Consumer.DURABLE, JOrphanUtils.booleanToString(setDurable.isSelected())); + element.setProperty(Consumer.TOPIC, JOrphanUtils.booleanToString(setTopic.isSelected())); + element.setProperty(Consumer.NOCONSUMER , setNoConsumer.getText()); + element.setProperty(Consumer.NOSUBJECT, setNoSubject.getText()); + element.setProperty(Consumer.MQSERVER, setMQServer.getSelectedItem().toString()); + element.setProperty(Consumer.TRANSACTED, JOrphanUtils.booleanToString(setTransacted.isSelected())); + element.setProperty(Consumer.BATCHSIZE, setBatchSize.getText()); + } + + /** + * Creates the URL panel. + * + * @return urlPanel + */ + private JPanel createURLPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_url")); + setURL = new JTextField(10); + setURL.setName(URL); + label.setLabelFor(setURL); + + JPanel urlPanel = new JPanel(new BorderLayout(5, 0)); + urlPanel.add(label, BorderLayout.WEST); + urlPanel.add(setURL, BorderLayout.CENTER); + + return urlPanel; + } + + /** + * Creates the duration panel. + * + * @return durationPanel + */ + private JPanel createDurationPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_duration")); + setDuration = new JTextField(10); + setDuration.setName(DURATION); + label.setLabelFor(setDuration); + + JPanel durationPanel = new JPanel(new BorderLayout(5, 0)); + durationPanel.add(label, BorderLayout.WEST); + durationPanel.add(setDuration, BorderLayout.CENTER); + + return durationPanel; + } + + /** + * Creates teh ramp up panel. + * + * @return rampUpPanel + */ + private JPanel createRampUpPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_ramp_up")); + setRampUp = new JTextField(10); + setRampUp.setName(RAMP_UP); + label.setLabelFor(setRampUp); + + JPanel rampUpPanel = new JPanel(new BorderLayout(5, 0)); + rampUpPanel.add(label, BorderLayout.WEST); + rampUpPanel.add(setRampUp, BorderLayout.CENTER); + + return rampUpPanel; + } + + /** + * Creates the durable panel. + * + * @return durablePanel + */ + private JPanel createDurablePanel() { + JLabel labelDeliveryMode = new JLabel(JMeterUtils.getResString("form_delivery_mode")); + + JLabel labelDurable = new JLabel(JMeterUtils.getResString("form_durable")); + setDurable = new JRadioButton(); + setDurable.setName(DURABLE); + labelDurable.setLabelFor(setDurable); + setDurable.setActionCommand(DURABLE); + setDurable.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformedDelivery(evt); + } + }); + setDurable.setSelected(false); + + JLabel labelNonDurable = new JLabel(JMeterUtils.getResString("form_non_durable")); + setNonDurable = new JRadioButton(); + setNonDurable.setName(NONDURABLE); + labelNonDurable.setLabelFor(setNonDurable); + setNonDurable.setActionCommand(NONDURABLE); + setNonDurable.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformedDelivery(evt); + } + }); + setNonDurable.setSelected(true); + + FlowLayout flow = new FlowLayout(FlowLayout.LEFT); + flow.setHgap(0); + flow.setVgap(0); + + JPanel durablePanel = new JPanel(flow); + durablePanel.add(labelDeliveryMode); + durablePanel.add(new JLabel(" ")); + durablePanel.add(setDurable); + durablePanel.add(labelDurable); + durablePanel.add(new JLabel(" ")); + durablePanel.add(setNonDurable); + durablePanel.add(labelNonDurable); + + return durablePanel; + } + + /** + * Creates the topic panel. + * + * @return topicPanel + */ + private JPanel createTopicPanel() { + JLabel labelMessagingDomain = new JLabel(JMeterUtils.getResString("messaging_domain")); + + JLabel labelTopic = new JLabel(JMeterUtils.getResString("form_topic")); + setTopic = new JRadioButton(); + setTopic.setName(TOPIC); + labelTopic.setLabelFor(setTopic); + setTopic.setActionCommand(TOPIC); + setTopic.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformed(evt); + } + }); + setTopic.setSelected(true); + + JLabel labelQueue = new JLabel(JMeterUtils.getResString("form_queue")); + setQueue = new JRadioButton(); + setQueue.setName(QUEUE); + labelQueue.setLabelFor(setQueue); + setQueue.setActionCommand(QUEUE); + setQueue.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformed(evt); + } + }); + setQueue.setSelected(false); + + FlowLayout flow = new FlowLayout(FlowLayout.LEFT); + flow.setHgap(0); + flow.setVgap(0); + + JPanel topicPanel = new JPanel(flow); + topicPanel.add(labelMessagingDomain); + topicPanel.add(new JLabel(" ")); + topicPanel.add(setTopic); + topicPanel.add(labelTopic); + topicPanel.add(new JLabel(" ")); + topicPanel.add(setQueue); + topicPanel.add(labelQueue); + + + return topicPanel; + } + + /** + * Creates the no consumer panel. + * + * @return noConsumerPanel + */ + private JPanel createNoConsumerPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_no_consumer")); + setNoConsumer = new JTextField(10); + setNoConsumer.setName(NOCONSUMER); + label.setLabelFor(setNoConsumer); + + JPanel noConsumerPanel = new JPanel(new BorderLayout(5, 0)); + noConsumerPanel.add(label, BorderLayout.WEST); + noConsumerPanel.add(setNoConsumer, BorderLayout.CENTER); + + return noConsumerPanel; + } + + /** + * Creates the no subject panel. + * + * @return noSubjectPanel + */ + private JPanel createNoSubjectPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_no_subject")); + setNoSubject = new JTextField(10); + setNoSubject.setName(NOSUBJECT); + label.setLabelFor(setNoSubject); + + JPanel noSubjectPanel = new JPanel(new BorderLayout(5, 0)); + noSubjectPanel.add(label, BorderLayout.WEST); + noSubjectPanel.add(setNoSubject, BorderLayout.CENTER); + + return noSubjectPanel; + } + + /** + * Creates the MQ Server Combo Box. + * + * @return mqServerPanel + */ + private JPanel createMQServerPanel() { + String[] mqServers = {ACTIVEMQ_SERVER, + JBOSSMQ_SERVER, + SONICMQ_SERVER, + TIBCOMQ_SERVER, + OPENJMS_SERVER, + JORAM_SERVER, + MANTARAY_SERVER}; + + JLabel label = new JLabel(JMeterUtils.getResString("form_mq_servers")); + setMQServer = new JComboBox(mqServers); + setMQServer.setName(MQSERVER); + label.setLabelFor(setMQServer); + + FlowLayout flow = new FlowLayout(FlowLayout.LEFT); + flow.setHgap(0); + flow.setVgap(0); + + JPanel mqServerPanel = new JPanel(flow); + mqServerPanel.add(label); + mqServerPanel.add(new JLabel(" ")); + mqServerPanel.add(setMQServer); + mqServerPanel.add(new JLabel(" ")); + + return mqServerPanel; + } + /** + * Creates the Transaction Panel. + * + * @return transactionPanel + */ + private JPanel createTransactedPanel() { + JLabel labelTransactionType = new JLabel(JMeterUtils.getResString("form_msg_transaction_type")); + + //create Transacted Type + JLabel labelTransacted = new JLabel(JMeterUtils.getResString("form_transacted")); + setTransacted = new JRadioButton(); + setTransacted.setName(TRANSACTED); + labelTransacted.setLabelFor(setTransacted); + setTransacted.setActionCommand(TRANSACTED); + setTransacted.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioActionPerformedTransaction(evt); + } + }); + setTransacted.setSelected(false); + + //create Non Transacted Type + JLabel labelNonTransacted = new JLabel(JMeterUtils.getResString("form_non_transacted")); + setNonTransacted = new JRadioButton(); + setNonTransacted.setName(NONTRANSACTED); + labelNonTransacted.setLabelFor(setNonTransacted); + setNonTransacted.setActionCommand(NONTRANSACTED); + setNonTransacted.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioActionPerformedTransaction(evt); + } + }); + + setNonTransacted.setSelected(true); + + //create Batch Size. + JLabel labelBatchSize = new JLabel(JMeterUtils.getResString("form_batch_size")); + setBatchSize = new JTextField(10); + setBatchSize.setName(BATCHSIZE); + labelBatchSize.setLabelFor(setBatchSize); + + FlowLayout flow = new FlowLayout(FlowLayout.LEFT); + flow.setHgap(0); + flow.setVgap(0); + + JPanel transactionPanel = new JPanel(flow); + transactionPanel.add(labelTransactionType); + transactionPanel.add(new JLabel(" ")); + transactionPanel.add(setNonTransacted); + transactionPanel.add(labelNonTransacted); + transactionPanel.add(new JLabel(" ")); + transactionPanel.add(setTransacted); + transactionPanel.add(labelTransacted); + transactionPanel.add(new JLabel(" ")); + transactionPanel.add(setBatchSize); + transactionPanel.add(new JLabel(" ")); + transactionPanel.add(labelBatchSize); + + return transactionPanel; + } + + /** + * Initializes the gui components. + */ + private void init() { + setLayout(new BorderLayout(0, 5)); + + if (displayName) { + setBorder(makeBorder()); + add(makeTitlePanel(), BorderLayout.NORTH); + } + + VerticalPanel mainPanel = new VerticalPanel(); + + mainPanel.add(createURLPanel()); + mainPanel.add(createDurationPanel()); + mainPanel.add(createRampUpPanel()); + mainPanel.add(createNoConsumerPanel()); + mainPanel.add(createNoSubjectPanel()); + mainPanel.add(createDurablePanel()); + mainPanel.add(createTopicPanel()); + mainPanel.add(createTransactedPanel()); + mainPanel.add(createMQServerPanel()); + + add(mainPanel, BorderLayout.CENTER); + } + + /** + * Listener action for selecting Messaging Domain. + * + * @param evt - event triggered. + */ + private void jRadioButtonActionPerformed(ActionEvent evt) { + String evtActionCommand = evt.getActionCommand(); + + if (evtActionCommand.equals(TOPIC)) { + setTopic.setSelected(true); + setQueue.setSelected(false); + setDurable.setEnabled(true); + setNonDurable.setEnabled(true); + setNonDurable.setSelected(true); + } else if (evtActionCommand.equals(QUEUE)) { + setTopic.setSelected(false); + setQueue.setSelected(true); + setDurable.setSelected(false); + setDurable.setEnabled(false); + setNonDurable.setSelected(false); + setNonDurable.setEnabled(false); + } + } + + /** + * Listener action for selecting Delivery Mode. + * + * @param evt - event triggered. + */ + private void jRadioButtonActionPerformedDelivery(ActionEvent evt) { + String evtActionCommand = evt.getActionCommand(); + + if (evtActionCommand.equals(DURABLE)) { + setDurable.setSelected(true); + setNonDurable.setSelected(false); + } else if (evtActionCommand.equals(NONDURABLE)) { + setDurable.setSelected(false); + setNonDurable.setSelected(true); + } + } + /** + * Listener action for selecting Transaction Type. + * + * @param evt - event triggered. + */ + private void jRadioActionPerformedTransaction(ActionEvent evt) { + String evtActionCommand = evt.getActionCommand(); + + if (evtActionCommand.equals(TRANSACTED)) { + setTransacted.setSelected(true); + setNonTransacted.setSelected(false); + setBatchSize.setEnabled(true); + + } else if (evtActionCommand.equals(NONTRANSACTED)) { + setTransacted.setSelected(false); + setNonTransacted.setSelected(true); + setBatchSize.setEnabled(false); + setBatchSize.setText(""); + } + } +} diff --git a/jmeter/src/java/org/activemq/sampler/config/gui/ConsumerSysTestConfigGui.java b/jmeter/src/java/org/activemq/sampler/config/gui/ConsumerSysTestConfigGui.java new file mode 100644 index 0000000000..6602b3be7d --- /dev/null +++ b/jmeter/src/java/org/activemq/sampler/config/gui/ConsumerSysTestConfigGui.java @@ -0,0 +1,373 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.sampler.config.gui; + +import org.apache.jmeter.config.ConfigTestElement; +import org.apache.jmeter.config.gui.AbstractConfigGui; +import org.apache.jmeter.gui.util.VerticalPanel; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.jorphan.util.JOrphanUtils; +import org.activemq.sampler.ConsumerSysTest; + + +import javax.swing.JTextField; +import javax.swing.JComboBox; +import javax.swing.JRadioButton; +import javax.swing.JPanel; +import javax.swing.JLabel; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Consumer configuration gui bean. + */ +public class ConsumerSysTestConfigGui extends AbstractConfigGui { + + //private final static String FILENAME = "filename"; + private final static String URL = "url"; + private final static String DURABLE = "durable"; + private final static String NONDURABLE = "nondurable"; + private final static String TOPIC = "topic"; + private final static String QUEUE = "queue"; + private final static String NOCONSUMER = "noconsumer"; + private final static String NOSUBJECT = "nosubject"; + private final static String CONSUMER_SYS_TEST_CONFIG_TITLE = "consumer_sys_test_config_title"; + + private JTextField setURL; + private JTextField setNoConsumer; + private JTextField setNoSubject; + + private JRadioButton setDurable; + private JRadioButton setNonDurable; + private JRadioButton setTopic; + private JRadioButton setQueue; + + private boolean displayName = true; + + /** + * Default constructor. + */ + public ConsumerSysTestConfigGui() { + this(true); + } + + /** + * Constructor. + * + * @param displayName - whether to display the name of the consumer. + */ + public ConsumerSysTestConfigGui(boolean displayName) { + this.displayName = displayName; + init(); + } + + /** + * Returns the consumer configuration title. + * + * @return consumer configuration title + */ + public String getLabelResource() { + return CONSUMER_SYS_TEST_CONFIG_TITLE; + } + + /** + * Configures the ConsumerConfigGui bean. + * + * @param element - consumer sampler properties. + */ + public void configure(TestElement element) { + super.configure(element); + + setURL.setText(element.getPropertyAsString(ConsumerSysTest.URL)); + + if (element.getProperty(ConsumerSysTest.DURABLE) == null) { + setDurable.setSelected(false); + setNonDurable.setSelected(true); + } else { + if (element.getPropertyAsBoolean(ConsumerSysTest.DURABLE)) { + setDurable.setSelected(true); + setNonDurable.setSelected(false); + } else { + setDurable.setSelected(false); + setNonDurable.setSelected(true); + } + } + + if (element.getProperty(ConsumerSysTest.TOPIC) == null) { + setTopic.setSelected(true); + setQueue.setSelected(false); + } else { + if (element.getPropertyAsBoolean(ConsumerSysTest.TOPIC)) { + setTopic.setSelected(true); + setQueue.setSelected(false); + } else { + setTopic.setSelected(false); + setQueue.setSelected(true); + } + } + + setNoConsumer.setText(element.getPropertyAsString(ConsumerSysTest.NOCONSUMER)); + setNoSubject.setText(element.getPropertyAsString(ConsumerSysTest.NOSUBJECT)); + } + + /** + * Creates a test element. + * + * @return element + */ + public TestElement createTestElement() { + ConfigTestElement element = new ConfigTestElement(); + modifyTestElement(element); + + return element; + } + + /** + * Sets the consumer sampler properties to the test element. + * + * @param element + */ + public void modifyTestElement(TestElement element) { + configureTestElement(element); + + element.setProperty(ConsumerSysTest.URL, setURL.getText()); + element.setProperty(ConsumerSysTest.DURABLE, JOrphanUtils.booleanToString(setDurable.isSelected())); + element.setProperty(ConsumerSysTest.TOPIC, JOrphanUtils.booleanToString(setTopic.isSelected())); + element.setProperty(ConsumerSysTest.NOCONSUMER , setNoConsumer.getText()); + element.setProperty(ConsumerSysTest.NOSUBJECT, setNoSubject.getText()); + } + + /** + * Creates the URL panel. + * + * @return urlPanel + */ + private JPanel createURLPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_url")); + setURL = new JTextField(10); + setURL.setName(URL); + label.setLabelFor(setURL); + + JPanel urlPanel = new JPanel(new BorderLayout(5, 0)); + urlPanel.add(label, BorderLayout.WEST); + urlPanel.add(setURL, BorderLayout.CENTER); + + return urlPanel; + } + + /** + * Creates the durable panel. + * + * @return durablePanel + */ + private JPanel createDurablePanel() { + JLabel labelDeliveryMode = new JLabel(JMeterUtils.getResString("form_delivery_mode")); + + JLabel labelDurable = new JLabel(JMeterUtils.getResString("form_durable")); + setDurable = new JRadioButton(); + setDurable.setName(DURABLE); + labelDurable.setLabelFor(setDurable); + setDurable.setActionCommand(DURABLE); + setDurable.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformedDelivery(evt); + } + }); + setDurable.setSelected(false); + + JLabel labelNonDurable = new JLabel(JMeterUtils.getResString("form_non_durable")); + setNonDurable = new JRadioButton(); + setNonDurable.setName(NONDURABLE); + labelNonDurable.setLabelFor(setNonDurable); + setNonDurable.setActionCommand(NONDURABLE); + setNonDurable.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformedDelivery(evt); + } + }); + setNonDurable.setSelected(true); + + FlowLayout flow = new FlowLayout(FlowLayout.LEFT); + flow.setHgap(0); + flow.setVgap(0); + + JPanel durablePanel = new JPanel(flow); + durablePanel.add(labelDeliveryMode); + durablePanel.add(new JLabel(" ")); + durablePanel.add(setDurable); + durablePanel.add(labelDurable); + durablePanel.add(new JLabel(" ")); + durablePanel.add(setNonDurable); + durablePanel.add(labelNonDurable); + + return durablePanel; + } + + /** + * Creates the topic panel. + * + * @return topicPanel + */ + private JPanel createTopicPanel() { + JLabel labelMessagingDomain = new JLabel(JMeterUtils.getResString("messaging_domain")); + + JLabel labelTopic = new JLabel(JMeterUtils.getResString("form_topic")); + setTopic = new JRadioButton(); + setTopic.setName(TOPIC); + labelTopic.setLabelFor(setTopic); + setTopic.setActionCommand(TOPIC); + setTopic.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformed(evt); + } + }); + setTopic.setSelected(true); + + JLabel labelQueue = new JLabel(JMeterUtils.getResString("form_queue")); + setQueue = new JRadioButton(); + setQueue.setName(QUEUE); + labelQueue.setLabelFor(setQueue); + setQueue.setActionCommand(QUEUE); + setQueue.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformed(evt); + } + }); + setQueue.setSelected(false); + + FlowLayout flow = new FlowLayout(FlowLayout.LEFT); + flow.setHgap(0); + flow.setVgap(0); + + JPanel topicPanel = new JPanel(flow); + topicPanel.add(labelMessagingDomain); + topicPanel.add(new JLabel(" ")); + topicPanel.add(setTopic); + topicPanel.add(labelTopic); + topicPanel.add(new JLabel(" ")); + topicPanel.add(setQueue); + topicPanel.add(labelQueue); + + + return topicPanel; + } + + /** + * Creates the no consumer panel. + * + * @return noConsumerPanel + */ + private JPanel createNoConsumerPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_no_consumer")); + setNoConsumer = new JTextField(10); + setNoConsumer.setName(NOCONSUMER); + label.setLabelFor(setNoConsumer); + + JPanel noConsumerPanel = new JPanel(new BorderLayout(5, 0)); + noConsumerPanel.add(label, BorderLayout.WEST); + noConsumerPanel.add(setNoConsumer, BorderLayout.CENTER); + + return noConsumerPanel; + } + + /** + * Creates the no subject panel. + * + * @return noSubjectPanel + */ + private JPanel createNoSubjectPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_no_subject")); + setNoSubject = new JTextField(10); + setNoSubject.setName(NOSUBJECT); + label.setLabelFor(setNoSubject); + + JPanel noSubjectPanel = new JPanel(new BorderLayout(5, 0)); + noSubjectPanel.add(label, BorderLayout.WEST); + noSubjectPanel.add(setNoSubject, BorderLayout.CENTER); + + return noSubjectPanel; + } + + /** + * Initializes the gui components. + */ + private void init() { + setLayout(new BorderLayout(0, 5)); + + if (displayName) { + setBorder(makeBorder()); + add(makeTitlePanel(), BorderLayout.NORTH); + } + + VerticalPanel mainPanel = new VerticalPanel(); + + mainPanel.add(createURLPanel()); + mainPanel.add(createNoConsumerPanel()); + mainPanel.add(createNoSubjectPanel()); + mainPanel.add(createDurablePanel()); + mainPanel.add(createTopicPanel()); + + add(mainPanel, BorderLayout.CENTER); + } + + /** + * Listener action for selecting Messaging Domain. + * + * @param evt - event triggered. + */ + private void jRadioButtonActionPerformed(ActionEvent evt) { + String evtActionCommand = evt.getActionCommand(); + + if (evtActionCommand.equals(TOPIC)) { + setTopic.setSelected(true); + setQueue.setSelected(false); + setDurable.setEnabled(true); + setNonDurable.setEnabled(true); + setNonDurable.setSelected(true); + } else if (evtActionCommand.equals(QUEUE)) { + setTopic.setSelected(false); + setQueue.setSelected(true); + setDurable.setSelected(false); + setDurable.setEnabled(false); + setNonDurable.setSelected(false); + setNonDurable.setEnabled(false); + } + } + + /** + * Listener action for selecting Delivery Mode. + * + * @param evt - event triggered. + */ + private void jRadioButtonActionPerformedDelivery(ActionEvent evt) { + String evtActionCommand = evt.getActionCommand(); + + if (evtActionCommand.equals(DURABLE)) { + setDurable.setSelected(true); + setNonDurable.setSelected(false); + } else if (evtActionCommand.equals(NONDURABLE)) { + setDurable.setSelected(false); + setNonDurable.setSelected(true); + } + } + +} diff --git a/jmeter/src/java/org/activemq/sampler/config/gui/ProducerConfigGui.java b/jmeter/src/java/org/activemq/sampler/config/gui/ProducerConfigGui.java new file mode 100755 index 0000000000..388ea49665 --- /dev/null +++ b/jmeter/src/java/org/activemq/sampler/config/gui/ProducerConfigGui.java @@ -0,0 +1,691 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.sampler.config.gui; + +import org.apache.jmeter.config.ConfigTestElement; +import org.apache.jmeter.config.gui.AbstractConfigGui; +import org.apache.jmeter.gui.util.VerticalPanel; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.jorphan.util.JOrphanUtils; +import org.activemq.sampler.Producer; + +import javax.swing.JTextField; +import javax.swing.JComboBox; +import javax.swing.JRadioButton; +import javax.swing.JPanel; +import javax.swing.JLabel; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Producer configuration gui bean. + */ +public class ProducerConfigGui extends AbstractConfigGui { + + private static final String URL = "url"; + private static final String DURATION = "duration"; + private static final String RAMP_UP = "ramp_up"; + private static final String DURABLE = "durable"; + private static final String NONDURABLE = "nondurable"; + private static final String TOPIC = "topic"; + private static final String QUEUE = "queue"; + private static final String MSGSIZE = "msgsize"; + private static final String NOPRODUCER = "noproducer"; + private static final String NOSUBJECT = "nosubject"; + private static final String PRODUCER_CONFIG_TITLE = "producer_config_title"; + private static final String MQSERVER = "mqserver"; + private static final String DEFMSGINTERVAL = "defmsginterval"; + private static final String CUSMSGINTERVAL = "cusmsginterval"; + private static final String MSGINTERVAL = "msginterval"; + private static final String ACTIVEMQ_SERVER = JMeterUtils.getResString("activemq_server"); + private static final String SONICMQ_SERVER = JMeterUtils.getResString("sonicmq_server"); + private static final String TIBCOMQ_SERVER = JMeterUtils.getResString("tibcomq_server"); + private static final String JBOSSMQ_SERVER = JMeterUtils.getResString("jbossmq_server"); + private static final String OPENJMS_SERVER = JMeterUtils.getResString("openjms_server"); + private static final String JORAM_SERVER = JMeterUtils.getResString("joram_server"); + private static final String MANTARAY_SERVER = JMeterUtils.getResString("mantaray_server"); + private static final String TRANSACTED = "transacted"; + private static final String NONTRANSACTED = "nontransacted"; + private static final String BATCHSIZE = "batchsize"; + + private JTextField setURL; + private JTextField setDuration; + private JTextField setRampUp; + private JTextField setMsgSize; + private JTextField setNoProducer; + private JTextField setNoSubject; + private JRadioButton setDurable; + private JRadioButton setNonDurable; + private JRadioButton setTopic; + private JRadioButton setQueue; + private JRadioButton setDefMsgInterval; + private JRadioButton setCusMsgInterval; + private JTextField setMsgInterval; + private JComboBox setMQServer; + private JRadioButton setTransacted; + private JRadioButton setNonTransacted; + private JTextField setBatchSize; + + private boolean displayName = true; + + /** + * Default constructor. + */ + public ProducerConfigGui() { + this(true); + } + + /** + * Constructor. + * + * @param displayName - whether to display the name of the producer. + */ + public ProducerConfigGui(boolean displayName) { + this.displayName = displayName; + init(); + } + + /** + * Returns the producer configuration title. + * + * @return producer configuration title + */ + public String getLabelResource() { + return PRODUCER_CONFIG_TITLE; + } + + /** + * Configures the ProducerConfigGui bean. + * + * @param element - producer sampler properties. + */ + public void configure(TestElement element) { + super.configure(element); + + setURL.setText(element.getPropertyAsString(Producer.URL)); + setDuration.setText(element.getPropertyAsString(Producer.DURATION)); + setRampUp.setText(element.getPropertyAsString(Producer.RAMP_UP)); + + if (element.getProperty(Producer.DURABLE) == null) { + setDurable.setSelected(true); + setNonDurable.setSelected(false); + } else { + if (element.getPropertyAsBoolean(Producer.DURABLE)) { + setDurable.setSelected(true); + setNonDurable.setSelected(false); + } else { + setDurable.setSelected(false); + setNonDurable.setSelected(true); + } + } + + if (element.getProperty(Producer.TOPIC) == null) { + setTopic.setSelected(true); + setQueue.setSelected(false); + } else { + if (element.getPropertyAsBoolean(Producer.TOPIC)) { + setTopic.setSelected(true); + setQueue.setSelected(false); + } else { + setTopic.setSelected(false); + setQueue.setSelected(true); + } + } + + setMsgSize.setText(element.getPropertyAsString(Producer.MSGSIZE)); + setNoProducer.setText(element.getPropertyAsString(Producer.NOPRODUCER)); + setNoSubject.setText(element.getPropertyAsString(Producer.NOSUBJECT)); + setMsgSize.setText(element.getPropertyAsString(Producer.MSGSIZE)); + + if (element.getProperty(Producer.DEFMSGINTERVAL) == null) { + setDefMsgInterval.setSelected(true); + setCusMsgInterval.setSelected(false); + setMsgInterval.setEnabled(false); + } else { + if (element.getPropertyAsBoolean(Producer.DEFMSGINTERVAL)) { + setDefMsgInterval.setSelected(true); + setCusMsgInterval.setSelected(false); + setMsgInterval.setEnabled(false); + } else { + setDefMsgInterval.setSelected(false); + setCusMsgInterval.setSelected(true); + setMsgInterval.setEnabled(true); + setMsgInterval.setText(element.getPropertyAsString(Producer.MSGINTERVAL)); + } + } + + setMQServer.setSelectedItem(element.getPropertyAsString(Producer.MQSERVER)); + + if (element.getProperty(Producer.TRANSACTED) == null) { + setTransacted.setSelected(false); + setNonTransacted.setSelected(true); + setBatchSize.setEnabled(false); + } else { + if (element.getPropertyAsBoolean(Producer.TRANSACTED)) { + setTransacted.setSelected(true); + setNonTransacted.setSelected(false); + setBatchSize.setEnabled(true); + setBatchSize.setText(element.getPropertyAsString(Producer.BATCHSIZE)); + } else { + setTransacted.setSelected(false); + setNonTransacted.setSelected(true); + setBatchSize.setEnabled(false); + setBatchSize.setText(""); + } + } + } + + /** + * Creates a test element. + * + * @return element + */ + public TestElement createTestElement() { + ConfigTestElement element = new ConfigTestElement(); + modifyTestElement(element); + + return element; + } + + /** + * Sets the producer sampler properties to the test element. + * + * @param element + */ + public void modifyTestElement(TestElement element) { + configureTestElement(element); + + element.setProperty(Producer.URL, setURL.getText()); + element.setProperty(Producer.DURATION, setDuration.getText()); + element.setProperty(Producer.RAMP_UP, setRampUp.getText()); + element.setProperty(Producer.DURABLE, JOrphanUtils.booleanToString(setDurable.isSelected())); + element.setProperty(Producer.TOPIC, JOrphanUtils.booleanToString(setTopic.isSelected())); + element.setProperty(Producer.MSGSIZE, setMsgSize.getText()); + element.setProperty(Producer.NOPRODUCER, setNoProducer.getText()); + element.setProperty(Producer.NOSUBJECT, setNoSubject.getText()); + element.setProperty(Producer.DEFMSGINTERVAL, JOrphanUtils.booleanToString(setDefMsgInterval.isSelected())); + element.setProperty(Producer.MSGINTERVAL, setMsgInterval.getText()); + element.setProperty(Producer.MQSERVER, setMQServer.getSelectedItem().toString()); + element.setProperty(Producer.TRANSACTED, JOrphanUtils.booleanToString(setTransacted.isSelected())); + element.setProperty(Producer.BATCHSIZE, setBatchSize.getText()); + } + + /** + * Creates the URL panel. + * + * @return urlPanel + */ + private JPanel createURLPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_url")); + setURL = new JTextField(10); + setURL.setName(URL); + label.setLabelFor(setURL); + + JPanel urlPanel = new JPanel(new BorderLayout(5, 0)); + urlPanel.add(label, BorderLayout.WEST); + urlPanel.add(setURL, BorderLayout.CENTER); + + return urlPanel; + } + + /** + * Creates the duration panel. + * + * @return durationPanel + */ + private JPanel createDurationPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_duration")); + setDuration = new JTextField(10); + setDuration.setName(DURATION); + label.setLabelFor(setDuration); + + JPanel durationPanel = new JPanel(new BorderLayout(5, 0)); + durationPanel.add(label, BorderLayout.WEST); + durationPanel.add(setDuration, BorderLayout.CENTER); + + return durationPanel; + } + + /** + * Creates teh ramp up panel. + * + * @return rampUpPanel + */ + private JPanel createRampUpPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_ramp_up")); + setRampUp = new JTextField(10); + setRampUp.setName(RAMP_UP); + label.setLabelFor(setRampUp); + + JPanel rampUpPanel = new JPanel(new BorderLayout(5, 0)); + rampUpPanel.add(label, BorderLayout.WEST); + rampUpPanel.add(setRampUp, BorderLayout.CENTER); + + return rampUpPanel; + } + + /** + * Creates the durable panel. + * + * @return durablePanel + */ + private JPanel createDurablePanel() { + JLabel labelDeliveryMode = new JLabel(JMeterUtils.getResString("form_delivery_mode")); + + JLabel labelDurable = new JLabel(JMeterUtils.getResString("form_persistent")); + setDurable = new JRadioButton(); + setDurable.setName(DURABLE); + labelDurable.setLabelFor(setDurable); + setDurable.setActionCommand(DURABLE); + setDurable.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformedDelivery(evt); + } + }); + setDurable.setSelected(false); + + JLabel labelNonDurable = new JLabel(JMeterUtils.getResString("form_non_persistent")); + setNonDurable = new JRadioButton(); + setNonDurable.setName(NONDURABLE); + labelNonDurable.setLabelFor(setNonDurable); + setNonDurable.setActionCommand(NONDURABLE); + setNonDurable.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformedDelivery(evt); + } + }); + setNonDurable.setSelected(true); + + FlowLayout flow = new FlowLayout(FlowLayout.LEFT); + flow.setHgap(0); + flow.setVgap(0); + + JPanel durablePanel = new JPanel(flow); + durablePanel.add(labelDeliveryMode); + durablePanel.add(new JLabel(" ")); + durablePanel.add(setDurable); + durablePanel.add(labelDurable); + durablePanel.add(new JLabel(" ")); + durablePanel.add(setNonDurable); + durablePanel.add(labelNonDurable); + + return durablePanel; + } + + /** + * Creates the topic panel. + * + * @return topicPanel + */ + private JPanel createTopicPanel() { + JLabel labelMessagingDomain = new JLabel(JMeterUtils.getResString("messaging_domain")); + + JLabel labelTopic = new JLabel(JMeterUtils.getResString("form_topic")); + setTopic = new JRadioButton(); + setTopic.setName(TOPIC); + labelTopic.setLabelFor(setTopic); + setTopic.setActionCommand(TOPIC); + setTopic.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformed(evt); + } + }); + setTopic.setSelected(true); + + JLabel labelQueue = new JLabel(JMeterUtils.getResString("form_queue")); + setQueue = new JRadioButton(); + setQueue.setName(QUEUE); + labelQueue.setLabelFor(setQueue); + setQueue.setActionCommand(QUEUE); + setQueue.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformed(evt); + } + }); + setQueue.setSelected(false); + + FlowLayout flow = new FlowLayout(FlowLayout.LEFT); + flow.setHgap(0); + flow.setVgap(0); + + JPanel topicPanel = new JPanel(flow); + topicPanel.add(labelMessagingDomain); + topicPanel.add(new JLabel(" ")); + topicPanel.add(setTopic); + topicPanel.add(labelTopic); + topicPanel.add(new JLabel(" ")); + topicPanel.add(setQueue); + topicPanel.add(labelQueue); + + return topicPanel; + } + + /** + * Creates the message size panel. + * + * @return msgSizePanel + */ + private JPanel createMsgSizePanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_msg_size")); + setMsgSize = new JTextField(10); + setMsgSize.setName(MSGSIZE); + label.setLabelFor(setMsgSize); + + JPanel msgSizePanel = new JPanel(new BorderLayout(5, 0)); + msgSizePanel.add(label, BorderLayout.WEST); + msgSizePanel.add(setMsgSize, BorderLayout.CENTER); + + return msgSizePanel; + } + + /** + * Creates the no prod panel. + * + * @return noProdPanel + */ + private JPanel createNoProducerPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_no_producer")); + setNoProducer = new JTextField(10); + setNoProducer.setName(NOPRODUCER); + label.setLabelFor(setNoProducer); + + JPanel noProdPanel = new JPanel(new BorderLayout(5, 0)); + noProdPanel.add(label, BorderLayout.WEST); + noProdPanel.add(setNoProducer, BorderLayout.CENTER); + + return noProdPanel; + } + + /** + * Creates the no subject panel. + * + * @return noSubjectPanel + */ + private JPanel createNoSubjectPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_no_subject")); + setNoSubject = new JTextField(10); + setNoSubject.setName(NOSUBJECT); + label.setLabelFor(setNoSubject); + + JPanel noSubjectPanel = new JPanel(new BorderLayout(5, 0)); + noSubjectPanel.add(label, BorderLayout.WEST); + noSubjectPanel.add(setNoSubject, BorderLayout.CENTER); + + return noSubjectPanel; + } + + /** + * Creates the default/cutom Message Interval Panel + * + * @return msgIntervalPanel + */ + private JPanel createDefMsgIntervalPanel() { + JLabel labelMsgInterval = new JLabel(JMeterUtils.getResString("form_msg_interval")); + + // create Default Message Interval. + JLabel labelDefMsgInterval = new JLabel(JMeterUtils.getResString("form_default_msg_interval")); + setDefMsgInterval = new JRadioButton(); + setDefMsgInterval.setName(DEFMSGINTERVAL); + labelDefMsgInterval.setLabelFor(setDefMsgInterval); + setDefMsgInterval.setActionCommand(DEFMSGINTERVAL); + setDefMsgInterval.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioActionPerformedMsgInterval(evt); + } + }); + setDefMsgInterval.setSelected(true); + + // create Custom Message Interval. + JLabel labelCusMsgInterval = new JLabel(JMeterUtils.getResString("form_custom_msg_interval")); + setCusMsgInterval = new JRadioButton(); + setCusMsgInterval.setName(CUSMSGINTERVAL); + labelDefMsgInterval.setLabelFor(setCusMsgInterval); + setCusMsgInterval.setActionCommand(CUSMSGINTERVAL); + setCusMsgInterval.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioActionPerformedMsgInterval(evt); + } + }); + setCusMsgInterval.setSelected(false); + + //create Custom Message Interval Value. + JLabel labelCusMsgIntervalValue = new JLabel(JMeterUtils.getResString("form_custom_msg_interval_value")); + setMsgInterval = new JTextField(10); + setMsgInterval.setName(MSGINTERVAL); + labelCusMsgIntervalValue.setLabelFor(setMsgInterval); + + + FlowLayout flow = new FlowLayout(FlowLayout.LEFT); + flow.setHgap(0); + flow.setVgap(0); + + JPanel msgIntervalPanel = new JPanel(flow); + msgIntervalPanel.add(labelMsgInterval); + msgIntervalPanel.add(new JLabel(" ")); + msgIntervalPanel.add(setDefMsgInterval); + msgIntervalPanel.add(labelDefMsgInterval); + msgIntervalPanel.add(new JLabel(" ")); + msgIntervalPanel.add(setCusMsgInterval); + msgIntervalPanel.add(labelCusMsgInterval); + msgIntervalPanel.add(new JLabel(" ")); + msgIntervalPanel.add(setMsgInterval); + msgIntervalPanel.add(new JLabel(" ")); + msgIntervalPanel.add(labelCusMsgIntervalValue); + + + return msgIntervalPanel; + } + + /** + * Creates the Transaction Panel. + * + * @return transactionPanel + */ + private JPanel createTransactedPanel() { + JLabel labelTransactionType = new JLabel(JMeterUtils.getResString("form_msg_transaction_type")); + + //create Transacted Type + JLabel labelTransacted = new JLabel(JMeterUtils.getResString("form_transacted")); + setTransacted = new JRadioButton(); + setTransacted.setName(TRANSACTED); + labelTransacted.setLabelFor(setTransacted); + setTransacted.setActionCommand(TRANSACTED); + setTransacted.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioActionPerformedTransaction(evt); + } + }); + setTransacted.setSelected(false); + + //create Non Transacted Type + JLabel labelNonTransacted = new JLabel(JMeterUtils.getResString("form_non_transacted")); + setNonTransacted = new JRadioButton(); + setNonTransacted.setName(NONTRANSACTED); + labelNonTransacted.setLabelFor(setNonTransacted); + setNonTransacted.setActionCommand(NONTRANSACTED); + setNonTransacted.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioActionPerformedTransaction(evt); + } + }); + + setNonTransacted.setSelected(true); + + //create Batch Size. + JLabel labelBatchSize = new JLabel(JMeterUtils.getResString("form_batch_size")); + setBatchSize = new JTextField(10); + setBatchSize.setName(BATCHSIZE); + labelBatchSize.setLabelFor(setBatchSize); + + FlowLayout flow = new FlowLayout(FlowLayout.LEFT); + flow.setHgap(0); + flow.setVgap(0); + + JPanel transactionPanel = new JPanel(flow); + transactionPanel.add(labelTransactionType); + transactionPanel.add(new JLabel(" ")); + transactionPanel.add(setNonTransacted); + transactionPanel.add(labelNonTransacted); + transactionPanel.add(new JLabel(" ")); + transactionPanel.add(setTransacted); + transactionPanel.add(labelTransacted); + transactionPanel.add(new JLabel(" ")); + transactionPanel.add(setBatchSize); + transactionPanel.add(new JLabel(" ")); + transactionPanel.add(labelBatchSize); + + return transactionPanel; + } + + /** + * Creates the MQ Server Combo Box. + * + * @return mqServerPanel + */ + private JPanel createMQServerPanel() { + String[] mqServers = {ACTIVEMQ_SERVER, + JBOSSMQ_SERVER, + SONICMQ_SERVER, + TIBCOMQ_SERVER, + OPENJMS_SERVER, + JORAM_SERVER, + MANTARAY_SERVER}; + + JLabel label = new JLabel(JMeterUtils.getResString("form_mq_servers")); + setMQServer = new JComboBox(mqServers); + setMQServer.setName(MQSERVER); + label.setLabelFor(setMQServer); + + FlowLayout flow = new FlowLayout(FlowLayout.LEFT); + flow.setHgap(0); + flow.setVgap(0); + + JPanel mqServerPanel = new JPanel(flow); + mqServerPanel.add(label); + mqServerPanel.add(new JLabel(" ")); + mqServerPanel.add(setMQServer); + mqServerPanel.add(new JLabel(" ")); + + return mqServerPanel; + } + + + /** + * Initializes the gui components. + */ + private void init() { + setLayout(new BorderLayout(0, 5)); + + if (displayName) { + setBorder(makeBorder()); + add(makeTitlePanel(), BorderLayout.NORTH); + } + + VerticalPanel mainPanel = new VerticalPanel(); + + mainPanel.add(createURLPanel()); + mainPanel.add(createDurationPanel()); + mainPanel.add(createRampUpPanel()); + mainPanel.add(createNoProducerPanel()); + mainPanel.add(createNoSubjectPanel()); + mainPanel.add(createMsgSizePanel()); + mainPanel.add(createDurablePanel()); + mainPanel.add(createTopicPanel()); + mainPanel.add(createTransactedPanel()); + mainPanel.add(createDefMsgIntervalPanel()); + mainPanel.add(createMQServerPanel()); + + add(mainPanel, BorderLayout.CENTER); + } + + /** + * Listener action for selecting Messaging Domain. + * + * @param evt - event triggered. + */ + private void jRadioButtonActionPerformed(ActionEvent evt) { + String evtActionCommand = evt.getActionCommand(); + + if (evtActionCommand.equals(TOPIC)) { + setTopic.setSelected(true); + setQueue.setSelected(false); + } else if (evtActionCommand.equals(QUEUE)) { + setTopic.setSelected(false); + setQueue.setSelected(true); + } + } + + /** + * Listener action for selecting Delivery Mode. + * + * @param evt - event triggered. + */ + private void jRadioButtonActionPerformedDelivery(ActionEvent evt) { + String evtActionCommand = evt.getActionCommand(); + + if (evtActionCommand.equals(DURABLE)) { + setDurable.setSelected(true); + setNonDurable.setSelected(false); + } else if (evtActionCommand.equals(NONDURABLE)) { + setDurable.setSelected(false); + setNonDurable.setSelected(true); + } + } + + /** + * @param evt - event triggered. + */ + private void jRadioActionPerformedMsgInterval(ActionEvent evt) { + String evtActionCommand = evt.getActionCommand(); + + if (evtActionCommand.equals(DEFMSGINTERVAL)) { + setDefMsgInterval.setSelected(true); + setCusMsgInterval.setSelected(false); + setMsgInterval.setEnabled(false); + setMsgInterval.setText(""); + } else if (evtActionCommand.equals(CUSMSGINTERVAL)) { + setDefMsgInterval.setSelected(false); + setCusMsgInterval.setSelected(true); + setMsgInterval.setEnabled(true); + } + } + + /** + * @param evt - event triggered. + */ + private void jRadioActionPerformedTransaction(ActionEvent evt) { + String evtActionCommand = evt.getActionCommand(); + + if (evtActionCommand.equals(TRANSACTED)) { + setTransacted.setSelected(true); + setNonTransacted.setSelected(false); + setBatchSize.setEnabled(true); + + } else if (evtActionCommand.equals(NONTRANSACTED)) { + setTransacted.setSelected(false); + setNonTransacted.setSelected(true); + setBatchSize.setEnabled(false); + setBatchSize.setText(""); + } + } + +} diff --git a/jmeter/src/java/org/activemq/sampler/config/gui/ProducerSysTestConfigGui.java b/jmeter/src/java/org/activemq/sampler/config/gui/ProducerSysTestConfigGui.java new file mode 100644 index 0000000000..ff31572b5c --- /dev/null +++ b/jmeter/src/java/org/activemq/sampler/config/gui/ProducerSysTestConfigGui.java @@ -0,0 +1,447 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.sampler.config.gui; + +import org.apache.jmeter.config.ConfigTestElement; +import org.apache.jmeter.config.gui.AbstractConfigGui; +import org.apache.jmeter.gui.util.VerticalPanel; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.jorphan.util.JOrphanUtils; +import org.activemq.sampler.Producer; +import org.activemq.sampler.ProducerSysTest; + +import javax.swing.JTextField; +import javax.swing.JComboBox; +import javax.swing.JRadioButton; +import javax.swing.JPanel; +import javax.swing.JLabel; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Producer configuration gui bean. + */ +public class ProducerSysTestConfigGui extends AbstractConfigGui { + + private static final String URL = "url"; + private static final String DURABLE = "durable"; + private static final String NONDURABLE = "nondurable"; + private static final String TOPIC = "topic"; + private static final String QUEUE = "queue"; + private static final String NOPRODUCER = "noproducer"; + private static final String NOSUBJECT = "nosubject"; + private static final String PRODUCER_SYS_TEST_CONFIG_TITLE = "producer_sys_test_config_title"; + private static final String NOMESSAGES = "nomessages"; + + + private JTextField setURL; + private JTextField setNoProducer; + private JTextField setNoSubject; + private JRadioButton setDurable; + private JRadioButton setNonDurable; + private JRadioButton setTopic; + private JRadioButton setQueue; + private JTextField setNoMessages; + + private boolean displayName = true; + + /** + * Default constructor. + */ + public ProducerSysTestConfigGui() { + this(true); + } + + /** + * Constructor. + * + * @param displayName - whether to display the name of the producer. + */ + public ProducerSysTestConfigGui(boolean displayName) { + this.displayName = displayName; + init(); + } + + /** + * Returns the producer configuration title. + * + * @return producer configuration title + */ + public String getLabelResource() { + return PRODUCER_SYS_TEST_CONFIG_TITLE; + } + + /** + * Configures the ProducerConfigGui bean. + * + * @param element - producer sampler properties. + */ + public void configure(TestElement element) { + super.configure(element); + + setURL.setText(element.getPropertyAsString(ProducerSysTest.URL)); + //setDuration.setText(element.getPropertyAsString(ProducerSysTest.DURATION)); + //setRampUp.setText(element.getPropertyAsString(ProducerSysTest.RAMP_UP)); + + if (element.getProperty(ProducerSysTest.DURABLE) == null) { + setDurable.setSelected(true); + setNonDurable.setSelected(false); + } else { + if (element.getPropertyAsBoolean(ProducerSysTest.DURABLE)) { + setDurable.setSelected(true); + setNonDurable.setSelected(false); + } else { + setDurable.setSelected(false); + setNonDurable.setSelected(true); + } + } + + if (element.getProperty(ProducerSysTest.TOPIC) == null) { + setTopic.setSelected(true); + setQueue.setSelected(false); + } else { + if (element.getPropertyAsBoolean(ProducerSysTest.TOPIC)) { + setTopic.setSelected(true); + setQueue.setSelected(false); + } else { + setTopic.setSelected(false); + setQueue.setSelected(true); + } + } + + //setMsgSize.setText(element.getPropertyAsString(ProducerSysTest.MSGSIZE)); + setNoProducer.setText(element.getPropertyAsString(ProducerSysTest.NOPRODUCER)); + setNoSubject.setText(element.getPropertyAsString(ProducerSysTest.NOSUBJECT)); + //setMsgSize.setText(element.getPropertyAsString(ProducerSysTest.MSGSIZE)); + setNoMessages.setText(element.getPropertyAsString(ProducerSysTest.NOMESSAGES)); + + /* + if (element.getProperty(ProducerSysTest.DEFMSGINTERVAL) == null) { + setDefMsgInterval.setSelected(true); + setCusMsgInterval.setSelected(false); + setMsgInterval.setEnabled(false); + } else { + if (element.getPropertyAsBoolean(ProducerSysTest.DEFMSGINTERVAL)) { + setDefMsgInterval.setSelected(true); + setCusMsgInterval.setSelected(false); + setMsgInterval.setEnabled(false); + } else { + setDefMsgInterval.setSelected(false); + setCusMsgInterval.setSelected(true); + setMsgInterval.setEnabled(true); + setMsgInterval.setText(element.getPropertyAsString(ProducerSysTest.MSGINTERVAL)); + } + } + */ + /* + setMQServer.setSelectedItem(element.getPropertyAsString(ProducerSysTest.MQSERVER)); + + + if (element.getProperty(ProducerSysTest.TRANSACTED) == null) { + setTransacted.setSelected(false); + setNonTransacted.setSelected(true); + setBatchSize.setEnabled(false); + } else { + if (element.getPropertyAsBoolean(ProducerSysTest.TRANSACTED)) { + setTransacted.setSelected(true); + setNonTransacted.setSelected(false); + setBatchSize.setEnabled(true); + setBatchSize.setText(element.getPropertyAsString(ProducerSysTest.BATCHSIZE)); + } else { + setTransacted.setSelected(false); + setNonTransacted.setSelected(true); + setBatchSize.setEnabled(false); + setBatchSize.setText(""); + } + } + */ + + } + + /** + * Creates a test element. + * + * @return element + */ + public TestElement createTestElement() { + ConfigTestElement element = new ConfigTestElement(); + modifyTestElement(element); + + return element; + } + + /** + * Sets the producer sampler properties to the test element. + * + * @param element + */ + public void modifyTestElement(TestElement element) { + configureTestElement(element); + + element.setProperty(ProducerSysTest.URL, setURL.getText()); + //element.setProperty(ProducerSysTest.DURATION, setDuration.getText()); + //element.setProperty(ProducerSysTest.RAMP_UP, setRampUp.getText()); + element.setProperty(ProducerSysTest.DURABLE, JOrphanUtils.booleanToString(setDurable.isSelected())); + element.setProperty(ProducerSysTest.TOPIC, JOrphanUtils.booleanToString(setTopic.isSelected())); + //element.setProperty(ProducerSysTest.MSGSIZE, setMsgSize.getText()); + element.setProperty(ProducerSysTest.NOPRODUCER, setNoProducer.getText()); + element.setProperty(ProducerSysTest.NOSUBJECT, setNoSubject.getText()); + element.setProperty(ProducerSysTest.NOMESSAGES, setNoMessages.getText()); + //element.setProperty(ProducerSysTest.DEFMSGINTERVAL, JOrphanUtils.booleanToString(setDefMsgInterval.isSelected())); + //element.setProperty(ProducerSysTest.MSGINTERVAL, setMsgInterval.getText()); + //element.setProperty(ProducerSysTest.MQSERVER, setMQServer.getSelectedItem().toString()); + //element.setProperty(ProducerSysTest.TRANSACTED, JOrphanUtils.booleanToString(setTransacted.isSelected())); + //element.setProperty(ProducerSysTest.BATCHSIZE, setBatchSize.getText()); + } + + /** + * Creates the URL panel. + * + * @return urlPanel + */ + private JPanel createURLPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_url")); + setURL = new JTextField(10); + setURL.setName(URL); + label.setLabelFor(setURL); + + JPanel urlPanel = new JPanel(new BorderLayout(5, 0)); + urlPanel.add(label, BorderLayout.WEST); + urlPanel.add(setURL, BorderLayout.CENTER); + + return urlPanel; + } + + /** + * Creates the durable panel. + * + * @return durablePanel + */ + private JPanel createDurablePanel() { + JLabel labelDeliveryMode = new JLabel(JMeterUtils.getResString("form_delivery_mode")); + + JLabel labelDurable = new JLabel(JMeterUtils.getResString("form_persistent")); + setDurable = new JRadioButton(); + setDurable.setName(DURABLE); + labelDurable.setLabelFor(setDurable); + setDurable.setActionCommand(DURABLE); + setDurable.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformedDelivery(evt); + } + }); + setDurable.setSelected(false); + + JLabel labelNonDurable = new JLabel(JMeterUtils.getResString("form_non_persistent")); + setNonDurable = new JRadioButton(); + setNonDurable.setName(NONDURABLE); + labelNonDurable.setLabelFor(setNonDurable); + setNonDurable.setActionCommand(NONDURABLE); + setNonDurable.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformedDelivery(evt); + } + }); + setNonDurable.setSelected(true); + + FlowLayout flow = new FlowLayout(FlowLayout.LEFT); + flow.setHgap(0); + flow.setVgap(0); + + JPanel durablePanel = new JPanel(flow); + durablePanel.add(labelDeliveryMode); + durablePanel.add(new JLabel(" ")); + durablePanel.add(setDurable); + durablePanel.add(labelDurable); + durablePanel.add(new JLabel(" ")); + durablePanel.add(setNonDurable); + durablePanel.add(labelNonDurable); + + return durablePanel; + } + + /** + * Creates the topic panel. + * + * @return topicPanel + */ + private JPanel createTopicPanel() { + JLabel labelMessagingDomain = new JLabel(JMeterUtils.getResString("messaging_domain")); + + JLabel labelTopic = new JLabel(JMeterUtils.getResString("form_topic")); + setTopic = new JRadioButton(); + setTopic.setName(TOPIC); + labelTopic.setLabelFor(setTopic); + setTopic.setActionCommand(TOPIC); + setTopic.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformed(evt); + } + }); + setTopic.setSelected(true); + + JLabel labelQueue = new JLabel(JMeterUtils.getResString("form_queue")); + setQueue = new JRadioButton(); + setQueue.setName(QUEUE); + labelQueue.setLabelFor(setQueue); + setQueue.setActionCommand(QUEUE); + setQueue.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jRadioButtonActionPerformed(evt); + } + }); + setQueue.setSelected(false); + + FlowLayout flow = new FlowLayout(FlowLayout.LEFT); + flow.setHgap(0); + flow.setVgap(0); + + JPanel topicPanel = new JPanel(flow); + topicPanel.add(labelMessagingDomain); + topicPanel.add(new JLabel(" ")); + topicPanel.add(setTopic); + topicPanel.add(labelTopic); + topicPanel.add(new JLabel(" ")); + topicPanel.add(setQueue); + topicPanel.add(labelQueue); + + return topicPanel; + } + + /** + * Creates the no prod panel. + * + * @return noProdPanel + */ + private JPanel createNoProducerPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_no_producer")); + setNoProducer = new JTextField(10); + setNoProducer.setName(NOPRODUCER); + label.setLabelFor(setNoProducer); + + JPanel noProdPanel = new JPanel(new BorderLayout(5, 0)); + noProdPanel.add(label, BorderLayout.WEST); + noProdPanel.add(setNoProducer, BorderLayout.CENTER); + + return noProdPanel; + } + + /** + * Creates the number subject panel. + * + * @return noSubjectPanel + */ + private JPanel createNoSubjectPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_no_subject")); + setNoSubject = new JTextField(10); + setNoSubject.setName(NOSUBJECT); + label.setLabelFor(setNoSubject); + + JPanel noSubjectPanel = new JPanel(new BorderLayout(5, 0)); + noSubjectPanel.add(label, BorderLayout.WEST); + noSubjectPanel.add(setNoSubject, BorderLayout.CENTER); + + return noSubjectPanel; + } + + /** + * Creates the number of messages panel. + * + * @return noMessagesPanel + */ + private JPanel createNoMessagesPanel() { + JLabel label = new JLabel(JMeterUtils.getResString("form_no_messages")); + setNoMessages = new JTextField(10); + setNoMessages.setName(NOMESSAGES); + label.setLabelFor(setNoMessages); + + JPanel noMessagesPanel = new JPanel(new BorderLayout(5, 0)); + noMessagesPanel.add(label, BorderLayout.WEST); + noMessagesPanel.add(setNoMessages, BorderLayout.CENTER); + + return noMessagesPanel; + } + + /** + * Initializes the gui components. + */ + private void init() { + setLayout(new BorderLayout(0, 5)); + + if (displayName) { + setBorder(makeBorder()); + add(makeTitlePanel(), BorderLayout.NORTH); + } + + VerticalPanel mainPanel = new VerticalPanel(); + + mainPanel.add(createURLPanel()); + //mainPanel.add(createDurationPanel()); + //mainPanel.add(createRampUpPanel()); + mainPanel.add(createNoProducerPanel()); + mainPanel.add(createNoSubjectPanel()); + mainPanel.add(createNoMessagesPanel()); + //mainPanel.add(createMsgSizePanel()); + mainPanel.add(createDurablePanel()); + mainPanel.add(createTopicPanel()); + //mainPanel.add(createTransactedPanel()); + //mainPanel.add(createDefMsgIntervalPanel()); + //mainPanel.add(createMQServerPanel()); + + add(mainPanel, BorderLayout.CENTER); + } + + /** + * Listener action for selecting Messaging Domain. + * + * @param evt - event triggered. + */ + private void jRadioButtonActionPerformed(ActionEvent evt) { + String evtActionCommand = evt.getActionCommand(); + + if (evtActionCommand.equals(TOPIC)) { + setTopic.setSelected(true); + setQueue.setSelected(false); + } else if (evtActionCommand.equals(QUEUE)) { + setTopic.setSelected(false); + setQueue.setSelected(true); + } + } + + /** + * Listener action for selecting Delivery Mode. + * + * @param evt - event triggered. + */ + private void jRadioButtonActionPerformedDelivery(ActionEvent evt) { + String evtActionCommand = evt.getActionCommand(); + + if (evtActionCommand.equals(DURABLE)) { + setDurable.setSelected(true); + setNonDurable.setSelected(false); + } else if (evtActionCommand.equals(NONDURABLE)) { + setDurable.setSelected(false); + setNonDurable.setSelected(true); + } + } + +} diff --git a/jmeter/src/java/org/activemq/sampler/control/gui/ConsumerSamplerGui.java b/jmeter/src/java/org/activemq/sampler/control/gui/ConsumerSamplerGui.java new file mode 100755 index 0000000000..9191323f45 --- /dev/null +++ b/jmeter/src/java/org/activemq/sampler/control/gui/ConsumerSamplerGui.java @@ -0,0 +1,112 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.sampler.control.gui; + +import org.apache.jmeter.gui.util.VerticalPanel; +import org.apache.jmeter.samplers.gui.AbstractSamplerGui; +import org.apache.jmeter.testelement.TestElement; +import org.activemq.sampler.config.gui.ConsumerConfigGui; +import org.activemq.sampler.Consumer; +import java.awt.BorderLayout; + +/** + * Form in JMeter to enter default values for generating the sampler set. + */ +public class ConsumerSamplerGui extends AbstractSamplerGui { + + //private LoginConfigGui loginPanel; + private ConsumerConfigGui TcpDefaultPanel; + + /** + * Constructor for the ConsumerSamplerGui object + */ + public ConsumerSamplerGui() { + + init(); + } + + /** + * Method for configuring the COnsumerSamplerGui + * + * @param element + */ + public void configure(TestElement element) { + + super.configure(element); + //loginPanel.configure(element); + TcpDefaultPanel.configure(element); + } + + /** + * Method for creating test elements + * + * @return returns a sampler + */ + public TestElement createTestElement() { + + Consumer sampler = new Consumer(); + modifyTestElement(sampler); + return sampler; + } + + /** + * Method to modify test elements + * + * @param sampler + */ + public void modifyTestElement(TestElement sampler) { + + sampler.clear(); + ((Consumer) sampler).addTestElement(TcpDefaultPanel.createTestElement()); + //((Consumer) sampler).addTestElement(loginPanel.createTestElement()); + this.configureTestElement(sampler); + } + + /** + * Getter method for the LabelResource property. + * + * @return String constant "consumer_sample_title" + */ + public String getLabelResource() { + + return "consumer_sample_title"; + } + + /** + * Method to initialize ConsumerSamplerGui. Sets up the layout of the GUI. + */ + private void init() { + + setLayout(new BorderLayout(0, 5)); + setBorder(makeBorder()); + + add(makeTitlePanel(), BorderLayout.NORTH); + + VerticalPanel mainPanel = new VerticalPanel(); + + TcpDefaultPanel = new ConsumerConfigGui(false); + mainPanel.add(TcpDefaultPanel); + + //loginPanel = new LoginConfigGui(false); + //loginPanel.setBorder(BorderFactory.createTitledBorder(JMeterUtils.getResString("login_config"))); + //mainPanel.add(loginPanel); + + add(mainPanel, BorderLayout.CENTER); + } + +} diff --git a/jmeter/src/java/org/activemq/sampler/control/gui/ConsumerSysTestSamplerGui.java b/jmeter/src/java/org/activemq/sampler/control/gui/ConsumerSysTestSamplerGui.java new file mode 100644 index 0000000000..6c6a995672 --- /dev/null +++ b/jmeter/src/java/org/activemq/sampler/control/gui/ConsumerSysTestSamplerGui.java @@ -0,0 +1,113 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.sampler.control.gui; + +import org.apache.jmeter.gui.util.VerticalPanel; +import org.apache.jmeter.samplers.gui.AbstractSamplerGui; +import org.apache.jmeter.testelement.TestElement; +import org.activemq.sampler.config.gui.ConsumerSysTestConfigGui; +import org.activemq.sampler.ConsumerSysTest; + +import java.awt.BorderLayout; + +/** + * Form in JMeter to enter default values for generating the sampler set. + */ +public class ConsumerSysTestSamplerGui extends AbstractSamplerGui { + + //private LoginConfigGui loginPanel; + private ConsumerSysTestConfigGui TcpDefaultPanel; + + /** + * Constructor for the ConsumerSamplerGui object + */ + public ConsumerSysTestSamplerGui() { + + init(); + } + + /** + * Method for configuring the COnsumerSamplerGui + * + * @param element + */ + public void configure(TestElement element) { + + super.configure(element); + //loginPanel.configure(element); + TcpDefaultPanel.configure(element); + } + + /** + * Method for creating test elements + * + * @return returns a sampler + */ + public TestElement createTestElement() { + + ConsumerSysTest sampler = new ConsumerSysTest(); + modifyTestElement(sampler); + return sampler; + } + + /** + * Method to modify test elements + * + * @param sampler + */ + public void modifyTestElement(TestElement sampler) { + + sampler.clear(); + ((ConsumerSysTest) sampler).addTestElement(TcpDefaultPanel.createTestElement()); + //((Consumer) sampler).addTestElement(loginPanel.createTestElement()); + this.configureTestElement(sampler); + } + + /** + * Getter method for the LabelResource property. + * + * @return String constant "consumer_sample_title" + */ + public String getLabelResource() { + + return "consumer_sys_test_sample_title"; + } + + /** + * Method to initialize ConsumerSamplerGui. Sets up the layout of the GUI. + */ + private void init() { + + setLayout(new BorderLayout(0, 5)); + setBorder(makeBorder()); + + add(makeTitlePanel(), BorderLayout.NORTH); + + VerticalPanel mainPanel = new VerticalPanel(); + + TcpDefaultPanel = new ConsumerSysTestConfigGui(false); + mainPanel.add(TcpDefaultPanel); + + //loginPanel = new LoginConfigGui(false); + //loginPanel.setBorder(BorderFactory.createTitledBorder(JMeterUtils.getResString("login_config"))); + //mainPanel.add(loginPanel); + + add(mainPanel, BorderLayout.CENTER); + } + +} diff --git a/jmeter/src/java/org/activemq/sampler/control/gui/ProducerSamplerGui.java b/jmeter/src/java/org/activemq/sampler/control/gui/ProducerSamplerGui.java new file mode 100755 index 0000000000..35860f8aee --- /dev/null +++ b/jmeter/src/java/org/activemq/sampler/control/gui/ProducerSamplerGui.java @@ -0,0 +1,116 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.sampler.control.gui; + +import org.apache.jmeter.config.gui.LoginConfigGui; +import org.apache.jmeter.gui.util.VerticalPanel; +import org.apache.jmeter.samplers.gui.AbstractSamplerGui; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.util.JMeterUtils; +import org.activemq.sampler.config.gui.ProducerConfigGui; +import org.activemq.sampler.Producer; + +import javax.swing.BorderFactory; + +import java.awt.BorderLayout; + +/** + * Form in JMeter to enter default values for generating the sampler set. + */ +public class ProducerSamplerGui extends AbstractSamplerGui { + + //private LoginConfigGui loginPanel; + private ProducerConfigGui TcpDefaultPanel; + + /** + * Constructor for the ProducerSamplerGui object + */ + public ProducerSamplerGui() { + + init(); + } + + /** + * Method for configuring the ProducerSamplerGui + * + * @param element + */ + public void configure(TestElement element) { + + super.configure(element); + //loginPanel.configure(element); + TcpDefaultPanel.configure(element); + } + + /** + * Method for creating test elements + * + * @return returns a sampler + */ + public TestElement createTestElement() { + + Producer sampler = new Producer(); + modifyTestElement(sampler); + return sampler; + } + + /** + * Method to modify test elements + * + * @param sampler + */ + public void modifyTestElement(TestElement sampler) { + + sampler.clear(); + ((Producer) sampler).addTestElement(TcpDefaultPanel.createTestElement()); + //((Producer) sampler).addTestElement(loginPanel.createTestElement()); + this.configureTestElement(sampler); + } + + /** + * Getter method for the LabelResource property. + * + * @return String constant "producer_sample_title" + */ + public String getLabelResource() { + + return "producer_sample_title"; + } + + /** + * Method to initialize ProducerSamplerGui. Sets up the layout of the GUI. + */ + private void init() { + + setLayout(new BorderLayout(0, 5)); + setBorder(makeBorder()); + + add(makeTitlePanel(), BorderLayout.NORTH); + + VerticalPanel mainPanel = new VerticalPanel(); + + TcpDefaultPanel = new ProducerConfigGui(false); + mainPanel.add(TcpDefaultPanel); + + //loginPanel = new LoginConfigGui(false); + //loginPanel.setBorder(BorderFactory.createTitledBorder(JMeterUtils.getResString("login_config"))); + //mainPanel.add(loginPanel); + + add(mainPanel, BorderLayout.CENTER); + } +} diff --git a/jmeter/src/java/org/activemq/sampler/control/gui/ProducerSysTestSamplerGui.java b/jmeter/src/java/org/activemq/sampler/control/gui/ProducerSysTestSamplerGui.java new file mode 100644 index 0000000000..78ade41371 --- /dev/null +++ b/jmeter/src/java/org/activemq/sampler/control/gui/ProducerSysTestSamplerGui.java @@ -0,0 +1,118 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.sampler.control.gui; + +import org.apache.jmeter.config.gui.LoginConfigGui; +import org.apache.jmeter.gui.util.VerticalPanel; +import org.apache.jmeter.samplers.gui.AbstractSamplerGui; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.util.JMeterUtils; +import org.activemq.sampler.config.gui.ProducerConfigGui; +import org.activemq.sampler.config.gui.ProducerSysTestConfigGui; +import org.activemq.sampler.Producer; +import org.activemq.sampler.ProducerSysTest; + +import javax.swing.BorderFactory; + +import java.awt.BorderLayout; + +/** + * Form in JMeter to enter default values for generating the sampler set. + */ +public class ProducerSysTestSamplerGui extends AbstractSamplerGui { + + //private LoginConfigGui loginPanel; + private ProducerSysTestConfigGui TcpDefaultPanel; + + /** + * Constructor for the ProducerSamplerGui object + */ + public ProducerSysTestSamplerGui() { + + init(); + } + + /** + * Method for configuring the ProducerSamplerGui + * + * @param element + */ + public void configure(TestElement element) { + + super.configure(element); + //loginPanel.configure(element); + TcpDefaultPanel.configure(element); + } + + /** + * Method for creating test elements + * + * @return returns a sampler + */ + public TestElement createTestElement() { + + ProducerSysTest sampler = new ProducerSysTest(); + modifyTestElement(sampler); + return sampler; + } + + /** + * Method to modify test elements + * + * @param sampler + */ + public void modifyTestElement(TestElement sampler) { + + sampler.clear(); + ((ProducerSysTest) sampler).addTestElement(TcpDefaultPanel.createTestElement()); + //((Producer) sampler).addTestElement(loginPanel.createTestElement()); + this.configureTestElement(sampler); + } + + /** + * Getter method for the LabelResource property. + * + * @return String constant "producer_sample_title" + */ + public String getLabelResource() { + + return "producer_sys_test_sample_title"; + } + + /** + * Method to initialize ProducerSamplerGui. Sets up the layout of the GUI. + */ + private void init() { + + setLayout(new BorderLayout(0, 5)); + setBorder(makeBorder()); + + add(makeTitlePanel(), BorderLayout.NORTH); + + VerticalPanel mainPanel = new VerticalPanel(); + + TcpDefaultPanel = new ProducerSysTestConfigGui(false); + mainPanel.add(TcpDefaultPanel); + + //loginPanel = new LoginConfigGui(false); + //loginPanel.setBorder(BorderFactory.createTitledBorder(JMeterUtils.getResString("login_config"))); + //mainPanel.add(loginPanel); + + add(mainPanel, BorderLayout.CENTER); + } +} diff --git a/jmeter/src/java/org/activemq/util/MergeProperties.java b/jmeter/src/java/org/activemq/util/MergeProperties.java new file mode 100755 index 0000000000..dacb9fb585 --- /dev/null +++ b/jmeter/src/java/org/activemq/util/MergeProperties.java @@ -0,0 +1,181 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.util; + +/** + * + * Task to merge the Jmeter properties + * + */ + +import java.io.File; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.io.FileOutputStream; + + + +/** + * This class is used by an ant task that will merge the jmeter property files. + * + */ +public class MergeProperties { + + // The file to merge properties into + protected File baseFile; + // The file to pull the properties from + protected File mergeFile; + + public File getBaseFile() { + return baseFile; + } + + public void setBaseFile(File baseFile) { + this.baseFile = baseFile; + } + + public File getMergeFile() { + return mergeFile; + } + + public void setMergeFile(File mergeFile) { + this.mergeFile = mergeFile; + } + + public void mergePropertyFiles() throws FileNotFoundException, IOException { + + if (!getBaseFile().exists()) { + throw new FileNotFoundException("Could not find file:" + getBaseFile()); + } + + if (!getMergeFile().exists()) { + throw new FileNotFoundException("Could not find file:" + getMergeFile()); + } + + BufferedReader mergeReader = new BufferedReader(new FileReader(mergeFile)); + BufferedReader baseReader = new BufferedReader(new FileReader(baseFile)); + + ArrayList baseList = new ArrayList(1024); + ArrayList mergeList = new ArrayList(1024); + + String line = null; + while ((line = mergeReader.readLine()) != null) { + mergeList.add(line); + } + while ((line = baseReader.readLine()) != null) { + baseList.add(line); + } + + if(previouslyMerged(baseList, mergeList)){ + + for(int i = 0; i < mergeList.size(); i++){ + int j = baseList.indexOf(mergeList.get(i)); + + if(j == -1){ + //check if key of mergeList.get(i) is in baseList + String mergeKey = (String) mergeList.get(i); + + if(mergeKey.indexOf('=') > -1){ + mergeKey = getKey(mergeKey); + } + + for(int k=0; k < baseList.size(); k++){ + String baseKey = (String) baseList.get(k); + + if(baseKey.indexOf('=') > -1){ + baseKey = getKey(baseKey); + } + + if (mergeKey.equals(baseKey)){ + j=k; + } + } + + if(j > -1){ + baseList.set(j, mergeList.get(i)); + } else { + baseList.add(mergeList.get(i)); + } + } + } + } else { + baseList.addAll((java.util.Collection) mergeList); + } + // write baseList to file + FileOutputStream writer = new FileOutputStream(baseFile); + writer.flush(); + for (int i = 0; i < baseList.size(); i++) + { + //System.out.println((String) baseList.get(i)); + writer.write(((String) baseList.get(i)).getBytes()); + writer.write(System.getProperty("line.separator", "\r\n").getBytes()); + writer.flush(); + } + + baseReader.close(); + mergeReader.close(); + writer.close(); + + } + + public static void main(String[] args) throws Exception { + MergeProperties mergeProperties = new MergeProperties(); + + try + { + if (args.length < 2) + { + System.out.println("Usage: java OverwriteProperties c:/temp/File1.props c:/temp/File2.props"); + System.out.println("Usage: File1 will be modified, new parameters from File 2 will be added,"); + throw new Exception("Incorrect number of arguments supplied"); + } + mergeProperties.setBaseFile(new File(args[0])); + mergeProperties.setMergeFile(new File(args[1])); + + mergeProperties.mergePropertyFiles(); + + } + catch (FileNotFoundException ex) + { + System.err.println(ex.getMessage()); + } + catch (IOException ex) + { + System.err.println(ex.getMessage()); + } + catch (SecurityException ex) + { + System.err.println(ex.getMessage()); + } + } + + public boolean previouslyMerged(ArrayList base, ArrayList merge) { + + return (base.contains(merge.get(0))); + } + + public String getKey(String prop) { + + return (prop.substring(0, prop.indexOf('='))); + } + +} diff --git a/jmeter/src/java/org/activemq/util/ant/MergePropertiesTask.java b/jmeter/src/java/org/activemq/util/ant/MergePropertiesTask.java new file mode 100755 index 0000000000..a9922894ea --- /dev/null +++ b/jmeter/src/java/org/activemq/util/ant/MergePropertiesTask.java @@ -0,0 +1,125 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.util.ant; + +/** + * + *MergePropertiesTask is the task definition for an Ant + * interface to the MergeProperties. + */ + +import java.io.File; + +import org.apache.tools.ant.Task; +import org.apache.tools.ant.BuildException; + +import org.activemq.util.MergeProperties; + + +public class MergePropertiesTask extends Task{ + + /** File to merge properties into */ + private File mergeBaseProperties; + + /** File to merge properties from */ + private File mergeProperties; + + /** Fail on error flag */ + private boolean failonerror = true; + + /** + * Sets the File to merge properties into + * + * @param mergeBaseProperties + * File to merge properties into + */ + + public void setMergeBaseProperties(File mergeBaseProperties) + { + this.mergeBaseProperties = mergeBaseProperties; + } + + /** + * Sets the File to merge properties from + * + * @param mergeProperties File to merge properties from + */ + + public void setMergeProperties(File mergeProperties) + { + this.mergeProperties = mergeProperties; + } + + public void setFailOnError(boolean failonerror) + { + this.failonerror = failonerror; + } + + /** + * Gets the File to merge properties into + * + * @return File to merge properties into + */ + public File getMergeBaseProperties() + { + + return mergeBaseProperties; + } + + /** + * Gets the File to merge properties from + * + * @return File to merge properties from + */ + public File getMergeProperties() + { + + return mergeProperties; + } + + /** + * Load the step and then execute it + * + * @exception BuildException + * Description of the Exception + */ + public void execute() throws BuildException + { + + try + { + MergeProperties mergeProperties = new MergeProperties(); + mergeProperties.setBaseFile(getMergeBaseProperties()); + mergeProperties.setMergeFile(getMergeProperties()); + + mergeProperties.mergePropertyFiles(); + } + catch (Exception e) + { + if (!this.failonerror) + { + log(e.toString()); + } + else + { + throw new BuildException(e.toString()); + } + } + } +} \ No newline at end of file diff --git a/jmeter/src/java/org/activemq/util/connection/ServerConnectionFactory.java b/jmeter/src/java/org/activemq/util/connection/ServerConnectionFactory.java new file mode 100755 index 0000000000..32c13bd796 --- /dev/null +++ b/jmeter/src/java/org/activemq/util/connection/ServerConnectionFactory.java @@ -0,0 +1,404 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.util.connection; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.ActiveMQConnection; +import org.mr.api.jms.MantaTopicConnectionFactory; +import org.mr.api.jms.MantaQueueConnectionFactory; + +import org.apache.jmeter.util.JMeterUtils; + +import javax.jms.Connection; +import javax.jms.Session; +import javax.jms.JMSException; +import javax.jms.Topic; +import javax.jms.Queue; +import javax.jms.ConnectionFactory; +import javax.jms.TopicConnectionFactory; +import javax.jms.QueueConnectionFactory; +import javax.jms.TopicSession; +import javax.jms.QueueSession; +import javax.jms.Destination; +import javax.jms.TopicConnection; +import javax.jms.QueueConnection; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Properties; + +/** + * Provides static methods for creating Session and Destination objects. + */ +public class ServerConnectionFactory { + + public static final String SONICMQ_SERVER = JMeterUtils.getResString("sonicmq_server"); + public static final String TIBCOMQ_SERVER = JMeterUtils.getResString("tibcomq_server"); + public static final String JBOSSMQ_SERVER = JMeterUtils.getResString("jbossmq_server"); + public static final String OPENJMS_SERVER = JMeterUtils.getResString("openjms_server"); + public static final String JORAM_SERVER = JMeterUtils.getResString("joram_server"); + public static final String JORAM_CONNECTION_FACTORY = JMeterUtils.getResString("joram_connection_factory"); + public static final String JORAM_USERNAME = JMeterUtils.getResString("joram_username"); + public static final String JORAM_PASSWORD = JMeterUtils.getResString("joram_password"); + public static final String JORAM_NAMING_PORT = JMeterUtils.getResString("joram_naming_port"); + public static final String MANTARAY_SERVER = JMeterUtils.getResString("mantaray_server"); + + // For testing within IntelliJ running main() + /* + public static final String SONICMQ_SERVER = "Sonic Server"; + public static final String TIBCOMQ_SERVER = "Tibco Server"; + public static final String JBOSSMQ_SERVER = "JbossMQ Server"; + public static final String OPENJMS_SERVER = "OpenJMS Server"; + public static final String ACTIVEMQ_SERVER = "ActiveMQ Server"; + public static final String JORAM_SERVER = "Joram Server"; + public static final String JORAM_CONNECTION_FACTORY = "!cf"; + public static final String JORAM_USERNAME = "root"; + public static final String JORAM_PASSWORD = "root"; + public static final String JORAM_NAMING_PORT = "16400"; + public static final String MANTARAY_SERVER = "Mantaray"; + */ + + public static final String SONICMQ_TOPIC = "progress.message.jclient.TopicConnectionFactory"; + public static final String SONICMQ_QUEUE = "progress.message.jclient.QueueConnectionFactory"; + public static final String TIBCOMQ_TOPIC = "com.tibco.tibjms.TibjmsTopicConnectionFactory"; + public static final String TIBCOMQ_QUEUE = "com.tibco.tibjms.TibjmsQueueConnectionFactory"; + public static final String NAMING_CONTEXT = "org.jnp.interfaces.NamingContextFactory"; + public static final String JNP_INTERFACES = "org.jnp.interfaces"; + public static final String OPENJMS_NAMING_CONTEXT = "org.exolab.jms.jndi.InitialContextFactory"; + public static final String OPENJMS_TOPIC = "TcpTopicConnectionFactory"; + public static final String OPENJMS_QUEUE = "TcpQueueConnectionFactory"; + public static final String JORAM_NAMING_CONTEXT = "fr.dyade.aaa.jndi2.client.NamingContextFactory"; + public static final String JORAM_TOPIC = "TopicConnectionFactory"; + public static final String JORAM_QUEUE = "QueueConnectionFactory"; + public static final String NAMING_HOST = "java.naming.factory.host"; + public static final String NAMING_PORT = "java.naming.factory.post"; + + public static Topic topicContext; + + private static int mantarayProducerPortCount = 0; + private static int mantarayConsumerPortCount = 0; + + /** + * Closes the connection passed through the parameter + * + * @param connection - Connection object to be closed. + * @param session - Session object to be closed. + * @throws JMSException + */ + public static void close(Connection connection, Session session) throws JMSException { + session.close(); + connection.close(); + } + + /** + * Dynamically creates a Connection object based on the type of broker. + * + * @param url - location of the broker. + * @param mqServer - type of broker that is running. + * @param isTopic - type of message domain. + * @param embeddedBroker - specified is the broker is embedded. + * @return + * @throws JMSException + */ + public static Connection createConnectionFactory(String url, + String mqServer, + boolean isTopic, + boolean embeddedBroker) throws JMSException { + if (SONICMQ_SERVER.equals(mqServer)) { + //Creates a Connection object for a SONIC MQ server. + if (isTopic) { + return createConnectionFactory(url, SONICMQ_TOPIC); + } else { + return createConnectionFactory(url, SONICMQ_QUEUE); + } + } else if (TIBCOMQ_SERVER.equals(mqServer)) { + //Creates a Connection object for a TIBCO MQ server. + if (isTopic) { + return createConnectionFactory(url, TIBCOMQ_TOPIC); + } else { + return createConnectionFactory(url, TIBCOMQ_QUEUE); + } + } else if (JBOSSMQ_SERVER.equals(mqServer)) { + //Creates a Connection object for a JBoss MQ server. + try { + InitialContext context = getInitialContext(url, JBOSSMQ_SERVER); + ConnectionFactory factory = (ConnectionFactory) context.lookup("ConnectionFactory"); + context.close(); + + return factory.createConnection(); + + } catch (NamingException e) { + throw new JMSException("Error creating InitialContext ", e.toString()); + } + } else if (OPENJMS_SERVER.equals(mqServer)) { + //Creates a Connection object for a OpenJMS server. + try { + Context context = getInitialContext(url, OPENJMS_SERVER); + if (isTopic) { + TopicConnectionFactory factory = (TopicConnectionFactory) + context.lookup(OPENJMS_TOPIC); + context.close(); + + return factory.createTopicConnection(); + + } else { + QueueConnectionFactory factory = (QueueConnectionFactory) + context.lookup(OPENJMS_QUEUE); + context.close(); + + return factory.createQueueConnection(); + + } + } catch (NamingException e) { + throw new JMSException("Error creating InitialContext ", e.toString()); + } + } else if (JORAM_SERVER.equals(mqServer)) { + //Creates a Connection object for a JORAM server. + try { + Context ictx = getInitialContext(url, JORAM_SERVER); + ConnectionFactory cf = (ConnectionFactory) ictx.lookup(JORAM_CONNECTION_FACTORY); + ictx.close(); + Connection cnx = cf.createConnection(JORAM_USERNAME, JORAM_PASSWORD); + + return cnx; + + } catch (NamingException e) { + throw new JMSException("Error creating InitialContext ", e.toString()); + } + } else if (MANTARAY_SERVER.equals(mqServer)) { + //Creates a Connection object for a Mantaray. + System.setProperty("mantaHome",url); + + if (isTopic) { + TopicConnectionFactory factory = (TopicConnectionFactory) new MantaTopicConnectionFactory(); + + return factory.createTopicConnection(); + + } else { + QueueConnectionFactory factory = (QueueConnectionFactory) new MantaQueueConnectionFactory(); + + return factory.createQueueConnection(); + + } + } else { + //Used to create a session from the default MQ server ActiveMQConnectionFactory. + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(url); + factory.setUseAsyncSend(true); + + if (embeddedBroker) { + factory.setUseEmbeddedBroker(true); + } + + ActiveMQConnection c = (ActiveMQConnection) factory.createConnection(); + + c.getPrefetchPolicy().setQueuePrefetch(1000); + c.getPrefetchPolicy().setQueueBrowserPrefetch(1000); + c.getPrefetchPolicy().setTopicPrefetch(1000); + c.getPrefetchPolicy().setDurableTopicPrefetch(1000); + + return c; + + } + + } + + /** + * Creates a Destination object through Session using subject. + * + * @param session - Session used to create the Destination. + * @param subject - the subject of the Destination to be created. + * @param mqServer - ype of broker that is running. + * @param url - location of the broker. + * @param isTopic - specified is the broker is embedded. + * @return + * @throws JMSException + */ + public static Destination createDestination(Session session, + String subject, + String url, + String mqServer, + boolean isTopic) throws JMSException { + if (JBOSSMQ_SERVER.equals(mqServer)) { + try { + if (isTopic) { + return (Topic) getInitialContext(url, JBOSSMQ_SERVER).lookup("topic/" + subject); + } else { + return (Queue) getInitialContext(url, JBOSSMQ_SERVER).lookup("queue/" + subject); + } + } catch (NamingException e) { + throw new JMSException("Error on lookup for Queue " + subject, e.toString()); + } + } else if (OPENJMS_SERVER.equals(mqServer)) { + if (isTopic) { + return ((TopicSession) session).createTopic(subject); + } else { + return ((QueueSession) session).createQueue(subject); + } + } else if (JORAM_SERVER.equals(mqServer)) { + try { + if (isTopic) { + return (Topic) getInitialContext(url, JORAM_SERVER).lookup(subject); + } else { + return (Queue) getInitialContext(url, JORAM_SERVER).lookup(subject); + } + } catch (NamingException e) { + throw new JMSException("Error on lookup for Queue " + subject, e.toString()); + } + } else { + if (isTopic) { + return session.createTopic(subject); + } else { + return session.createQueue(subject); + } + } + } + + /** + * Creates a Session object. + * + * @param connection - Connection object where the session will be created from. + * @return + * @throws JMSException + */ + public static Session createSession(Connection connection, + boolean isTransacted, + String mqServer, + boolean isTopic) throws JMSException { + if (OPENJMS_SERVER.equals(mqServer) || MANTARAY_SERVER.equals(mqServer)) { + if (isTransacted) { + if (isTopic) { + TopicSession session = ((TopicConnection) connection).createTopicSession(false, Session.SESSION_TRANSACTED); + + return ((Session) session); + + } else { + QueueSession session = ((QueueConnection) connection).createQueueSession(false, Session.SESSION_TRANSACTED); + + return ((Session) session); + + } + } else { + if (isTopic) { + TopicSession session = ((TopicConnection) connection).createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + + return ((Session) session); + + } else { + QueueSession session = ((QueueConnection) connection).createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + + return ((Session) session); + + } + } + } else { + // check when to use Transacted or Non-Transacted type. + if (isTransacted) { + return connection.createSession(true, Session.SESSION_TRANSACTED); + } else { + return connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + } + } + } + + /** + * Dynamically creates a ConnectionFactory object depending on the MQ Factory class. + * + * @param url - location of the broker. + * @param connFactoryClass - fully qualified name of connection factory to be initialized. + * @return + * @throws JMSException + */ + public static Connection createConnectionFactory(String url, String connFactoryClass) throws JMSException { + Class classObject; + Constructor constructor; + Class[] classParameter = {url.getClass()}; + Object[] constArgs = {url}; + + try { + classObject = Class.forName(connFactoryClass); + constructor = classObject.getConstructor(classParameter); + ConnectionFactory factory = (ConnectionFactory) constructor.newInstance(constArgs); + + return factory.createConnection(); + + } catch (ClassNotFoundException e) { + throw new JMSException("Unable to find class ", e.toString()); + } catch (NoSuchMethodException e) { + throw new JMSException("No such getConstructor(Class[] class) method found ", e.toString()); + } catch (InstantiationException e) { + throw new JMSException("Unable to instantiate class ", e.toString()); + } catch (IllegalAccessException e) { + throw new JMSException("Unable to instantiate class ", e.toString()); + } catch (InvocationTargetException e) { + throw new JMSException("Unable to instantiate class ", e.toString()); + } + } + + /** + * Creates an InitialContext object which contains the information of the broker. + * This is used if the broker uses JNDI. + * + * @param url - location of the broker. + * @return + * @throws JMSException + */ + public static InitialContext getInitialContext(String url, String mqServer) throws JMSException { + Properties properties = new Properties(); + + if (JBOSSMQ_SERVER.equals(mqServer)) { + //Creates a Context oject for JBOSS MQ server + properties.put(Context.INITIAL_CONTEXT_FACTORY, NAMING_CONTEXT); + properties.put(Context.URL_PKG_PREFIXES, JNP_INTERFACES); + properties.put(Context.PROVIDER_URL, url); + + } else if (OPENJMS_SERVER.equals(mqServer)) { + //Creates a Context object for OPENJMS server + properties.put(Context.INITIAL_CONTEXT_FACTORY, OPENJMS_NAMING_CONTEXT); + properties.put(Context.PROVIDER_URL, url); + + } else if (JORAM_SERVER.equals(mqServer)) { + //Creates a Context object for JORAM server + //The JNDI's host is set to be the same as with the Joram broker + properties.put(Context.INITIAL_CONTEXT_FACTORY, JORAM_NAMING_CONTEXT); + properties.put(NAMING_HOST, getHost(url)); + properties.put(NAMING_PORT, JORAM_NAMING_PORT); + + } + + try { + return new InitialContext(properties); + } catch (NamingException e) { + throw new JMSException("Error creating InitialContext ", e.toString()); + } + } + + /** + * Returns the host part of the URL. + * + * @param url - location of the broker. + * @return host + */ + private static String getHost(String url) { + return url.substring(url.lastIndexOf("/") + 1, url.lastIndexOf(":")); + } + +} diff --git a/jmeter/src/java/org/apache/jmeter/visualizers/ConsumerSysTableVisualizer.java b/jmeter/src/java/org/apache/jmeter/visualizers/ConsumerSysTableVisualizer.java new file mode 100644 index 0000000000..02805b7197 --- /dev/null +++ b/jmeter/src/java/org/apache/jmeter/visualizers/ConsumerSysTableVisualizer.java @@ -0,0 +1,355 @@ +package org.apache.jmeter.visualizers; + +import org.apache.jmeter.visualizers.gui.AbstractVisualizer; +import org.apache.jmeter.samplers.Clearable; +import org.apache.jmeter.samplers.SampleResult; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.jorphan.gui.ObjectTableModel; +import org.apache.jorphan.gui.layout.VerticalLayout; +import org.apache.jorphan.reflect.Functor; +import org.apache.jorphan.logging.LoggingManager; +import org.apache.log.Logger; + +import org.activemq.sampler.ConsumerSysTest; +import org.activemq.sampler.ProducerSysTest; + +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.JScrollPane; +import javax.swing.JPanel; +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.FlowLayout; +import java.util.*; + +import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap; + +/** + * A tableVisualizer that can display Producer System Test Output. + */ +public class ConsumerSysTableVisualizer extends AbstractVisualizer implements Clearable { + + private static Logger log = LoggingManager.getLoggerForClass(); + public static boolean msgNotOrdered = false; + + private final String[] COLUMNS = new String[]{ + JMeterUtils.getResString("table_visualizer_sample_consumerid"), + JMeterUtils.getResString("table_visualizer_sample_consumerseq_number"), + JMeterUtils.getResString("table_visualizer_sample_prodname"), + JMeterUtils.getResString("table_visualizer_sample_producerseq_number"), + JMeterUtils.getResString("table_visualizer_sample_message")}; + + private ObjectTableModel model = null; + + private JTable table = null; + private JScrollPane tableScrollPanel = null; + private JTextField messageField = null; + + /** + * Constructor for the TableVisualizer object. + */ + public ConsumerSysTableVisualizer() { + super(); + + model = new ObjectTableModel(COLUMNS, + new Functor[]{new Functor("getConsumerID"), + new Functor("getConsumerSeq"), + new Functor("getProdName"), + new Functor("getProducerSeq"), + new Functor("getMsgBody")}, + new Functor[]{null, null, null, null, null}, + new Class[]{String.class, Integer.class, String.class, Integer.class, String.class}); + + init(); + } + + /** + * @return Label key to get from label resource + */ + public String getLabelResource() { + + return "view_cons_sys_results_in_table"; + } + + /** + * @param res SampleResult from the JMeter Sampler + */ + public void add(SampleResult res) { + Thread timer = new Thread() { + public void run() { + timerLoop(); + } + }; + + timer.start(); + } + + /** + * clear/resets the field. + */ + public synchronized void clear() { + log.debug("Clear called", new Exception("Debug")); + model.clearData(); + repaint(); + } + + /** + * Initialize the User Interface + */ + private void init() { + this.setLayout(new BorderLayout()); + + // Main Panel + JPanel mainPanel = new JPanel(); + Border margin = new EmptyBorder(10, 10, 5, 10); + + mainPanel.setBorder(margin); + mainPanel.setLayout(new VerticalLayout(5, VerticalLayout.LEFT)); + + // Name + mainPanel.add(makeTitlePanel()); + + // Set up the table itself + table = new JTable(model); + + // table.getTableHeader().setReorderingAllowed(false); + tableScrollPanel = new JScrollPane(table); + tableScrollPanel.setViewportBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); + + // Set up footer of table which displays the messages + JPanel messagePanel = new JPanel(); + JLabel messageLabel = + new JLabel(JMeterUtils.getResString("graph_results_message")); + messageLabel.setForeground(Color.black); + + messageField = new JTextField(50); + messageField.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + messageField.setEditable(false); + messageField.setForeground(Color.black); + messageField.setBackground(getBackground()); + messagePanel.add(messageLabel); + messagePanel.add(messageField); + + // Set up info Panel table + JPanel tableMsgPanel = new JPanel(); + tableMsgPanel.setLayout(new FlowLayout()); + tableMsgPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + tableMsgPanel.add(messagePanel); + + // Set up the table with footer + JPanel tablePanel = new JPanel(); + tablePanel.setLayout(new BorderLayout()); + tablePanel.add(tableScrollPanel, BorderLayout.CENTER); + tablePanel.add(tableMsgPanel, BorderLayout.SOUTH); + + // Add the main panel and the graph + this.add(mainPanel, BorderLayout.NORTH); + this.add(tablePanel, BorderLayout.CENTER); + } + + /** + * Gets the number of processed messages. + */ + protected synchronized void timerLoop() { + + Map ProducerTextMap = new HashMap(); + Map currentProducerMap = null; + String ProducerName = null; + String MsgBody = null; + String ConsumerName = null; + String ProdSequenceNo = null; + String mapKey = null; + int expectedNoOfMessages = ConsumerSysTest.noOfMessages; + int consumerCount = ConsumerSysTest.ConsumerCount; + boolean dowhile = true; + Map consumerMap = new ConcurrentHashMap(); + Map prodNameMap = new TreeMap(); + Map prodMsgMap = new TreeMap(); + + while (dowhile) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + ConsumerSysTest consumer = new ConsumerSysTest(); + currentProducerMap = Collections.synchronizedMap(consumer.resetProducerMap()); +//System.out.println("CURR MAP = " + currentProducerMap); +// ConsumerSysTest.ProducerMap.clear(); + + if (currentProducerMap.size() == 0) { + dowhile = false; + } + + // Put the map values to another map for parsing. + for (int i = 1; i <= currentProducerMap.size(); i++) { + String ProdMsg = (String) currentProducerMap.get(String.valueOf(i)); +//System.out.println(""); + ProducerName = ProdMsg.substring(0, ProdMsg.indexOf("#")); + MsgBody = ProdMsg.substring(ProdMsg.indexOf("#")+1, ProdMsg.indexOf("#", ProdMsg.indexOf("#")+1)); + ProdSequenceNo = ProdMsg.substring(ProdMsg.indexOf("#", ProdMsg.indexOf("#", ProdMsg.indexOf("#")+1)) + 1, ProdMsg.lastIndexOf("#")); + ConsumerName = ProdMsg.substring(ProdMsg.lastIndexOf("#") +1, ProdMsg.length()); + + if (ConsumerSysTest.destination) { + mapKey = ConsumerName + ProducerName; + } else { + mapKey = ProducerName; + } + + if (ProducerTextMap.containsKey(mapKey)) { + // Increment the counter value + Integer value = (Integer) ProducerTextMap.get(mapKey); + + ProducerTextMap.put(mapKey, new Integer(value.intValue()+1)); + } else { + // Put the Producer Name in the map + ProducerTextMap.put(mapKey, new Integer(1)); + } + + Integer ConsumerSeqID = (Integer) ProducerTextMap.get(mapKey); + Integer ProducerSeqID = Integer.valueOf(ProdSequenceNo); + + if (ConsumerSysTest.destination) { + // Check for duplicate message. + if (ConsumerSeqID.intValue() > expectedNoOfMessages) { + messageField.setText(JMeterUtils.getResString("duplicate_message")); + } else if (MsgBody.equals(ProducerSysTest.LAST_MESSAGE)) { + // Check for message order. + if (ConsumerSeqID.intValue() != expectedNoOfMessages) { + messageField.setText(JMeterUtils.getResString("not_in_order_message")); + } else if (currentProducerMap.size() == i) { + if (messageField.getText().length() == 0) { + messageField.setText(JMeterUtils.getResString("system_test_pass")); + } + } + } + } else { + //Create map for each consumer + for (int j = 0 ; j < consumerCount ; j++) { + if (!consumerMap.containsKey(new String(ConsumerName))) { + consumerMap.put(new String(ConsumerName), new LinkedHashMap()); + } + } + + //create Producer Name Map + if (!prodNameMap.containsKey(ProducerName)) { + prodNameMap.put(ProducerName, (null)); + } + + //Get the current size of consumer + int seqVal = 0; + Object[] cObj = consumerMap.keySet().toArray(); + for (int k = 0; k < cObj.length; k++) { + String cMapKey = (String)cObj[k]; + Map cMapVal = (Map)consumerMap.get(cObj[k]); + if (cMapKey.equals(ConsumerName)) { + seqVal = cMapVal.size(); + break; + } + } + + //Put object to its designated consumer map + Object[] consumerObj = consumerMap.keySet().toArray(); + for (int j = 0; j < consumerObj.length; j++) { + String cMapKey = (String)consumerObj[j]; + Map cMapVal = (LinkedHashMap)consumerMap.get(consumerObj[j]); + if (cMapKey.equals(ConsumerName)) { + cMapVal.put(new Integer(seqVal), (ProducerName + "/" + ProducerSeqID)); + } + } + } + + // Add data to table row + if (ConsumerSysTest.destination) { + SystemTestMsgSample msgSample = new SystemTestMsgSample(ConsumerName, ProducerName, MsgBody, ProducerSeqID, ConsumerSeqID); + model.addRow(msgSample); + } else { + String msgKey = ConsumerName + "#" + ProducerName + "#" + String.valueOf(ProducerSeqID); + String msgVal = String.valueOf(ConsumerSeqID) + "#" + MsgBody; + if (!prodMsgMap.containsKey(msgKey)) { + prodMsgMap.put((msgKey), (msgVal)); + } + } + } + } + if (!ConsumerSysTest.destination) { + //Validate message sequence + validateMsg(prodNameMap, consumerMap); + //Populate msg sample + populateMsgSample(prodMsgMap); + if (msgNotOrdered) { + messageField.setText(JMeterUtils.getResString("not_in_order_message")); + } else { + messageField.setText(JMeterUtils.getResString("system_test_pass")); + } + } + } + + private boolean validateMsg(Map prodNameMap, Map cMap) { + Object[] cObj = cMap.keySet().toArray(); + for (int j = 0; j < cObj.length; j++) { + Map childMap = (Map)cMap.get(cObj[j]); + + Object[] nameObj = prodNameMap.keySet().toArray(); + for (int i = 0; i < nameObj.length; i++) { + String prodName = (String)nameObj[i]; + String tempProdHolder = null; + String tempProdIDHolder = null; + + Object[] childObj = childMap.keySet().toArray(); + for (int k = 0; k < childObj.length; k++) { + Integer childMapKey = (Integer)childObj[k]; + String childMapVal = (String)childMap.get(childObj[k]); + String prodVal = childMapVal.substring(0, childMapVal.indexOf("/")); + String prodIDVal = childMapVal.substring(childMapVal.indexOf("/")+1, childMapVal.length()); + + if (prodVal.equals(prodName)) { + if (tempProdHolder == null) { + tempProdHolder = prodVal; + tempProdIDHolder = prodIDVal; + continue; + } + if (Integer.parseInt(prodIDVal) > Integer.parseInt(tempProdIDHolder)) { + tempProdHolder = prodVal; + tempProdIDHolder = prodIDVal; + } else { + msgNotOrdered = true; + break; + } + } else { + continue; + } + } + } + } + return msgNotOrdered; + } + + private void populateMsgSample(Map msgMap) { + Object[] msgObj = msgMap.keySet().toArray(); + for(int i = 0; i < msgObj.length; i++) { + String mapKey = (String)msgObj[i]; + String mapVal = (String)msgMap.get(msgObj[i]); + + String ConsumerName = mapKey.substring(0, mapKey.indexOf("#")); + String ProducerName = mapKey.substring(mapKey.indexOf("#")+1, mapKey.indexOf("#", mapKey.lastIndexOf("#"))); + String ProdSequenceNo = mapKey.substring(mapKey.lastIndexOf("#")+1, mapKey.length());; + String MsgKey = mapVal.substring(0, mapVal.indexOf("#")); + String MsgBody = mapVal.substring(mapVal.indexOf("#")+1, mapVal.length()); + + Integer ConsumerSeqID = Integer.valueOf(MsgKey); + Integer ProducerSeqID = Integer.valueOf(ProdSequenceNo); + + SystemTestMsgSample msgSample = new SystemTestMsgSample(ConsumerName, ProducerName, MsgBody, ProducerSeqID, ConsumerSeqID); + model.addRow(msgSample); + } + } + +} \ No newline at end of file diff --git a/jmeter/src/java/org/apache/jmeter/visualizers/ConsumerTableVisualizer.java b/jmeter/src/java/org/apache/jmeter/visualizers/ConsumerTableVisualizer.java new file mode 100755 index 0000000000..c8186d0f61 --- /dev/null +++ b/jmeter/src/java/org/apache/jmeter/visualizers/ConsumerTableVisualizer.java @@ -0,0 +1,255 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.jmeter.visualizers; + +import org.apache.jmeter.samplers.Clearable; +import org.apache.jmeter.samplers.SampleResult; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.jmeter.visualizers.gui.AbstractVisualizer; +import org.apache.jorphan.gui.ObjectTableModel; +import org.apache.jorphan.gui.layout.VerticalLayout; +import org.apache.jorphan.logging.LoggingManager; +import org.apache.jorphan.reflect.Functor; +import org.apache.log.Logger; +import org.activemq.sampler.Consumer; + +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.JScrollPane; +import javax.swing.JPanel; +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.FlowLayout; +import java.text.DecimalFormat; + +/** + * A tableVisualizer that can display Consumer Output + */ +public class ConsumerTableVisualizer extends AbstractVisualizer implements Clearable { + + private static final DecimalFormat dFormat = new DecimalFormat("#,###,###"); + + private static Logger log = LoggingManager.getLoggerForClass(); + + private final String[] COLUMNS = new String[]{ + JMeterUtils.getResString("table_visualizer_sample_num"), + JMeterUtils.getResString("table_visualizer_sample"), + JMeterUtils.getResString("table_visualizer_processed")}; + + private ObjectTableModel model = null; + + private JTable table = null; + + private JTextField averageField = null; + private JTextField totalMsgsField = null; + + private JScrollPane tableScrollPanel = null; + + private double average = 0; + private int total = 0; + + private final long SECOND = 1000; + private final long INSECONDS = 60; + private final long MINUTE = SECOND * INSECONDS; + + + /** + * Constructor for the TableVisualizer object. + */ + public ConsumerTableVisualizer() { + + super(); + + model = new ObjectTableModel(COLUMNS, + new Functor[]{new Functor("getCount"), + new Functor("getData"), + new Functor("getProcessed")}, + new Functor[]{null, null, null}, + new Class[]{Long.class, Long.class, Long.class}); + + init(); + + } + + /** + * @return Label key to get from label resource + */ + public String getLabelResource() { + + return "view_cons_results_in_table"; + } + + /** + * Sets the average Field and total Messages sent, that would be displayed. + */ + protected synchronized void updateTextFields() { + averageField.setText(dFormat.format(average)); + totalMsgsField.setText(dFormat.format(total)); + } + + /** + * @param res SampleResult from the JMeter Sampler + */ + public void add(SampleResult res) { + Thread timer = new Thread() { + public void run() { + timerLoop(); + } + }; + + timer.start(); + } + + /** + * clear/resets the field. + */ + public synchronized void clear() { + log.debug("Clear called", new Exception("Debug")); + model.clearData(); + averageField.setText("0000"); + totalMsgsField.setText("0000"); + repaint(); + } + + /** + * Initialize the User Interface + */ + private void init() { + this.setLayout(new BorderLayout()); + + // Main Panel + JPanel mainPanel = new JPanel(); + Border margin = new EmptyBorder(10, 10, 5, 10); + + mainPanel.setBorder(margin); + mainPanel.setLayout(new VerticalLayout(5, VerticalLayout.LEFT)); + + // Name + mainPanel.add(makeTitlePanel()); + + // Set up the table itself + table = new JTable(model); + + // table.getTableHeader().setReorderingAllowed(false); + tableScrollPanel = new JScrollPane(table); + tableScrollPanel.setViewportBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); + + // Set up footer of table which displays numerics of the graphs + JPanel averagePanel = new JPanel(); + JLabel averageLabel = + new JLabel(JMeterUtils.getResString("graph_results_average")); + averageLabel.setForeground(Color.black); + averageField = new JTextField(15); + averageField.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + averageField.setEditable(false); + averageField.setForeground(Color.black); + averageField.setBackground(getBackground()); + averagePanel.add(averageLabel); + averagePanel.add(averageField); + + JPanel totalMsgsPanel = new JPanel(); + JLabel totalMsgsLabel = + new JLabel(JMeterUtils.getResString("graph_results_total_msgs")); + totalMsgsLabel.setForeground(Color.black); + totalMsgsField = new JTextField(15); + totalMsgsField.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + totalMsgsField.setEditable(false); + totalMsgsField.setForeground(Color.black); + totalMsgsField.setBackground(getBackground()); + totalMsgsPanel.add(totalMsgsLabel); + totalMsgsPanel.add(totalMsgsField); + + + JPanel tableInfoPanel = new JPanel(); + tableInfoPanel.setLayout(new FlowLayout()); + tableInfoPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + tableInfoPanel.add(averagePanel); + tableInfoPanel.add(totalMsgsPanel); + + // Set up the table with footer + JPanel tablePanel = new JPanel(); + tablePanel.setLayout(new BorderLayout()); + tablePanel.add(tableScrollPanel, BorderLayout.CENTER); + tablePanel.add(tableInfoPanel, BorderLayout.SOUTH); + + // Add the main panel and the graph + this.add(mainPanel, BorderLayout.NORTH); + this.add(tablePanel, BorderLayout.CENTER); + } + + /** + * gets the number of processed messages. + */ + protected void timerLoop() { + + long startTime = System.currentTimeMillis(); + long difInTime = 0; + long currTime = 0; + long difInMins = 0; + long difInSec = 0; + long ramp_upInSec = 0; + long timeInSec = 0; + int msgCounter = 0; + long duration = Consumer.duration; + long ramp_up = Consumer.ramp_up; + + while (difInMins < duration) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + currTime = System.currentTimeMillis(); + difInTime = currTime - startTime; + difInMins = difInTime / MINUTE; + timeInSec = difInTime / SECOND; + difInSec = ((duration * INSECONDS) - timeInSec); + ramp_upInSec = ramp_up * INSECONDS; + long processed = Consumer.resetCount(); + + if (processed > 0 && + (difInMins >= ramp_up) && + ((duration - difInMins) >= ramp_up) && + (difInSec >= ramp_upInSec)) { + + if (timeInSec > ramp_upInSec) { + total += processed; + average = total / (timeInSec - ramp_upInSec); + } + + // Update the footer data. + updateTextFields(); + } + + // Add data to table row. + MessageSample newS = new MessageSample(msgCounter++, timeInSec, processed); + model.addRow(newS); + + if (difInMins == duration) { + Consumer.stopThread = true; + } + } + } +} diff --git a/jmeter/src/java/org/apache/jmeter/visualizers/MessageSample.java b/jmeter/src/java/org/apache/jmeter/visualizers/MessageSample.java new file mode 100755 index 0000000000..c66868ab08 --- /dev/null +++ b/jmeter/src/java/org/apache/jmeter/visualizers/MessageSample.java @@ -0,0 +1,89 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.jmeter.visualizers; + + +import java.io.Serializable; + +public class MessageSample implements Serializable, Comparable { + + public long count; + public long processed; + public long data; + + public MessageSample(long num, long data, long processed) { + this.count = num; + this.data = data; + this.processed = processed; + } + + /** + * @return Returns the count. + */ + public long getCount() { + return count; + } + + /** + * @param count The count to set. + */ + public void setCount(long count) { + this.count = count; + } + + /** + * @return Returns the data. + */ + public long getData() { + return data; + } + + /** + * @param data The data to set. + */ + public void setData(long data) { + this.data = data; + } + + + /** + * @return Returns the processed. + */ + public long getProcessed() { + return processed; + } + + /** + * @param processed The processed to set. + */ + public void setProcessed(long processed) { + this.processed = processed; + } + + public MessageSample() { + } + + /* (non-Javadoc) + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + public int compareTo(Object o) { + MessageSample oo = (MessageSample) o; + return ((count - oo.count) < 0 ? -1 : (count == oo.count ? 0 : 1)); + } + +} diff --git a/jmeter/src/java/org/apache/jmeter/visualizers/ProducerTableVisualizer.java b/jmeter/src/java/org/apache/jmeter/visualizers/ProducerTableVisualizer.java new file mode 100755 index 0000000000..23461255b3 --- /dev/null +++ b/jmeter/src/java/org/apache/jmeter/visualizers/ProducerTableVisualizer.java @@ -0,0 +1,256 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.jmeter.visualizers; + +import org.apache.jmeter.samplers.Clearable; +import org.apache.jmeter.samplers.SampleResult; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.jmeter.visualizers.gui.AbstractVisualizer; +import org.apache.jorphan.gui.ObjectTableModel; +import org.apache.jorphan.gui.layout.VerticalLayout; +import org.apache.jorphan.logging.LoggingManager; +import org.apache.jorphan.reflect.Functor; +import org.apache.log.Logger; +import org.activemq.sampler.Producer; + +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.JScrollPane; +import javax.swing.JPanel; +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.FlowLayout; +import java.text.DecimalFormat; + +/** + * A tableVisualizer that can display Producer Output + */ +public class ProducerTableVisualizer extends AbstractVisualizer implements Clearable { + + private static Logger log = LoggingManager.getLoggerForClass(); + + private final String[] COLUMNS = new String[]{ + JMeterUtils.getResString("table_visualizer_sample_num"), + JMeterUtils.getResString("table_visualizer_sample"), + JMeterUtils.getResString("table_visualizer_processed")}; + + private static final long SECOND = 1000; + private static final long INSECONDS = 60; + private static final long MINUTE = SECOND * INSECONDS; + private static final DecimalFormat dFormat = new DecimalFormat("#,###,###"); + + private ObjectTableModel model = null; + private JTable table = null; + private JTextField averageField = null; + private JTextField totalMsgsField = null; + private JScrollPane tableScrollPanel = null; + + private double average = 0; + private int total = 0; + + /** + * Constructor for the TableVisualizer object. + */ + public ProducerTableVisualizer() { + + super(); + + model = new ObjectTableModel(COLUMNS, + new Functor[]{new Functor("getCount"), + new Functor("getData"), + new Functor("getProcessed")}, + new Functor[]{null, null, null}, + new Class[]{Long.class, Long.class, Long.class}); + + init(); + + } + + /** + * @return Label key to get from label resource + */ + public String getLabelResource() { + + return "view_prod_results_in_table"; + } + + /** + * Sets the average Field and total Messages sent, that would be displayed. + */ + protected synchronized void updateTextFields() { + + averageField.setText(dFormat.format(average)); + totalMsgsField.setText(dFormat.format(total)); + } + + /** + * @param res SampleResult from the JMeter Sampler + */ + public void add(SampleResult res) { + + Thread timer = new Thread() { + public void run() { + timerLoop(); + } + }; + + timer.start(); + } + + /** + * clear/resets the field. + */ + public synchronized void clear() { + + log.debug("Clear called", new Exception("Debug")); + model.clearData(); + averageField.setText("0000"); + totalMsgsField.setText("0000"); + repaint(); + } + + /** + * Initialize the User Interface + */ + private void init() { + + this.setLayout(new BorderLayout()); + + // Main Panel + JPanel mainPanel = new JPanel(); + Border margin = new EmptyBorder(10, 10, 5, 10); + + mainPanel.setBorder(margin); + mainPanel.setLayout(new VerticalLayout(5, VerticalLayout.LEFT)); + + // Name + mainPanel.add(makeTitlePanel()); + + // Set up the table itself + table = new JTable(model); + + // table.getTableHeader().setReorderingAllowed(false); + tableScrollPanel = new JScrollPane(table); + tableScrollPanel.setViewportBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); + + // Set up footer of table which displays numerics of the graphs + JPanel averagePanel = new JPanel(); + JLabel averageLabel = + new JLabel(JMeterUtils.getResString("graph_results_average")); + averageLabel.setForeground(Color.black); + averageField = new JTextField(15); + averageField.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + averageField.setEditable(false); + averageField.setForeground(Color.black); + averageField.setBackground(getBackground()); + averagePanel.add(averageLabel); + averagePanel.add(averageField); + + JPanel totalMsgsPanel = new JPanel(); + JLabel totalMsgsLabel = + new JLabel(JMeterUtils.getResString("graph_results_total_msgs")); + totalMsgsLabel.setForeground(Color.black); + totalMsgsField = new JTextField(15); + totalMsgsField.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + totalMsgsField.setEditable(false); + totalMsgsField.setForeground(Color.black); + totalMsgsField.setBackground(getBackground()); + totalMsgsPanel.add(totalMsgsLabel); + totalMsgsPanel.add(totalMsgsField); + + + JPanel tableInfoPanel = new JPanel(); + tableInfoPanel.setLayout(new FlowLayout()); + tableInfoPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + + tableInfoPanel.add(averagePanel); + tableInfoPanel.add(totalMsgsPanel); + + // Set up the table with footer + JPanel tablePanel = new JPanel(); + tablePanel.setLayout(new BorderLayout()); + tablePanel.add(tableScrollPanel, BorderLayout.CENTER); + tablePanel.add(tableInfoPanel, BorderLayout.SOUTH); + + // Add the main panel and the graph + this.add(mainPanel, BorderLayout.NORTH); + this.add(tablePanel, BorderLayout.CENTER); + } + + /** + * gets the number of processed messages. + */ + protected void timerLoop() { + + long startTime = System.currentTimeMillis(); + long difInTime = 0; + long currTime = 0; + long difInMins = 0; + long difInSec = 0; + long ramp_upInSec = 0; + long timeInSec = 0; + int msgCounter = 0; + long duration = Producer.duration; + long ramp_up = Producer.ramp_up; + + while (difInMins < duration) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + currTime = System.currentTimeMillis(); + difInTime = currTime - startTime; + difInMins = difInTime / MINUTE; + timeInSec = difInTime / SECOND; + difInSec = ((duration * INSECONDS) - timeInSec); + ramp_upInSec = ramp_up * INSECONDS; + long processed = Producer.resetCount(); + + if (processed > 0 && + (difInMins >= ramp_up) && + ((duration - difInMins) >= ramp_up) && + (difInSec >= ramp_upInSec)) { + + if (timeInSec > ramp_upInSec) { + total += processed; + average = total / (timeInSec - ramp_upInSec); + } + + // Update the footer data. + updateTextFields(); + } + + // Add data to table row. + MessageSample newS = new MessageSample(msgCounter++, timeInSec, processed); + model.addRow(newS); + + // check if it's time to stop the Thread. + if (difInMins == duration){ + Producer.stopThread = true; + } + + } + } +} diff --git a/jmeter/src/java/org/apache/jmeter/visualizers/SystemTestMsgSample.java b/jmeter/src/java/org/apache/jmeter/visualizers/SystemTestMsgSample.java new file mode 100644 index 0000000000..b5e63bca07 --- /dev/null +++ b/jmeter/src/java/org/apache/jmeter/visualizers/SystemTestMsgSample.java @@ -0,0 +1,105 @@ +package org.apache.jmeter.visualizers; + +import java.io.Serializable; + +public class SystemTestMsgSample implements Serializable, Comparable { + public String consumerid; + public String prodName; + public String msgBody; + public Integer producerSeqID; + public Integer consumerSeqID; + + public SystemTestMsgSample(String consumerid, String prodName, String msgBody, Integer prodSeq, Integer conSeq) { + this.consumerid = consumerid; + this.prodName = prodName; + this.msgBody = msgBody; + this.producerSeqID = prodSeq; + this.consumerSeqID = conSeq; + } + + /** + * + * @return get consumer identifier. + */ + public String getConsumerID() { + return consumerid; + } + + /** + * + * @param consumerid - sets consumer identifier + */ + public void setConsumerID(String consumerid) { + this.consumerid = consumerid; + } + + /** + * + * @return Returns the Producer name. + */ + public String getProdName() { + return prodName; + } + + /** + * + * @param prodName - The Producer name to set. + */ + public void setProdName(String prodName) { + this.prodName = prodName; + } + + /** + * + * @return Return the Producer message. + */ + public String getMsgBody() { + return msgBody; + } + + /** + * + * @param msgBody - The Producer message to set. + */ + public void setMSgBody(String msgBody) { + this.msgBody = msgBody; + } + + /** + * + * @return Returns the current producer message count. + */ + public Integer getProducerSeq() { + return producerSeqID; + } + + /** + * + * @param count - The message count to set. + */ + public void setProducerSeq(Integer count) { + this.producerSeqID = count; + } + + /** + * + * @return Returns the current consumer message count. + */ + public Integer getConsumerSeq() { + return consumerSeqID; + } + + /** + * + * @param count - The message count to set. + */ + public void setConsumerSeq(Integer count) { + this.consumerSeqID = count; + } + + + public int compareTo(Object o) { + return -1; + } + +} diff --git a/jmeter/src/test/java/org/activemq/usecases/NoDestinationErrorTest.java b/jmeter/src/test/java/org/activemq/usecases/NoDestinationErrorTest.java new file mode 100644 index 0000000000..9de9cc64f3 --- /dev/null +++ b/jmeter/src/test/java/org/activemq/usecases/NoDestinationErrorTest.java @@ -0,0 +1,125 @@ +package org.activemq.usecases; + +import javax.jms.*; +import java.util.HashMap; +import java.util.Collections; +import java.util.Map; +import java.net.URI; + +import junit.framework.TestCase; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.ActiveMQConnection; +import org.activemq.broker.BrokerService; +import org.activemq.broker.BrokerFactory; +import org.activemq.broker.TransportConnector; + +/** +* This Unit Test is created to test the memory leakage when a new producer is created and closed for each message that was sent. +* The error occured after sending and receiving messages 65536 times. +* This test validate if the messages sent by producer is being received by the consumer. +* A new producer is created and closed for each message that was sent. +* The said procedure is done fore more than 65536 times. +*/ + + +public final class NoDestinationErrorTest extends TestCase { + public static final String ACTIVEMQ_SERVER = "ActiveMQ Server"; + public static final boolean TRANSACTED_FALSE = false; + public static final String TOOL_DEFAULT = "TOOL.DEFAULT"; + public static final String LAST_MESSAGE = "LAST"; + + public String userName; + public String password; + + private MessageProducer producer; + private MessageConsumer consumer; + + public static Map ProducerMap = Collections.synchronizedMap(new HashMap()); + protected BrokerService broker; + private boolean isTopic = false; + + protected ConnectionFactory factory; + protected ActiveMQConnection connection; + + + public NoDestinationErrorTest() { + super(); + } + + public void doTest() throws Exception { + ConnectionFactory factory = createConnectionFactory(); + ActiveMQConnection connection = (ActiveMQConnection) factory.createConnection(userName, password); + connection.start(); + + Session session = createSession(connection, TRANSACTED_FALSE); + Destination destination = createDestination(session, "subject", isTopic); + consumer = session.createConsumer(destination); + + for (int i=0; i<70000; i++) { + TextMessage sentMessage; + sentMessage = session.createTextMessage("message " + i); + producer = session.createProducer(destination); + producer.send(sentMessage); + producer.close(); + + TextMessage rcvMessage = null; + rcvMessage = (TextMessage)consumer.receive(i); + String message = rcvMessage.getText(); + assertNotNull("Message received should not be null", message); + System.out.println(message); + } + + connection.close(); + } + + /** + * Creates the connection to the broker. + * + * @return Connection - broker connection. + */ + protected ConnectionFactory createConnectionFactory() throws JMSException { + return new ActiveMQConnectionFactory("vm://localhost?broker.persistent=true"); + } + + /** + * Creates the connection session. + * + * @param connection - broker connection. + * @param isTransacted - true if the session will be session transacted. + * otherwise the the session will be using auto acknowledge. + * @return Session - connection session. + */ + private static Session createSession(Connection connection, + boolean isTransacted) throws JMSException { + if (isTransacted) { + return connection.createSession(true, Session.SESSION_TRANSACTED); + } else { + return connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + } + } + + /** + * Creates the session destination. + * + * @param session - connection session. + * @param subject - destination name. + * @param isTopic - true if the destination is a topic, + * otherwise the destination is a queue. + * @return Destination - session destination. + */ + private static Destination createDestination(Session session, + String subject, + boolean isTopic) throws JMSException { + if (isTopic) { + return session.createTopic(subject); + } else { + return session.createQueue(subject); + } + } + + public void testSendReceive() throws Exception { + doTest(); + } + +} diff --git a/jmeter/src/test/java/org/activemq/usecases/NonPersistentDurableTopicSystemTest.java b/jmeter/src/test/java/org/activemq/usecases/NonPersistentDurableTopicSystemTest.java new file mode 100644 index 0000000000..19386b8a2a --- /dev/null +++ b/jmeter/src/test/java/org/activemq/usecases/NonPersistentDurableTopicSystemTest.java @@ -0,0 +1,58 @@ +package org.activemq.usecases; + +public class NonPersistentDurableTopicSystemTest extends SystemTestSupport { + + /** + * Unit test for non-persistent durable topic messages with the following settings: + * 1 Producer, 1 Consumer, 1 Subject, 10 Messages + * + * @throws Exception + */ + public void testNonPersistentDurableTopicMessageA() throws Exception{ + SystemTestSupport st = new SystemTestSupport(true, + false, + true, + 1, + 1, + 1, + 10, + "testNonPersistentDurableTopicMessageA()"); + st.doTest(); + } + + /** + * Unit test for non-persistent durable topic messages with the following settings: + * 10 Producers, 10 Consumers, 1 Subject, 10 Messages + * + * @throws Exception + */ + public void testNonPersistentDurableTopicMessageB() throws Exception{ + SystemTestSupport st = new SystemTestSupport(true, + false, + true, + 10, + 10, + 1, + 10, + "testNonPersistentDurableTopicMessageB()"); + st.doTest(); + } + + /** + * Unit test for non-persistent durable topic messages with the following settings: + * 10 Producers, 10 Consumers, 10 Subjects, 10 Messages + * + * @throws Exception + */ + public void testNonPersistentDurableTopicMessageC() throws Exception{ + SystemTestSupport st = new SystemTestSupport(true, + false, + true, + 10, + 10, + 10, + 10, + "testNonPersistentDurableTopicMessageC()"); + st.doTest(); + } +} \ No newline at end of file diff --git a/jmeter/src/test/java/org/activemq/usecases/NonPersistentNonDurableTopicSystemTest.java b/jmeter/src/test/java/org/activemq/usecases/NonPersistentNonDurableTopicSystemTest.java new file mode 100644 index 0000000000..505a66d272 --- /dev/null +++ b/jmeter/src/test/java/org/activemq/usecases/NonPersistentNonDurableTopicSystemTest.java @@ -0,0 +1,58 @@ +package org.activemq.usecases; + +public class NonPersistentNonDurableTopicSystemTest extends SystemTestSupport { + + /** + * Unit test for non-persistent non-durable topic messages with the following settings: + * 1 Producer, 1 Consumer, 1 Subject, 10 Messages + * + * @throws Exception + */ + public void testNonPersistentNonDurableTopicMessageA() throws Exception{ + SystemTestSupport st = new SystemTestSupport(true, + false, + false, + 1, + 1, + 1, + 10, + "testNonPersistentNonDurableTopicMessageA()"); + st.doTest(); + } + + /** + * Unit test for non-persistent non-durable topic messages with the following settings: + * 10 Producers, 10 Consumers, 1 Subject, 10 Messages + * + * @throws Exception + */ + public void testNonPersistentNonDurableTopicMessageB() throws Exception{ + SystemTestSupport st = new SystemTestSupport(true, + false, + false, + 10, + 10, + 1, + 10, + "testNonPersistentNonDurableTopicMessageB()"); + st.doTest(); + } + + /** + * Unit test for non-persistent non-durable topic messages with the following settings: + * 10 Producers, 10 Consumers, 10 Subjects, 10 Messages + * + * @throws Exception + */ + public void testNonPersistentNonDurableTopicMessageC() throws Exception{ + SystemTestSupport st = new SystemTestSupport(true, + false, + false, + 10, + 10, + 10, + 10, + "testNonPersistentNonDurableTopicMessageC()"); + st.doTest(); + } +} \ No newline at end of file diff --git a/jmeter/src/test/java/org/activemq/usecases/PersistentDurableTopicSystemTest.java b/jmeter/src/test/java/org/activemq/usecases/PersistentDurableTopicSystemTest.java new file mode 100644 index 0000000000..2f5363e0fb --- /dev/null +++ b/jmeter/src/test/java/org/activemq/usecases/PersistentDurableTopicSystemTest.java @@ -0,0 +1,58 @@ +package org.activemq.usecases; + +public class PersistentDurableTopicSystemTest extends SystemTestSupport { + + /** + * Unit test for persistent durable topic messages with the following settings: + * 1 Producer, 1 Consumer, 1 Subject, 10 Messages + * + * @throws Exception + */ + public void testPersistentDurableTopicMessageA() throws Exception { + SystemTestSupport st = new SystemTestSupport(true, + true, + true, + 1, + 1, + 1, + 10, + "testPersistentDurableTopicMessageA()"); + st.doTest(); + } + + /** + * Unit test for persistent durable topic messages with the following settings: + * 10 Producers, 10 Consumers, 1 Subject, 10 Messages + * + * @throws Exception + */ + public void testPersistentDurableTopicMessageB() throws Exception{ + SystemTestSupport st = new SystemTestSupport(true, + true, + true, + 10, + 10, + 1, + 10, + "testPersistentDurableTopicMessageB()"); + st.doTest(); + } + + /** + * Unit test for persistent durable topic messages with the following settings: + * 10 Producers, 10 Consumers, 10 Subjects, 10 Messages + * + * @throws Exception + */ + public void testPersistentDurableTopicMessageC() throws Exception{ + SystemTestSupport st = new SystemTestSupport(true, + true, + true, + 10, + 10, + 10, + 10, + "testPersistentDurableTopicMessageC()"); + st.doTest(); + } +} \ No newline at end of file diff --git a/jmeter/src/test/java/org/activemq/usecases/PersistentNonDurableTopicSystemTest.java b/jmeter/src/test/java/org/activemq/usecases/PersistentNonDurableTopicSystemTest.java new file mode 100644 index 0000000000..a9bf8ad1eb --- /dev/null +++ b/jmeter/src/test/java/org/activemq/usecases/PersistentNonDurableTopicSystemTest.java @@ -0,0 +1,58 @@ +package org.activemq.usecases; + +public class PersistentNonDurableTopicSystemTest extends SystemTestSupport { + + /** + * Unit test for persistent non-durable topic messages with the following settings: + * 1 Producer, 1 Consumer, 1 Subject, 10 Messages + * + * @throws Exception + */ + public void testPersistentNonDurableTopicMessageA() throws Exception{ + SystemTestSupport st = new SystemTestSupport(true, + true, + false, + 1, + 1, + 1, + 10, + "testPersistentNonDurableTopicMessageA()"); + st.doTest(); + } + + /** + * Unit test for persistent non-durable topic messages with the following settings: + * 10 Producers, 10 Consumers, 1 Subject, 10 Messages + * + * @throws Exception + */ + public void testPersistentNonDurableTopicMessageB() throws Exception{ + SystemTestSupport st = new SystemTestSupport(true, + true, + false, + 10, + 10, + 1, + 10, + "testPersistentNonDurableTopicMessageB()"); + st.doTest(); + } + + /** + * Unit test for persistent non-durable topic messages with the following settings: + * 10 Producers, 10 Consumers, 10 Subjects, 10 Messages + * + * @throws Exception + */ + public void testPersistentNonDurableTopicMessageC() throws Exception{ + SystemTestSupport st = new SystemTestSupport(true, + true, + false, + 10, + 10, + 10, + 10, + "testPersistentNonDurableTopicMessageC()"); + st.doTest(); + } +} \ No newline at end of file diff --git a/jmeter/src/test/java/org/activemq/usecases/QueueSystemTest.java b/jmeter/src/test/java/org/activemq/usecases/QueueSystemTest.java new file mode 100644 index 0000000000..b3cb67405b --- /dev/null +++ b/jmeter/src/test/java/org/activemq/usecases/QueueSystemTest.java @@ -0,0 +1,116 @@ +package org.activemq.usecases; + +public class QueueSystemTest extends SystemTestSupport { + + /** + * Unit test for persistent queue messages with the following settings: + * 1 Producer, 1 Consumer, 1 Subject, 10 Messages + * + * @throws Exception + */ + ///* + public void testPersistentQueueMessageA() throws Exception{ + SystemTestSupport st = new SystemTestSupport(false, + true, + false, + 1, + 1, + 1, + 10, + "testPersistentQueueMessageA()"); + st.doTest(); + } + + + /** + * Unit test for persistent queue messages with the following settings: + * 10 Producers, 10 Consumers, 1 Subject, 10 Messages + * + * @throws Exception + */ + public void testPersistentQueueMessageB() throws Exception{ + SystemTestSupport st = new SystemTestSupport(false, + true, + false, + 10, + 10, + 1, + 10, + "testPersistentQueueMessageB()"); + st.doTest(); + } + + + /** + * Unit test for persistent queue messages with the following settings: + * 10 Producers, 10 Consumers, 10 Subjects, 10 Messages + * + * @throws Exception + */ + public void testPersistentQueueMessageC() throws Exception{ + SystemTestSupport st = new SystemTestSupport(false, + true, + false, + 10, + 10, + 10, + 10, + "testPersistentQueueMessageC()"); + st.doTest(); + } + + /** + * Unit test for non-persistent queue messages with the following settings: + * 1 Producer, 1 Consumer, 1 Subject, 10 Messages + * + * @throws Exception + */ + public void testNonPersistentQueueMessageA() throws Exception { + SystemTestSupport st = new SystemTestSupport(false, + false, + false, + 1, + 1, + 1, + 10, + "testNonPersistentQueueMessageA()"); + st.doTest(); + } + + /** + * Unit test for non-persistent queue messages with the following settings: + * 10 Producers, 10 Consumers, 1 Subject, 10 Messages + * + * @throws Exception + */ + public void testNonPersistentQueueMessageB() throws Exception{ + SystemTestSupport st = new SystemTestSupport(false, + false, + false, + 10, + 10, + 1, + 10, + "testNonPersistentQueueMessageB()"); + st.doTest(); + } + + + /** + * Unit test for non-persistent queue messages with the following settings: + * 10 Producers, 10 Consumers, 10 Subjects, 10 Messages + * + * @throws Exception + */ + public void testNonPersistentQueueMessageC() throws Exception{ + SystemTestSupport st = new SystemTestSupport(false, + false, + false, + 10, + 10, + 10, + 10, + "testNonPersistentQueueMessageC()"); + st.doTest(); + } +} \ No newline at end of file diff --git a/jmeter/src/test/java/org/activemq/usecases/SystemTestSupport.java b/jmeter/src/test/java/org/activemq/usecases/SystemTestSupport.java new file mode 100644 index 0000000000..571e181d35 --- /dev/null +++ b/jmeter/src/test/java/org/activemq/usecases/SystemTestSupport.java @@ -0,0 +1,553 @@ +package org.activemq.usecases; + +import junit.framework.TestCase; + +import javax.jms.*; +import java.util.*; +import java.net.URI; + +import org.activemq.ActiveMQConnection; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.command.ActiveMQMessage; +import org.activemq.command.ActiveMQDestination; +import org.activemq.broker.BrokerService; +import org.activemq.broker.BrokerFactory; +import org.activemq.broker.TransportConnector; + +import org.apache.jorphan.logging.LoggingManager; +import org.apache.log.Logger; + +import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap; + +public class SystemTestSupport extends TestCase implements MessageListener { + public static final String ACTIVEMQ_SERVER = "ActiveMQ Server"; + public static final boolean TRANSACTED_FALSE = false; + public static final String TOOL_DEFAULT = "TOOL.DEFAULT"; + public static final String LAST_MESSAGE = "#LAST"; + + public String userName; + public String password; + + private static int msgCounter = 0; + public static Map ProducerMap = Collections.synchronizedMap(new HashMap()); + + private int producerCount = 0; + private int consumerCount = 0; + private int subjectCount = 0; + private int messageCount = 0; + private boolean isPersistent = true; + private boolean isDurable = true; + private boolean isTopic = true; + private boolean testStillRunning = true; + + protected BrokerService broker; + + Map consumerMap = new ConcurrentHashMap(); + Map prodNameMap = new TreeMap(); + Map prodMsgMap = new TreeMap(); + + /** + * Default constructor + */ + protected SystemTestSupport(){ + super(); + ProducerMap.clear(); + msgCounter = 0; + testStillRunning = true; + } + + /** + * Constructor + * + * @param isTopic - true when topic, false when queue. + * @param isPersistent - true when the delivery mode is persistent. + * @param isDurable - true when the suscriber is durable(For topic only). + * @param producerCount - number of producers. + * @param consumerCount - number of consumers. + * @param subjectCount - number of destinations. + * @param messageCount - number of messages to be delivered. + * @param testTitle - test title/name. + * + */ + protected SystemTestSupport(boolean isTopic, + boolean isPersistent, + boolean isDurable, + int producerCount, + int consumerCount, + int subjectCount, + int messageCount, + String testTitle){ + super(); + this.isTopic = isTopic; + this.isPersistent = isPersistent; + this.isDurable = isDurable; + this.producerCount = producerCount; + this.consumerCount = consumerCount; + this.subjectCount = subjectCount; + this.messageCount = messageCount; + this.testParameterSettings(testTitle); + + ProducerMap.clear(); + consumerMap.clear(); + prodNameMap.clear(); + prodMsgMap.clear(); + msgCounter = 0; + testStillRunning = true; + } + + /**************************************************** + * + * Producer section + * + ****************************************************/ + + /** + * Creates the message producer threads. + */ + protected void publish() throws JMSException { + String subjects[] = getSubjects(); + + for (int i = 0; i < producerCount; i++) { + final int x = i; + final String subject = subjects[i % subjects.length]; + + Thread thread = new Thread() { + public void run() { + try { + publish(x, subject); + + } catch (Exception e) { + e.printStackTrace(); + } + } + }; + + thread.start(); + } + } + + /** + * Creates the producer and send the messages. + * + * @param x - producer number. + * @param subject - the destination where the messages will be sent. + */ + protected void publish(int x, String subject) throws Exception { + ConnectionFactory factory = createConnectionFactory(); + ActiveMQConnection connection = (ActiveMQConnection) factory.createConnection(userName, password); + connection.start(); + + String messageBody; + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination destination = createDestination(session, subject); + MessageProducer publisher = session.createProducer(destination); + + if (isPersistent) { + publisher.setDeliveryMode(DeliveryMode.PERSISTENT); + } else { + publisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + } + + StringBuffer sb = new StringBuffer(); + + //Sending messages + for (int i = 1; i <= messageCount; ++i) { + if (messageCount != i) + messageBody = "#BODY"; + else + messageBody = LAST_MESSAGE; + + sb.append("PROD"); + sb.append(x); + sb.append(messageBody); + sb.append("#"); + sb.append(i); + + TextMessage message = session.createTextMessage(sb.toString()); + publisher.send(message); + + sb.delete(0, sb.length()); + } + + publisher.close(); + session.close(); + connection.stop(); + connection = null; + } + + /**************************************************** + * + * Consumer section + * + ****************************************************/ + + /** + * Generates the topic/queue destinations. + * + * @return String[] - topic/queue destination name. + */ + protected String[] getSubjects() { + //Create the subjects. + String[] subjects = new String[subjectCount]; + + //Appended to the subject to determine if its a queue or topic. + String prefix = null; + if (this.isTopic) { + prefix = ".TOPIC"; + } else { + prefix = ".QUEUE"; + } + + for (int i = 0; i < subjects.length; i++) { + subjects[i] = TOOL_DEFAULT + prefix + i; + } + + return subjects; + } + + /** + * Suscribes the consumers to the topic/queue destinations. + */ + protected void subscribe() throws Exception { + String subjects[] = getSubjects(); + + for (int i = 0; i < consumerCount; i++) { + String subject = subjects[i % subjectCount]; + subscribe(subject); + } + } + + /** + * Suscribes the consumer to the topic/queue specified by the subject. + * + * @param subject - the Destination where the consumer waits upon for messages. + */ + protected void subscribe(String subject) throws Exception { + ConnectionFactory factory = createConnectionFactory(); + ActiveMQConnection connection = (ActiveMQConnection) factory.createConnection(userName, password); + connection.start(); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination destination = createDestination(session, subject); + + MessageConsumer consumer = session.createConsumer(destination); + consumer.setMessageListener(this); + connection.stop(); + } + + /** + * Processes the received message. + * + * @param message - message received by the listener. + */ + public void onMessage(Message message) { + try { + ActiveMQMessage amsg = (ActiveMQMessage) message; + TextMessage textMessage = (TextMessage) message; + + StringBuffer sb = new StringBuffer(); + sb.append(textMessage.getText()); + sb.append("#"); + sb.append(amsg.getJMSMessageID()); + + msgCounter++; + String strMsgCounter = String.valueOf(msgCounter); + ProducerMap.put(strMsgCounter, sb.toString()); + + } catch (JMSException e) { + System.out.println("Unable to force deserialize the content " + e); + } + } + + /** + * Validates the result of the producing and consumption of messages by the server. + * It checks for duplicate messages, message count and order. + */ + protected synchronized void timerLoop() { + System.out.println("MessagingSystemTest.timerLoop() * started * "); + Map ProducerTextMap = new HashMap(); + Map currentProducerMap = null; + String producerName = null; + String msgBody = null; + String consumerName = null; + String ProdSequenceNo = null; + String mapKey = null; + int expectedNoOfMessages = messageCount; + boolean dowhile = true; + + while (dowhile) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + //Retrieve the map containing the received messages data + currentProducerMap = resetProducerMap(); + + if (currentProducerMap.size() == 0) { + dowhile = false; + } + + //Put the map values to another map for parsing. + for (int i = 1; i <= currentProducerMap.size(); i++) { + String ProdMsg = (String) currentProducerMap.get(String.valueOf(i)); + producerName = ProdMsg.substring(0, ProdMsg.indexOf("#")); + msgBody = ProdMsg.substring(ProdMsg.indexOf("#") + 1, ProdMsg.lastIndexOf("#")); + ProdSequenceNo = ProdMsg.substring(ProdMsg.indexOf("#", ProdMsg.indexOf("#", ProdMsg.indexOf("#")+1)) + 1, ProdMsg.lastIndexOf("#")); + consumerName = ProdMsg.substring(ProdMsg.lastIndexOf("#"), ProdMsg.length()); + + if (isTopic) { + mapKey = consumerName + producerName; + } else { + mapKey = producerName; + } + + if (ProducerTextMap.containsKey(mapKey)) { + //Increment the counter value + Integer value = (Integer) ProducerTextMap.get(mapKey); + ProducerTextMap.put(mapKey, new Integer(value.intValue() + 1)); + } else { + //Put the Producer Name in the map + ProducerTextMap.put(mapKey, new Integer(1)); + } + + Integer messageCounter = (Integer) ProducerTextMap.get(mapKey); + Integer ProducerSeqID = Integer.valueOf(ProdSequenceNo); + + if (isTopic) { + // Check for duplicate message. + if (messageCounter.intValue() > expectedNoOfMessages) { + assertTrue("Should not have received duplicate messages!", messageCounter.intValue() <= expectedNoOfMessages); + break; + } else if (LAST_MESSAGE.equals(msgBody)) { + System.out.println("entered MsgBody.equals(LAST_MESSAGE)..." + mapKey + + " " + messageCounter.intValue() +"=" + expectedNoOfMessages); + + // Validates that the messages received is equal to the number + // of expected messages + if (messageCounter.intValue() != expectedNoOfMessages) { + System.out.println("entered messageCounter.intValue() != expectedNoOfMessages..."); + + // Checks for message order. + assertTrue("Should have received messages in order!", messageCounter.intValue() == expectedNoOfMessages); + if (messageCounter.intValue() != expectedNoOfMessages) { + System.out.println("Should have received messages in order!"); + } + break; + } else if (currentProducerMap.size() == i) { + System.out.println("MessagingSystemTest.timerLoop() says system_test_pass!!!... "); + break; + } + } + + } else { + //Create map for each consumer + for (int j = 0 ; j < consumerCount ; j++) { + if (!consumerMap.containsKey(new String(consumerName))) { + consumerMap.put(new String(consumerName), new LinkedHashMap()); + } + } + + //create Producer Name Map + if (!prodNameMap.containsKey(producerName)) { + prodNameMap.put(producerName, (null)); + } + + //Get the current size of consumer + int seqVal = 0; + Object[] cObj = consumerMap.keySet().toArray(); + for (int k = 0; k < cObj.length; k++) { + String cMapKey = (String)cObj[k]; + Map cMapVal = (Map)consumerMap.get(cObj[k]); + if (cMapKey.equals(consumerName)) { + seqVal = cMapVal.size(); + break; + } + } + + //Put object to its designated consumer map + Object[] consumerObj = consumerMap.keySet().toArray(); + for (int j = 0; j < consumerObj.length; j++) { + String cMapKey = (String)consumerObj[j]; + Map cMapVal = (LinkedHashMap)consumerMap.get(consumerObj[j]); + if (cMapKey.equals(consumerName)) { + cMapVal.put(new Integer(seqVal), (producerName + "/" + ProducerSeqID)); + } + } + } + + // Add data to table row + if (!isTopic) { + String msgKey = consumerName + "#" + producerName + "#" + String.valueOf(ProdSequenceNo); + String msgVal = String.valueOf(messageCounter) + "#" + msgBody; + + if (!prodMsgMap.containsKey(msgKey)) { + prodMsgMap.put((msgKey), (msgVal)); + } + } + } + } + + if (!isTopic) { + //Validate message sequence + boolean isMsgNotOrdered = validateMsg(prodNameMap, consumerMap); + assertFalse("Should have received messages in order!", isMsgNotOrdered); + } + + testStillRunning = false; + System.out.println("MessagingSystemTest.timerLoop() * ended * "); + } + + /** + * Returns the message entries and clears the map for another set + * of messages to be processed. + * + * @return Map - messages to be processed. + */ + protected synchronized Map resetProducerMap() { + Map copy = Collections.synchronizedMap(new HashMap(ProducerMap)); + ProducerMap.clear(); + msgCounter = 0; + + return copy; + } + + /**************************************************** + * + * Utility section + * + ****************************************************/ + + /** + * Creates the session destination. + * + * @param session - connection session. + * @param subject - destination name. + * @return Destination - session destination. + */ + protected ActiveMQDestination createDestination(Session session, String subject) + throws JMSException { + if (isTopic) { + return (ActiveMQDestination) session.createQueue(subject); + } else { + return (ActiveMQDestination) session.createTopic(subject); + } + } + + protected ConnectionFactory createConnectionFactory() throws Exception { + return new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false"); + } + + /**************************************************** + * + * Unit test section + * + ****************************************************/ + + /** + * Executes the unit test by running the producers and consumers. + * It checks for duplicate messages, message count and order. + */ + protected void doTest() throws Exception { + System.out.println("MessagingSystemTest.doTest() * start *"); + + //Set up the consumers + subscribe(); + + System.out.println("MessagingSystemTest.doTest() after suscribe()..."); + + //Set up the producers + publish(); + + System.out.println("MessagingSystemTest.doTest() after publish()..."); + + //Run the test + Thread timer = new Thread() { + public void run() { + timerLoop(); + } + }; + timer.setPriority(Thread.MIN_PRIORITY); + timer.start(); + + while (testStillRunning) { + try { + if (Thread.currentThread() == timer) { + Thread.sleep(1000); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + System.out.println("MessagingSystemTest.doTest() * end *"); + } + + /** + * Validates the messages received. + * + * @param prodNameMap + * @param cMap + * @return + */ + protected boolean validateMsg(Map prodNameMap, Map cMap) { + boolean ret = false; + Object[] cObj = cMap.keySet().toArray(); + for (int j = 0; j < cObj.length; j++) { + Map childMap = (Map)cMap.get(cObj[j]); + + Object[] nameObj = prodNameMap.keySet().toArray(); + for (int i = 0; i < nameObj.length; i++) { + String prodName = (String)nameObj[i]; + String tempProdHolder = null; + String tempProdIDHolder = null; + + Object[] childObj = childMap.keySet().toArray(); + for (int k = 0; k < childObj.length; k++) { + Integer childMapKey = (Integer)childObj[k]; + String childMapVal = (String)childMap.get(childObj[k]); + String prodVal = childMapVal.substring(0, childMapVal.indexOf("/")); + String prodIDVal = childMapVal.substring(childMapVal.indexOf("/")+1, childMapVal.length()); + + if (prodVal.equals(prodName)) { + if (tempProdHolder == null) { + tempProdHolder = prodVal; + tempProdIDHolder = prodIDVal; + continue; + } + if (Integer.parseInt(prodIDVal) > Integer.parseInt(tempProdIDHolder)) { + tempProdHolder = prodVal; + tempProdIDHolder = prodIDVal; + } else { + ret = true; + break; + } + } else { + continue; + } + } + } + } + return ret; + } + + /** + * Prints the test settings. + * + * @param strTestTitle - unit test name. + */ + public void testParameterSettings(String strTestTitle) { + System.out.println(strTestTitle); + System.out.println("============================================================"); + System.out.println("Test settings:"); + System.out.println("isTopic=" + new Boolean(isTopic).toString()); + System.out.println("isPersistent=" + new Boolean(isPersistent).toString()); + System.out.println("isDurable=" + new Boolean(isDurable).toString()); + System.out.println("producerCount=" + producerCount); + System.out.println("consumerCount=" + consumerCount); + System.out.println("subjectCount=" + subjectCount); + System.out.println("messageCount=" + messageCount); + System.out.println(""); + } +} diff --git a/jmeter/src/test/resources/log4j.properties b/jmeter/src/test/resources/log4j.properties new file mode 100644 index 0000000000..9bd5d778b4 --- /dev/null +++ b/jmeter/src/test/resources/log4j.properties @@ -0,0 +1,18 @@ +# +# The logging properties used during tests.. +# +log4j.rootLogger=INFO, out + +log4j.logger.org.activemq.spring=WARN + +# CONSOLE appender not used by default +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n + +# File appender +log4j.appender.out=org.apache.log4j.FileAppender +log4j.appender.out.layout=org.apache.log4j.PatternLayout +log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +log4j.appender.out.file=target/test-reports/activemq-test.log +log4j.appender.out.append=true diff --git a/maven.xml b/maven.xml new file mode 100755 index 0000000000..bf348c0524 --- /dev/null +++ b/maven.xml @@ -0,0 +1,254 @@ + + + + + + + + + + + + + + ${maven.multiproject.includes} + + + + ${maven.multiproject.excludes} + + + + + + + + + + + ${modules} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${modules.directories} + + + +---------------------------------------- + | Cleaning: ${directory} + +---------------------------------------- + + + + + + + + + + + + + + +---------------------------------------- + | Cleaning: repo + +---------------------------------------- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cleaning the directory + + + Compiling the codes + + + Building the bin and src bundle + + + Creating deploy "site" + + + + + + + + + + diff --git a/openwire-c/LICENSE.txt b/openwire-c/LICENSE.txt new file mode 100755 index 0000000000..7e61cc0570 --- /dev/null +++ b/openwire-c/LICENSE.txt @@ -0,0 +1,19 @@ +/** + * + * Copyright 2004 Protique Ltd + * Copyright 2004 Hiram Chirino + * + * Licensed 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. + * + **/ + diff --git a/openwire-c/README.txt b/openwire-c/README.txt new file mode 100755 index 0000000000..ca244668d3 --- /dev/null +++ b/openwire-c/README.txt @@ -0,0 +1,12 @@ +Welcome to libopenwire +====================== + +libopenwire is a c library which allows you to talk the highly efficient native wire protocol that +ActiveMQ brokers prefer. + +Please help us make libopenwire better - we appreciate any feedback you may have. + +Enjoy! + +----------------- +The ActiveMQ team diff --git a/openwire-c/docs/amqc.1 b/openwire-c/docs/amqc.1 new file mode 100755 index 0000000000..e82a2d0693 --- /dev/null +++ b/openwire-c/docs/amqc.1 @@ -0,0 +1,79 @@ +.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. +.\"See Also: +.\"man mdoc.samples for a complete listing of options +.\"man mdoc for the short list of editing options +.\"/usr/share/misc/mdoc.template +.Dd 8/9/05 \" DATE +.Dt amqc 1 \" Program name and manual section number +.Os Darwin +.Sh NAME \" Section Header - required - don't modify +.Nm amqc, +.\" The following lines are read in generating the apropos(man -k) database. Use only key +.\" words here as the database is built based on the words here and in the .ND line. +.Nm Other_name_for_same_program(), +.Nm Yet another name for the same program. +.\" Use .Nm macro to designate other names for the documented program. +.Nd This line parsed for whatis database. +.Sh SYNOPSIS \" Section Header - required - don't modify +.Nm +.Op Fl abcd \" [-abcd] +.Op Fl a Ar path \" [-a path] +.Op Ar file \" [file] +.Op Ar \" [file ...] +.Ar arg0 \" Underlined argument - use .Ar anywhere to underline +arg2 ... \" Arguments +.Sh DESCRIPTION \" Section Header - required - don't modify +Use the .Nm macro to refer to your program throughout the man page like such: +.Nm +Underlining is accomplished with the .Ar macro like this: +.Ar underlined text . +.Pp \" Inserts a space +A list of items with descriptions: +.Bl -tag -width -indent \" Begins a tagged list +.It item a \" Each item preceded by .It macro +Description of item a +.It item b +Description of item b +.El \" Ends the list +.Pp +A list of flags and their descriptions: +.Bl -tag -width -indent \" Differs from above in tag removed +.It Fl a \"-a flag as a list item +Description of -a flag +.It Fl b +Description of -b flag +.El \" Ends the list +.Pp +.\" .Sh ENVIRONMENT \" May not be needed +.\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 +.\" .It Ev ENV_VAR_1 +.\" Description of ENV_VAR_1 +.\" .It Ev ENV_VAR_2 +.\" Description of ENV_VAR_2 +.\" .El +.Sh FILES \" File used or created by the topic of the man page +.Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact +.It Pa /usr/share/file_name +FILE_1 description +.It Pa /Users/joeuser/Library/really_long_file_name +FILE_2 description +.El \" Ends the list +.\" .Sh DIAGNOSTICS \" May not be needed +.\" .Bl -diag +.\" .It Diagnostic Tag +.\" Diagnostic informtion here. +.\" .It Diagnostic Tag +.\" Diagnostic informtion here. +.\" .El +.Sh SEE ALSO +.\" List links in ascending order by section, alphabetically within a section. +.\" Please do not reference files that do not exist without filing a bug report +.Xr a 1 , +.Xr b 1 , +.Xr c 1 , +.Xr a 2 , +.Xr b 2 , +.Xr a 3 , +.Xr b 3 +.\" .Sh BUGS \" Document known, unremedied bugs +.\" .Sh HISTORY \" Document history if command behaves in a unique manner \ No newline at end of file diff --git a/openwire-c/openwire.xcodeproj/project.pbxproj b/openwire-c/openwire.xcodeproj/project.pbxproj new file mode 100755 index 0000000000..59437060aa --- /dev/null +++ b/openwire-c/openwire.xcodeproj/project.pbxproj @@ -0,0 +1,665 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXBuildFile section */ + 53C445DF08E4A71D006387E9 /* ow_marshal.c in Sources */ = {isa = PBXBuildFile; fileRef = 53C445B208E4A19E006387E9 /* ow_marshal.c */; }; + 53C445E008E4A71D006387E9 /* ow_commands_v1.c in Sources */ = {isa = PBXBuildFile; fileRef = 53C445B508E4A19E006387E9 /* ow_commands_v1.c */; }; + 53C445E108E4A71D006387E9 /* ow.c in Sources */ = {isa = PBXBuildFile; fileRef = 53C445B708E4A19E006387E9 /* ow.c */; }; + 53C445E208E4A71D006387E9 /* ow_buffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 53C445B008E4A19E006387E9 /* ow_buffer.c */; }; + 53C445E308E4A729006387E9 /* ow.h in Headers */ = {isa = PBXBuildFile; fileRef = 53C445B808E4A19E006387E9 /* ow.h */; }; + 53C445E508E4A729006387E9 /* ow_commands_v1.h in Headers */ = {isa = PBXBuildFile; fileRef = 53C445B608E4A19E006387E9 /* ow_commands_v1.h */; }; + 53C445E608E4A729006387E9 /* ow_command_types_v1.h in Headers */ = {isa = PBXBuildFile; fileRef = 53C445B408E4A19E006387E9 /* ow_command_types_v1.h */; }; + 53C445E708E4A758006387E9 /* amqcs.c in Sources */ = {isa = PBXBuildFile; fileRef = 53C445BB08E4A20A006387E9 /* amqcs.c */; }; + 53C445E808E4A75C006387E9 /* amqcs.h in Headers */ = {isa = PBXBuildFile; fileRef = 53C445BC08E4A20A006387E9 /* amqcs.h */; }; + 53C445E908E4A77A006387E9 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 53C445BE08E4A266006387E9 /* main.c */; }; + 53C445EA08E4A7DD006387E9 /* openwire.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 53C445C308E4A3CA006387E9 /* openwire.dylib */; }; + 53C445EB08E4A7E3006387E9 /* activemq.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 53C445CD08E4A6B2006387E9 /* activemq.dylib */; }; + 53C4461008E4AD63006387E9 /* libapr-1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 53D4A4E508A9091000CAE09B /* libapr-1.a */; }; + 53C4462608E4AFED006387E9 /* libapr-1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 53D4A4E508A9091000CAE09B /* libapr-1.a */; }; + 53C4462908E4B01A006387E9 /* openwire.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 53C445C308E4A3CA006387E9 /* openwire.dylib */; }; + 53C4462E08E4B06A006387E9 /* libapr-1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 53D4A4E508A9091000CAE09B /* libapr-1.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildStyle section */ + 014CEA520018CE5811CA2923 /* Development */ = { + isa = PBXBuildStyle; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + ZERO_LINK = YES; + }; + name = Development; + }; + 014CEA530018CE5811CA2923 /* Deployment */ = { + isa = PBXBuildStyle; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + ZERO_LINK = NO; + }; + name = Deployment; + }; +/* End PBXBuildStyle section */ + +/* Begin PBXContainerItemProxy section */ + 53C445D308E4A6CF006387E9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 53C445C208E4A3CA006387E9; + remoteInfo = openwire; + }; + 53C445D508E4A6D5006387E9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 53C445CC08E4A6B2006387E9; + remoteInfo = activemq; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 530E4E5E08AC5438007E3535 /* GenerateCMarshalling.groovy */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = GenerateCMarshalling.groovy; path = "../activemq-core/src/gram/script/GenerateCMarshalling.groovy"; sourceTree = SOURCE_ROOT; }; + 53C445B008E4A19E006387E9 /* ow_buffer.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = ow_buffer.c; path = src/libopenwire/ow_buffer.c; sourceTree = ""; }; + 53C445B208E4A19E006387E9 /* ow_marshal.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = ow_marshal.c; path = src/libopenwire/ow_marshal.c; sourceTree = ""; }; + 53C445B408E4A19E006387E9 /* ow_command_types_v1.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ow_command_types_v1.h; path = src/libopenwire/ow_command_types_v1.h; sourceTree = ""; }; + 53C445B508E4A19E006387E9 /* ow_commands_v1.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = ow_commands_v1.c; path = src/libopenwire/ow_commands_v1.c; sourceTree = ""; }; + 53C445B608E4A19E006387E9 /* ow_commands_v1.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ow_commands_v1.h; path = src/libopenwire/ow_commands_v1.h; sourceTree = ""; }; + 53C445B708E4A19E006387E9 /* ow.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = ow.c; path = src/libopenwire/ow.c; sourceTree = ""; }; + 53C445B808E4A19E006387E9 /* ow.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ow.h; path = src/libopenwire/ow.h; sourceTree = ""; }; + 53C445BB08E4A20A006387E9 /* amqcs.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = amqcs.c; path = src/libactivemq/amqcs.c; sourceTree = ""; }; + 53C445BC08E4A20A006387E9 /* amqcs.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = amqcs.h; path = src/libactivemq/amqcs.h; sourceTree = ""; }; + 53C445BE08E4A266006387E9 /* main.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = main.c; path = src/examples/main.c; sourceTree = ""; }; + 53C445C308E4A3CA006387E9 /* openwire.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = openwire.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + 53C445CD08E4A6B2006387E9 /* activemq.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = activemq.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + 53C445D108E4A6CA006387E9 /* main */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = main; sourceTree = BUILT_PRODUCTS_DIR; }; + 53D4A4E508A9091000CAE09B /* libapr-1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libapr-1.a"; path = "../../apr/lib/libapr-1.a"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 53C445C108E4A3CA006387E9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 53C4461008E4AD63006387E9 /* libapr-1.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 53C445CB08E4A6B2006387E9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 53C4462908E4B01A006387E9 /* openwire.dylib in Frameworks */, + 53C4462608E4AFED006387E9 /* libapr-1.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 53C445CF08E4A6CA006387E9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 53C4462E08E4B06A006387E9 /* libapr-1.a in Frameworks */, + 53C445EB08E4A7E3006387E9 /* activemq.dylib in Frameworks */, + 53C445EA08E4A7DD006387E9 /* openwire.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* amqc */ = { + isa = PBXGroup; + children = ( + 53C445B908E4A1A7006387E9 /* libopenwire */, + 53C445BA08E4A1F1006387E9 /* libactivemq */, + 53C445BD08E4A249006387E9 /* examples */, + 53D4A4E508A9091000CAE09B /* libapr-1.a */, + 530E4E5E08AC5438007E3535 /* GenerateCMarshalling.groovy */, + 53C445C408E4A3CA006387E9 /* Products */, + ); + name = amqc; + sourceTree = ""; + }; + 53C445B908E4A1A7006387E9 /* libopenwire */ = { + isa = PBXGroup; + children = ( + 53C445B008E4A19E006387E9 /* ow_buffer.c */, + 53C445B208E4A19E006387E9 /* ow_marshal.c */, + 53C445B408E4A19E006387E9 /* ow_command_types_v1.h */, + 53C445B508E4A19E006387E9 /* ow_commands_v1.c */, + 53C445B608E4A19E006387E9 /* ow_commands_v1.h */, + 53C445B708E4A19E006387E9 /* ow.c */, + 53C445B808E4A19E006387E9 /* ow.h */, + ); + name = libopenwire; + sourceTree = ""; + }; + 53C445BA08E4A1F1006387E9 /* libactivemq */ = { + isa = PBXGroup; + children = ( + 53C445BB08E4A20A006387E9 /* amqcs.c */, + 53C445BC08E4A20A006387E9 /* amqcs.h */, + ); + name = libactivemq; + sourceTree = ""; + }; + 53C445BD08E4A249006387E9 /* examples */ = { + isa = PBXGroup; + children = ( + 53C445BE08E4A266006387E9 /* main.c */, + ); + name = examples; + sourceTree = ""; + }; + 53C445C408E4A3CA006387E9 /* Products */ = { + isa = PBXGroup; + children = ( + 53C445C308E4A3CA006387E9 /* openwire.dylib */, + 53C445CD08E4A6B2006387E9 /* activemq.dylib */, + 53C445D108E4A6CA006387E9 /* main */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 53C445BF08E4A3CA006387E9 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 53C445E308E4A729006387E9 /* ow.h in Headers */, + 53C445E508E4A729006387E9 /* ow_commands_v1.h in Headers */, + 53C445E608E4A729006387E9 /* ow_command_types_v1.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 53C445C908E4A6B2006387E9 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 53C445E808E4A75C006387E9 /* amqcs.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 53C445C208E4A3CA006387E9 /* openwire */ = { + isa = PBXNativeTarget; + buildConfigurationList = 53C445C508E4A3CB006387E9 /* Build configuration list for PBXNativeTarget "openwire" */; + buildPhases = ( + 53C445BF08E4A3CA006387E9 /* Headers */, + 53C445C008E4A3CA006387E9 /* Sources */, + 53C445C108E4A3CA006387E9 /* Frameworks */, + ); + buildRules = ( + ); + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/lib; + LIBRARY_STYLE = DYNAMIC; + OPTIMIZATION_CFLAGS = ""; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = openwire; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + dependencies = ( + ); + name = openwire; + productName = openwire; + productReference = 53C445C308E4A3CA006387E9 /* openwire.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + 53C445CC08E4A6B2006387E9 /* activemq */ = { + isa = PBXNativeTarget; + buildConfigurationList = 53C445D708E4A6F4006387E9 /* Build configuration list for PBXNativeTarget "activemq" */; + buildPhases = ( + 53C445C908E4A6B2006387E9 /* Headers */, + 53C445CA08E4A6B2006387E9 /* Sources */, + 53C445CB08E4A6B2006387E9 /* Frameworks */, + ); + buildRules = ( + ); + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/lib; + LIBRARY_STYLE = DYNAMIC; + OPTIMIZATION_CFLAGS = ""; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = activemq; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + dependencies = ( + ); + name = activemq; + productName = activemq; + productReference = 53C445CD08E4A6B2006387E9 /* activemq.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + 53C445D008E4A6CA006387E9 /* main */ = { + isa = PBXNativeTarget; + buildConfigurationList = 53C445DB08E4A6F4006387E9 /* Build configuration list for PBXNativeTarget "main" */; + buildPhases = ( + 53C445CE08E4A6CA006387E9 /* Sources */, + 53C445CF08E4A6CA006387E9 /* Frameworks */, + ); + buildRules = ( + ); + buildSettings = { + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = "$(HOME)/bin"; + OPTIMIZATION_CFLAGS = ""; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = main; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + dependencies = ( + 53C445D408E4A6CF006387E9 /* PBXTargetDependency */, + 53C445D608E4A6D5006387E9 /* PBXTargetDependency */, + ); + name = main; + productName = main; + productReference = 53C445D108E4A6CA006387E9 /* main */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 53D4A41D08A8931000CAE09B /* Build configuration list for PBXProject "openwire" */; + buildSettings = { + }; + buildStyles = ( + 014CEA520018CE5811CA2923 /* Development */, + 014CEA530018CE5811CA2923 /* Deployment */, + ); + hasScannedForEncodings = 1; + mainGroup = 08FB7794FE84155DC02AAC07 /* amqc */; + productRefGroup = 53C445C408E4A3CA006387E9 /* Products */; + projectDirPath = ""; + targets = ( + 53C445C208E4A3CA006387E9 /* openwire */, + 53C445CC08E4A6B2006387E9 /* activemq */, + 53C445D008E4A6CA006387E9 /* main */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 53C445C008E4A3CA006387E9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53C445DF08E4A71D006387E9 /* ow_marshal.c in Sources */, + 53C445E008E4A71D006387E9 /* ow_commands_v1.c in Sources */, + 53C445E108E4A71D006387E9 /* ow.c in Sources */, + 53C445E208E4A71D006387E9 /* ow_buffer.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 53C445CA08E4A6B2006387E9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53C445E708E4A758006387E9 /* amqcs.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 53C445CE08E4A6CA006387E9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53C445E908E4A77A006387E9 /* main.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 53C445D408E4A6CF006387E9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 53C445C208E4A3CA006387E9 /* openwire */; + targetProxy = 53C445D308E4A6CF006387E9 /* PBXContainerItemProxy */; + }; + 53C445D608E4A6D5006387E9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 53C445CC08E4A6B2006387E9 /* activemq */; + targetProxy = 53C445D508E4A6D5006387E9 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 53C445C608E4A3CB006387E9 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/lib; + LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../apr/lib\""; + LIBRARY_STYLE = DYNAMIC; + OPTIMIZATION_CFLAGS = "-O0"; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = openwire; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Development; + }; + 53C445C708E4A3CB006387E9 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/lib; + LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../apr/lib\""; + LIBRARY_STYLE = DYNAMIC; + OPTIMIZATION_CFLAGS = "-O0"; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = openwire; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Deployment; + }; + 53C445C808E4A3CB006387E9 /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/lib; + LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../apr/lib\""; + LIBRARY_STYLE = DYNAMIC; + OPTIMIZATION_CFLAGS = "-O0"; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = openwire; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Default; + }; + 53C445D808E4A6F4006387E9 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/lib; + LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../apr/lib\""; + LIBRARY_STYLE = DYNAMIC; + OPTIMIZATION_CFLAGS = "-O0"; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = activemq; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Development; + }; + 53C445D908E4A6F4006387E9 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/lib; + LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../apr/lib\""; + LIBRARY_STYLE = DYNAMIC; + OPTIMIZATION_CFLAGS = "-O0"; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = activemq; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Deployment; + }; + 53C445DA08E4A6F4006387E9 /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/lib; + LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../apr/lib\""; + LIBRARY_STYLE = DYNAMIC; + OPTIMIZATION_CFLAGS = "-O0"; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = activemq; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Default; + }; + 53C445DC08E4A6F4006387E9 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = "$(HOME)/bin"; + LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../apr/lib\""; + OPTIMIZATION_CFLAGS = "-O0"; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = main; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Development; + }; + 53C445DD08E4A6F4006387E9 /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = "$(HOME)/bin"; + LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../apr/lib\""; + OPTIMIZATION_CFLAGS = "-O0"; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = main; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Deployment; + }; + 53C445DE08E4A6F4006387E9 /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = "$(HOME)/bin"; + LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../apr/lib\""; + OPTIMIZATION_CFLAGS = "-O0"; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = main; + SECTORDER_FLAGS = ""; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Default; + }; + 53D4A41E08A8931000CAE09B /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ppc; + HEADER_SEARCH_PATHS = "/usr/local/apr/include/apr-1"; + LIBRARY_SEARCH_PATHS = /usr/local/apr/lib; + }; + name = Development; + }; + 53D4A41F08A8931000CAE09B /* Deployment */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ppc; + HEADER_SEARCH_PATHS = "/usr/local/apr/include/apr-1"; + LIBRARY_SEARCH_PATHS = /usr/local/apr/lib; + }; + name = Deployment; + }; + 53D4A42008A8931000CAE09B /* Default */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ppc; + HEADER_SEARCH_PATHS = "/usr/local/apr/include/apr-1"; + LIBRARY_SEARCH_PATHS = /usr/local/apr/lib; + }; + name = Default; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 53C445C508E4A3CB006387E9 /* Build configuration list for PBXNativeTarget "openwire" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 53C445C608E4A3CB006387E9 /* Development */, + 53C445C708E4A3CB006387E9 /* Deployment */, + 53C445C808E4A3CB006387E9 /* Default */, + ); + defaultConfigurationIsVisible = 0; + }; + 53C445D708E4A6F4006387E9 /* Build configuration list for PBXNativeTarget "activemq" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 53C445D808E4A6F4006387E9 /* Development */, + 53C445D908E4A6F4006387E9 /* Deployment */, + 53C445DA08E4A6F4006387E9 /* Default */, + ); + defaultConfigurationIsVisible = 0; + }; + 53C445DB08E4A6F4006387E9 /* Build configuration list for PBXNativeTarget "main" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 53C445DC08E4A6F4006387E9 /* Development */, + 53C445DD08E4A6F4006387E9 /* Deployment */, + 53C445DE08E4A6F4006387E9 /* Default */, + ); + defaultConfigurationIsVisible = 0; + }; + 53D4A41D08A8931000CAE09B /* Build configuration list for PBXProject "openwire" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 53D4A41E08A8931000CAE09B /* Development */, + 53D4A41F08A8931000CAE09B /* Deployment */, + 53D4A42008A8931000CAE09B /* Default */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Default; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/openwire-c/src/examples/main.c b/openwire-c/src/examples/main.c new file mode 100755 index 0000000000..bf1827775d --- /dev/null +++ b/openwire-c/src/examples/main.c @@ -0,0 +1,81 @@ +/** + * + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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. + */ + +#include +#include "amqcs.h" + +int die(int exitCode, const char *message, apr_status_t reason) { + char msgbuf[80]; + apr_strerror(reason, msgbuf, sizeof(msgbuf)); + fprintf(stderr, "%s: %s (%d)\n", message, msgbuf, reason); + exit(exitCode); + return reason; +} + +static void terminate(void) +{ + apr_terminate(); +} + +int main(int argc, char *argv[]) +{ + apr_status_t rc; + amqcs_connection *connection; + apr_pool_t *pool; + amqcs_connect_options connect_options; + + memset(&connect_options, 0, sizeof(connect_options)); + strcpy(connect_options.hostname, "127.0.0.1"); + connect_options.port = 61616; + + setbuf(stdout, NULL); + + rc = apr_initialize(); + if( rc!=APR_SUCCESS ) + return rc; + atexit(terminate); + + rc = apr_pool_create(&pool, NULL); + rc==APR_SUCCESS || die(-2, "Could not allocate pool", rc); + + fprintf(stdout, "Connecting......"); + rc=amqcs_connect( &connection, &connect_options, pool); + rc==APR_SUCCESS || die(-2, "Could not connect", rc); + fprintf(stdout, "OK\n"); + + fprintf(stdout, "Sending message."); + { + char *buffer = "Hello World!"; + ow_ActiveMQQueue *dest; + ow_ActiveMQTextMessage *message = ow_ActiveMQTextMessage_create(pool); + message->content = ow_byte_array_create_with_data(pool,sizeof(buffer),buffer); + dest = ow_ActiveMQQueue_create(pool); + dest->physicalName = ow_string_create_from_cstring(pool,"TEST.QUEUE"); + rc = amqcs_send(connection, (ow_ActiveMQDestination*)dest, (ow_ActiveMQMessage*)message, 1,4,0,pool); + rc==APR_SUCCESS || die(-2, "Could not send message", rc); + } + fprintf(stdout, "OK\n"); + + fprintf(stdout, "Disconnecting..."); + rc=amqcs_disconnect(&connection); + rc==APR_SUCCESS || die(-2, "Could not disconnect", rc); + fprintf(stdout, "OK\n"); + + apr_pool_destroy(pool); + + return 0; +} diff --git a/openwire-c/src/libactivemq/amqcs.c b/openwire-c/src/libactivemq/amqcs.c new file mode 100755 index 0000000000..e0cf3a5fb5 --- /dev/null +++ b/openwire-c/src/libactivemq/amqcs.c @@ -0,0 +1,254 @@ +/** + * + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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. + */ +#include "amqcs.h" + +#include +#include "amqcs.h" + +#ifdef WIN32 +#define snprintf _snprintf +#endif + +static ow_byte ACTIVEMQ_MAGIC [] = {0x41,0x63,0x74,0x69,0x76,0x65,0x4D,0x51}; // ActiveMQ + +/** +* An ActiveMQ Single Threaded Client Connection + */ +struct amqcs_connection { + ow_transport *transport; + ow_ConnectionInfo *info; + ow_short lastCommandId; + ow_boolean useAsyncSend; + ow_boolean useMessageTimestamps; + ow_ProducerId *producerId; + ow_long lastMessageId; + ow_TransactionId *transactionId; +}; + +apr_status_t amqcs_async_send(amqcs_connection *connection, ow_BaseCommand *object) { + object->commandId = ++(connection->lastCommandId); + object->responseRequired = 0; + return ow_write_command(connection->transport, (ow_DataStructure*)object);; +} + +apr_status_t amqcs_sync_send(amqcs_connection *connection, ow_BaseCommand *object, apr_pool_t *pool) { + + apr_status_t rc; + ow_DataStructure *response; + + object->commandId = ++(connection->lastCommandId); + object->responseRequired = 1; + rc = ow_write_command(connection->transport, (ow_DataStructure*)object); + if( rc != APR_SUCCESS ) + return rc; + + for(;;) { + rc = ow_read_command(connection->transport,&response,pool); + if( rc != APR_SUCCESS ) + return rc; + + if( ow_is_a_Message(response) ) { + //TODO: implement message dispatching. + printf("Got a message.\n"); + } + + if( ow_is_a_Response(response) ) { + ow_Response *r = (ow_Response *)response; + if( r->correlationId == object->commandId ) { + if( ow_is_a_ExceptionResponse(response) ) { + return APR_EGENERAL; + } + return APR_SUCCESS; + } + } + } +} + +ow_ConnectionId *create_ConnectionId(amqcs_connection *connection, apr_pool_t *pool) { + + ow_ConnectionId *rc; + char buff[255]; + apr_time_t now = apr_time_now(); + + snprintf(buff, sizeof(buff), + "ID:%s:%d:%s:%d:%lld", + connection->transport->local_ip, + connection->transport->local_sa->port, + connection->transport->remote_ip, + connection->transport->remote_sa->port, + now + ); + + rc = ow_ConnectionId_create(pool); + rc->connectionId = ow_string_create_from_cstring(pool, buff); + return rc; +} + +ow_ProducerId *create_ProducerId(amqcs_connection *connection, apr_pool_t *pool) { + ow_ProducerId *rc; + rc = ow_ProducerId_create(pool); + rc->connectionId = connection->info->connectionId->connectionId; + rc->sessionId = -1; + rc->producerId = 1; + return rc; +} + +ow_WireFormatInfo *create_WireFormatInfo(apr_pool_t *pool) { + ow_WireFormatInfo *info = ow_WireFormatInfo_create(pool); + info->version = OW_WIREFORMAT_VERSION; + info->options = 0; + info->magic = ow_byte_array_create_with_data(pool, 8, ACTIVEMQ_MAGIC); + return info; +} + +apr_status_t amqcs_connect(amqcs_connection **conn, amqcs_connect_options *options, apr_pool_t *pool) { + + apr_status_t rc; + ow_DataStructure *command; + amqcs_connection *connection; + + apr_pool_t *temp_pool; + + rc = apr_pool_create(&temp_pool, pool); + if( rc != APR_SUCCESS ) + return rc; + +#define FAIL(rc) { apr_pool_destroy(temp_pool); return rc; } +#define CHECK_SUCCESS if( rc!=APR_SUCCESS ) { FAIL(rc); } + + connection = apr_pcalloc(pool, sizeof(amqcs_connection)); + if( connection == NULL ) + FAIL(APR_ENOMEM); + + rc = ow_connect(&connection->transport, options->hostname, options->port, pool); + CHECK_SUCCESS; + + // Negociate the WireFormat + rc = ow_write_command(connection->transport, (ow_DataStructure*)create_WireFormatInfo(temp_pool)); + CHECK_SUCCESS; + rc = ow_read_command(connection->transport, &command, temp_pool); + CHECK_SUCCESS; + + // Expecting a WireFormat back from the broker. + if( command->structType != OW_WIREFORMATINFO_TYPE ) + FAIL(APR_EGENERAL); + + { + // Verify that it's a valid wireformat + ow_WireFormatInfo *info = (ow_WireFormatInfo*)command; + if( memcmp(ACTIVEMQ_MAGIC, info->magic->values, sizeof(ACTIVEMQ_MAGIC)) !=0 ) { + FAIL(APR_EGENERAL); + } + // We can not connect to brokers that lower wireformat version + if( info->version < OW_WIREFORMAT_VERSION ) { + FAIL(APR_EGENERAL); + } + } + + // Send the Connection Info + { + connection->info = ow_ConnectionInfo_create(pool); + connection->info->connectionId = create_ConnectionId(connection, pool); + connection->info->clientId = ow_string_create_from_cstring(pool, options->clientId); + connection->info->userName = ow_string_create_from_cstring(pool, options->userId); + connection->info->password = ow_string_create_from_cstring(pool, options->password); + rc = amqcs_sync_send( connection, (ow_BaseCommand*)connection->info, temp_pool ); + CHECK_SUCCESS; + } + + connection->useAsyncSend = options->useAsyncSend; + connection->producerId = create_ProducerId(connection, pool); + +#undef CHECK_SUCCESS +#undef FAIL + + *conn = connection; + return APR_SUCCESS; +} + +apr_status_t amqcs_disconnect(amqcs_connection **connection) { + apr_status_t rc; + apr_pool_t *temp_pool; + + if( connection == NULL || *connection==NULL ) + return APR_EGENERAL; + + rc = apr_pool_create(&temp_pool, NULL); + if( rc == APR_SUCCESS ) { + // Send the RemoveInfo packet for the connection. + { + ow_RemoveInfo *info = ow_RemoveInfo_create(temp_pool); + info->objectId = (ow_DataStructure*)(*connection)->info->connectionId; + amqcs_async_send(*connection, (ow_BaseCommand*)info); + } + // Send the Shutdown packet. + { + ow_ShutdownInfo *info = ow_ShutdownInfo_create(temp_pool); + amqcs_async_send(*connection, (ow_BaseCommand*)info); + } + } + + rc = ow_disconnect(&(*connection)->transport); + *connection = NULL; + return rc; +} + +apr_status_t amqcs_send(amqcs_connection *connection, ow_ActiveMQDestination *dest, ow_ActiveMQMessage *message, + ow_int deliveryMode, ow_int priority, ow_long timeToLive, apr_pool_t *pool) { + + apr_pool_t *temp_pool; + apr_status_t rc; + + rc = apr_pool_create(&temp_pool, NULL); + if( rc!=APR_SUCCESS ) + return rc; + + message->messageId = ow_MessageId_create(pool); + message->messageId->producerId = connection->producerId; + message->messageId->producerSequenceId = ++(connection->lastMessageId); + message->destination = dest; + message->priority = priority; + message->persistent = deliveryMode==2; + if( connection->useMessageTimestamps ) { + message->timestamp = apr_time_now(); + if( timeToLive > 0 ) { + message->expiration = message->timestamp+timeToLive; + } + } + + message->producerId = connection->producerId; + message->originalDestination = dest; + message->destination = dest; + message->originalTransactionId = connection->transactionId; + message->transactionId = connection->transactionId; + +#define CHECK_SUCCESS if( rc!=APR_SUCCESS ) {apr_pool_destroy(temp_pool); return rc;} + + if( connection->useAsyncSend ) { + rc = amqcs_async_send( connection, (ow_BaseCommand*)message); + CHECK_SUCCESS; + } else { + rc = amqcs_sync_send( connection, (ow_BaseCommand*)message, temp_pool ); + CHECK_SUCCESS; + } + +#undef CHECK_SUCCESS + + apr_pool_destroy(temp_pool); + return APR_SUCCESS; +} + diff --git a/openwire-c/src/libactivemq/amqcs.h b/openwire-c/src/libactivemq/amqcs.h new file mode 100755 index 0000000000..5b9cd3fc81 --- /dev/null +++ b/openwire-c/src/libactivemq/amqcs.h @@ -0,0 +1,48 @@ +/** + * + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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. + */ +#ifndef AMQCS_H +#define AMQCS_H + +#include "ow.h" +#include "ow_commands_v1.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct amqcs_connection amqcs_connection; + +typedef struct amqcs_connect_options { + ow_char hostname[255]; + ow_int port; + ow_char userId[255]; + ow_char password[255]; + ow_char clientId[255]; + ow_boolean useAsyncSend; + + ow_byte reserved[1000]; +} amqcs_connect_options; + +apr_status_t amqcs_send(amqcs_connection *connection, ow_ActiveMQDestination *dest, ow_ActiveMQMessage *message, ow_int deliveryMode, ow_int priority, ow_long timeToLive, apr_pool_t *pool); +apr_status_t amqcs_disconnect(amqcs_connection **connection); +apr_status_t amqcs_connect(amqcs_connection **conn, amqcs_connect_options *options, apr_pool_t *pool); + +#ifdef __cplusplus +} +#endif + +#endif /* ! AMQCS_H */ \ No newline at end of file diff --git a/openwire-c/src/libopenwire/ow.c b/openwire-c/src/libopenwire/ow.c new file mode 100755 index 0000000000..a4f4d908ee --- /dev/null +++ b/openwire-c/src/libopenwire/ow.c @@ -0,0 +1,273 @@ +/** + * + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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. + */ + +#include + +#include "ow.h" +#include "ow_command_types_v1.h" + +/************************************************************************ + * Transport Connect and Disconnect + ************************************************************************/ +apr_status_t ow_connect(ow_transport **transport_ref, const char *hostname, int port, apr_pool_t *pool) +{ + apr_status_t rc; + int socket_family; + ow_transport *transport=NULL; + + // + // Allocate the transport and a memory pool for the transport. + // + transport = apr_pcalloc(pool, sizeof(transport)); + if( transport == NULL ) + return APR_ENOMEM; + +#define CHECK_SUCCESS if( rc!=APR_SUCCESS ) { return rc; } + + // Look up the remote address + rc = apr_sockaddr_info_get(&transport->remote_sa, hostname, APR_UNSPEC, port, 0, pool); + CHECK_SUCCESS; + + // Create and Connect the socket. + socket_family = transport->remote_sa->sa.sin.sin_family; + rc = apr_socket_create(&transport->socket, socket_family, SOCK_STREAM, APR_PROTO_TCP, pool); + CHECK_SUCCESS; + rc = apr_socket_connect(transport->socket, transport->remote_sa); + CHECK_SUCCESS; + + // Get the Socket Info + rc = apr_socket_addr_get(&transport->remote_sa, APR_REMOTE, transport->socket); + CHECK_SUCCESS; + rc = apr_sockaddr_ip_get(&transport->remote_ip, transport->remote_sa); + CHECK_SUCCESS; + rc = apr_socket_addr_get(&transport->local_sa, APR_LOCAL, transport->socket); + CHECK_SUCCESS; + rc = apr_sockaddr_ip_get(&transport->local_ip, transport->local_sa); + CHECK_SUCCESS; + + // Set socket options. + // rc = apr_socket_timeout_set( transport->socket, 2*APR_USEC_PER_SEC); + // CHECK_SUCCESS; + +#undef CHECK_SUCCESS + + *transport_ref = transport; + return rc; +} + +apr_status_t ow_disconnect(ow_transport **transport_ref) +{ + apr_status_t result, rc; + ow_transport *transport = *transport_ref; + + if( transport_ref == NULL || *transport_ref==NULL ) + return APR_EGENERAL; + + result = APR_SUCCESS; + rc = apr_socket_shutdown(transport->socket, APR_SHUTDOWN_WRITE); + if( result!=APR_SUCCESS ) + result = rc; + + if( transport->socket != NULL ) { + rc = apr_socket_close(transport->socket); + if( result!=APR_SUCCESS ) + result = rc; + transport->socket=NULL; + } + *transport_ref=NULL; + return rc; +} + +/************************************************************************ + * Read and Write byte data from a transport + ************************************************************************/ +apr_status_t ow_write(ow_transport *transport, const ow_byte *data, apr_size_t size) +{ + apr_size_t remaining = size; + size=0; + while( remaining>0 ) { + apr_size_t length = remaining; + apr_status_t rc = apr_socket_send(transport->socket, data, &length); + data+=length; + remaining -= length; + // size += length; + if( rc != APR_SUCCESS ) { + return rc; + } + } + return APR_SUCCESS; +} + +apr_status_t ow_read(ow_transport *transport, ow_byte *data, apr_size_t size) +{ + apr_size_t remaining = size; + size=0; + while( remaining>0 ) { + apr_size_t length = remaining; + apr_status_t rc = apr_socket_recv(transport->socket, data, &length); + data+=length; + remaining -= length; + // size += length; + if( rc != APR_SUCCESS ) { + return rc; + } + } + return APR_SUCCESS; +} + +/************************************************************************ + * Read and Write data structures from a transport + ************************************************************************/ +apr_status_t ow_write_command(ow_transport *transport, ow_DataStructure *object) +{ + apr_pool_t *pool; + apr_status_t rc; + ow_byte_buffer *buffer; + ow_bit_buffer *bitbuffer; + ow_int command_size; + + rc = apr_pool_create(&pool, NULL); + if( rc != APR_SUCCESS ) + return rc; + + buffer = ow_byte_buffer_create(pool); + bitbuffer = ow_bit_buffer_create(pool); + +#define CHECK_SUCCESS if( rc!=APR_SUCCESS ) { apr_pool_destroy(pool); return rc; } + + rc = ow_byte_buffer_append_byte(buffer, object->structType); + CHECK_SUCCESS; + rc = ow_marshal1_object(bitbuffer, object); + CHECK_SUCCESS; + rc = ow_byte_buffer_append_bit_buffer(buffer, bitbuffer); + CHECK_SUCCESS; + rc = ow_marshal2_object(buffer, bitbuffer, object); + CHECK_SUCCESS; + + command_size = ENDIAN_FLIP_INT(buffer->size); + + rc = ow_write(transport, (ow_byte*)&command_size, sizeof(command_size)); + CHECK_SUCCESS; + rc = ow_byte_buffer_write(buffer, transport); + CHECK_SUCCESS; +#undef CHECK_SUCCESS + + apr_pool_destroy(pool); + return rc; +} + +apr_status_t ow_read_command(ow_transport *transport, ow_DataStructure **object, apr_pool_t *pool) +{ + apr_status_t rc; + ow_byte_array data; + ow_bit_buffer *bitbuffer; + ow_byte type; + +#define CHECK_SUCCESS if( rc!=APR_SUCCESS ) { return rc; } + // Find out how much data to read in. + rc = ow_read(transport, (ow_byte*)&data.size, sizeof(data.size)); + CHECK_SUCCESS; + + data.size = ENDIAN_FLIP_INT(data.size); + + // Read in the data + data.values = apr_pcalloc(pool,data.size); + rc = ow_read(transport, data.values, data.size); + CHECK_SUCCESS; + + rc = ow_byte_array_read_byte(&data, &type); + CHECK_SUCCESS; + rc = ow_byte_array_read_bit_buffer(&data, &bitbuffer, pool); + CHECK_SUCCESS; +#undef CHECK_SUCCESS + + *object = ow_create_object(type, pool); + if( *object == 0 ) + return APR_ENOMEM; + + rc = ow_unmarshal_object(&data, bitbuffer, *object, pool); + if( rc != APR_SUCCESS ) + *object = 0; + + return rc; +} + + +/************************************************************************ + * Core data structure functions. + ************************************************************************/ +ow_byte_array *ow_byte_array_create(apr_pool_t *pool, apr_size_t size) +{ + ow_byte_array *b = apr_pcalloc(pool,sizeof(ow_byte_array)); + if( b!=NULL ) { + b->size=size; + b->values = apr_pcalloc(pool,size*sizeof(ow_byte)); + if( b->values == NULL ) + return NULL; + } + return b; +} + +ow_byte_array *ow_byte_array_create_with_data(apr_pool_t *pool, apr_size_t size, ow_byte *data) +{ + ow_byte_array *b = apr_pcalloc(pool,sizeof(ow_byte_array)); + if( b!=NULL ) { + b->size=size; + b->values = data; + } + return b; +} + +ow_string *ow_string_create(apr_pool_t *pool, apr_size_t size) +{ + ow_string *b = apr_pcalloc(pool,sizeof(ow_string)); + if( b!=NULL ) { + b->size=size; + b->values = apr_pcalloc(pool,size*sizeof(ow_char)); + if( b->values == NULL ) + return NULL; + } + return b; +} + +ow_string *ow_string_create_from_cstring(apr_pool_t *pool, const ow_char *source) +{ + ow_string *b = ow_string_create(pool,strlen(source)); + if( b!=NULL ) { + strcpy(b->values, source); + } + return b; +} + +ow_DataStructure_array *ow_DataStructure_array_create(apr_pool_t *pool, apr_size_t size) +{ + ow_DataStructure_array *b = apr_pcalloc(pool,sizeof(ow_DataStructure_array)); + if( b!=NULL ) { + b->size=size; + b->values = apr_pcalloc(pool,size*sizeof(ow_DataStructure*)); + if( b->values == NULL ) + return NULL; + } + return b; +} + +ow_throwable *ow_throwable_create(apr_pool_t *pool) +{ + ow_throwable *b = apr_pcalloc(pool,sizeof(ow_throwable)); + return b; +} + diff --git a/openwire-c/src/libopenwire/ow.h b/openwire-c/src/libopenwire/ow.h new file mode 100755 index 0000000000..547ead82b8 --- /dev/null +++ b/openwire-c/src/libopenwire/ow.h @@ -0,0 +1,224 @@ +/** + * + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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. + */ + +#ifndef AMQC_H +#define AMQC_H + +#include "apr_general.h" +#include "apr_network_io.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/************************************************************************ + * Map the ow primitive types to the java counterparts. + ************************************************************************/ +typedef char ow_boolean; +typedef char ow_byte; +typedef char ow_char; +typedef apr_int16_t ow_short; +typedef apr_int32_t ow_int; +typedef apr_int64_t ow_long; +typedef float ow_float; +typedef double ow_double; + +/************************************************************************ + * Helpers to handle endian conversions. + ************************************************************************/ +#if APR_IS_BIGENDIAN +#define ENDIAN_FLIP 0 +#define ENDIAN_FLIP_INT(x) x +#else +#define ENDIAN_FLIP 1 +#define ENDIAN_FLIP_INT(x) \ + ( x << 24 ) & 0xFF000000 | \ + ( x << 8 ) & 0x00FF0000 | \ + ( x >> 8 ) & 0x0000FF00 | \ + ( x >> 24 ) & 0x000000FF +#endif + +typedef struct ow_transport { + apr_socket_t *socket; + apr_sockaddr_t *local_sa; + char *local_ip; + apr_sockaddr_t *remote_sa; + char *remote_ip; +} ow_transport; + +typedef struct ow_byte_array { + ow_int size; + ow_byte *values; +} ow_byte_array; + +typedef struct ow_string { + ow_int size; + ow_char *values; +} ow_string; + +typedef struct ow_throwable { + struct ow_string errorClass; + struct ow_string message; +} ow_throwable; + +typedef struct ow_DataStructure { + ow_short structType; +} ow_DataStructure; + +typedef struct ow_DataStructure_array { + ow_int size; + struct ow_DataStructure **values; +} ow_DataStructure_array; + +typedef struct ow_bit_buffer { + ow_short arrayLimit; + ow_short arrayPos; + ow_short bytePos; + ow_byte values[200]; +} ow_bit_buffer; + +typedef struct ow_byte_buffer_entry { + struct ow_byte_buffer_entry *next; + ow_int size; + ow_byte *values; +} ow_byte_buffer_entry; + +typedef struct ow_byte_buffer { + ow_int size; + ow_int last_alloc; + apr_pool_t *pool; + struct ow_byte_buffer_entry *head; +} ow_byte_buffer; + + +/************************************************************************ + * The transport interface related functions + ************************************************************************/ +apr_status_t ow_connect(ow_transport **transport_ref, const char *hostname, int port, apr_pool_t *pool); +apr_status_t ow_disconnect(ow_transport **transport_ref); +apr_status_t ow_write(ow_transport *transport, const ow_byte *data, apr_size_t size); +apr_status_t ow_read(ow_transport *transport, ow_byte *data, apr_size_t size); +apr_status_t ow_write_command(ow_transport *transport, ow_DataStructure *object); +apr_status_t ow_read_command(ow_transport *transport, ow_DataStructure **object, apr_pool_t *pool); + +/************************************************************************ + * The primitive handling functions. + ************************************************************************/ +ow_byte_array *ow_byte_array_create(apr_pool_t *pool, apr_size_t size); +ow_byte_array *ow_byte_array_create_with_data(apr_pool_t *pool, apr_size_t size, ow_byte *data); +ow_string *ow_string_create(apr_pool_t *pool, apr_size_t size); +ow_string *ow_string_create_from_cstring(apr_pool_t *pool, const ow_char *source); +ow_DataStructure_array *ow_DataStructure_array_create(apr_pool_t *pool, apr_size_t size); +ow_throwable *ow_throwable_create(apr_pool_t *pool); + +/************************************************************************ + * The ow_bit_buffer and ow_byte_buffer related functions + ************************************************************************/ +ow_bit_buffer *ow_bit_buffer_create(apr_pool_t *pool); + +ow_boolean ow_bit_buffer_read(ow_bit_buffer *buffer); +void ow_bit_buffer_append(ow_bit_buffer *buffer, ow_boolean value); +void ow_bit_buffer_clear(ow_bit_buffer *buffer); + +ow_byte_buffer *ow_byte_buffer_create(apr_pool_t *pool); + +apr_status_t ow_byte_buffer_entry_write(ow_byte_buffer_entry *b, ow_transport *transport); +apr_status_t ow_byte_buffer_write(ow_byte_buffer *buffer, ow_transport *transport); + +apr_status_t ow_byte_buffer_append_bit_buffer(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer); +apr_status_t ow_byte_array_read_bit_buffer(ow_byte_array *buffer, ow_bit_buffer **value, apr_pool_t *pool); + +/************************************************************************ + * The primitive marshalling functions. + ************************************************************************/ +apr_status_t ow_byte_buffer_append(ow_byte_buffer *b, ow_byte *buffer, ow_int size, ow_boolean flip); +apr_status_t ow_byte_array_read(ow_byte_array *array, ow_byte *dest, ow_int size, ow_boolean flip); + +apr_status_t ow_byte_buffer_append_byte(ow_byte_buffer *buffer, ow_byte value); +apr_status_t ow_byte_array_read_byte(ow_byte_array *buffer, ow_byte *value); + +apr_status_t ow_byte_buffer_append_boolean(ow_byte_buffer *buffer, ow_boolean value); +apr_status_t ow_byte_array_read_boolean(ow_byte_array *buffer, ow_boolean *value); + +apr_status_t ow_byte_buffer_append_char(ow_byte_buffer *buffer, ow_char value); +apr_status_t ow_byte_array_read_char(ow_byte_array *buffer, ow_char *value); + +apr_status_t ow_byte_buffer_append_short(ow_byte_buffer *buffer, ow_short value); +apr_status_t ow_byte_array_read_short(ow_byte_array *buffer, ow_short *value); + +apr_status_t ow_byte_buffer_append_int(ow_byte_buffer *buffer, ow_int value); +apr_status_t ow_byte_array_read_int(ow_byte_array *buffer, ow_int *value); + +apr_status_t ow_byte_buffer_append_long(ow_byte_buffer *buffer, ow_long value); +apr_status_t ow_byte_array_read_long(ow_byte_array *buffer, ow_long *value); + +apr_status_t ow_byte_buffer_append_float(ow_byte_buffer *buffer, ow_float value); +apr_status_t ow_byte_array_read_float(ow_byte_array *buffer, ow_float *value); + +apr_status_t ow_byte_buffer_append_double(ow_byte_buffer *buffer, ow_double value); +apr_status_t ow_byte_array_read_double(ow_byte_array *buffer, ow_double *value); + +/************************************************************************ + * The base command marshalling functions + ************************************************************************/ +apr_status_t ow_marshal1_nested_object(ow_bit_buffer *bitbuffer, ow_DataStructure *object); +apr_status_t ow_marshal2_nested_object(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure *object); +apr_status_t ow_unmarshal_nested_object(ow_byte_array *data, ow_bit_buffer *bitbuffer, ow_DataStructure **object, apr_pool_t *pool); + +apr_status_t ow_marshal1_cached_object(ow_bit_buffer *bitbuffer, ow_DataStructure *object); +apr_status_t ow_marshal2_cached_object(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure *object); +apr_status_t ow_unmarshal_cached_object(ow_byte_array *data, ow_bit_buffer *bitbuffer, ow_DataStructure **object, apr_pool_t *pool); + +apr_status_t ow_marshal1_string(ow_bit_buffer *buffer, ow_string *value); +apr_status_t ow_marshal2_string(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_string *value); +apr_status_t ow_unmarshal_string(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_string **value, apr_pool_t *pool); + +apr_status_t ow_marshal1_throwable(ow_bit_buffer *buffer, ow_throwable *value); +apr_status_t ow_marshal2_throwable(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_throwable *value); +apr_status_t ow_unmarshal_throwable(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_throwable **value, apr_pool_t *pool); + +apr_status_t ow_marshal2_byte_array_const_size(ow_byte_buffer *buffer, ow_byte_array *value, ow_int size); +apr_status_t ow_marshal2_byte_array(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_byte_array *value); +apr_status_t ow_unmarshal_byte_array(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_byte_array **value, apr_pool_t *pool); +apr_status_t ow_unmarshal_byte_array_const_size(ow_byte_array *buffer, ow_byte_array **value, ow_int size, apr_pool_t *pool); + +apr_status_t ow_marshal1_DataStructure(ow_bit_buffer *buffer, ow_DataStructure *object); +apr_status_t ow_marshal2_DataStructure(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure *object); +apr_status_t ow_unmarshal_DataStructure(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure *object, apr_pool_t *pool); + +apr_status_t ow_marshal1_DataStructure_array_const_size(ow_bit_buffer *buffer, ow_DataStructure_array *value, ow_int size); +apr_status_t ow_marshal2_DataStructure_array_const_size(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure_array *value, ow_int size); +apr_status_t ow_unmarshal_DataStructure_array_const_size(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure_array **value, ow_int size, apr_pool_t *pool); + +apr_status_t ow_marshal1_DataStructure_array(ow_bit_buffer *buffer, ow_DataStructure_array *value); +apr_status_t ow_marshal2_DataStructure_array(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure_array *value); +apr_status_t ow_unmarshal_DataStructure_array(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure_array **value, apr_pool_t *pool); + +ow_DataStructure *ow_create_object(ow_byte type, apr_pool_t *pool); +apr_status_t ow_marshal1_object(ow_bit_buffer *buffer, ow_DataStructure *object); +apr_status_t ow_marshal2_object(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure *object); +apr_status_t ow_unmarshal_object(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure *object, apr_pool_t *pool); + +apr_status_t ow_marshal1_long(ow_bit_buffer *buffer, ow_long value); +apr_status_t ow_marshal2_long(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_long value); +apr_status_t ow_unmarshal_long(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_long *value, apr_pool_t *pool); + +#ifdef __cplusplus +} +#endif + +#endif /* ! AMQC_H */ diff --git a/openwire-c/src/libopenwire/ow_buffer.c b/openwire-c/src/libopenwire/ow_buffer.c new file mode 100755 index 0000000000..f130d75afc --- /dev/null +++ b/openwire-c/src/libopenwire/ow_buffer.c @@ -0,0 +1,252 @@ +/** + * + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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. + */ + +#include "ow.h" + +/************************************************************************ + * bit_buffer functions + ************************************************************************/ + +ow_bit_buffer *ow_bit_buffer_create(apr_pool_t *pool) +{ + ow_bit_buffer *b = apr_pcalloc(pool,sizeof(ow_bit_buffer)); + return b; +} + +ow_boolean ow_bit_buffer_read(ow_bit_buffer *buffer) { + ow_byte b = buffer->values[buffer->arrayPos]; + ow_boolean rc = ((b>>buffer->bytePos)&0x01)!=0; + buffer->bytePos++; + if( buffer->bytePos >= 8 ) { + buffer->bytePos=0; + buffer->arrayPos++; + } + return rc; +} + +void ow_bit_buffer_append(ow_bit_buffer *buffer, ow_boolean value) { + if( value ) { + buffer->values[buffer->arrayPos] |= (0x01 << buffer->bytePos); + } + if( buffer->bytePos == 0 ) { + buffer->arrayLimit++; + } + buffer->bytePos++; + if( buffer->bytePos >= 8 ) { + buffer->bytePos=0; + buffer->arrayPos++; + } +} + +void ow_bit_buffer_clear(ow_bit_buffer *buffer) { + buffer->arrayPos=0; + buffer->bytePos=0; +} + +/************************************************************************ + * byte_buffer functions + ************************************************************************/ + +ow_byte_buffer *ow_byte_buffer_create(apr_pool_t *pool) +{ + ow_byte_buffer *b = apr_pcalloc(pool,sizeof(ow_byte_buffer)); + if( b!=NULL ) { + b->size=0; + b->head = NULL; + b->last_alloc = 0; + b->pool = pool; + } + return b; +} + +apr_status_t ow_byte_buffer_append(ow_byte_buffer *b, ow_byte *buffer, ow_int size, ow_boolean flip) +{ + + // If we are starting from the back + if(flip) { + buffer += size; + } + + while( size > 0 ) { + + int n,r,i; + + // Do we need to allocate another block? + if( b->head == NULL || b->head->size==b->last_alloc ) { + int next_alloc; + ow_byte_buffer_entry *entry = apr_pcalloc(b->pool, sizeof(ow_byte_buffer_entry)); + if( entry == NULL ) + return APR_ENOMEM; + entry->next = b->head; + b->head = entry; + next_alloc = b->last_alloc==0 ? 512 : b->last_alloc*2; + entry->values = apr_pcalloc(b->pool, next_alloc); + if( entry->values == NULL ) + return APR_ENOMEM; + b->last_alloc = next_alloc; + } + + r = b->last_alloc - b->head->size; + n = size > r ? r : size; + + if(flip) { + char *p = b->head->values + b->head->size; + for( i=0; i < n; i++ ) { + buffer--; + *p = *buffer; + p++; + } + } else { + memcpy(b->head->values+b->head->size,buffer,n); + buffer += n; + } + b->head->size += n; + b->size += n; + size -= n; + } + + return APR_SUCCESS; +} + +apr_status_t ow_byte_array_read(ow_byte_array *array, ow_byte *dest, ow_int size, ow_boolean flip) { + int i; + if( array->size < size ) { + return APR_EOF; + } + if( flip ) { + for( i=size-1; i >= 0; i-- ) { + dest[i] = *(array->values); + array->values++; + } + array->size-=size; + } else { + memcpy(dest,array->values,size); + array->size-=size; + array->values+=size; + } + return APR_SUCCESS; +} + +apr_status_t ow_byte_buffer_append_bit_buffer(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer) { + apr_status_t rc = ow_byte_buffer_append_byte(buffer, bitbuffer->arrayLimit); + if( rc != APR_SUCCESS ) + return rc; + rc = ow_byte_buffer_append(buffer, bitbuffer->values, bitbuffer->arrayLimit, 0); + if( rc != APR_SUCCESS ) + return rc; + ow_bit_buffer_clear(bitbuffer); + return rc; +} +apr_status_t ow_byte_array_read_bit_buffer(ow_byte_array *buffer, ow_bit_buffer **value, apr_pool_t *pool) { + apr_status_t rc; + ow_byte size; + rc = ow_byte_array_read_byte(buffer, &size); + if( rc != APR_SUCCESS ) + return rc; + + if( size >= 0 ) { + + if( buffer->size < size ) + return APR_EOF; + + *value = apr_pcalloc(pool, sizeof(ow_bit_buffer)); + if( *value == NULL ) + return APR_ENOMEM; + + (*value)->arrayLimit = size; + return ow_byte_array_read(buffer, (*value)->values, size, 0); + + } else { + *value=NULL; + } + return ow_byte_array_read(buffer,(ow_byte*)value, sizeof(ow_double), ENDIAN_FLIP); +} + +apr_status_t ow_byte_buffer_entry_write(ow_byte_buffer_entry *b, ow_transport *transport) +{ + if( b!=NULL ) { + apr_status_t rc = ow_byte_buffer_entry_write(b->next, transport); + if( rc != APR_SUCCESS ) + return rc; + return ow_write(transport, b->values, b->size); + } + return APR_SUCCESS; +} +apr_status_t ow_byte_buffer_write(ow_byte_buffer *buffer, ow_transport *transport) { + return ow_byte_buffer_entry_write(buffer->head, transport); +} + + +/************************************************************************ + * Functions that marshal/unmarshal primitives to the byte_buffer + ************************************************************************/ + +apr_status_t ow_byte_buffer_append_byte(ow_byte_buffer *buffer, ow_byte value) { + return ow_byte_buffer_append(buffer,(ow_byte*)&value, sizeof(ow_byte), ENDIAN_FLIP); +} +apr_status_t ow_byte_array_read_byte(ow_byte_array *buffer, ow_byte *value) { + return ow_byte_array_read(buffer,(ow_byte*)value, sizeof(ow_byte), ENDIAN_FLIP); +} + +apr_status_t ow_byte_buffer_append_boolean(ow_byte_buffer *buffer, ow_boolean value) { + return ow_byte_buffer_append(buffer,(ow_byte*)&value, sizeof(ow_boolean), ENDIAN_FLIP); +} +apr_status_t ow_byte_array_read_boolean(ow_byte_array *buffer, ow_boolean *value) { + return ow_byte_array_read(buffer,(ow_byte*)value, sizeof(ow_boolean), ENDIAN_FLIP); +} + +apr_status_t ow_byte_buffer_append_char(ow_byte_buffer *buffer, ow_char value) { + return ow_byte_buffer_append(buffer,(ow_byte*)&value, sizeof(ow_char), ENDIAN_FLIP); +} +apr_status_t ow_byte_array_read_char(ow_byte_array *buffer, ow_char *value) { + return ow_byte_array_read(buffer,(ow_byte*)value, sizeof(ow_char), ENDIAN_FLIP); +} + +apr_status_t ow_byte_buffer_append_short(ow_byte_buffer *buffer, ow_short value) { + return ow_byte_buffer_append(buffer,(ow_byte*)&value, sizeof(ow_short), ENDIAN_FLIP); +} +apr_status_t ow_byte_array_read_short(ow_byte_array *buffer, ow_short *value) { + return ow_byte_array_read(buffer,(ow_byte*)value, sizeof(ow_short), ENDIAN_FLIP); +} + +apr_status_t ow_byte_buffer_append_int(ow_byte_buffer *buffer, ow_int value) { + return ow_byte_buffer_append(buffer,(ow_byte*)&value, sizeof(ow_int), ENDIAN_FLIP); +} +apr_status_t ow_byte_array_read_int(ow_byte_array *buffer, ow_int *value) { + return ow_byte_array_read(buffer,(ow_byte*)value, sizeof(ow_int), ENDIAN_FLIP); +} + +apr_status_t ow_byte_buffer_append_long(ow_byte_buffer *buffer, ow_long value) { + return ow_byte_buffer_append(buffer,(ow_byte*)&value, sizeof(ow_long), ENDIAN_FLIP); +} +apr_status_t ow_byte_array_read_long(ow_byte_array *buffer, ow_long *value) { + return ow_byte_array_read(buffer,(ow_byte*)value, sizeof(ow_long), ENDIAN_FLIP); +} + +apr_status_t ow_byte_buffer_append_float(ow_byte_buffer *buffer, ow_float value) { + return ow_byte_buffer_append(buffer,(ow_byte*)&value, sizeof(ow_float), ENDIAN_FLIP); +} +apr_status_t ow_byte_array_read_float(ow_byte_array *buffer, ow_float *value) { + return ow_byte_array_read(buffer,(ow_byte*)value, sizeof(ow_float), ENDIAN_FLIP); +} + +apr_status_t ow_byte_buffer_append_double(ow_byte_buffer *buffer, ow_double value) { + return ow_byte_buffer_append(buffer,(ow_byte*)&value, sizeof(ow_double), ENDIAN_FLIP); +} +apr_status_t ow_byte_array_read_double(ow_byte_array *buffer, ow_double *value) { + return ow_byte_array_read(buffer,(ow_byte*)value, sizeof(ow_double), ENDIAN_FLIP); +} diff --git a/openwire-c/src/libopenwire/ow_command_types_v1.h b/openwire-c/src/libopenwire/ow_command_types_v1.h new file mode 100755 index 0000000000..cc24004f0b --- /dev/null +++ b/openwire-c/src/libopenwire/ow_command_types_v1.h @@ -0,0 +1,101 @@ +/** + * + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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. + */ + +#ifndef OW_COMMAND_TYPES_V1_H +#define OW_COMMAND_TYPES_V1_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define OW_WIREFORMAT_STACK_TRACE_MASK 0x00000001; +#define OW_WIREFORMAT_TCP_NO_DELAY_MASK 0x00000002; +#define OW_WIREFORMAT_CACHE_MASK 0x00000004; +#define OW_WIREFORMAT_COMPRESSION_MASK 0x00000008; + +#define OW_NULL_TYPE 0 +#define OW_WIREFORMATINFO_TYPE 1 +#define OW_BROKERINFO_TYPE 2 +#define OW_CONNECTIONINFO_TYPE 3 +#define OW_SESSIONINFO_TYPE 4 +#define OW_CONSUMERINFO_TYPE 5 +#define OW_PRODUCERINFO_TYPE 6 +#define OW_TRANSACTIONINFO_TYPE 7 +#define OW_DESTINATIONINFO_TYPE 8 +#define OW_REMOVESUBSCRIPTIONINFO_TYPE 9 +#define OW_KEEPALIVEINFO_TYPE 10 +#define OW_SHUTDOWNINFO_TYPE 11 +#define OW_REMOVEINFO_TYPE 12 +#define OW_REDELIVERYPOLICY_TYPE 13 +#define OW_CONTROLCOMMAND_TYPE 14 + +#define OW_MESSAGEDISPATCH_TYPE 21 +#define OW_MESSAGEACK_TYPE 22 + +#define OW_ACTIVEMQMESSAGE_TYPE 23 +#define OW_ACTIVEMQBYTESMESSAGE_TYPE 24 +#define OW_ACTIVEMQMAPMESSAGE_TYPE 25 +#define OW_ACTIVEMQOBJECTMESSAGE_TYPE 26 +#define OW_ACTIVEMQSTREAMMESSAGE_TYPE 27 +#define OW_ACTIVEMQTEXTMESSAGE_TYPE 28 + +#define OW_RESPONSE_TYPE 30 +#define OW_EXCEPTIONRESPONSE_TYPE 31 +#define OW_DATARESPONSE_TYPE 32 +#define OW_DATAARRAYRESPONSE_TYPE 33 +#define OW_INTEGERRESPONSE_TYPE 34 + +#define OW_JOURNALTOPICACK_TYPE 50 +#define OW_JOURNALADD_TYPE 51 +#define OW_JOURNALQUEUEACK_TYPE 52 +#define OW_JOURNALTRACE_TYPE 53 +#define OW_JOURNALTRANSACTION_TYPE 54 +#define OW_SUBSCRIPTIONINFO_TYPE 55 + +#define OW_BYTE_TYPE 70 +#define OW_CHAR_TYPE 71 +#define OW_SHORT_TYPE 72 +#define OW_INTEGER_TYPE 73 +#define OW_LONG_TYPE 74 +#define OW_DOUBLE_TYPE 75 +#define OW_FLOAT_TYPE 76 +#define OW_STRING_TYPE 77 +#define OW_BOOLEAN_TYPE 78 +#define OW_BYTE_ARRAY_TYPE 79 + + +#define OW_ACTIVEMQQUEUE_TYPE 100 +#define OW_ACTIVEMQTOPIC_TYPE 101 +#define OW_ACTIVEMQTEMPQUEUE_TYPE 102 +#define OW_ACTIVEMQTEMPTOPIC_TYPE 103 + +#define OW_MESSAGEID_TYPE 110 +#define OW_LOCALTRANSACTIONID_TYPE 111 +#define OW_XATRANSACTIONID_TYPE 112 + +#define OW_CONNECTIONID_TYPE 120 +#define OW_SESSIONID_TYPE 121 +#define OW_CONSUMERID_TYPE 122 +#define OW_PRODUCERID_TYPE 123 +#define OW_BROKERID_TYPE 124 + +#ifdef __cplusplus +} +#endif + +#endif /* ! OW_COMMAND_TYPES_V1_H */ diff --git a/openwire-c/src/libopenwire/ow_commands_v1.c b/openwire-c/src/libopenwire/ow_commands_v1.c new file mode 100644 index 0000000000..41e4071d1c --- /dev/null +++ b/openwire-c/src/libopenwire/ow_commands_v1.c @@ -0,0 +1,2713 @@ +/** + * + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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. + */ + +/***************************************************************************************** + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + *****************************************************************************************/ + + +#include "ow_commands_v1.h" + +#define SUCCESS_CHECK( f ) { apr_status_t rc=f; if(rc!=APR_SUCCESS) return rc; } + + +ow_boolean ow_is_a_MessageId(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_MESSAGEID_TYPE: + return 1; + } + return 0; +} + + +ow_MessageId *ow_MessageId_create(apr_pool_t *pool) +{ + ow_MessageId *value = apr_pcalloc(pool,sizeof(ow_MessageId)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_MESSAGEID_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_MessageId(ow_bit_buffer *buffer, ow_MessageId *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->producerId)); + ow_marshal1_long(buffer, object->producerSequenceId); + ow_marshal1_long(buffer, object->brokerSequenceId); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_MessageId(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_MessageId *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->producerId)); + SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->producerSequenceId)); + SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->brokerSequenceId)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_MessageId(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_MessageId *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->producerId, pool)); + SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->producerSequenceId, pool)); + SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->brokerSequenceId, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_SubscriptionInfo(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_SUBSCRIPTIONINFO_TYPE: + return 1; + } + return 0; +} + + +ow_SubscriptionInfo *ow_SubscriptionInfo_create(apr_pool_t *pool) +{ + ow_SubscriptionInfo *value = apr_pcalloc(pool,sizeof(ow_SubscriptionInfo)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_SUBSCRIPTIONINFO_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_SubscriptionInfo(ow_bit_buffer *buffer, ow_SubscriptionInfo *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + ow_marshal1_string(buffer, object->clientId); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->destination)); + ow_marshal1_string(buffer, object->selector); + ow_marshal1_string(buffer, object->subcriptionName); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_SubscriptionInfo(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_SubscriptionInfo *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->clientId)); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->selector)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->subcriptionName)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_SubscriptionInfo(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_SubscriptionInfo *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->clientId, pool)); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->destination, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->selector, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->subcriptionName, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ActiveMQDestination(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_ACTIVEMQTOPIC_TYPE: + case OW_ACTIVEMQQUEUE_TYPE: + case OW_ACTIVEMQTEMPTOPIC_TYPE: + case OW_ACTIVEMQTEMPQUEUE_TYPE: + return 1; + } + return 0; +} + + +apr_status_t ow_marshal1_ActiveMQDestination(ow_bit_buffer *buffer, ow_ActiveMQDestination *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + ow_marshal1_string(buffer, object->physicalName); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ActiveMQDestination(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQDestination *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->physicalName)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ActiveMQDestination(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQDestination *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->physicalName, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ConnectionId(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_CONNECTIONID_TYPE: + return 1; + } + return 0; +} + + +ow_ConnectionId *ow_ConnectionId_create(apr_pool_t *pool) +{ + ow_ConnectionId *value = apr_pcalloc(pool,sizeof(ow_ConnectionId)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_CONNECTIONID_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ConnectionId(ow_bit_buffer *buffer, ow_ConnectionId *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + ow_marshal1_string(buffer, object->connectionId); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ConnectionId(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ConnectionId *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->connectionId)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ConnectionId(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ConnectionId *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->connectionId, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_JournalTrace(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_JOURNALTRACE_TYPE: + return 1; + } + return 0; +} + + +ow_JournalTrace *ow_JournalTrace_create(apr_pool_t *pool) +{ + ow_JournalTrace *value = apr_pcalloc(pool,sizeof(ow_JournalTrace)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_JOURNALTRACE_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_JournalTrace(ow_bit_buffer *buffer, ow_JournalTrace *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + ow_marshal1_string(buffer, object->message); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_JournalTrace(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_JournalTrace *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->message)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_JournalTrace(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_JournalTrace *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->message, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_KeepAliveInfo(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_KEEPALIVEINFO_TYPE: + return 1; + } + return 0; +} + + +ow_KeepAliveInfo *ow_KeepAliveInfo_create(apr_pool_t *pool) +{ + ow_KeepAliveInfo *value = apr_pcalloc(pool,sizeof(ow_KeepAliveInfo)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_KEEPALIVEINFO_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_KeepAliveInfo(ow_bit_buffer *buffer, ow_KeepAliveInfo *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_KeepAliveInfo(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_KeepAliveInfo *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_KeepAliveInfo(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_KeepAliveInfo *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_JournalQueueAck(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_JOURNALQUEUEACK_TYPE: + return 1; + } + return 0; +} + + +ow_JournalQueueAck *ow_JournalQueueAck_create(apr_pool_t *pool) +{ + ow_JournalQueueAck *value = apr_pcalloc(pool,sizeof(ow_JournalQueueAck)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_JOURNALQUEUEACK_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_JournalQueueAck(ow_bit_buffer *buffer, ow_JournalQueueAck *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->messageAck)); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_JournalQueueAck(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_JournalQueueAck *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->messageAck)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_JournalQueueAck(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_JournalQueueAck *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->destination, pool)); + SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->messageAck, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_BrokerId(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_BROKERID_TYPE: + return 1; + } + return 0; +} + + +ow_BrokerId *ow_BrokerId_create(apr_pool_t *pool) +{ + ow_BrokerId *value = apr_pcalloc(pool,sizeof(ow_BrokerId)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_BROKERID_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_BrokerId(ow_bit_buffer *buffer, ow_BrokerId *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + ow_marshal1_string(buffer, object->brokerId); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_BrokerId(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_BrokerId *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->brokerId)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_BrokerId(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_BrokerId *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->brokerId, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_BaseCommand(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_SESSIONINFO_TYPE: + case OW_SHUTDOWNINFO_TYPE: + case OW_DESTINATIONINFO_TYPE: + case OW_CONSUMERINFO_TYPE: + case OW_CONNECTIONINFO_TYPE: + case OW_TRANSACTIONINFO_TYPE: + case OW_RESPONSE_TYPE: + case OW_REMOVEINFO_TYPE: + case OW_ACTIVEMQMESSAGE_TYPE: + case OW_CONTROLCOMMAND_TYPE: + case OW_FLUSHCOMMAND_TYPE: + case OW_INTEGERRESPONSE_TYPE: + case OW_REMOVESUBSCRIPTIONINFO_TYPE: + case OW_DATAARRAYRESPONSE_TYPE: + case OW_BROKERINFO_TYPE: + case OW_MESSAGEDISPATCH_TYPE: + case OW_ACTIVEMQSTREAMMESSAGE_TYPE: + case OW_PRODUCERINFO_TYPE: + case OW_MESSAGEACK_TYPE: + case OW_ACTIVEMQBYTESMESSAGE_TYPE: + case OW_ACTIVEMQTEXTMESSAGE_TYPE: + case OW_ACTIVEMQMAPMESSAGE_TYPE: + case OW_DATARESPONSE_TYPE: + case OW_ACTIVEMQOBJECTMESSAGE_TYPE: + case OW_EXCEPTIONRESPONSE_TYPE: + return 1; + } + return 0; +} + + +apr_status_t ow_marshal1_BaseCommand(ow_bit_buffer *buffer, ow_BaseCommand *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + + ow_bit_buffer_append(buffer, object->responseRequired); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_BaseCommand(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_BaseCommand *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_byte_buffer_append_short(buffer, object->commandId)); + ow_bit_buffer_read(bitbuffer); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_BaseCommand(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_BaseCommand *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_byte_array_read_short(buffer, &object->commandId)); + object->responseRequired = ow_bit_buffer_read(bitbuffer); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_SessionInfo(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_SESSIONINFO_TYPE: + return 1; + } + return 0; +} + + +ow_SessionInfo *ow_SessionInfo_create(apr_pool_t *pool) +{ + ow_SessionInfo *value = apr_pcalloc(pool,sizeof(ow_SessionInfo)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_SESSIONINFO_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_SessionInfo(ow_bit_buffer *buffer, ow_SessionInfo *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->sessionId)); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_SessionInfo(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_SessionInfo *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->sessionId)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_SessionInfo(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_SessionInfo *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->sessionId, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_Message(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_ACTIVEMQMESSAGE_TYPE: + case OW_ACTIVEMQSTREAMMESSAGE_TYPE: + case OW_ACTIVEMQBYTESMESSAGE_TYPE: + case OW_ACTIVEMQTEXTMESSAGE_TYPE: + case OW_ACTIVEMQMAPMESSAGE_TYPE: + case OW_ACTIVEMQOBJECTMESSAGE_TYPE: + return 1; + } + return 0; +} + + +apr_status_t ow_marshal1_Message(ow_bit_buffer *buffer, ow_Message *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->producerId)); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->transactionId)); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->originalDestination)); + SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->messageId)); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->originalTransactionId)); + ow_marshal1_string(buffer, object->groupID); + + ow_marshal1_string(buffer, object->correlationId); + ow_bit_buffer_append(buffer, object->persistent); + ow_marshal1_long(buffer, object->expiration); + + SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->replyTo)); + ow_marshal1_long(buffer, object->timestamp); + ow_marshal1_string(buffer, object->type); + + ow_bit_buffer_append(buffer, object->content!=0 ); + + + ow_bit_buffer_append(buffer, object->marshalledProperties!=0 ); + + SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->dataStructure)); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->targetConsumerId)); + ow_bit_buffer_append(buffer, object->compressed); + + SUCCESS_CHECK(ow_marshal1_DataStructure_array(buffer, object->brokerPath)); + ow_marshal1_long(buffer, object->arrival); + ow_marshal1_string(buffer, object->userID); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_Message(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_Message *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->producerId)); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->transactionId)); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->originalDestination)); + SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->messageId)); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->originalTransactionId)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->groupID)); + SUCCESS_CHECK(ow_byte_buffer_append_int(buffer, object->groupSequence)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->correlationId)); + ow_bit_buffer_read(bitbuffer); + SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->expiration)); + SUCCESS_CHECK(ow_byte_buffer_append_byte(buffer, object->priority)); + SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->replyTo)); + SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->timestamp)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->type)); + SUCCESS_CHECK(ow_marshal2_byte_array(buffer, bitbuffer, object->content)); + SUCCESS_CHECK(ow_marshal2_byte_array(buffer, bitbuffer, object->marshalledProperties)); + SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->dataStructure)); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->targetConsumerId)); + ow_bit_buffer_read(bitbuffer); + SUCCESS_CHECK(ow_byte_buffer_append_int(buffer, object->redeliveryCounter)); + SUCCESS_CHECK(ow_marshal2_DataStructure_array(buffer, bitbuffer, object->brokerPath)); + SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->arrival)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->userID)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_Message(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_Message *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->producerId, pool)); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->destination, pool)); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->transactionId, pool)); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->originalDestination, pool)); + SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->messageId, pool)); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->originalTransactionId, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->groupID, pool)); + SUCCESS_CHECK(ow_byte_array_read_int(buffer, &object->groupSequence)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->correlationId, pool)); + object->persistent = ow_bit_buffer_read(bitbuffer); + SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->expiration, pool)); + SUCCESS_CHECK(ow_byte_array_read_byte(buffer, &object->priority)); + SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->replyTo, pool)); + SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->timestamp, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->type, pool)); + SUCCESS_CHECK(ow_unmarshal_byte_array(buffer, bitbuffer, &object->content, pool)); + SUCCESS_CHECK(ow_unmarshal_byte_array(buffer, bitbuffer, &object->marshalledProperties, pool)); + SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->dataStructure, pool)); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->targetConsumerId, pool)); + object->compressed = ow_bit_buffer_read(bitbuffer); + SUCCESS_CHECK(ow_byte_array_read_int(buffer, &object->redeliveryCounter)); + SUCCESS_CHECK(ow_unmarshal_DataStructure_array(buffer, bitbuffer, &object->brokerPath, pool)); + SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->arrival, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->userID, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ShutdownInfo(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_SHUTDOWNINFO_TYPE: + return 1; + } + return 0; +} + + +ow_ShutdownInfo *ow_ShutdownInfo_create(apr_pool_t *pool) +{ + ow_ShutdownInfo *value = apr_pcalloc(pool,sizeof(ow_ShutdownInfo)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_SHUTDOWNINFO_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ShutdownInfo(ow_bit_buffer *buffer, ow_ShutdownInfo *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ShutdownInfo(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ShutdownInfo *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ShutdownInfo(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ShutdownInfo *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_JournalTopicAck(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_JOURNALTOPICACK_TYPE: + return 1; + } + return 0; +} + + +ow_JournalTopicAck *ow_JournalTopicAck_create(apr_pool_t *pool) +{ + ow_JournalTopicAck *value = apr_pcalloc(pool,sizeof(ow_JournalTopicAck)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_JOURNALTOPICACK_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_JournalTopicAck(ow_bit_buffer *buffer, ow_JournalTopicAck *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->messageId)); + ow_marshal1_long(buffer, object->messageSequenceId); + ow_marshal1_string(buffer, object->subscritionName); + ow_marshal1_string(buffer, object->clientId); + SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->transactionId)); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_JournalTopicAck(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_JournalTopicAck *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->messageId)); + SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->messageSequenceId)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->subscritionName)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->clientId)); + SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->transactionId)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_JournalTopicAck(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_JournalTopicAck *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->destination, pool)); + SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->messageId, pool)); + SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->messageSequenceId, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->subscritionName, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->clientId, pool)); + SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->transactionId, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_DestinationInfo(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_DESTINATIONINFO_TYPE: + return 1; + } + return 0; +} + + +ow_DestinationInfo *ow_DestinationInfo_create(apr_pool_t *pool) +{ + ow_DestinationInfo *value = apr_pcalloc(pool,sizeof(ow_DestinationInfo)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_DESTINATIONINFO_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_DestinationInfo(ow_bit_buffer *buffer, ow_DestinationInfo *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->connectionId)); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->destination)); + + ow_marshal1_long(buffer, object->timeout); + SUCCESS_CHECK(ow_marshal1_DataStructure_array(buffer, object->brokerPath)); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_DestinationInfo(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DestinationInfo *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->connectionId)); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_byte_buffer_append_byte(buffer, object->operationType)); + SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->timeout)); + SUCCESS_CHECK(ow_marshal2_DataStructure_array(buffer, bitbuffer, object->brokerPath)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_DestinationInfo(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_DestinationInfo *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->connectionId, pool)); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->destination, pool)); + SUCCESS_CHECK(ow_byte_array_read_byte(buffer, &object->operationType)); + SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->timeout, pool)); + SUCCESS_CHECK(ow_unmarshal_DataStructure_array(buffer, bitbuffer, &object->brokerPath, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ConsumerId(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_CONSUMERID_TYPE: + return 1; + } + return 0; +} + + +ow_ConsumerId *ow_ConsumerId_create(apr_pool_t *pool) +{ + ow_ConsumerId *value = apr_pcalloc(pool,sizeof(ow_ConsumerId)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_CONSUMERID_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ConsumerId(ow_bit_buffer *buffer, ow_ConsumerId *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + ow_marshal1_string(buffer, object->connectionId); + ow_marshal1_long(buffer, object->sessionId); + ow_marshal1_long(buffer, object->consumerId); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ConsumerId(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ConsumerId *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->connectionId)); + SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->sessionId)); + SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->consumerId)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ConsumerId(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ConsumerId *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->connectionId, pool)); + SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->sessionId, pool)); + SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->consumerId, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_SessionId(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_SESSIONID_TYPE: + return 1; + } + return 0; +} + + +ow_SessionId *ow_SessionId_create(apr_pool_t *pool) +{ + ow_SessionId *value = apr_pcalloc(pool,sizeof(ow_SessionId)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_SESSIONID_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_SessionId(ow_bit_buffer *buffer, ow_SessionId *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + ow_marshal1_string(buffer, object->connectionId); + ow_marshal1_long(buffer, object->sessionId); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_SessionId(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_SessionId *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->connectionId)); + SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->sessionId)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_SessionId(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_SessionId *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->connectionId, pool)); + SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->sessionId, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ConsumerInfo(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_CONSUMERINFO_TYPE: + return 1; + } + return 0; +} + + +ow_ConsumerInfo *ow_ConsumerInfo_create(apr_pool_t *pool) +{ + ow_ConsumerInfo *value = apr_pcalloc(pool,sizeof(ow_ConsumerInfo)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_CONSUMERINFO_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ConsumerInfo(ow_bit_buffer *buffer, ow_ConsumerInfo *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->consumerId)); + ow_bit_buffer_append(buffer, object->browser); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->destination)); + + ow_bit_buffer_append(buffer, object->dispatchAsync); + ow_marshal1_string(buffer, object->selector); + ow_marshal1_string(buffer, object->subcriptionName); + ow_bit_buffer_append(buffer, object->noLocal); + ow_bit_buffer_append(buffer, object->exclusive); + ow_bit_buffer_append(buffer, object->retroactive); + + SUCCESS_CHECK(ow_marshal1_DataStructure_array(buffer, object->brokerPath)); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ConsumerInfo(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ConsumerInfo *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->consumerId)); + ow_bit_buffer_read(bitbuffer); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_byte_buffer_append_int(buffer, object->prefetchSize)); + ow_bit_buffer_read(bitbuffer); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->selector)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->subcriptionName)); + ow_bit_buffer_read(bitbuffer); + ow_bit_buffer_read(bitbuffer); + ow_bit_buffer_read(bitbuffer); + SUCCESS_CHECK(ow_byte_buffer_append_byte(buffer, object->priority)); + SUCCESS_CHECK(ow_marshal2_DataStructure_array(buffer, bitbuffer, object->brokerPath)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ConsumerInfo(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ConsumerInfo *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->consumerId, pool)); + object->browser = ow_bit_buffer_read(bitbuffer); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->destination, pool)); + SUCCESS_CHECK(ow_byte_array_read_int(buffer, &object->prefetchSize)); + object->dispatchAsync = ow_bit_buffer_read(bitbuffer); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->selector, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->subcriptionName, pool)); + object->noLocal = ow_bit_buffer_read(bitbuffer); + object->exclusive = ow_bit_buffer_read(bitbuffer); + object->retroactive = ow_bit_buffer_read(bitbuffer); + SUCCESS_CHECK(ow_byte_array_read_byte(buffer, &object->priority)); + SUCCESS_CHECK(ow_unmarshal_DataStructure_array(buffer, bitbuffer, &object->brokerPath, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ConnectionInfo(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_CONNECTIONINFO_TYPE: + return 1; + } + return 0; +} + + +ow_ConnectionInfo *ow_ConnectionInfo_create(apr_pool_t *pool) +{ + ow_ConnectionInfo *value = apr_pcalloc(pool,sizeof(ow_ConnectionInfo)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_CONNECTIONINFO_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ConnectionInfo(ow_bit_buffer *buffer, ow_ConnectionInfo *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->connectionId)); + ow_marshal1_string(buffer, object->clientId); + ow_marshal1_string(buffer, object->password); + ow_marshal1_string(buffer, object->userName); + SUCCESS_CHECK(ow_marshal1_DataStructure_array(buffer, object->brokerPath)); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ConnectionInfo(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ConnectionInfo *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->connectionId)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->clientId)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->password)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->userName)); + SUCCESS_CHECK(ow_marshal2_DataStructure_array(buffer, bitbuffer, object->brokerPath)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ConnectionInfo(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ConnectionInfo *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->connectionId, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->clientId, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->password, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->userName, pool)); + SUCCESS_CHECK(ow_unmarshal_DataStructure_array(buffer, bitbuffer, &object->brokerPath, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ActiveMQTopic(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_ACTIVEMQTOPIC_TYPE: + return 1; + } + return 0; +} + + +ow_ActiveMQTopic *ow_ActiveMQTopic_create(apr_pool_t *pool) +{ + ow_ActiveMQTopic *value = apr_pcalloc(pool,sizeof(ow_ActiveMQTopic)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_ACTIVEMQTOPIC_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ActiveMQTopic(ow_bit_buffer *buffer, ow_ActiveMQTopic *object) +{ + ow_marshal1_ActiveMQDestination(buffer, (ow_ActiveMQDestination*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ActiveMQTopic(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQTopic *object) +{ + ow_marshal2_ActiveMQDestination(buffer, bitbuffer, (ow_ActiveMQDestination*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ActiveMQTopic(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQTopic *object, apr_pool_t *pool) +{ + ow_unmarshal_ActiveMQDestination(buffer, bitbuffer, (ow_ActiveMQDestination*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_RedeliveryPolicy(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_REDELIVERYPOLICY_TYPE: + return 1; + } + return 0; +} + + +ow_RedeliveryPolicy *ow_RedeliveryPolicy_create(apr_pool_t *pool) +{ + ow_RedeliveryPolicy *value = apr_pcalloc(pool,sizeof(ow_RedeliveryPolicy)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_REDELIVERYPOLICY_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_RedeliveryPolicy(ow_bit_buffer *buffer, ow_RedeliveryPolicy *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + + ow_marshal1_long(buffer, object->initialRedeliveryDelay); + + ow_bit_buffer_append(buffer, object->useExponentialBackOff); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_RedeliveryPolicy(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_RedeliveryPolicy *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_byte_buffer_append_short(buffer, object->backOffMultiplier)); + SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->initialRedeliveryDelay)); + SUCCESS_CHECK(ow_byte_buffer_append_int(buffer, object->maximumRedeliveries)); + ow_bit_buffer_read(bitbuffer); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_RedeliveryPolicy(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_RedeliveryPolicy *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_byte_array_read_short(buffer, &object->backOffMultiplier)); + SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->initialRedeliveryDelay, pool)); + SUCCESS_CHECK(ow_byte_array_read_int(buffer, &object->maximumRedeliveries)); + object->useExponentialBackOff = ow_bit_buffer_read(bitbuffer); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_JournalTransaction(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_JOURNALTRANSACTION_TYPE: + return 1; + } + return 0; +} + + +ow_JournalTransaction *ow_JournalTransaction_create(apr_pool_t *pool) +{ + ow_JournalTransaction *value = apr_pcalloc(pool,sizeof(ow_JournalTransaction)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_JOURNALTRANSACTION_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_JournalTransaction(ow_bit_buffer *buffer, ow_JournalTransaction *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->transactionId)); + + ow_bit_buffer_append(buffer, object->wasPrepared); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_JournalTransaction(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_JournalTransaction *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->transactionId)); + SUCCESS_CHECK(ow_byte_buffer_append_byte(buffer, object->type)); + ow_bit_buffer_read(bitbuffer); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_JournalTransaction(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_JournalTransaction *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->transactionId, pool)); + SUCCESS_CHECK(ow_byte_array_read_byte(buffer, &object->type)); + object->wasPrepared = ow_bit_buffer_read(bitbuffer); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ProducerId(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_PRODUCERID_TYPE: + return 1; + } + return 0; +} + + +ow_ProducerId *ow_ProducerId_create(apr_pool_t *pool) +{ + ow_ProducerId *value = apr_pcalloc(pool,sizeof(ow_ProducerId)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_PRODUCERID_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ProducerId(ow_bit_buffer *buffer, ow_ProducerId *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + ow_marshal1_string(buffer, object->connectionId); + ow_marshal1_long(buffer, object->producerId); + ow_marshal1_long(buffer, object->sessionId); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ProducerId(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ProducerId *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->connectionId)); + SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->producerId)); + SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->sessionId)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ProducerId(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ProducerId *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->connectionId, pool)); + SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->producerId, pool)); + SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->sessionId, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ActiveMQQueue(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_ACTIVEMQQUEUE_TYPE: + return 1; + } + return 0; +} + + +ow_ActiveMQQueue *ow_ActiveMQQueue_create(apr_pool_t *pool) +{ + ow_ActiveMQQueue *value = apr_pcalloc(pool,sizeof(ow_ActiveMQQueue)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_ACTIVEMQQUEUE_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ActiveMQQueue(ow_bit_buffer *buffer, ow_ActiveMQQueue *object) +{ + ow_marshal1_ActiveMQDestination(buffer, (ow_ActiveMQDestination*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ActiveMQQueue(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQQueue *object) +{ + ow_marshal2_ActiveMQDestination(buffer, bitbuffer, (ow_ActiveMQDestination*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ActiveMQQueue(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQQueue *object, apr_pool_t *pool) +{ + ow_unmarshal_ActiveMQDestination(buffer, bitbuffer, (ow_ActiveMQDestination*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_TransactionInfo(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_TRANSACTIONINFO_TYPE: + return 1; + } + return 0; +} + + +ow_TransactionInfo *ow_TransactionInfo_create(apr_pool_t *pool) +{ + ow_TransactionInfo *value = apr_pcalloc(pool,sizeof(ow_TransactionInfo)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_TRANSACTIONINFO_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_TransactionInfo(ow_bit_buffer *buffer, ow_TransactionInfo *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->connectionId)); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->transactionId)); + + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_TransactionInfo(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_TransactionInfo *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->connectionId)); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->transactionId)); + SUCCESS_CHECK(ow_byte_buffer_append_byte(buffer, object->type)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_TransactionInfo(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_TransactionInfo *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->connectionId, pool)); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->transactionId, pool)); + SUCCESS_CHECK(ow_byte_array_read_byte(buffer, &object->type)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_Response(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_RESPONSE_TYPE: + case OW_INTEGERRESPONSE_TYPE: + case OW_DATAARRAYRESPONSE_TYPE: + case OW_DATARESPONSE_TYPE: + case OW_EXCEPTIONRESPONSE_TYPE: + return 1; + } + return 0; +} + + +ow_Response *ow_Response_create(apr_pool_t *pool) +{ + ow_Response *value = apr_pcalloc(pool,sizeof(ow_Response)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_RESPONSE_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_Response(ow_bit_buffer *buffer, ow_Response *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_Response(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_Response *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_byte_buffer_append_short(buffer, object->correlationId)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_Response(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_Response *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + SUCCESS_CHECK(ow_byte_array_read_short(buffer, &object->correlationId)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_RemoveInfo(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_REMOVEINFO_TYPE: + return 1; + } + return 0; +} + + +ow_RemoveInfo *ow_RemoveInfo_create(apr_pool_t *pool) +{ + ow_RemoveInfo *value = apr_pcalloc(pool,sizeof(ow_RemoveInfo)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_REMOVEINFO_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_RemoveInfo(ow_bit_buffer *buffer, ow_RemoveInfo *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->objectId)); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_RemoveInfo(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_RemoveInfo *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->objectId)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_RemoveInfo(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_RemoveInfo *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->objectId, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_WireFormatInfo(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_WIREFORMATINFO_TYPE: + return 1; + } + return 0; +} + + +ow_WireFormatInfo *ow_WireFormatInfo_create(apr_pool_t *pool) +{ + ow_WireFormatInfo *value = apr_pcalloc(pool,sizeof(ow_WireFormatInfo)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_WIREFORMATINFO_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_WireFormatInfo(ow_bit_buffer *buffer, ow_WireFormatInfo *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + + + + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_WireFormatInfo(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_WireFormatInfo *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + SUCCESS_CHECK(ow_marshal2_byte_array_const_size(buffer, object->magic, 8)); + SUCCESS_CHECK(ow_byte_buffer_append_int(buffer, object->version)); + SUCCESS_CHECK(ow_byte_buffer_append_int(buffer, object->options)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_WireFormatInfo(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_WireFormatInfo *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + SUCCESS_CHECK(ow_unmarshal_byte_array_const_size(buffer, &object->magic, 8, pool)); + SUCCESS_CHECK(ow_byte_array_read_int(buffer, &object->version)); + SUCCESS_CHECK(ow_byte_array_read_int(buffer, &object->options)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_TransactionId(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_XATRANSACTIONID_TYPE: + case OW_LOCALTRANSACTIONID_TYPE: + return 1; + } + return 0; +} + + +apr_status_t ow_marshal1_TransactionId(ow_bit_buffer *buffer, ow_TransactionId *object) +{ + ow_marshal1_DataStructure(buffer, (ow_DataStructure*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_TransactionId(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_TransactionId *object) +{ + ow_marshal2_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_TransactionId(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_TransactionId *object, apr_pool_t *pool) +{ + ow_unmarshal_DataStructure(buffer, bitbuffer, (ow_DataStructure*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ActiveMQMessage(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_ACTIVEMQMESSAGE_TYPE: + case OW_ACTIVEMQSTREAMMESSAGE_TYPE: + case OW_ACTIVEMQBYTESMESSAGE_TYPE: + case OW_ACTIVEMQTEXTMESSAGE_TYPE: + case OW_ACTIVEMQMAPMESSAGE_TYPE: + case OW_ACTIVEMQOBJECTMESSAGE_TYPE: + return 1; + } + return 0; +} + + +ow_ActiveMQMessage *ow_ActiveMQMessage_create(apr_pool_t *pool) +{ + ow_ActiveMQMessage *value = apr_pcalloc(pool,sizeof(ow_ActiveMQMessage)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_ACTIVEMQMESSAGE_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ActiveMQMessage(ow_bit_buffer *buffer, ow_ActiveMQMessage *object) +{ + ow_marshal1_Message(buffer, (ow_Message*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ActiveMQMessage(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQMessage *object) +{ + ow_marshal2_Message(buffer, bitbuffer, (ow_Message*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ActiveMQMessage(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQMessage *object, apr_pool_t *pool) +{ + ow_unmarshal_Message(buffer, bitbuffer, (ow_Message*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ControlCommand(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_CONTROLCOMMAND_TYPE: + return 1; + } + return 0; +} + + +ow_ControlCommand *ow_ControlCommand_create(apr_pool_t *pool) +{ + ow_ControlCommand *value = apr_pcalloc(pool,sizeof(ow_ControlCommand)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_CONTROLCOMMAND_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ControlCommand(ow_bit_buffer *buffer, ow_ControlCommand *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + ow_marshal1_string(buffer, object->command); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ControlCommand(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ControlCommand *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->command)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ControlCommand(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ControlCommand *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->command, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_FlushCommand(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_FLUSHCOMMAND_TYPE: + return 1; + } + return 0; +} + + +ow_FlushCommand *ow_FlushCommand_create(apr_pool_t *pool) +{ + ow_FlushCommand *value = apr_pcalloc(pool,sizeof(ow_FlushCommand)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_FLUSHCOMMAND_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_FlushCommand(ow_bit_buffer *buffer, ow_FlushCommand *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_FlushCommand(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_FlushCommand *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_FlushCommand(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_FlushCommand *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_IntegerResponse(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_INTEGERRESPONSE_TYPE: + return 1; + } + return 0; +} + + +ow_IntegerResponse *ow_IntegerResponse_create(apr_pool_t *pool) +{ + ow_IntegerResponse *value = apr_pcalloc(pool,sizeof(ow_IntegerResponse)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_INTEGERRESPONSE_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_IntegerResponse(ow_bit_buffer *buffer, ow_IntegerResponse *object) +{ + ow_marshal1_Response(buffer, (ow_Response*)object); + + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_IntegerResponse(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_IntegerResponse *object) +{ + ow_marshal2_Response(buffer, bitbuffer, (ow_Response*)object); + SUCCESS_CHECK(ow_byte_buffer_append_int(buffer, object->result)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_IntegerResponse(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_IntegerResponse *object, apr_pool_t *pool) +{ + ow_unmarshal_Response(buffer, bitbuffer, (ow_Response*)object, pool); + SUCCESS_CHECK(ow_byte_array_read_int(buffer, &object->result)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_RemoveSubscriptionInfo(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_REMOVESUBSCRIPTIONINFO_TYPE: + return 1; + } + return 0; +} + + +ow_RemoveSubscriptionInfo *ow_RemoveSubscriptionInfo_create(apr_pool_t *pool) +{ + ow_RemoveSubscriptionInfo *value = apr_pcalloc(pool,sizeof(ow_RemoveSubscriptionInfo)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_REMOVESUBSCRIPTIONINFO_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_RemoveSubscriptionInfo(ow_bit_buffer *buffer, ow_RemoveSubscriptionInfo *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->connectionId)); + ow_marshal1_string(buffer, object->subcriptionName); + ow_marshal1_string(buffer, object->clientId); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_RemoveSubscriptionInfo(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_RemoveSubscriptionInfo *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->connectionId)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->subcriptionName)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->clientId)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_RemoveSubscriptionInfo(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_RemoveSubscriptionInfo *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->connectionId, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->subcriptionName, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->clientId, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ActiveMQTempDestination(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_ACTIVEMQTEMPTOPIC_TYPE: + case OW_ACTIVEMQTEMPQUEUE_TYPE: + return 1; + } + return 0; +} + + +apr_status_t ow_marshal1_ActiveMQTempDestination(ow_bit_buffer *buffer, ow_ActiveMQTempDestination *object) +{ + ow_marshal1_ActiveMQDestination(buffer, (ow_ActiveMQDestination*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ActiveMQTempDestination(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQTempDestination *object) +{ + ow_marshal2_ActiveMQDestination(buffer, bitbuffer, (ow_ActiveMQDestination*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ActiveMQTempDestination(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQTempDestination *object, apr_pool_t *pool) +{ + ow_unmarshal_ActiveMQDestination(buffer, bitbuffer, (ow_ActiveMQDestination*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_DataArrayResponse(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_DATAARRAYRESPONSE_TYPE: + return 1; + } + return 0; +} + + +ow_DataArrayResponse *ow_DataArrayResponse_create(apr_pool_t *pool) +{ + ow_DataArrayResponse *value = apr_pcalloc(pool,sizeof(ow_DataArrayResponse)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_DATAARRAYRESPONSE_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_DataArrayResponse(ow_bit_buffer *buffer, ow_DataArrayResponse *object) +{ + ow_marshal1_Response(buffer, (ow_Response*)object); + SUCCESS_CHECK(ow_marshal1_DataStructure_array(buffer, object->data)); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_DataArrayResponse(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataArrayResponse *object) +{ + ow_marshal2_Response(buffer, bitbuffer, (ow_Response*)object); + SUCCESS_CHECK(ow_marshal2_DataStructure_array(buffer, bitbuffer, object->data)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_DataArrayResponse(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_DataArrayResponse *object, apr_pool_t *pool) +{ + ow_unmarshal_Response(buffer, bitbuffer, (ow_Response*)object, pool); + SUCCESS_CHECK(ow_unmarshal_DataStructure_array(buffer, bitbuffer, &object->data, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_BrokerInfo(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_BROKERINFO_TYPE: + return 1; + } + return 0; +} + + +ow_BrokerInfo *ow_BrokerInfo_create(apr_pool_t *pool) +{ + ow_BrokerInfo *value = apr_pcalloc(pool,sizeof(ow_BrokerInfo)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_BROKERINFO_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_BrokerInfo(ow_bit_buffer *buffer, ow_BrokerInfo *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->brokerId)); + ow_marshal1_string(buffer, object->brokerURL); + SUCCESS_CHECK(ow_marshal1_DataStructure_array(buffer, object->peerBrokerInfos)); + SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->redeliveryPolicy)); + ow_marshal1_string(buffer, object->brokerName); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_BrokerInfo(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_BrokerInfo *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->brokerId)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->brokerURL)); + SUCCESS_CHECK(ow_marshal2_DataStructure_array(buffer, bitbuffer, object->peerBrokerInfos)); + SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->redeliveryPolicy)); + SUCCESS_CHECK(ow_marshal2_string(buffer, bitbuffer, object->brokerName)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_BrokerInfo(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_BrokerInfo *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->brokerId, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->brokerURL, pool)); + SUCCESS_CHECK(ow_unmarshal_DataStructure_array(buffer, bitbuffer, &object->peerBrokerInfos, pool)); + SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->redeliveryPolicy, pool)); + SUCCESS_CHECK(ow_unmarshal_string(buffer, bitbuffer, &object->brokerName, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_MessageDispatch(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_MESSAGEDISPATCH_TYPE: + return 1; + } + return 0; +} + + +ow_MessageDispatch *ow_MessageDispatch_create(apr_pool_t *pool) +{ + ow_MessageDispatch *value = apr_pcalloc(pool,sizeof(ow_MessageDispatch)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_MESSAGEDISPATCH_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_MessageDispatch(ow_bit_buffer *buffer, ow_MessageDispatch *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->consumerId)); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->message)); + + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_MessageDispatch(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_MessageDispatch *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->consumerId)); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->message)); + SUCCESS_CHECK(ow_byte_buffer_append_int(buffer, object->redeliveryCounter)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_MessageDispatch(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_MessageDispatch *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->consumerId, pool)); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->destination, pool)); + SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->message, pool)); + SUCCESS_CHECK(ow_byte_array_read_int(buffer, &object->redeliveryCounter)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ActiveMQStreamMessage(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_ACTIVEMQSTREAMMESSAGE_TYPE: + return 1; + } + return 0; +} + + +ow_ActiveMQStreamMessage *ow_ActiveMQStreamMessage_create(apr_pool_t *pool) +{ + ow_ActiveMQStreamMessage *value = apr_pcalloc(pool,sizeof(ow_ActiveMQStreamMessage)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_ACTIVEMQSTREAMMESSAGE_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ActiveMQStreamMessage(ow_bit_buffer *buffer, ow_ActiveMQStreamMessage *object) +{ + ow_marshal1_ActiveMQMessage(buffer, (ow_ActiveMQMessage*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ActiveMQStreamMessage(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQStreamMessage *object) +{ + ow_marshal2_ActiveMQMessage(buffer, bitbuffer, (ow_ActiveMQMessage*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ActiveMQStreamMessage(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQStreamMessage *object, apr_pool_t *pool) +{ + ow_unmarshal_ActiveMQMessage(buffer, bitbuffer, (ow_ActiveMQMessage*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ActiveMQTempTopic(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_ACTIVEMQTEMPTOPIC_TYPE: + return 1; + } + return 0; +} + + +ow_ActiveMQTempTopic *ow_ActiveMQTempTopic_create(apr_pool_t *pool) +{ + ow_ActiveMQTempTopic *value = apr_pcalloc(pool,sizeof(ow_ActiveMQTempTopic)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_ACTIVEMQTEMPTOPIC_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ActiveMQTempTopic(ow_bit_buffer *buffer, ow_ActiveMQTempTopic *object) +{ + ow_marshal1_ActiveMQTempDestination(buffer, (ow_ActiveMQTempDestination*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ActiveMQTempTopic(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQTempTopic *object) +{ + ow_marshal2_ActiveMQTempDestination(buffer, bitbuffer, (ow_ActiveMQTempDestination*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ActiveMQTempTopic(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQTempTopic *object, apr_pool_t *pool) +{ + ow_unmarshal_ActiveMQTempDestination(buffer, bitbuffer, (ow_ActiveMQTempDestination*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ProducerInfo(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_PRODUCERINFO_TYPE: + return 1; + } + return 0; +} + + +ow_ProducerInfo *ow_ProducerInfo_create(apr_pool_t *pool) +{ + ow_ProducerInfo *value = apr_pcalloc(pool,sizeof(ow_ProducerInfo)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_PRODUCERINFO_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ProducerInfo(ow_bit_buffer *buffer, ow_ProducerInfo *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->producerId)); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_marshal1_DataStructure_array(buffer, object->brokerPath)); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ProducerInfo(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ProducerInfo *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->producerId)); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_marshal2_DataStructure_array(buffer, bitbuffer, object->brokerPath)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ProducerInfo(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ProducerInfo *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->producerId, pool)); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->destination, pool)); + SUCCESS_CHECK(ow_unmarshal_DataStructure_array(buffer, bitbuffer, &object->brokerPath, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_MessageAck(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_MESSAGEACK_TYPE: + return 1; + } + return 0; +} + + +ow_MessageAck *ow_MessageAck_create(apr_pool_t *pool) +{ + ow_MessageAck *value = apr_pcalloc(pool,sizeof(ow_MessageAck)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_MESSAGEACK_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_MessageAck(ow_bit_buffer *buffer, ow_MessageAck *object) +{ + ow_marshal1_BaseCommand(buffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->transactionId)); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->consumerId)); + + SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->firstMessageId)); + SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->lastMessageId)); + + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_MessageAck(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_MessageAck *object) +{ + ow_marshal2_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->destination)); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->transactionId)); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->consumerId)); + SUCCESS_CHECK(ow_byte_buffer_append_byte(buffer, object->ackType)); + SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->firstMessageId)); + SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->lastMessageId)); + SUCCESS_CHECK(ow_byte_buffer_append_int(buffer, object->messageCount)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_MessageAck(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_MessageAck *object, apr_pool_t *pool) +{ + ow_unmarshal_BaseCommand(buffer, bitbuffer, (ow_BaseCommand*)object, pool); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->destination, pool)); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->transactionId, pool)); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->consumerId, pool)); + SUCCESS_CHECK(ow_byte_array_read_byte(buffer, &object->ackType)); + SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->firstMessageId, pool)); + SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->lastMessageId, pool)); + SUCCESS_CHECK(ow_byte_array_read_int(buffer, &object->messageCount)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ActiveMQBytesMessage(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_ACTIVEMQBYTESMESSAGE_TYPE: + return 1; + } + return 0; +} + + +ow_ActiveMQBytesMessage *ow_ActiveMQBytesMessage_create(apr_pool_t *pool) +{ + ow_ActiveMQBytesMessage *value = apr_pcalloc(pool,sizeof(ow_ActiveMQBytesMessage)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_ACTIVEMQBYTESMESSAGE_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ActiveMQBytesMessage(ow_bit_buffer *buffer, ow_ActiveMQBytesMessage *object) +{ + ow_marshal1_ActiveMQMessage(buffer, (ow_ActiveMQMessage*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ActiveMQBytesMessage(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQBytesMessage *object) +{ + ow_marshal2_ActiveMQMessage(buffer, bitbuffer, (ow_ActiveMQMessage*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ActiveMQBytesMessage(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQBytesMessage *object, apr_pool_t *pool) +{ + ow_unmarshal_ActiveMQMessage(buffer, bitbuffer, (ow_ActiveMQMessage*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ActiveMQTextMessage(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_ACTIVEMQTEXTMESSAGE_TYPE: + return 1; + } + return 0; +} + + +ow_ActiveMQTextMessage *ow_ActiveMQTextMessage_create(apr_pool_t *pool) +{ + ow_ActiveMQTextMessage *value = apr_pcalloc(pool,sizeof(ow_ActiveMQTextMessage)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_ACTIVEMQTEXTMESSAGE_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ActiveMQTextMessage(ow_bit_buffer *buffer, ow_ActiveMQTextMessage *object) +{ + ow_marshal1_ActiveMQMessage(buffer, (ow_ActiveMQMessage*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ActiveMQTextMessage(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQTextMessage *object) +{ + ow_marshal2_ActiveMQMessage(buffer, bitbuffer, (ow_ActiveMQMessage*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ActiveMQTextMessage(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQTextMessage *object, apr_pool_t *pool) +{ + ow_unmarshal_ActiveMQMessage(buffer, bitbuffer, (ow_ActiveMQMessage*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ActiveMQMapMessage(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_ACTIVEMQMAPMESSAGE_TYPE: + return 1; + } + return 0; +} + + +ow_ActiveMQMapMessage *ow_ActiveMQMapMessage_create(apr_pool_t *pool) +{ + ow_ActiveMQMapMessage *value = apr_pcalloc(pool,sizeof(ow_ActiveMQMapMessage)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_ACTIVEMQMAPMESSAGE_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ActiveMQMapMessage(ow_bit_buffer *buffer, ow_ActiveMQMapMessage *object) +{ + ow_marshal1_ActiveMQMessage(buffer, (ow_ActiveMQMessage*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ActiveMQMapMessage(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQMapMessage *object) +{ + ow_marshal2_ActiveMQMessage(buffer, bitbuffer, (ow_ActiveMQMessage*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ActiveMQMapMessage(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQMapMessage *object, apr_pool_t *pool) +{ + ow_unmarshal_ActiveMQMessage(buffer, bitbuffer, (ow_ActiveMQMessage*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_DataResponse(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_DATARESPONSE_TYPE: + return 1; + } + return 0; +} + + +ow_DataResponse *ow_DataResponse_create(apr_pool_t *pool) +{ + ow_DataResponse *value = apr_pcalloc(pool,sizeof(ow_DataResponse)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_DATARESPONSE_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_DataResponse(ow_bit_buffer *buffer, ow_DataResponse *object) +{ + ow_marshal1_Response(buffer, (ow_Response*)object); + SUCCESS_CHECK(ow_marshal1_nested_object(buffer, (ow_DataStructure*)object->data)); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_DataResponse(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataResponse *object) +{ + ow_marshal2_Response(buffer, bitbuffer, (ow_Response*)object); + SUCCESS_CHECK(ow_marshal2_nested_object(buffer, bitbuffer, (ow_DataStructure*)object->data)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_DataResponse(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_DataResponse *object, apr_pool_t *pool) +{ + ow_unmarshal_Response(buffer, bitbuffer, (ow_Response*)object, pool); + SUCCESS_CHECK(ow_unmarshal_nested_object(buffer, bitbuffer, (ow_DataStructure**)&object->data, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_XATransactionId(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_XATRANSACTIONID_TYPE: + return 1; + } + return 0; +} + + +ow_XATransactionId *ow_XATransactionId_create(apr_pool_t *pool) +{ + ow_XATransactionId *value = apr_pcalloc(pool,sizeof(ow_XATransactionId)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_XATRANSACTIONID_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_XATransactionId(ow_bit_buffer *buffer, ow_XATransactionId *object) +{ + ow_marshal1_TransactionId(buffer, (ow_TransactionId*)object); + + + ow_bit_buffer_append(buffer, object->globalTransactionId!=0 ); + + + ow_bit_buffer_append(buffer, object->branchQualifier!=0 ); + + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_XATransactionId(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_XATransactionId *object) +{ + ow_marshal2_TransactionId(buffer, bitbuffer, (ow_TransactionId*)object); + SUCCESS_CHECK(ow_byte_buffer_append_int(buffer, object->formatId)); + SUCCESS_CHECK(ow_marshal2_byte_array(buffer, bitbuffer, object->globalTransactionId)); + SUCCESS_CHECK(ow_marshal2_byte_array(buffer, bitbuffer, object->branchQualifier)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_XATransactionId(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_XATransactionId *object, apr_pool_t *pool) +{ + ow_unmarshal_TransactionId(buffer, bitbuffer, (ow_TransactionId*)object, pool); + SUCCESS_CHECK(ow_byte_array_read_int(buffer, &object->formatId)); + SUCCESS_CHECK(ow_unmarshal_byte_array(buffer, bitbuffer, &object->globalTransactionId, pool)); + SUCCESS_CHECK(ow_unmarshal_byte_array(buffer, bitbuffer, &object->branchQualifier, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ActiveMQObjectMessage(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_ACTIVEMQOBJECTMESSAGE_TYPE: + return 1; + } + return 0; +} + + +ow_ActiveMQObjectMessage *ow_ActiveMQObjectMessage_create(apr_pool_t *pool) +{ + ow_ActiveMQObjectMessage *value = apr_pcalloc(pool,sizeof(ow_ActiveMQObjectMessage)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_ACTIVEMQOBJECTMESSAGE_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ActiveMQObjectMessage(ow_bit_buffer *buffer, ow_ActiveMQObjectMessage *object) +{ + ow_marshal1_ActiveMQMessage(buffer, (ow_ActiveMQMessage*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ActiveMQObjectMessage(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQObjectMessage *object) +{ + ow_marshal2_ActiveMQMessage(buffer, bitbuffer, (ow_ActiveMQMessage*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ActiveMQObjectMessage(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQObjectMessage *object, apr_pool_t *pool) +{ + ow_unmarshal_ActiveMQMessage(buffer, bitbuffer, (ow_ActiveMQMessage*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ExceptionResponse(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_EXCEPTIONRESPONSE_TYPE: + return 1; + } + return 0; +} + + +ow_ExceptionResponse *ow_ExceptionResponse_create(apr_pool_t *pool) +{ + ow_ExceptionResponse *value = apr_pcalloc(pool,sizeof(ow_ExceptionResponse)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_EXCEPTIONRESPONSE_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ExceptionResponse(ow_bit_buffer *buffer, ow_ExceptionResponse *object) +{ + ow_marshal1_Response(buffer, (ow_Response*)object); + SUCCESS_CHECK(ow_marshal1_throwable(buffer, object->exception)); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ExceptionResponse(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ExceptionResponse *object) +{ + ow_marshal2_Response(buffer, bitbuffer, (ow_Response*)object); + SUCCESS_CHECK(ow_marshal2_throwable(buffer, bitbuffer, object->exception)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ExceptionResponse(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ExceptionResponse *object, apr_pool_t *pool) +{ + ow_unmarshal_Response(buffer, bitbuffer, (ow_Response*)object, pool); + SUCCESS_CHECK(ow_unmarshal_throwable(buffer, bitbuffer, &object->exception, pool)); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_ActiveMQTempQueue(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_ACTIVEMQTEMPQUEUE_TYPE: + return 1; + } + return 0; +} + + +ow_ActiveMQTempQueue *ow_ActiveMQTempQueue_create(apr_pool_t *pool) +{ + ow_ActiveMQTempQueue *value = apr_pcalloc(pool,sizeof(ow_ActiveMQTempQueue)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_ACTIVEMQTEMPQUEUE_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_ActiveMQTempQueue(ow_bit_buffer *buffer, ow_ActiveMQTempQueue *object) +{ + ow_marshal1_ActiveMQTempDestination(buffer, (ow_ActiveMQTempDestination*)object); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_ActiveMQTempQueue(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQTempQueue *object) +{ + ow_marshal2_ActiveMQTempDestination(buffer, bitbuffer, (ow_ActiveMQTempDestination*)object); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_ActiveMQTempQueue(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_ActiveMQTempQueue *object, apr_pool_t *pool) +{ + ow_unmarshal_ActiveMQTempDestination(buffer, bitbuffer, (ow_ActiveMQTempDestination*)object, pool); + + return APR_SUCCESS; +} + +ow_boolean ow_is_a_LocalTransactionId(ow_DataStructure *object) { + if( object == 0 ) + return 0; + + switch(object->structType) { + case OW_LOCALTRANSACTIONID_TYPE: + return 1; + } + return 0; +} + + +ow_LocalTransactionId *ow_LocalTransactionId_create(apr_pool_t *pool) +{ + ow_LocalTransactionId *value = apr_pcalloc(pool,sizeof(ow_LocalTransactionId)); + if( value!=0 ) { + ((ow_DataStructure*)value)->structType = OW_LOCALTRANSACTIONID_TYPE; + } + return value; +} + + +apr_status_t ow_marshal1_LocalTransactionId(ow_bit_buffer *buffer, ow_LocalTransactionId *object) +{ + ow_marshal1_TransactionId(buffer, (ow_TransactionId*)object); + ow_marshal1_long(buffer, object->transactionId); + SUCCESS_CHECK(ow_marshal1_cached_object(buffer, (ow_DataStructure*)object->connectionId)); + + return APR_SUCCESS; +} +apr_status_t ow_marshal2_LocalTransactionId(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_LocalTransactionId *object) +{ + ow_marshal2_TransactionId(buffer, bitbuffer, (ow_TransactionId*)object); + SUCCESS_CHECK(ow_marshal2_long(buffer, bitbuffer, object->transactionId)); + SUCCESS_CHECK(ow_marshal2_cached_object(buffer, bitbuffer, (ow_DataStructure*)object->connectionId)); + + return APR_SUCCESS; +} + +apr_status_t ow_unmarshal_LocalTransactionId(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_LocalTransactionId *object, apr_pool_t *pool) +{ + ow_unmarshal_TransactionId(buffer, bitbuffer, (ow_TransactionId*)object, pool); + SUCCESS_CHECK(ow_unmarshal_long(buffer, bitbuffer, &object->transactionId, pool)); + SUCCESS_CHECK(ow_unmarshal_cached_object(buffer, bitbuffer, (ow_DataStructure**)&object->connectionId, pool)); + + return APR_SUCCESS; +} + +ow_DataStructure *ow_create_object(ow_byte type, apr_pool_t *pool) +{ + switch( type ) { + + case OW_MESSAGEID_TYPE: return (ow_DataStructure *)ow_MessageId_create(pool); + case OW_SUBSCRIPTIONINFO_TYPE: return (ow_DataStructure *)ow_SubscriptionInfo_create(pool); + case OW_CONNECTIONID_TYPE: return (ow_DataStructure *)ow_ConnectionId_create(pool); + case OW_JOURNALTRACE_TYPE: return (ow_DataStructure *)ow_JournalTrace_create(pool); + case OW_KEEPALIVEINFO_TYPE: return (ow_DataStructure *)ow_KeepAliveInfo_create(pool); + case OW_JOURNALQUEUEACK_TYPE: return (ow_DataStructure *)ow_JournalQueueAck_create(pool); + case OW_BROKERID_TYPE: return (ow_DataStructure *)ow_BrokerId_create(pool); + case OW_SESSIONINFO_TYPE: return (ow_DataStructure *)ow_SessionInfo_create(pool); + case OW_SHUTDOWNINFO_TYPE: return (ow_DataStructure *)ow_ShutdownInfo_create(pool); + case OW_JOURNALTOPICACK_TYPE: return (ow_DataStructure *)ow_JournalTopicAck_create(pool); + case OW_DESTINATIONINFO_TYPE: return (ow_DataStructure *)ow_DestinationInfo_create(pool); + case OW_CONSUMERID_TYPE: return (ow_DataStructure *)ow_ConsumerId_create(pool); + case OW_SESSIONID_TYPE: return (ow_DataStructure *)ow_SessionId_create(pool); + case OW_CONSUMERINFO_TYPE: return (ow_DataStructure *)ow_ConsumerInfo_create(pool); + case OW_CONNECTIONINFO_TYPE: return (ow_DataStructure *)ow_ConnectionInfo_create(pool); + case OW_ACTIVEMQTOPIC_TYPE: return (ow_DataStructure *)ow_ActiveMQTopic_create(pool); + case OW_REDELIVERYPOLICY_TYPE: return (ow_DataStructure *)ow_RedeliveryPolicy_create(pool); + case OW_JOURNALTRANSACTION_TYPE: return (ow_DataStructure *)ow_JournalTransaction_create(pool); + case OW_PRODUCERID_TYPE: return (ow_DataStructure *)ow_ProducerId_create(pool); + case OW_ACTIVEMQQUEUE_TYPE: return (ow_DataStructure *)ow_ActiveMQQueue_create(pool); + case OW_TRANSACTIONINFO_TYPE: return (ow_DataStructure *)ow_TransactionInfo_create(pool); + case OW_RESPONSE_TYPE: return (ow_DataStructure *)ow_Response_create(pool); + case OW_REMOVEINFO_TYPE: return (ow_DataStructure *)ow_RemoveInfo_create(pool); + case OW_WIREFORMATINFO_TYPE: return (ow_DataStructure *)ow_WireFormatInfo_create(pool); + case OW_ACTIVEMQMESSAGE_TYPE: return (ow_DataStructure *)ow_ActiveMQMessage_create(pool); + case OW_CONTROLCOMMAND_TYPE: return (ow_DataStructure *)ow_ControlCommand_create(pool); + case OW_FLUSHCOMMAND_TYPE: return (ow_DataStructure *)ow_FlushCommand_create(pool); + case OW_INTEGERRESPONSE_TYPE: return (ow_DataStructure *)ow_IntegerResponse_create(pool); + case OW_REMOVESUBSCRIPTIONINFO_TYPE: return (ow_DataStructure *)ow_RemoveSubscriptionInfo_create(pool); + case OW_DATAARRAYRESPONSE_TYPE: return (ow_DataStructure *)ow_DataArrayResponse_create(pool); + case OW_BROKERINFO_TYPE: return (ow_DataStructure *)ow_BrokerInfo_create(pool); + case OW_MESSAGEDISPATCH_TYPE: return (ow_DataStructure *)ow_MessageDispatch_create(pool); + case OW_ACTIVEMQSTREAMMESSAGE_TYPE: return (ow_DataStructure *)ow_ActiveMQStreamMessage_create(pool); + case OW_ACTIVEMQTEMPTOPIC_TYPE: return (ow_DataStructure *)ow_ActiveMQTempTopic_create(pool); + case OW_PRODUCERINFO_TYPE: return (ow_DataStructure *)ow_ProducerInfo_create(pool); + case OW_MESSAGEACK_TYPE: return (ow_DataStructure *)ow_MessageAck_create(pool); + case OW_ACTIVEMQBYTESMESSAGE_TYPE: return (ow_DataStructure *)ow_ActiveMQBytesMessage_create(pool); + case OW_ACTIVEMQTEXTMESSAGE_TYPE: return (ow_DataStructure *)ow_ActiveMQTextMessage_create(pool); + case OW_ACTIVEMQMAPMESSAGE_TYPE: return (ow_DataStructure *)ow_ActiveMQMapMessage_create(pool); + case OW_DATARESPONSE_TYPE: return (ow_DataStructure *)ow_DataResponse_create(pool); + case OW_XATRANSACTIONID_TYPE: return (ow_DataStructure *)ow_XATransactionId_create(pool); + case OW_ACTIVEMQOBJECTMESSAGE_TYPE: return (ow_DataStructure *)ow_ActiveMQObjectMessage_create(pool); + case OW_EXCEPTIONRESPONSE_TYPE: return (ow_DataStructure *)ow_ExceptionResponse_create(pool); + case OW_ACTIVEMQTEMPQUEUE_TYPE: return (ow_DataStructure *)ow_ActiveMQTempQueue_create(pool); + case OW_LOCALTRANSACTIONID_TYPE: return (ow_DataStructure *)ow_LocalTransactionId_create(pool); + } + return 0; +} + +apr_status_t ow_marshal1_object(ow_bit_buffer *buffer, ow_DataStructure *object) +{ + switch( object->structType ) { + + case OW_MESSAGEID_TYPE: return ow_marshal1_MessageId(buffer, (ow_MessageId*)object); + case OW_SUBSCRIPTIONINFO_TYPE: return ow_marshal1_SubscriptionInfo(buffer, (ow_SubscriptionInfo*)object); + case OW_CONNECTIONID_TYPE: return ow_marshal1_ConnectionId(buffer, (ow_ConnectionId*)object); + case OW_JOURNALTRACE_TYPE: return ow_marshal1_JournalTrace(buffer, (ow_JournalTrace*)object); + case OW_KEEPALIVEINFO_TYPE: return ow_marshal1_KeepAliveInfo(buffer, (ow_KeepAliveInfo*)object); + case OW_JOURNALQUEUEACK_TYPE: return ow_marshal1_JournalQueueAck(buffer, (ow_JournalQueueAck*)object); + case OW_BROKERID_TYPE: return ow_marshal1_BrokerId(buffer, (ow_BrokerId*)object); + case OW_SESSIONINFO_TYPE: return ow_marshal1_SessionInfo(buffer, (ow_SessionInfo*)object); + case OW_SHUTDOWNINFO_TYPE: return ow_marshal1_ShutdownInfo(buffer, (ow_ShutdownInfo*)object); + case OW_JOURNALTOPICACK_TYPE: return ow_marshal1_JournalTopicAck(buffer, (ow_JournalTopicAck*)object); + case OW_DESTINATIONINFO_TYPE: return ow_marshal1_DestinationInfo(buffer, (ow_DestinationInfo*)object); + case OW_CONSUMERID_TYPE: return ow_marshal1_ConsumerId(buffer, (ow_ConsumerId*)object); + case OW_SESSIONID_TYPE: return ow_marshal1_SessionId(buffer, (ow_SessionId*)object); + case OW_CONSUMERINFO_TYPE: return ow_marshal1_ConsumerInfo(buffer, (ow_ConsumerInfo*)object); + case OW_CONNECTIONINFO_TYPE: return ow_marshal1_ConnectionInfo(buffer, (ow_ConnectionInfo*)object); + case OW_ACTIVEMQTOPIC_TYPE: return ow_marshal1_ActiveMQTopic(buffer, (ow_ActiveMQTopic*)object); + case OW_REDELIVERYPOLICY_TYPE: return ow_marshal1_RedeliveryPolicy(buffer, (ow_RedeliveryPolicy*)object); + case OW_JOURNALTRANSACTION_TYPE: return ow_marshal1_JournalTransaction(buffer, (ow_JournalTransaction*)object); + case OW_PRODUCERID_TYPE: return ow_marshal1_ProducerId(buffer, (ow_ProducerId*)object); + case OW_ACTIVEMQQUEUE_TYPE: return ow_marshal1_ActiveMQQueue(buffer, (ow_ActiveMQQueue*)object); + case OW_TRANSACTIONINFO_TYPE: return ow_marshal1_TransactionInfo(buffer, (ow_TransactionInfo*)object); + case OW_RESPONSE_TYPE: return ow_marshal1_Response(buffer, (ow_Response*)object); + case OW_REMOVEINFO_TYPE: return ow_marshal1_RemoveInfo(buffer, (ow_RemoveInfo*)object); + case OW_WIREFORMATINFO_TYPE: return ow_marshal1_WireFormatInfo(buffer, (ow_WireFormatInfo*)object); + case OW_ACTIVEMQMESSAGE_TYPE: return ow_marshal1_ActiveMQMessage(buffer, (ow_ActiveMQMessage*)object); + case OW_CONTROLCOMMAND_TYPE: return ow_marshal1_ControlCommand(buffer, (ow_ControlCommand*)object); + case OW_FLUSHCOMMAND_TYPE: return ow_marshal1_FlushCommand(buffer, (ow_FlushCommand*)object); + case OW_INTEGERRESPONSE_TYPE: return ow_marshal1_IntegerResponse(buffer, (ow_IntegerResponse*)object); + case OW_REMOVESUBSCRIPTIONINFO_TYPE: return ow_marshal1_RemoveSubscriptionInfo(buffer, (ow_RemoveSubscriptionInfo*)object); + case OW_DATAARRAYRESPONSE_TYPE: return ow_marshal1_DataArrayResponse(buffer, (ow_DataArrayResponse*)object); + case OW_BROKERINFO_TYPE: return ow_marshal1_BrokerInfo(buffer, (ow_BrokerInfo*)object); + case OW_MESSAGEDISPATCH_TYPE: return ow_marshal1_MessageDispatch(buffer, (ow_MessageDispatch*)object); + case OW_ACTIVEMQSTREAMMESSAGE_TYPE: return ow_marshal1_ActiveMQStreamMessage(buffer, (ow_ActiveMQStreamMessage*)object); + case OW_ACTIVEMQTEMPTOPIC_TYPE: return ow_marshal1_ActiveMQTempTopic(buffer, (ow_ActiveMQTempTopic*)object); + case OW_PRODUCERINFO_TYPE: return ow_marshal1_ProducerInfo(buffer, (ow_ProducerInfo*)object); + case OW_MESSAGEACK_TYPE: return ow_marshal1_MessageAck(buffer, (ow_MessageAck*)object); + case OW_ACTIVEMQBYTESMESSAGE_TYPE: return ow_marshal1_ActiveMQBytesMessage(buffer, (ow_ActiveMQBytesMessage*)object); + case OW_ACTIVEMQTEXTMESSAGE_TYPE: return ow_marshal1_ActiveMQTextMessage(buffer, (ow_ActiveMQTextMessage*)object); + case OW_ACTIVEMQMAPMESSAGE_TYPE: return ow_marshal1_ActiveMQMapMessage(buffer, (ow_ActiveMQMapMessage*)object); + case OW_DATARESPONSE_TYPE: return ow_marshal1_DataResponse(buffer, (ow_DataResponse*)object); + case OW_XATRANSACTIONID_TYPE: return ow_marshal1_XATransactionId(buffer, (ow_XATransactionId*)object); + case OW_ACTIVEMQOBJECTMESSAGE_TYPE: return ow_marshal1_ActiveMQObjectMessage(buffer, (ow_ActiveMQObjectMessage*)object); + case OW_EXCEPTIONRESPONSE_TYPE: return ow_marshal1_ExceptionResponse(buffer, (ow_ExceptionResponse*)object); + case OW_ACTIVEMQTEMPQUEUE_TYPE: return ow_marshal1_ActiveMQTempQueue(buffer, (ow_ActiveMQTempQueue*)object); + case OW_LOCALTRANSACTIONID_TYPE: return ow_marshal1_LocalTransactionId(buffer, (ow_LocalTransactionId*)object); + } + return APR_EGENERAL; +} + +apr_status_t ow_marshal2_object(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure *object) +{ + switch( object->structType ) { + + case OW_MESSAGEID_TYPE: return ow_marshal2_MessageId(buffer, bitbuffer, (ow_MessageId*)object); + case OW_SUBSCRIPTIONINFO_TYPE: return ow_marshal2_SubscriptionInfo(buffer, bitbuffer, (ow_SubscriptionInfo*)object); + case OW_CONNECTIONID_TYPE: return ow_marshal2_ConnectionId(buffer, bitbuffer, (ow_ConnectionId*)object); + case OW_JOURNALTRACE_TYPE: return ow_marshal2_JournalTrace(buffer, bitbuffer, (ow_JournalTrace*)object); + case OW_KEEPALIVEINFO_TYPE: return ow_marshal2_KeepAliveInfo(buffer, bitbuffer, (ow_KeepAliveInfo*)object); + case OW_JOURNALQUEUEACK_TYPE: return ow_marshal2_JournalQueueAck(buffer, bitbuffer, (ow_JournalQueueAck*)object); + case OW_BROKERID_TYPE: return ow_marshal2_BrokerId(buffer, bitbuffer, (ow_BrokerId*)object); + case OW_SESSIONINFO_TYPE: return ow_marshal2_SessionInfo(buffer, bitbuffer, (ow_SessionInfo*)object); + case OW_SHUTDOWNINFO_TYPE: return ow_marshal2_ShutdownInfo(buffer, bitbuffer, (ow_ShutdownInfo*)object); + case OW_JOURNALTOPICACK_TYPE: return ow_marshal2_JournalTopicAck(buffer, bitbuffer, (ow_JournalTopicAck*)object); + case OW_DESTINATIONINFO_TYPE: return ow_marshal2_DestinationInfo(buffer, bitbuffer, (ow_DestinationInfo*)object); + case OW_CONSUMERID_TYPE: return ow_marshal2_ConsumerId(buffer, bitbuffer, (ow_ConsumerId*)object); + case OW_SESSIONID_TYPE: return ow_marshal2_SessionId(buffer, bitbuffer, (ow_SessionId*)object); + case OW_CONSUMERINFO_TYPE: return ow_marshal2_ConsumerInfo(buffer, bitbuffer, (ow_ConsumerInfo*)object); + case OW_CONNECTIONINFO_TYPE: return ow_marshal2_ConnectionInfo(buffer, bitbuffer, (ow_ConnectionInfo*)object); + case OW_ACTIVEMQTOPIC_TYPE: return ow_marshal2_ActiveMQTopic(buffer, bitbuffer, (ow_ActiveMQTopic*)object); + case OW_REDELIVERYPOLICY_TYPE: return ow_marshal2_RedeliveryPolicy(buffer, bitbuffer, (ow_RedeliveryPolicy*)object); + case OW_JOURNALTRANSACTION_TYPE: return ow_marshal2_JournalTransaction(buffer, bitbuffer, (ow_JournalTransaction*)object); + case OW_PRODUCERID_TYPE: return ow_marshal2_ProducerId(buffer, bitbuffer, (ow_ProducerId*)object); + case OW_ACTIVEMQQUEUE_TYPE: return ow_marshal2_ActiveMQQueue(buffer, bitbuffer, (ow_ActiveMQQueue*)object); + case OW_TRANSACTIONINFO_TYPE: return ow_marshal2_TransactionInfo(buffer, bitbuffer, (ow_TransactionInfo*)object); + case OW_RESPONSE_TYPE: return ow_marshal2_Response(buffer, bitbuffer, (ow_Response*)object); + case OW_REMOVEINFO_TYPE: return ow_marshal2_RemoveInfo(buffer, bitbuffer, (ow_RemoveInfo*)object); + case OW_WIREFORMATINFO_TYPE: return ow_marshal2_WireFormatInfo(buffer, bitbuffer, (ow_WireFormatInfo*)object); + case OW_ACTIVEMQMESSAGE_TYPE: return ow_marshal2_ActiveMQMessage(buffer, bitbuffer, (ow_ActiveMQMessage*)object); + case OW_CONTROLCOMMAND_TYPE: return ow_marshal2_ControlCommand(buffer, bitbuffer, (ow_ControlCommand*)object); + case OW_FLUSHCOMMAND_TYPE: return ow_marshal2_FlushCommand(buffer, bitbuffer, (ow_FlushCommand*)object); + case OW_INTEGERRESPONSE_TYPE: return ow_marshal2_IntegerResponse(buffer, bitbuffer, (ow_IntegerResponse*)object); + case OW_REMOVESUBSCRIPTIONINFO_TYPE: return ow_marshal2_RemoveSubscriptionInfo(buffer, bitbuffer, (ow_RemoveSubscriptionInfo*)object); + case OW_DATAARRAYRESPONSE_TYPE: return ow_marshal2_DataArrayResponse(buffer, bitbuffer, (ow_DataArrayResponse*)object); + case OW_BROKERINFO_TYPE: return ow_marshal2_BrokerInfo(buffer, bitbuffer, (ow_BrokerInfo*)object); + case OW_MESSAGEDISPATCH_TYPE: return ow_marshal2_MessageDispatch(buffer, bitbuffer, (ow_MessageDispatch*)object); + case OW_ACTIVEMQSTREAMMESSAGE_TYPE: return ow_marshal2_ActiveMQStreamMessage(buffer, bitbuffer, (ow_ActiveMQStreamMessage*)object); + case OW_ACTIVEMQTEMPTOPIC_TYPE: return ow_marshal2_ActiveMQTempTopic(buffer, bitbuffer, (ow_ActiveMQTempTopic*)object); + case OW_PRODUCERINFO_TYPE: return ow_marshal2_ProducerInfo(buffer, bitbuffer, (ow_ProducerInfo*)object); + case OW_MESSAGEACK_TYPE: return ow_marshal2_MessageAck(buffer, bitbuffer, (ow_MessageAck*)object); + case OW_ACTIVEMQBYTESMESSAGE_TYPE: return ow_marshal2_ActiveMQBytesMessage(buffer, bitbuffer, (ow_ActiveMQBytesMessage*)object); + case OW_ACTIVEMQTEXTMESSAGE_TYPE: return ow_marshal2_ActiveMQTextMessage(buffer, bitbuffer, (ow_ActiveMQTextMessage*)object); + case OW_ACTIVEMQMAPMESSAGE_TYPE: return ow_marshal2_ActiveMQMapMessage(buffer, bitbuffer, (ow_ActiveMQMapMessage*)object); + case OW_DATARESPONSE_TYPE: return ow_marshal2_DataResponse(buffer, bitbuffer, (ow_DataResponse*)object); + case OW_XATRANSACTIONID_TYPE: return ow_marshal2_XATransactionId(buffer, bitbuffer, (ow_XATransactionId*)object); + case OW_ACTIVEMQOBJECTMESSAGE_TYPE: return ow_marshal2_ActiveMQObjectMessage(buffer, bitbuffer, (ow_ActiveMQObjectMessage*)object); + case OW_EXCEPTIONRESPONSE_TYPE: return ow_marshal2_ExceptionResponse(buffer, bitbuffer, (ow_ExceptionResponse*)object); + case OW_ACTIVEMQTEMPQUEUE_TYPE: return ow_marshal2_ActiveMQTempQueue(buffer, bitbuffer, (ow_ActiveMQTempQueue*)object); + case OW_LOCALTRANSACTIONID_TYPE: return ow_marshal2_LocalTransactionId(buffer, bitbuffer, (ow_LocalTransactionId*)object); + } + return APR_EGENERAL; +} + +apr_status_t ow_unmarshal_object(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure *object, apr_pool_t *pool) +{ + switch( object->structType ) { + + case OW_MESSAGEID_TYPE: return ow_unmarshal_MessageId(buffer, bitbuffer, (ow_MessageId*)object, pool); + case OW_SUBSCRIPTIONINFO_TYPE: return ow_unmarshal_SubscriptionInfo(buffer, bitbuffer, (ow_SubscriptionInfo*)object, pool); + case OW_CONNECTIONID_TYPE: return ow_unmarshal_ConnectionId(buffer, bitbuffer, (ow_ConnectionId*)object, pool); + case OW_JOURNALTRACE_TYPE: return ow_unmarshal_JournalTrace(buffer, bitbuffer, (ow_JournalTrace*)object, pool); + case OW_KEEPALIVEINFO_TYPE: return ow_unmarshal_KeepAliveInfo(buffer, bitbuffer, (ow_KeepAliveInfo*)object, pool); + case OW_JOURNALQUEUEACK_TYPE: return ow_unmarshal_JournalQueueAck(buffer, bitbuffer, (ow_JournalQueueAck*)object, pool); + case OW_BROKERID_TYPE: return ow_unmarshal_BrokerId(buffer, bitbuffer, (ow_BrokerId*)object, pool); + case OW_SESSIONINFO_TYPE: return ow_unmarshal_SessionInfo(buffer, bitbuffer, (ow_SessionInfo*)object, pool); + case OW_SHUTDOWNINFO_TYPE: return ow_unmarshal_ShutdownInfo(buffer, bitbuffer, (ow_ShutdownInfo*)object, pool); + case OW_JOURNALTOPICACK_TYPE: return ow_unmarshal_JournalTopicAck(buffer, bitbuffer, (ow_JournalTopicAck*)object, pool); + case OW_DESTINATIONINFO_TYPE: return ow_unmarshal_DestinationInfo(buffer, bitbuffer, (ow_DestinationInfo*)object, pool); + case OW_CONSUMERID_TYPE: return ow_unmarshal_ConsumerId(buffer, bitbuffer, (ow_ConsumerId*)object, pool); + case OW_SESSIONID_TYPE: return ow_unmarshal_SessionId(buffer, bitbuffer, (ow_SessionId*)object, pool); + case OW_CONSUMERINFO_TYPE: return ow_unmarshal_ConsumerInfo(buffer, bitbuffer, (ow_ConsumerInfo*)object, pool); + case OW_CONNECTIONINFO_TYPE: return ow_unmarshal_ConnectionInfo(buffer, bitbuffer, (ow_ConnectionInfo*)object, pool); + case OW_ACTIVEMQTOPIC_TYPE: return ow_unmarshal_ActiveMQTopic(buffer, bitbuffer, (ow_ActiveMQTopic*)object, pool); + case OW_REDELIVERYPOLICY_TYPE: return ow_unmarshal_RedeliveryPolicy(buffer, bitbuffer, (ow_RedeliveryPolicy*)object, pool); + case OW_JOURNALTRANSACTION_TYPE: return ow_unmarshal_JournalTransaction(buffer, bitbuffer, (ow_JournalTransaction*)object, pool); + case OW_PRODUCERID_TYPE: return ow_unmarshal_ProducerId(buffer, bitbuffer, (ow_ProducerId*)object, pool); + case OW_ACTIVEMQQUEUE_TYPE: return ow_unmarshal_ActiveMQQueue(buffer, bitbuffer, (ow_ActiveMQQueue*)object, pool); + case OW_TRANSACTIONINFO_TYPE: return ow_unmarshal_TransactionInfo(buffer, bitbuffer, (ow_TransactionInfo*)object, pool); + case OW_RESPONSE_TYPE: return ow_unmarshal_Response(buffer, bitbuffer, (ow_Response*)object, pool); + case OW_REMOVEINFO_TYPE: return ow_unmarshal_RemoveInfo(buffer, bitbuffer, (ow_RemoveInfo*)object, pool); + case OW_WIREFORMATINFO_TYPE: return ow_unmarshal_WireFormatInfo(buffer, bitbuffer, (ow_WireFormatInfo*)object, pool); + case OW_ACTIVEMQMESSAGE_TYPE: return ow_unmarshal_ActiveMQMessage(buffer, bitbuffer, (ow_ActiveMQMessage*)object, pool); + case OW_CONTROLCOMMAND_TYPE: return ow_unmarshal_ControlCommand(buffer, bitbuffer, (ow_ControlCommand*)object, pool); + case OW_FLUSHCOMMAND_TYPE: return ow_unmarshal_FlushCommand(buffer, bitbuffer, (ow_FlushCommand*)object, pool); + case OW_INTEGERRESPONSE_TYPE: return ow_unmarshal_IntegerResponse(buffer, bitbuffer, (ow_IntegerResponse*)object, pool); + case OW_REMOVESUBSCRIPTIONINFO_TYPE: return ow_unmarshal_RemoveSubscriptionInfo(buffer, bitbuffer, (ow_RemoveSubscriptionInfo*)object, pool); + case OW_DATAARRAYRESPONSE_TYPE: return ow_unmarshal_DataArrayResponse(buffer, bitbuffer, (ow_DataArrayResponse*)object, pool); + case OW_BROKERINFO_TYPE: return ow_unmarshal_BrokerInfo(buffer, bitbuffer, (ow_BrokerInfo*)object, pool); + case OW_MESSAGEDISPATCH_TYPE: return ow_unmarshal_MessageDispatch(buffer, bitbuffer, (ow_MessageDispatch*)object, pool); + case OW_ACTIVEMQSTREAMMESSAGE_TYPE: return ow_unmarshal_ActiveMQStreamMessage(buffer, bitbuffer, (ow_ActiveMQStreamMessage*)object, pool); + case OW_ACTIVEMQTEMPTOPIC_TYPE: return ow_unmarshal_ActiveMQTempTopic(buffer, bitbuffer, (ow_ActiveMQTempTopic*)object, pool); + case OW_PRODUCERINFO_TYPE: return ow_unmarshal_ProducerInfo(buffer, bitbuffer, (ow_ProducerInfo*)object, pool); + case OW_MESSAGEACK_TYPE: return ow_unmarshal_MessageAck(buffer, bitbuffer, (ow_MessageAck*)object, pool); + case OW_ACTIVEMQBYTESMESSAGE_TYPE: return ow_unmarshal_ActiveMQBytesMessage(buffer, bitbuffer, (ow_ActiveMQBytesMessage*)object, pool); + case OW_ACTIVEMQTEXTMESSAGE_TYPE: return ow_unmarshal_ActiveMQTextMessage(buffer, bitbuffer, (ow_ActiveMQTextMessage*)object, pool); + case OW_ACTIVEMQMAPMESSAGE_TYPE: return ow_unmarshal_ActiveMQMapMessage(buffer, bitbuffer, (ow_ActiveMQMapMessage*)object, pool); + case OW_DATARESPONSE_TYPE: return ow_unmarshal_DataResponse(buffer, bitbuffer, (ow_DataResponse*)object, pool); + case OW_XATRANSACTIONID_TYPE: return ow_unmarshal_XATransactionId(buffer, bitbuffer, (ow_XATransactionId*)object, pool); + case OW_ACTIVEMQOBJECTMESSAGE_TYPE: return ow_unmarshal_ActiveMQObjectMessage(buffer, bitbuffer, (ow_ActiveMQObjectMessage*)object, pool); + case OW_EXCEPTIONRESPONSE_TYPE: return ow_unmarshal_ExceptionResponse(buffer, bitbuffer, (ow_ExceptionResponse*)object, pool); + case OW_ACTIVEMQTEMPQUEUE_TYPE: return ow_unmarshal_ActiveMQTempQueue(buffer, bitbuffer, (ow_ActiveMQTempQueue*)object, pool); + case OW_LOCALTRANSACTIONID_TYPE: return ow_unmarshal_LocalTransactionId(buffer, bitbuffer, (ow_LocalTransactionId*)object, pool); + } + return APR_EGENERAL; +} diff --git a/openwire-c/src/libopenwire/ow_commands_v1.h b/openwire-c/src/libopenwire/ow_commands_v1.h new file mode 100644 index 0000000000..d7e6618801 --- /dev/null +++ b/openwire-c/src/libopenwire/ow_commands_v1.h @@ -0,0 +1,774 @@ +/** + * + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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. + */ + +/***************************************************************************************** + * + * NOTE!: This file is auto generated - do not modify! + * if you need to make a change, please see the modify the groovy scripts in the + * under src/gram/script and then use maven openwire:generate to regenerate + * this file. + * + *****************************************************************************************/ + +#ifndef OW_COMMANDS_V1_H +#define OW_COMMANDS_V1_H + +#include "ow.h" +#include "ow_command_types_v1.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define OW_WIREFORMAT_VERSION 1 + +apr_status_t ow_bitmarshall(ow_bit_buffer *buffer, ow_DataStructure *object); +apr_status_t ow_marshall(ow_byte_buffer *buffer, ow_DataStructure *object); + +typedef struct ow_MessageId { + + ow_byte structType; + struct ow_ProducerId *producerId; + ow_long producerSequenceId; + ow_long brokerSequenceId; + +} ow_MessageId; +ow_MessageId *ow_MessageId_create(apr_pool_t *pool); +ow_boolean ow_is_a_MessageId(ow_DataStructure *object); + +typedef struct ow_SubscriptionInfo { + + ow_byte structType; + ow_string *clientId; + struct ow_ActiveMQDestination *destination; + ow_string *selector; + ow_string *subcriptionName; + +} ow_SubscriptionInfo; +ow_SubscriptionInfo *ow_SubscriptionInfo_create(apr_pool_t *pool); +ow_boolean ow_is_a_SubscriptionInfo(ow_DataStructure *object); + +typedef struct ow_ActiveMQDestination { + + ow_byte structType; + ow_string *physicalName; + +} ow_ActiveMQDestination; +ow_ActiveMQDestination *ow_ActiveMQDestination_create(apr_pool_t *pool); +ow_boolean ow_is_a_ActiveMQDestination(ow_DataStructure *object); + +typedef struct ow_ConnectionId { + + ow_byte structType; + ow_string *connectionId; + +} ow_ConnectionId; +ow_ConnectionId *ow_ConnectionId_create(apr_pool_t *pool); +ow_boolean ow_is_a_ConnectionId(ow_DataStructure *object); + +typedef struct ow_JournalTrace { + + ow_byte structType; + ow_string *message; + +} ow_JournalTrace; +ow_JournalTrace *ow_JournalTrace_create(apr_pool_t *pool); +ow_boolean ow_is_a_JournalTrace(ow_DataStructure *object); + +typedef struct ow_KeepAliveInfo { + + ow_byte structType; + +} ow_KeepAliveInfo; +ow_KeepAliveInfo *ow_KeepAliveInfo_create(apr_pool_t *pool); +ow_boolean ow_is_a_KeepAliveInfo(ow_DataStructure *object); + +typedef struct ow_JournalQueueAck { + + ow_byte structType; + struct ow_ActiveMQDestination *destination; + struct ow_MessageAck *messageAck; + +} ow_JournalQueueAck; +ow_JournalQueueAck *ow_JournalQueueAck_create(apr_pool_t *pool); +ow_boolean ow_is_a_JournalQueueAck(ow_DataStructure *object); + +typedef struct ow_BrokerId { + + ow_byte structType; + ow_string *brokerId; + +} ow_BrokerId; +ow_BrokerId *ow_BrokerId_create(apr_pool_t *pool); +ow_boolean ow_is_a_BrokerId(ow_DataStructure *object); + +typedef struct ow_BaseCommand { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + +} ow_BaseCommand; +ow_BaseCommand *ow_BaseCommand_create(apr_pool_t *pool); +ow_boolean ow_is_a_BaseCommand(ow_DataStructure *object); + +typedef struct ow_SessionInfo { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_SessionId *sessionId; + +} ow_SessionInfo; +ow_SessionInfo *ow_SessionInfo_create(apr_pool_t *pool); +ow_boolean ow_is_a_SessionInfo(ow_DataStructure *object); + +typedef struct ow_Message { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ProducerId *producerId; + struct ow_ActiveMQDestination *destination; + struct ow_TransactionId *transactionId; + struct ow_ActiveMQDestination *originalDestination; + struct ow_MessageId *messageId; + struct ow_TransactionId *originalTransactionId; + ow_string *groupID; + ow_int groupSequence; + ow_string *correlationId; + ow_boolean persistent; + ow_long expiration; + ow_byte priority; + struct ow_ActiveMQDestination *replyTo; + ow_long timestamp; + ow_string *type; + ow_byte_array *content; + ow_byte_array *marshalledProperties; + struct ow_DataStructure *dataStructure; + struct ow_ConsumerId *targetConsumerId; + ow_boolean compressed; + ow_int redeliveryCounter; + ow_DataStructure_array *brokerPath; + ow_long arrival; + ow_string *userID; + +} ow_Message; +ow_Message *ow_Message_create(apr_pool_t *pool); +ow_boolean ow_is_a_Message(ow_DataStructure *object); + +typedef struct ow_ShutdownInfo { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + +} ow_ShutdownInfo; +ow_ShutdownInfo *ow_ShutdownInfo_create(apr_pool_t *pool); +ow_boolean ow_is_a_ShutdownInfo(ow_DataStructure *object); + +typedef struct ow_JournalTopicAck { + + ow_byte structType; + struct ow_ActiveMQDestination *destination; + struct ow_MessageId *messageId; + ow_long messageSequenceId; + ow_string *subscritionName; + ow_string *clientId; + struct ow_TransactionId *transactionId; + +} ow_JournalTopicAck; +ow_JournalTopicAck *ow_JournalTopicAck_create(apr_pool_t *pool); +ow_boolean ow_is_a_JournalTopicAck(ow_DataStructure *object); + +typedef struct ow_DestinationInfo { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ConnectionId *connectionId; + struct ow_ActiveMQDestination *destination; + ow_byte operationType; + ow_long timeout; + ow_DataStructure_array *brokerPath; + +} ow_DestinationInfo; +ow_DestinationInfo *ow_DestinationInfo_create(apr_pool_t *pool); +ow_boolean ow_is_a_DestinationInfo(ow_DataStructure *object); + +typedef struct ow_ConsumerId { + + ow_byte structType; + ow_string *connectionId; + ow_long sessionId; + ow_long consumerId; + +} ow_ConsumerId; +ow_ConsumerId *ow_ConsumerId_create(apr_pool_t *pool); +ow_boolean ow_is_a_ConsumerId(ow_DataStructure *object); + +typedef struct ow_SessionId { + + ow_byte structType; + ow_string *connectionId; + ow_long sessionId; + +} ow_SessionId; +ow_SessionId *ow_SessionId_create(apr_pool_t *pool); +ow_boolean ow_is_a_SessionId(ow_DataStructure *object); + +typedef struct ow_ConsumerInfo { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ConsumerId *consumerId; + ow_boolean browser; + struct ow_ActiveMQDestination *destination; + ow_int prefetchSize; + ow_boolean dispatchAsync; + ow_string *selector; + ow_string *subcriptionName; + ow_boolean noLocal; + ow_boolean exclusive; + ow_boolean retroactive; + ow_byte priority; + ow_DataStructure_array *brokerPath; + +} ow_ConsumerInfo; +ow_ConsumerInfo *ow_ConsumerInfo_create(apr_pool_t *pool); +ow_boolean ow_is_a_ConsumerInfo(ow_DataStructure *object); + +typedef struct ow_ConnectionInfo { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ConnectionId *connectionId; + ow_string *clientId; + ow_string *password; + ow_string *userName; + ow_DataStructure_array *brokerPath; + +} ow_ConnectionInfo; +ow_ConnectionInfo *ow_ConnectionInfo_create(apr_pool_t *pool); +ow_boolean ow_is_a_ConnectionInfo(ow_DataStructure *object); + +typedef struct ow_ActiveMQTopic { + + ow_byte structType; + ow_string *physicalName; + +} ow_ActiveMQTopic; +ow_ActiveMQTopic *ow_ActiveMQTopic_create(apr_pool_t *pool); +ow_boolean ow_is_a_ActiveMQTopic(ow_DataStructure *object); + +typedef struct ow_RedeliveryPolicy { + + ow_byte structType; + ow_short backOffMultiplier; + ow_long initialRedeliveryDelay; + ow_int maximumRedeliveries; + ow_boolean useExponentialBackOff; + +} ow_RedeliveryPolicy; +ow_RedeliveryPolicy *ow_RedeliveryPolicy_create(apr_pool_t *pool); +ow_boolean ow_is_a_RedeliveryPolicy(ow_DataStructure *object); + +typedef struct ow_JournalTransaction { + + ow_byte structType; + struct ow_TransactionId *transactionId; + ow_byte type; + ow_boolean wasPrepared; + +} ow_JournalTransaction; +ow_JournalTransaction *ow_JournalTransaction_create(apr_pool_t *pool); +ow_boolean ow_is_a_JournalTransaction(ow_DataStructure *object); + +typedef struct ow_ProducerId { + + ow_byte structType; + ow_string *connectionId; + ow_long producerId; + ow_long sessionId; + +} ow_ProducerId; +ow_ProducerId *ow_ProducerId_create(apr_pool_t *pool); +ow_boolean ow_is_a_ProducerId(ow_DataStructure *object); + +typedef struct ow_ActiveMQQueue { + + ow_byte structType; + ow_string *physicalName; + +} ow_ActiveMQQueue; +ow_ActiveMQQueue *ow_ActiveMQQueue_create(apr_pool_t *pool); +ow_boolean ow_is_a_ActiveMQQueue(ow_DataStructure *object); + +typedef struct ow_TransactionInfo { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ConnectionId *connectionId; + struct ow_TransactionId *transactionId; + ow_byte type; + +} ow_TransactionInfo; +ow_TransactionInfo *ow_TransactionInfo_create(apr_pool_t *pool); +ow_boolean ow_is_a_TransactionInfo(ow_DataStructure *object); + +typedef struct ow_Response { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + ow_short correlationId; + +} ow_Response; +ow_Response *ow_Response_create(apr_pool_t *pool); +ow_boolean ow_is_a_Response(ow_DataStructure *object); + +typedef struct ow_RemoveInfo { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_DataStructure *objectId; + +} ow_RemoveInfo; +ow_RemoveInfo *ow_RemoveInfo_create(apr_pool_t *pool); +ow_boolean ow_is_a_RemoveInfo(ow_DataStructure *object); + +typedef struct ow_WireFormatInfo { + + ow_byte structType; + ow_byte_array *magic; + ow_int version; + ow_int options; + +} ow_WireFormatInfo; +ow_WireFormatInfo *ow_WireFormatInfo_create(apr_pool_t *pool); +ow_boolean ow_is_a_WireFormatInfo(ow_DataStructure *object); + +typedef struct ow_TransactionId { + + ow_byte structType; + +} ow_TransactionId; +ow_TransactionId *ow_TransactionId_create(apr_pool_t *pool); +ow_boolean ow_is_a_TransactionId(ow_DataStructure *object); + +typedef struct ow_ActiveMQMessage { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ProducerId *producerId; + struct ow_ActiveMQDestination *destination; + struct ow_TransactionId *transactionId; + struct ow_ActiveMQDestination *originalDestination; + struct ow_MessageId *messageId; + struct ow_TransactionId *originalTransactionId; + ow_string *groupID; + ow_int groupSequence; + ow_string *correlationId; + ow_boolean persistent; + ow_long expiration; + ow_byte priority; + struct ow_ActiveMQDestination *replyTo; + ow_long timestamp; + ow_string *type; + ow_byte_array *content; + ow_byte_array *marshalledProperties; + struct ow_DataStructure *dataStructure; + struct ow_ConsumerId *targetConsumerId; + ow_boolean compressed; + ow_int redeliveryCounter; + ow_DataStructure_array *brokerPath; + ow_long arrival; + ow_string *userID; + +} ow_ActiveMQMessage; +ow_ActiveMQMessage *ow_ActiveMQMessage_create(apr_pool_t *pool); +ow_boolean ow_is_a_ActiveMQMessage(ow_DataStructure *object); + +typedef struct ow_ControlCommand { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + ow_string *command; + +} ow_ControlCommand; +ow_ControlCommand *ow_ControlCommand_create(apr_pool_t *pool); +ow_boolean ow_is_a_ControlCommand(ow_DataStructure *object); + +typedef struct ow_FlushCommand { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + +} ow_FlushCommand; +ow_FlushCommand *ow_FlushCommand_create(apr_pool_t *pool); +ow_boolean ow_is_a_FlushCommand(ow_DataStructure *object); + +typedef struct ow_IntegerResponse { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + ow_short correlationId; + ow_int result; + +} ow_IntegerResponse; +ow_IntegerResponse *ow_IntegerResponse_create(apr_pool_t *pool); +ow_boolean ow_is_a_IntegerResponse(ow_DataStructure *object); + +typedef struct ow_RemoveSubscriptionInfo { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ConnectionId *connectionId; + ow_string *subcriptionName; + ow_string *clientId; + +} ow_RemoveSubscriptionInfo; +ow_RemoveSubscriptionInfo *ow_RemoveSubscriptionInfo_create(apr_pool_t *pool); +ow_boolean ow_is_a_RemoveSubscriptionInfo(ow_DataStructure *object); + +typedef struct ow_ActiveMQTempDestination { + + ow_byte structType; + ow_string *physicalName; + +} ow_ActiveMQTempDestination; +ow_ActiveMQTempDestination *ow_ActiveMQTempDestination_create(apr_pool_t *pool); +ow_boolean ow_is_a_ActiveMQTempDestination(ow_DataStructure *object); + +typedef struct ow_DataArrayResponse { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + ow_short correlationId; + ow_DataStructure_array *data; + +} ow_DataArrayResponse; +ow_DataArrayResponse *ow_DataArrayResponse_create(apr_pool_t *pool); +ow_boolean ow_is_a_DataArrayResponse(ow_DataStructure *object); + +typedef struct ow_BrokerInfo { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_BrokerId *brokerId; + ow_string *brokerURL; + ow_DataStructure_array *peerBrokerInfos; + struct ow_RedeliveryPolicy *redeliveryPolicy; + ow_string *brokerName; + +} ow_BrokerInfo; +ow_BrokerInfo *ow_BrokerInfo_create(apr_pool_t *pool); +ow_boolean ow_is_a_BrokerInfo(ow_DataStructure *object); + +typedef struct ow_MessageDispatch { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ConsumerId *consumerId; + struct ow_ActiveMQDestination *destination; + struct ow_Message *message; + ow_int redeliveryCounter; + +} ow_MessageDispatch; +ow_MessageDispatch *ow_MessageDispatch_create(apr_pool_t *pool); +ow_boolean ow_is_a_MessageDispatch(ow_DataStructure *object); + +typedef struct ow_ActiveMQStreamMessage { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ProducerId *producerId; + struct ow_ActiveMQDestination *destination; + struct ow_TransactionId *transactionId; + struct ow_ActiveMQDestination *originalDestination; + struct ow_MessageId *messageId; + struct ow_TransactionId *originalTransactionId; + ow_string *groupID; + ow_int groupSequence; + ow_string *correlationId; + ow_boolean persistent; + ow_long expiration; + ow_byte priority; + struct ow_ActiveMQDestination *replyTo; + ow_long timestamp; + ow_string *type; + ow_byte_array *content; + ow_byte_array *marshalledProperties; + struct ow_DataStructure *dataStructure; + struct ow_ConsumerId *targetConsumerId; + ow_boolean compressed; + ow_int redeliveryCounter; + ow_DataStructure_array *brokerPath; + ow_long arrival; + ow_string *userID; + +} ow_ActiveMQStreamMessage; +ow_ActiveMQStreamMessage *ow_ActiveMQStreamMessage_create(apr_pool_t *pool); +ow_boolean ow_is_a_ActiveMQStreamMessage(ow_DataStructure *object); + +typedef struct ow_ActiveMQTempTopic { + + ow_byte structType; + ow_string *physicalName; + +} ow_ActiveMQTempTopic; +ow_ActiveMQTempTopic *ow_ActiveMQTempTopic_create(apr_pool_t *pool); +ow_boolean ow_is_a_ActiveMQTempTopic(ow_DataStructure *object); + +typedef struct ow_ProducerInfo { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ProducerId *producerId; + struct ow_ActiveMQDestination *destination; + ow_DataStructure_array *brokerPath; + +} ow_ProducerInfo; +ow_ProducerInfo *ow_ProducerInfo_create(apr_pool_t *pool); +ow_boolean ow_is_a_ProducerInfo(ow_DataStructure *object); + +typedef struct ow_MessageAck { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ActiveMQDestination *destination; + struct ow_TransactionId *transactionId; + struct ow_ConsumerId *consumerId; + ow_byte ackType; + struct ow_MessageId *firstMessageId; + struct ow_MessageId *lastMessageId; + ow_int messageCount; + +} ow_MessageAck; +ow_MessageAck *ow_MessageAck_create(apr_pool_t *pool); +ow_boolean ow_is_a_MessageAck(ow_DataStructure *object); + +typedef struct ow_ActiveMQBytesMessage { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ProducerId *producerId; + struct ow_ActiveMQDestination *destination; + struct ow_TransactionId *transactionId; + struct ow_ActiveMQDestination *originalDestination; + struct ow_MessageId *messageId; + struct ow_TransactionId *originalTransactionId; + ow_string *groupID; + ow_int groupSequence; + ow_string *correlationId; + ow_boolean persistent; + ow_long expiration; + ow_byte priority; + struct ow_ActiveMQDestination *replyTo; + ow_long timestamp; + ow_string *type; + ow_byte_array *content; + ow_byte_array *marshalledProperties; + struct ow_DataStructure *dataStructure; + struct ow_ConsumerId *targetConsumerId; + ow_boolean compressed; + ow_int redeliveryCounter; + ow_DataStructure_array *brokerPath; + ow_long arrival; + ow_string *userID; + +} ow_ActiveMQBytesMessage; +ow_ActiveMQBytesMessage *ow_ActiveMQBytesMessage_create(apr_pool_t *pool); +ow_boolean ow_is_a_ActiveMQBytesMessage(ow_DataStructure *object); + +typedef struct ow_ActiveMQTextMessage { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ProducerId *producerId; + struct ow_ActiveMQDestination *destination; + struct ow_TransactionId *transactionId; + struct ow_ActiveMQDestination *originalDestination; + struct ow_MessageId *messageId; + struct ow_TransactionId *originalTransactionId; + ow_string *groupID; + ow_int groupSequence; + ow_string *correlationId; + ow_boolean persistent; + ow_long expiration; + ow_byte priority; + struct ow_ActiveMQDestination *replyTo; + ow_long timestamp; + ow_string *type; + ow_byte_array *content; + ow_byte_array *marshalledProperties; + struct ow_DataStructure *dataStructure; + struct ow_ConsumerId *targetConsumerId; + ow_boolean compressed; + ow_int redeliveryCounter; + ow_DataStructure_array *brokerPath; + ow_long arrival; + ow_string *userID; + +} ow_ActiveMQTextMessage; +ow_ActiveMQTextMessage *ow_ActiveMQTextMessage_create(apr_pool_t *pool); +ow_boolean ow_is_a_ActiveMQTextMessage(ow_DataStructure *object); + +typedef struct ow_ActiveMQMapMessage { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ProducerId *producerId; + struct ow_ActiveMQDestination *destination; + struct ow_TransactionId *transactionId; + struct ow_ActiveMQDestination *originalDestination; + struct ow_MessageId *messageId; + struct ow_TransactionId *originalTransactionId; + ow_string *groupID; + ow_int groupSequence; + ow_string *correlationId; + ow_boolean persistent; + ow_long expiration; + ow_byte priority; + struct ow_ActiveMQDestination *replyTo; + ow_long timestamp; + ow_string *type; + ow_byte_array *content; + ow_byte_array *marshalledProperties; + struct ow_DataStructure *dataStructure; + struct ow_ConsumerId *targetConsumerId; + ow_boolean compressed; + ow_int redeliveryCounter; + ow_DataStructure_array *brokerPath; + ow_long arrival; + ow_string *userID; + +} ow_ActiveMQMapMessage; +ow_ActiveMQMapMessage *ow_ActiveMQMapMessage_create(apr_pool_t *pool); +ow_boolean ow_is_a_ActiveMQMapMessage(ow_DataStructure *object); + +typedef struct ow_DataResponse { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + ow_short correlationId; + struct ow_DataStructure *data; + +} ow_DataResponse; +ow_DataResponse *ow_DataResponse_create(apr_pool_t *pool); +ow_boolean ow_is_a_DataResponse(ow_DataStructure *object); + +typedef struct ow_XATransactionId { + + ow_byte structType; + ow_int formatId; + ow_byte_array *globalTransactionId; + ow_byte_array *branchQualifier; + +} ow_XATransactionId; +ow_XATransactionId *ow_XATransactionId_create(apr_pool_t *pool); +ow_boolean ow_is_a_XATransactionId(ow_DataStructure *object); + +typedef struct ow_ActiveMQObjectMessage { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + struct ow_ProducerId *producerId; + struct ow_ActiveMQDestination *destination; + struct ow_TransactionId *transactionId; + struct ow_ActiveMQDestination *originalDestination; + struct ow_MessageId *messageId; + struct ow_TransactionId *originalTransactionId; + ow_string *groupID; + ow_int groupSequence; + ow_string *correlationId; + ow_boolean persistent; + ow_long expiration; + ow_byte priority; + struct ow_ActiveMQDestination *replyTo; + ow_long timestamp; + ow_string *type; + ow_byte_array *content; + ow_byte_array *marshalledProperties; + struct ow_DataStructure *dataStructure; + struct ow_ConsumerId *targetConsumerId; + ow_boolean compressed; + ow_int redeliveryCounter; + ow_DataStructure_array *brokerPath; + ow_long arrival; + ow_string *userID; + +} ow_ActiveMQObjectMessage; +ow_ActiveMQObjectMessage *ow_ActiveMQObjectMessage_create(apr_pool_t *pool); +ow_boolean ow_is_a_ActiveMQObjectMessage(ow_DataStructure *object); + +typedef struct ow_ExceptionResponse { + + ow_byte structType; + ow_short commandId; + ow_boolean responseRequired; + ow_short correlationId; + ow_throwable *exception; + +} ow_ExceptionResponse; +ow_ExceptionResponse *ow_ExceptionResponse_create(apr_pool_t *pool); +ow_boolean ow_is_a_ExceptionResponse(ow_DataStructure *object); + +typedef struct ow_ActiveMQTempQueue { + + ow_byte structType; + ow_string *physicalName; + +} ow_ActiveMQTempQueue; +ow_ActiveMQTempQueue *ow_ActiveMQTempQueue_create(apr_pool_t *pool); +ow_boolean ow_is_a_ActiveMQTempQueue(ow_DataStructure *object); + +typedef struct ow_LocalTransactionId { + + ow_byte structType; + ow_long transactionId; + struct ow_ConnectionId *connectionId; + +} ow_LocalTransactionId; +ow_LocalTransactionId *ow_LocalTransactionId_create(apr_pool_t *pool); +ow_boolean ow_is_a_LocalTransactionId(ow_DataStructure *object); + +#ifdef __cplusplus +} +#endif + +#endif /* ! OW_COMMANDS_V1_H */ diff --git a/openwire-c/src/libopenwire/ow_marshal.c b/openwire-c/src/libopenwire/ow_marshal.c new file mode 100755 index 0000000000..d0c082ddd0 --- /dev/null +++ b/openwire-c/src/libopenwire/ow_marshal.c @@ -0,0 +1,424 @@ +/** + * + * Copyright 2005 LogicBlaze Inc. + * + * Licensed 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. + */ + +#include "ow.h" +#include "ow_commands_v1.h" + +/************************************************************************ + * marshal/unmarshal nested objects + ************************************************************************/ + +ow_boolean ow_is_a_MarshallAware(ow_DataStructure *object) { + return ow_is_a_Message(object); +} + +apr_status_t ow_marshal1_nested_object(ow_bit_buffer *bitbuffer, ow_DataStructure *object) +{ + ow_bit_buffer_append(bitbuffer, object!=0); + if( object == 0 ) + return APR_SUCCESS; + + if( ow_is_a_MarshallAware(object) ) { + ow_bit_buffer_append(bitbuffer, 0); + } + + return ow_marshal1_object(bitbuffer, object); +} +apr_status_t ow_marshal2_nested_object(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure *object) +{ + apr_status_t rc; + if( !ow_bit_buffer_read(bitbuffer) ) { + return APR_SUCCESS; + } + + rc = ow_byte_buffer_append_byte(buffer, object->structType); + if( rc != APR_SUCCESS ) + return rc; + + if( ow_is_a_MarshallAware(object) ) { + ow_bit_buffer_read(bitbuffer); + } + + rc = ow_marshal2_object(buffer, bitbuffer, object); + return rc; +} + +apr_status_t ow_unmarshal_nested_object(ow_byte_array *data, ow_bit_buffer *bitbuffer, ow_DataStructure **object, apr_pool_t *pool) +{ + + if( ow_bit_buffer_read(bitbuffer) ) { + apr_status_t rc; + ow_byte type; + + + rc = ow_byte_array_read_byte(data, &type); + if( rc!=APR_SUCCESS ) { return rc; } + + *object = ow_create_object(type, pool); + if( object == 0 ) + return APR_ENOMEM; + + if( ow_is_a_MarshallAware(*object) && ow_bit_buffer_read(bitbuffer) ) { + ow_bit_buffer *bitbuffer2; + ow_int size; + + rc = ow_byte_array_read_int(data, &size); + if( rc!=APR_SUCCESS ) { *object=0; return rc; } + rc = ow_byte_array_read_byte(data, &type); + if( rc!=APR_SUCCESS ) { *object=0; return rc; } + + rc = ow_byte_array_read_bit_buffer(data, &bitbuffer2, pool); + + rc = ow_unmarshal_object(data, bitbuffer2, *object, pool); + if( rc != APR_SUCCESS ) + *object = 0; + + } else { + + rc = ow_unmarshal_object(data, bitbuffer, *object, pool); + if( rc != APR_SUCCESS ) + *object = 0; + + } + + return rc; + + } else { + *object=0; + return APR_SUCCESS; + } + + +} + +/************************************************************************ + * marshal/unmarshal cached objects + ************************************************************************/ +apr_status_t ow_marshal1_cached_object(ow_bit_buffer *bitbuffer, ow_DataStructure *object) { + return ow_marshal1_nested_object(bitbuffer, object); +} +apr_status_t ow_marshal2_cached_object(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure *object) +{ + return ow_marshal2_nested_object(buffer, bitbuffer, object); +} +apr_status_t ow_unmarshal_cached_object(ow_byte_array *data, ow_bit_buffer *bitbuffer, ow_DataStructure **object, apr_pool_t *pool) { + return ow_unmarshal_nested_object(data, bitbuffer, object, pool); +} + + +/************************************************************************ + * marshal/unmarshal strings + ************************************************************************/ +apr_status_t ow_marshal1_string(ow_bit_buffer *buffer, ow_string *value) { + ow_bit_buffer_append(buffer, value!=NULL); + if( value != NULL ) { + // this would be true if we were sure it was an ascii string (optimization to avoid utf-8 encode) + ow_bit_buffer_append(buffer, 0); + } + return APR_SUCCESS; +} +apr_status_t ow_marshal2_string(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_string *value) { + apr_status_t rc = APR_SUCCESS; + if( ow_bit_buffer_read(bitbuffer) ) { + ow_bit_buffer_read(bitbuffer); + rc = ow_byte_buffer_append_short(buffer, value->size); + if( rc != APR_SUCCESS ) return rc; + rc = ow_byte_buffer_append(buffer, value->values, value->size, 0); + } + return rc; +} +apr_status_t ow_unmarshal_string(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_string **value, apr_pool_t *pool) { + apr_status_t rc; + ow_short size; + if( ow_bit_buffer_read(bitbuffer) ) { + + // tells us if it is an ascii string (optimization to avoid utf-8 decode) + ow_bit_buffer_read(bitbuffer); + + rc = ow_byte_array_read_short(buffer, &size); + if( rc != APR_SUCCESS ) + return rc; + + if( buffer->size < size ) + return APR_EOF; + + *value = apr_pcalloc(pool, sizeof(ow_string)); + if( *value == NULL ) + return APR_ENOMEM; + + (*value)->size = size; + (*value)->values = buffer->values; + + buffer->values += size; + buffer->size -= size; + + } else { + *value=NULL; + } + return APR_SUCCESS; +} + +/************************************************************************ +* marshal/unmarshal variable size ow_byte_array +************************************************************************/ +apr_status_t ow_marshal2_byte_array(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_byte_array *value) { + apr_status_t rc = APR_SUCCESS; + if( ow_bit_buffer_read(bitbuffer) ) { + rc = ow_byte_buffer_append_int(buffer, value->size); + if( rc != APR_SUCCESS ) + return rc; + rc = ow_byte_buffer_append(buffer, value->values, value->size, 0); + } + return rc; +} +apr_status_t ow_unmarshal_byte_array(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_byte_array **value, apr_pool_t *pool) { + + if( ow_bit_buffer_read(bitbuffer) ) { + + apr_status_t rc; + ow_int size; + + rc = ow_byte_array_read_int(buffer, &size); + if( rc != APR_SUCCESS ) + return rc; + + if( buffer->size < size ) + return APR_EOF; + + *value = apr_pcalloc(pool, sizeof(ow_byte_array)); + if( *value == NULL ) + return APR_ENOMEM; + + (*value)->size = size; + (*value)->values = buffer->values; + + buffer->values += size; + buffer->size -= size; + + } else { + *value=NULL; + } + return APR_SUCCESS; +} + +/************************************************************************ + * marshal/unmarshal constant size ow_byte_array + ************************************************************************/ +apr_status_t ow_marshal2_byte_array_const_size(ow_byte_buffer *buffer, ow_byte_array *value, ow_int size) { + apr_status_t rc; + if( value->size != size ) { + return APR_EGENERAL; + } + rc = ow_byte_buffer_append(buffer, value->values, size, 0); + return rc; +} +apr_status_t ow_unmarshal_byte_array_const_size(ow_byte_array *buffer, ow_byte_array **value, ow_int size, apr_pool_t *pool) { + if( buffer->size < size ) + return APR_EOF; + + *value = apr_pcalloc(pool, sizeof(ow_byte_array)); + if( *value == NULL ) + return APR_ENOMEM; + + (*value)->size = size; + (*value)->values = buffer->values; + + buffer->values += size; + buffer->size -= size; + return APR_SUCCESS; +} + +/************************************************************************ + * marshal/unmarshal a DataStructure stucture + ************************************************************************/ +apr_status_t ow_marshal1_DataStructure(ow_bit_buffer *buffer, ow_DataStructure *object) { + return APR_SUCCESS; +} +apr_status_t ow_marshal2_DataStructure(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure *object) { + return APR_SUCCESS; +} +apr_status_t ow_unmarshal_DataStructure(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure *object, apr_pool_t *pool) +{ + return APR_SUCCESS; +} + +/************************************************************************ +* marshal/unmarshal a variable size DataStructure_array +************************************************************************/ +apr_status_t ow_marshal1_DataStructure_array(ow_bit_buffer *buffer, ow_DataStructure_array *value) { + apr_status_t rc = APR_SUCCESS; + if( value != NULL ) { + ow_bit_buffer_append(buffer, 1); + rc = ow_marshal1_DataStructure_array_const_size(buffer, value, value->size); + } else { + ow_bit_buffer_append(buffer, 0); + } + return rc; +} +apr_status_t ow_marshal2_DataStructure_array(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure_array *value) { + apr_status_t rc = APR_SUCCESS; + if( ow_bit_buffer_read(bitbuffer) ) { + rc = ow_byte_buffer_append_short(buffer, value->size); + if( rc != APR_SUCCESS ) + return rc; + rc = ow_marshal2_DataStructure_array_const_size(buffer, bitbuffer, value, value->size); + } + return rc; +} +apr_status_t ow_unmarshal_DataStructure_array(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure_array **value, apr_pool_t *pool) { + + apr_status_t rc; + ow_short size; + + if( ow_bit_buffer_read(bitbuffer) ) { + rc = ow_byte_array_read_short(buffer, &size); + if( rc != APR_SUCCESS ) + return rc; + return ow_unmarshal_DataStructure_array_const_size(buffer, bitbuffer, value, size, pool); + } else { + *value=NULL; + } + return APR_SUCCESS; +} + +/************************************************************************ + * marshal/unmarshal a constant size DataStructure_array + ************************************************************************/ +apr_status_t ow_marshal1_DataStructure_array_const_size(ow_bit_buffer *buffer, ow_DataStructure_array *value, ow_int size) { + apr_status_t rc; + ow_int i; + for( i=0; i < value->size; i++ ) { + rc = ow_marshal1_nested_object(buffer, value->values[i]); + if( rc != APR_SUCCESS ) + return rc; + } + return APR_SUCCESS; +} +apr_status_t ow_marshal2_DataStructure_array_const_size(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure_array *value, ow_int size) { + apr_status_t rc; + ow_int i; + if( value->size != size ) { + return APR_EGENERAL; + } + for( i=0; i < value->size; i++ ) { + rc = ow_marshal2_nested_object(buffer, bitbuffer, value->values[i]); + if( rc != APR_SUCCESS ) + return rc; + } + return APR_SUCCESS; +} +apr_status_t ow_unmarshal_DataStructure_array_const_size(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_DataStructure_array **value, ow_int size, apr_pool_t *pool) { + + ow_int i; + *value = apr_pcalloc(pool, sizeof(ow_DataStructure_array)); + if( *value == NULL ) + return APR_ENOMEM; + + (*value)->size = size; + (*value)->values = apr_pcalloc(pool, sizeof(ow_DataStructure*)*size); + + for( i=0; i < (*value)->size; i++ ) { + apr_status_t rc = ow_unmarshal_nested_object(buffer, bitbuffer, &((*value)->values[i]), pool); + if( rc != APR_SUCCESS ) + return rc; + } + + return APR_SUCCESS; +} + +/************************************************************************ + * marshal/unmarshal throwable + ************************************************************************/ +apr_status_t ow_marshal1_throwable(ow_bit_buffer *buffer, ow_throwable *value) { + ow_bit_buffer_append(buffer, value!=NULL); + return APR_SUCCESS; +} +apr_status_t ow_marshal2_throwable(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_throwable *value) { + apr_status_t rc = APR_SUCCESS; + if( ow_bit_buffer_read(bitbuffer) ) { + //TODO: needs to be implemented. + } + return rc; +} +apr_status_t ow_unmarshal_throwable(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_throwable **value, apr_pool_t *pool) { + if( ow_bit_buffer_read(bitbuffer) ) { + //TODO: needs to be implemented. + } else { + *value=NULL; + } + return APR_SUCCESS; +} + +/************************************************************************ +* marshal/unmarshal long +************************************************************************/ +apr_status_t ow_marshal1_long(ow_bit_buffer *buffer, ow_long value) { + if( value == 0 ) { + ow_bit_buffer_append(buffer, 0); + ow_bit_buffer_append(buffer, 0); + } else if ( (value & 0xFFFFFFFFFFFF0000ll ) == 0 ) { + ow_bit_buffer_append(buffer, 0); + ow_bit_buffer_append(buffer, 1); + } else if ( (value & 0xFFFFFFFF00000000ll ) == 0) { + ow_bit_buffer_append(buffer, 1); + ow_bit_buffer_append(buffer, 0); + } else { + ow_bit_buffer_append(buffer, 1); + ow_bit_buffer_append(buffer, 1); + } + return APR_SUCCESS; +} + +apr_status_t ow_marshal2_long(ow_byte_buffer *buffer, ow_bit_buffer *bitbuffer, ow_long value) { + apr_status_t rc = APR_SUCCESS; + if( ow_bit_buffer_read(bitbuffer) ) { + if( ow_bit_buffer_read(bitbuffer) ) { + ow_byte_buffer_append_long(buffer,value); + } else { + ow_byte_buffer_append_int(buffer,value); + } + } else { + if( ow_bit_buffer_read(bitbuffer) ) { + ow_byte_buffer_append_short(buffer,value); + } + } + return rc; +} +apr_status_t ow_unmarshal_long(ow_byte_array *buffer, ow_bit_buffer *bitbuffer, ow_long *value, apr_pool_t *pool) { + if( ow_bit_buffer_read(bitbuffer) ) { + if( ow_bit_buffer_read(bitbuffer) ) { + ow_long t; + ow_byte_array_read_long(buffer, &t); + *value = t; + } else { + ow_int t; + ow_byte_array_read_int(buffer, &t); + *value = t; + } + } else { + if( ow_bit_buffer_read(bitbuffer) ) { + ow_short t; + ow_byte_array_read_short(buffer, &t); + *value = t; + } else { + *value=0; + } + } + return APR_SUCCESS; +} + + diff --git a/openwire-c/win32/main/ReadMe.txt b/openwire-c/win32/main/ReadMe.txt new file mode 100644 index 0000000000..fb07f5fedc --- /dev/null +++ b/openwire-c/win32/main/ReadMe.txt @@ -0,0 +1,32 @@ +======================================================================== + CONSOLE APPLICATION : main Project Overview +======================================================================== + +AppWizard has created this main application for you. +This file contains a summary of what you will find in each of the files that +make up your main application. + + +main.vcproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +main.cpp + This is the main application source file. + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named main.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/openwire-c/win32/main/main.vcproj b/openwire-c/win32/main/main.vcproj new file mode 100644 index 0000000000..d19449be5a --- /dev/null +++ b/openwire-c/win32/main/main.vcproj @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openwire-c/win32/openwire.sln b/openwire-c/win32/openwire.sln new file mode 100644 index 0000000000..c7fd81dbcd --- /dev/null +++ b/openwire-c/win32/openwire.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "main", "main\main.vcproj", "{553F3295-09D0-4E96-98CB-B0925DA141D8}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {553F3295-09D0-4E96-98CB-B0925DA141D8}.Debug.ActiveCfg = Debug|Win32 + {553F3295-09D0-4E96-98CB-B0925DA141D8}.Debug.Build.0 = Debug|Win32 + {553F3295-09D0-4E96-98CB-B0925DA141D8}.Release.ActiveCfg = Release|Win32 + {553F3295-09D0-4E96-98CB-B0925DA141D8}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/openwire-c/win32/openwire.suo b/openwire-c/win32/openwire.suo new file mode 100644 index 0000000000..2174469085 Binary files /dev/null and b/openwire-c/win32/openwire.suo differ diff --git a/openwire-c/win32/win32.suo b/openwire-c/win32/win32.suo new file mode 100644 index 0000000000..240cc5198f Binary files /dev/null and b/openwire-c/win32/win32.suo differ diff --git a/openwire-dotnet/comms-library/amqnet/AbstractPacket.cs b/openwire-dotnet/comms-library/amqnet/AbstractPacket.cs new file mode 100755 index 0000000000..4d316e3df9 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/AbstractPacket.cs @@ -0,0 +1,172 @@ +using System; +using ActiveMQ; + +namespace ActiveMQ +{ + ///

+ /// Summary description for AbstractPacket. + /// + public abstract class AbstractPacket { + + public const int NON_PERSISTENT = 1; + public const int PERSISTENT = 2; + + /** + * Message flag indexes (used for writing/reading to/from a Stream + */ + public const int RECEIPT_REQUIRED_INDEX = 0; + public const int BROKERS_VISITED_INDEX =1; + private short id = 0; + protected byte[] data = new byte[100]; + private bool receiptRequired; + + protected AbstractPacket() + { + + } + + public short getId() + { + return this.id; + } + + public virtual void setId(short newId) + { + this.id = newId; + } + + public virtual bool isReceiptRequired() + { + return this.receiptRequired; + } + + + public virtual bool isReceipt() + { + return false; + } + + public void setReceiptRequired(bool value) + { + this.receiptRequired = value; + } + + public virtual bool isJMSMessage() + { + return false; + } + + public int hashCode() + { + return this.id; + } + + public virtual short getPacketType() + { + return id; + } + + public String toString() + { + return getPacketTypeAsString(getPacketType()) + ": id = " + getId(); + } + + + public static String getPacketTypeAsString(int type) + { + String packetTypeStr = ""; + switch (type) + { + case PacketConstants.ACTIVEMQ_MESSAGE: + packetTypeStr = "ACTIVEMQ_MESSAGE"; + break; + case PacketConstants.ACTIVEMQ_TEXT_MESSAGE: + packetTypeStr = "ACTIVEMQ_TEXT_MESSAGE"; + break; + case PacketConstants.ACTIVEMQ_OBJECT_MESSAGE: + packetTypeStr = "ACTIVEMQ_OBJECT_MESSAGE"; + break; + case PacketConstants.ACTIVEMQ_BYTES_MESSAGE: + packetTypeStr = "ACTIVEMQ_BYTES_MESSAGE"; + break; + case PacketConstants.ACTIVEMQ_STREAM_MESSAGE: + packetTypeStr = "ACTIVEMQ_STREAM_MESSAGE"; + break; + case PacketConstants.ACTIVEMQ_MAP_MESSAGE: + packetTypeStr = "ACTIVEMQ_MAP_MESSAGE"; + break; + case PacketConstants.ACTIVEMQ_MSG_ACK: + packetTypeStr = "ACTIVEMQ_MSG_ACK"; + break; + case PacketConstants.RECEIPT_INFO: + packetTypeStr = "RECEIPT_INFO"; + break; + case PacketConstants.CONSUMER_INFO: + packetTypeStr = "CONSUMER_INFO"; + break; + case PacketConstants.PRODUCER_INFO: + packetTypeStr = "PRODUCER_INFO"; + break; + case PacketConstants.TRANSACTION_INFO: + packetTypeStr = "TRANSACTION_INFO"; + break; + case PacketConstants.XA_TRANSACTION_INFO: + packetTypeStr = "XA_TRANSACTION_INFO"; + break; + case PacketConstants.ACTIVEMQ_BROKER_INFO: + packetTypeStr = "ACTIVEMQ_BROKER_INFO"; + break; + case PacketConstants.ACTIVEMQ_CONNECTION_INFO: + packetTypeStr = "ACTIVEMQ_CONNECTION_INFO"; + break; + case PacketConstants.SESSION_INFO: + packetTypeStr = "SESSION_INFO"; + break; + case PacketConstants.DURABLE_UNSUBSCRIBE: + packetTypeStr = "DURABLE_UNSUBSCRIBE"; + break; + case PacketConstants.RESPONSE_RECEIPT_INFO: + packetTypeStr = "RESPONSE_RECEIPT_INFO"; + break; + case PacketConstants.INT_RESPONSE_RECEIPT_INFO: + packetTypeStr = "INT_RESPONSE_RECEIPT_INFO"; + break; + case PacketConstants.CAPACITY_INFO: + packetTypeStr = "CAPACITY_INFO"; + break; + case PacketConstants.CAPACITY_INFO_REQUEST: + packetTypeStr = "CAPACITY_INFO_REQUEST"; + break; + case PacketConstants.WIRE_FORMAT_INFO: + packetTypeStr = "WIRE_FORMAT_INFO"; + break; + case PacketConstants.KEEP_ALIVE: + packetTypeStr = "KEEP_ALIVE"; + break; + case PacketConstants.CACHED_VALUE_COMMAND: + packetTypeStr = "CachedValue"; + break; + default : + packetTypeStr = "UNKNOWN PACKET TYPE: " + type; + break; + } + return packetTypeStr; + } + + protected virtual bool equals(Object left, Object right) + { + return left == right || (left != null && left.Equals(right)); + } + + public byte[] getData() + { + return data; + } + + public void setBitArray(byte[] data) + { + this.data = data; + } + } + } + diff --git a/openwire-dotnet/comms-library/amqnet/ActiveMQ.csproj b/openwire-dotnet/comms-library/amqnet/ActiveMQ.csproj new file mode 100755 index 0000000000..d089ac106c --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/ActiveMQ.csproj @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openwire-dotnet/comms-library/amqnet/ActiveMQ.csproj.user b/openwire-dotnet/comms-library/amqnet/ActiveMQ.csproj.user new file mode 100755 index 0000000000..e1b802f2a2 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/ActiveMQ.csproj.user @@ -0,0 +1,48 @@ + + + + + + + + + + + + diff --git a/openwire-dotnet/comms-library/amqnet/ActiveMQ.sln b/openwire-dotnet/comms-library/amqnet/ActiveMQ.sln new file mode 100755 index 0000000000..08f5409f37 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/ActiveMQ.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveMQ", "ActiveMQ.csproj", "{8B07A13A-8E84-48F9-A0FE-12715CC4C842}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug + ConfigName.1 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {8B07A13A-8E84-48F9-A0FE-12715CC4C842}.Debug.ActiveCfg = Debug|.NET + {8B07A13A-8E84-48F9-A0FE-12715CC4C842}.Debug.Build.0 = Debug|.NET + {8B07A13A-8E84-48F9-A0FE-12715CC4C842}.Release.ActiveCfg = Release|.NET + {8B07A13A-8E84-48F9-A0FE-12715CC4C842}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/openwire-dotnet/comms-library/amqnet/ActiveMQ.suo b/openwire-dotnet/comms-library/amqnet/ActiveMQ.suo new file mode 100755 index 0000000000..3599f1b012 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/ActiveMQ.suo @@ -0,0 +1 @@ +ÐÏࡱ \ No newline at end of file diff --git a/openwire-dotnet/comms-library/amqnet/ActiveMQDestination.cs b/openwire-dotnet/comms-library/amqnet/ActiveMQDestination.cs new file mode 100755 index 0000000000..eb429d59dc --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/ActiveMQDestination.cs @@ -0,0 +1,531 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for ActiveMQDestination. + /// + public abstract class ActiveMQDestination { + + /** + * Topic Destination object + */ + public const int ACTIVEMQ_TOPIC = 1; + /** + * Temporary Topic Destination object + */ + public const int ACTIVEMQ_TEMPORARY_TOPIC = 2; + + /** + * Queue Destination object + */ + public const int ACTIVEMQ_QUEUE = 3; + /** + * Temporary Queue Destination object + */ + public const int ACTIVEMQ_TEMPORARY_QUEUE = 4; + + /** + * prefix for Advisory message destinations + */ + public const String ADVISORY_PREFIX = "ActiveMQ.Advisory."; + + /** + * prefix for consumer advisory destinations + */ + public const String CONSUMER_ADVISORY_PREFIX = ADVISORY_PREFIX + "Consumers."; + + /** + * prefix for producer advisory destinations + */ + public const String PRODUCER_ADVISORY_PREFIX = ADVISORY_PREFIX + "Producers."; + + /** + * prefix for connection advisory destinations + */ + public const String CONNECTION_ADVISORY_PREFIX = ADVISORY_PREFIX + "Connections."; + + /** + * The default target for ordered destinations + */ + public const String DEFAULT_ORDERED_TARGET = "coordinator"; + + private const int NULL_DESTINATION = 10; + + private const String TEMP_PREFIX = "{TD{"; + private const String TEMP_POSTFIX = "}TD}"; + private const String COMPOSITE_SEPARATOR = ","; + private const String QUEUE_PREFIX = "queue://"; + private const String TOPIC_PREFIX = "topic://"; + + + private String physicalName = ""; + + // Cached transient data + private bool exclusive; + private bool ordered; + private bool advisory; + private String orderedTarget = DEFAULT_ORDERED_TARGET; + + /** + * The Default Constructor + */ + protected ActiveMQDestination() { + } + + /** + * Construct the ActiveMQDestination with a defined physical name; + * + * @param name + */ + + protected ActiveMQDestination(String name) { + this.physicalName = name; + this.advisory = name != null && name.startsWith(ADVISORY_PREFIX); + } + + + + /** + * @return Returns the advisory. + */ + public bool isAdvisory() { + return advisory; + } + /** + * @param advisory The advisory to set. + */ + public void setAdvisory(bool advisory) { + this.advisory = advisory; + } + + /** + * @return true if this is a destination for Consumer advisories + */ + public bool isConsumerAdvisory(){ + return isAdvisory() && physicalName.startsWith(ActiveMQDestination.CONSUMER_ADVISORY_PREFIX); + } + + /** + * @return true if this is a destination for Producer advisories + */ + public bool isProducerAdvisory(){ + return isAdvisory() && physicalName.startsWith(ActiveMQDestination.PRODUCER_ADVISORY_PREFIX); + } + + /** + * @return true if this is a destination for Connection advisories + */ + public bool isConnectionAdvisory(){ + return isAdvisory() && physicalName.startsWith(ActiveMQDestination.CONNECTION_ADVISORY_PREFIX); + } + + /** + * @return Returns the exclusive. + */ + public bool isExclusive() { + return exclusive; + } + /** + * @param exclusive The exclusive to set. + */ + public void setExclusive(bool exclusive) { + this.exclusive = exclusive; + } + /** + * @return Returns the ordered. + */ + public bool isOrdered() { + return ordered; + } + /** + * @param ordered The ordered to set. + */ + public void setOrdered(bool ordered) { + this.ordered = ordered; + } + /** + * @return Returns the orderedTarget. + */ + public String getOrderedTarget() { + return orderedTarget; + } + /** + * @param orderedTarget The orderedTarget to set. + */ + public void setOrderedTarget(String orderedTarget) { + this.orderedTarget = orderedTarget; + } + /** + * A helper method to return a descriptive string for the topic or queue + * @param destination + * + * @return a descriptive string for this queue or topic + */ + public static String inspect(ActiveMQDestination destination) { + if (destination is Topic) { + return "Topic(" + destination.toString() + ")"; + } + else { + return "Queue(" + destination.toString() + ")"; + } + } + + /** + * @param destination + * @return @throws JMSException + * @throws javax.jms.JMSException + */ + public static ActiveMQDestination transformDestination(ActiveMQDestination destination) { + ActiveMQDestination result = null; + if (destination != null) { + if (destination is ActiveMQDestination) { + result = (ActiveMQDestination) destination; + } + else { + if (destination is TemporaryQueue) { + result = new ActiveMQTemporaryQueue(((Queue) destination).getQueueName()); + } + else if (destination is TemporaryTopic) { + result = new ActiveMQTemporaryTopic(((Topic) destination).getTopicName()); + } + else if (destination is Queue) { + result = new ActiveMQTemporaryQueue(((Queue) destination).getQueueName()); + } + else if (destination is Topic) { + result = new ActiveMQTemporaryTopic(((Topic) destination).getTopicName()); + } + } + } + return result; + } + + /** + * Write an ActiveMQDestination to a Stream + * + * @param destination + * @param dataOut + * @throws IOException + */ + + public static void writeToStream(ActiveMQDestination destination, Object dataOut) { + //TODO SERILIZATION + } + + /** + * Read an ActiveMQDestination from a Stream + * + * @param dataIn + * @return the ActiveMQDestination + * @throws IOException + */ + + public static ActiveMQDestination readFromStream(Object dataIn) { + //TODO Serilization + } + + /** + * Create a Destination + * @param type + * @param pyhsicalName + * @return + */ + public static ActiveMQDestination createDestination(int type,String pyhsicalName){ + ActiveMQDestination result = null; + if (type == ACTIVEMQ_TOPIC) { + result = new ActiveMQTopic(pyhsicalName); + } + else if (type == ACTIVEMQ_TEMPORARY_TOPIC) { + result = new ActiveMQTemporaryTopic(pyhsicalName); + } + else if (type == ACTIVEMQ_QUEUE) { + result = new ActiveMQQueue(pyhsicalName); + } + else { + result = new ActiveMQTemporaryQueue(pyhsicalName); + } + return result; + } + + /** + * Create a temporary name from the clientId + * + * @param clientId + * @return + */ + public static String createTemporaryName(String clientId) { + return TEMP_PREFIX + clientId + TEMP_POSTFIX; + } + + /** + * From a temporary destination find the clientId of the Connection that created it + * + * @param destination + * @return the clientId or null if not a temporary destination + */ + public static String getClientId(ActiveMQDestination destination) { + String answer = null; + if (destination != null && destination.isTemporary()) { + String name = destination.getPhysicalName(); + int start = name.indexOf(TEMP_PREFIX); + if (start >= 0) { + start += TEMP_PREFIX.length(); + int stop = name.lastIndexOf(TEMP_POSTFIX); + if (stop > start && stop < name.length()) { + answer = name.substring(start, stop); + } + } + } + return answer; + } + + + /** + * @param o object to compare + * @return 1 if this > o else 0 if they are equal or -1 if this < o + */ + public int compareTo(Object o) { + if (o is ActiveMQDestination) { + return compareTo((ActiveMQDestination) o); + } + return -1; + } + + /** + * Lets sort by name first then lets sort topics greater than queues + * + * @param that another destination to compare against + * @return 1 if this > that else 0 if they are equal or -1 if this < that + */ + public int compareTo(ActiveMQDestination that) { + int answer = 0; + if (physicalName != that.physicalName) { + if (physicalName == null) { + return -1; + } + else if (that.physicalName == null) { + return 1; + } + answer = physicalName.compareTo(that.physicalName); + } + if (answer == 0) { + if (isTopic()) { + if (that.isQueue()) { + return 1; + } + } + else { + if (that.isTopic()) { + return -1; + } + } + } + return answer; + } + + + /** + * @return Returns the Destination type + */ + + public abstract int getDestinationType(); + + + /** + * @return Returns the physicalName. + */ + public String getPhysicalName() { + return this.physicalName; + } + + /** + * @param newPhysicalName The physicalName to set. + */ + public void setPhysicalName(String newPhysicalName) { + this.physicalName = newPhysicalName; + } + + /** + * Returns true if a temporary Destination + * + * @return true/false + */ + + public bool isTemporary() { + return getDestinationType() == ACTIVEMQ_TEMPORARY_TOPIC || + getDestinationType() == ACTIVEMQ_TEMPORARY_QUEUE; + } + + /** + * Returns true if a Topic Destination + * + * @return true/false + */ + + public bool isTopic() { + return getDestinationType() == ACTIVEMQ_TOPIC || + getDestinationType() == ACTIVEMQ_TEMPORARY_TOPIC; + } + + /** + * Returns true if a Queue Destination + * + * @return true/false + */ + public bool isQueue() { + return !isTopic(); + } + + /** + * Returns true if this destination represents a collection of + * destinations; allowing a set of destinations to be published to or subscribed + * from in one JMS operation. + *

+ * If this destination is a composite then you can call {@link #getChildDestinations()} + * to return the list of child destinations. + * + * @return true if this destination represents a collection of child destinations. + */ + public bool isComposite() { + return physicalName.indexOf(COMPOSITE_SEPARATOR) > 0; + } + + /** + * Returns a list of child destinations if this destination represents a composite + * destination. + * + * @return + */ + /*public List getChildDestinations() { + List answer = new ArrayList(); + StringTokenizer iter = new StringTokenizer(physicalName, COMPOSITE_SEPARATOR); + while (iter.hasMoreTokens()) { + String name = iter.nextToken(); + Destination child = null; + if (name.startsWith(QUEUE_PREFIX)) { + child = new ActiveMQQueue(name.substring(QUEUE_PREFIX.length())); + } + else if (name.startsWith(TOPIC_PREFIX)) { + child = new ActiveMQTopic(name.substring(TOPIC_PREFIX.length())); + } + else { + child = createDestination(name); + } + answer.add(child); + } + if (answer.size() == 1) { + // lets put ourselves inside the collection + // as we are not really a composite destination + answer.set(0, this); + } + return answer; + }*/ + + /** + * @return string representation of this instance + */ + + public String toString() { + return this.physicalName; + } + + /** + * @return hashCode for this instance + */ + + public int hashCode() { + int answer = 0xcafebabe; + + if (this.physicalName != null) { + answer = physicalName.hashCode(); + } + if (isTopic()) { + answer ^= 0xfabfab; + } + return answer; + } + + /** + * if the object passed in is equivalent, return true + * + * @param obj the object to compare + * @return true if this instance and obj are equivalent + */ + + public bool equals(Object obj) { + bool result = this == obj; + if (!result && obj != null && obj is ActiveMQDestination) { + ActiveMQDestination other = (ActiveMQDestination) obj; + result = this.getDestinationType() == other.getDestinationType() && + this.physicalName.equals(other.physicalName); + } + return result; + } + + + /** + * @return true if the destination matches multiple possible destinations + */ + public bool isWildcard() { + if (physicalName != null) { + return physicalName.indexOf(DestinationFilter.ANY_CHILD) >= 0 + || physicalName.indexOf(DestinationFilter.ANY_DESCENDENT) >= 0; + } + return false; + } + + /** + * @param destination + * @return true if the given destination matches this destination; including wildcards + */ + public bool matches(ActiveMQDestination destination) { + if (isWildcard()) { + return getDestinationFilter().matches(destination); + } + else { + return equals(destination); + } + } + + + /** + * @return the DestinationFilter + */ + public DestinationFilter getDestinationFilter() { + if (filter == null) { + filter = DestinationFilter.parseFilter(this); + } + return filter; + } + + /** + * @return the associated paths associated with this Destination + */ + public String[] getDestinationPaths() { + if (paths == null) { + paths = DestinationPath.getDestinationPaths(physicalName); + } + return paths; + } + + + + + + + + // Implementation methods + //------------------------------------------------------------------------- + + + /** + * Factory method to create a child destination if this destination is a composite + * @param name + * @return the created Destination + */ + public abstract ActiveMQDestination createDestination(String name); + + +} + +} diff --git a/openwire-dotnet/comms-library/amqnet/ActiveMQMessage.cs b/openwire-dotnet/comms-library/amqnet/ActiveMQMessage.cs new file mode 100755 index 0000000000..aaf19f1867 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/ActiveMQMessage.cs @@ -0,0 +1,2127 @@ +using System; +using System.Collections; + +namespace ActiveMQ +{ + ///

+ /// Summary description for ActiveMQMessage. + /// + public class ActiveMQMessage : AbstractPacket + { + + const int DEFAULT_DELIVERY_MODE = PERSISTENT; + + /** + * The message producer's default priority is 4. + */ + const int DEFAULT_PRIORITY = 4; + + /** + * The message producer's default time to live is unlimited; the message + * never expires. + */ + const long DEFAULT_TIME_TO_LIVE = 0; + + /** + * message property types + */ + const byte EOF = 2; + const byte BYTES = 3; + const byte STRING = 4; + const byte BOOLEAN = 5; + const byte CHAR = 6; + const byte BYTE = 7; + const byte SHORT = 8; + const byte INT = 9; + const byte LONG = 10; + const byte FLOAT = 11; + const byte DOUBLE = 12; + const byte NULL = 13; + + /** + * Message flag indexes (used for writing/reading to/from a Stream + */ + + public const int CORRELATION_INDEX = 2; + public const int TYPE_INDEX = 3; + public const int BROKER_NAME_INDEX = 4; + public const int CLUSTER_NAME_INDEX = 5; + public const int TRANSACTION_ID_INDEX = 6; + public const int REPLY_TO_INDEX = 7; + public const int TIMESTAMP_INDEX = 8; + public const int EXPIRATION_INDEX = 9; + public const int REDELIVERED_INDEX = 10; + public const int XA_TRANS_INDEX = 11; + public const int CID_INDEX = 12; + public const int PROPERTIES_INDEX = 13; + public const int DISPATCHED_FROM_DLQ_INDEX = 14; + public const int PAYLOAD_INDEX = 15; + public const int EXTERNAL_MESSAGE_ID_INDEX = 16; + public const int MESSAGE_PART_INDEX = 17; + public const int CACHED_VALUES_INDEX = 18; + public const int CACHED_DESTINATION_INDEX = 19; + public const int LONG_SEQUENCE_INDEX = 20; + + + + const String DELIVERY_COUNT_NAME = "JMSXDeliveryCount"; + /** + * readOnlyMessage denotes if the message is read only + */ + protected bool readOnlyMessage; + + private String jmsMessageID; + private String jmsClientID; + private String jmsCorrelationID; + private String producerKey; + private ActiveMQDestination jmsDestination; + private ActiveMQDestination jmsReplyTo; + private int jmsDeliveryMode = DEFAULT_DELIVERY_MODE; + private bool jmsRedelivered; + private String jmsType; + private long jmsExpiration; + private int jmsPriority = DEFAULT_PRIORITY; + private long jmsTimestamp; + private Hashtable properties; + private bool readOnlyProperties; + private String entryBrokerName; + private String entryClusterName; + private int[] consumerNos; //these are set by the broker, and only relevant to consuming connections + private Object transactionId; + private bool xaTransacted; + private String consumerIdentifier; //this is only used on the Client for acknowledging receipt of a message + private bool messageConsumed;//only used on the client - to denote if its been delivered and read + private bool transientConsumed;//only used on the client - to denote if its been delivered and read + private long sequenceNumber;//the sequence for this message from the producerId + private int deliveryCount = 1;//number of times the message has been delivered + private bool dispatchedFromDLQ; + private MessageAcknowledge messageAcknowledge; + private byte[] bodyAsBytes; + private Object jmsMessageIdentity; + private short messsageHandle;//refers to the id of the MessageProducer that sent the message + private bool externalMessageId;//is the messageId set from another JMS implementation ? + private bool messagePart;//is the message split into multiple packets + private short numberOfParts; + private short partNumber; + private String parentMessageID;//if split into multiple parts - the 'real' or first messageId + + + /** + * Retrieve if a JMS Message type or not + * + * @return true if it is a JMS Message + */ + public override bool isJMSMessage() + { + return true; + } + + + /** + * @return pretty print of this Message + */ + public override String ToString() + { + return super.toString() + " ActiveMQMessage{ " + + ", jmsMessageID = " + jmsMessageID + + ", bodyAsBytes = " + bodyAsBytes + + ", readOnlyMessage = " + readOnlyMessage + + ", jmsClientID = '" + jmsClientID + "' " + + ", jmsCorrelationID = '" + jmsCorrelationID + "' " + + ", jmsDestination = " + jmsDestination + + ", jmsReplyTo = " + jmsReplyTo + + ", jmsDeliveryMode = " + jmsDeliveryMode + + ", jmsRedelivered = " + jmsRedelivered + + ", jmsType = '" + jmsType + "' " + + ", jmsExpiration = " + jmsExpiration + + ", jmsPriority = " + jmsPriority + + ", jmsTimestamp = " + jmsTimestamp + + ", properties = " + properties + + ", readOnlyProperties = " + readOnlyProperties + + ", entryBrokerName = '" + entryBrokerName + "' " + + ", entryClusterName = '" + entryClusterName + "' " + + ", consumerNos = " + consumerNos + + ", transactionId = '" + transactionId + "' " + + ", xaTransacted = " + xaTransacted + + ", consumerIdentifer = '" + consumerIdentifier + "' " + + ", messageConsumed = " + messageConsumed + + ", transientConsumed = " + transientConsumed + + ", sequenceNumber = " + sequenceNumber + + ", deliveryCount = " + deliveryCount + + ", dispatchedFromDLQ = " + dispatchedFromDLQ + + ", messageAcknowledge = " + messageAcknowledge + + ", jmsMessageIdentity = " + jmsMessageIdentity + + ", producerKey = " + producerKey + + " }"; + } + + + /** + * @return Returns the messageAcknowledge. + */ + public MessageAcknowledge getMessageAcknowledge() + { + return messageAcknowledge; + } + + /** + * @param messageAcknowledge The messageAcknowledge to set. + */ + public void setMessageAcknowledge(MessageAcknowledge messageAcknowledge) + { + this.messageAcknowledge = messageAcknowledge; + } + + /** + * Return the type of Packet + * + * @return integer representation of the type of Packet + */ + + public new int getPacketType() + { + return ACTIVEMQ_MESSAGE; + } + + + /** + * set the message readOnly + * + * @param value + */ + public void setReadOnly(bool value) + { + this.readOnlyProperties = value; + this.readOnlyMessage = value; + } + + /** + * test to see if a particular Consumer at a Connection + * is meant to receive this Message + * + * @param consumerNumber + * @return true if a target + */ + + public bool isConsumerTarget(int consumerNumber) + { + if (consumerNos != null) + { + for (int i = 0; i < consumerNos.length; i++) + { + if (consumerNos[i] == consumerNumber) + { + return true; + } + } + } + return false; + } + + /** + * @return consumer Nos as a String + */ + public String getConsumerNosAsString() + { + String result = ""; + if (consumerNos != null) + { + for (int i = 0; i < consumerNos.length; i++) + { + result += consumerNos[i] + ","; + } + } + return result; + } + + /** + * @return true if the message is non-persistent or intended for a temporary destination + */ + public bool isTemporary() + { + return jmsDeliveryMode == DeliveryMode.NON_PERSISTENT || + (jmsDestination != null && jmsDestination.isTemporary()); + } + + /** + * @return Returns hash code for this instance + */ + + public new int GetHashCode() + { + return this.getJMSMessageID() != null ? this.getJMSMessageID().hashCode() : super.hashCode(); + } + + /** + * Returns true if this instance is equivalent to obj + * + * @param obj the other instance to test + * @return true/false + */ + + public bool equals(Object obj) + { + bool result = obj == this; + if (!result && obj != null && obj is ActiveMQMessage) + { + ActiveMQMessage other = (ActiveMQMessage) obj; + //the call getJMSMessageID() will initialize the messageID + //if it hasn't already been set + result = this.getJMSMessageID() == other.getJMSMessageID(); + if (!result) + { + if (this.jmsMessageID != null && this.jmsMessageID.length() > 0 || + other.jmsMessageID != null && other.jmsMessageID.length() > 0) + { + if (this.jmsMessageID != null && other.jmsMessageID != null) + { + result = this.jmsMessageID.equals(other.jmsMessageID); + } + } + else + { + result = this.getId() == other.getId(); + } + } + } + return result; + } + + + + + /** + * @return Returns a shallow copy of the message instance + * @ + */ + + public ActiveMQMessage shallowCopy() + { + ActiveMQMessage other = new ActiveMQMessage(); + this.initializeOther(other); + return other; + } + + /** + * @return Returns a deep copy of the message - note the header fields are only shallow copied + * @ + */ + + public ActiveMQMessage deepCopy() + { + return shallowCopy(); + } + + + /** + * Indicates if the Message has expired + * + * @param currentTime - + * the current time in milliseconds + * @return true if the message can be expired + */ + public bool isExpired(long currentTime) + { + bool result = false; + long expiration = this.jmsExpiration; + if (jmsExpiration > 0 && jmsExpiration < currentTime) + { + result = true; + } + return result; + } + + /** + * @return true if the message is expired + */ + public bool isExpired() + { + return !dispatchedFromDLQ && jmsExpiration > 0 && isExpired(System.currentTimeMillis()); + } + + /** + * @return true if an advisory message + */ + public bool isAdvisory() + { + return jmsDestination != null && jmsDestination.isAdvisory(); + } + + /** + * Initializes another message with current values from this instance + * + * @param other the other ActiveMQMessage to initialize + */ + protected void initializeOther(ActiveMQMessage other) + { + super.initializeOther(other); + other.jmsMessageID = this.jmsMessageID; + other.jmsClientID = this.jmsClientID; + other.jmsCorrelationID = this.jmsCorrelationID; + other.jmsDestination = this.jmsDestination; + other.jmsReplyTo = this.jmsReplyTo; + other.jmsDeliveryMode = this.jmsDeliveryMode; + other.jmsRedelivered = this.jmsRedelivered; + other.jmsType = this.jmsType; + other.jmsExpiration = this.jmsExpiration; + other.jmsPriority = this.jmsPriority; + other.jmsTimestamp = this.jmsTimestamp; + other.properties = this.properties != null ? new Hashtable(this.properties) : null; + other.readOnlyProperties = this.readOnlyProperties; + other.readOnlyMessage = this.readOnlyMessage; + other.entryBrokerName = this.entryBrokerName; + other.entryClusterName = this.entryClusterName; + other.consumerNos = this.consumerNos; + other.transactionId = this.transactionId; + other.xaTransacted = this.xaTransacted; + other.bodyAsBytes = this.bodyAsBytes; + other.messageAcknowledge = this.messageAcknowledge; + other.jmsMessageIdentity = this.jmsMessageIdentity; + other.sequenceNumber = this.sequenceNumber; + other.deliveryCount = this.deliveryCount; + other.dispatchedFromDLQ = this.dispatchedFromDLQ; + other.messsageHandle = this.messsageHandle; + other.consumerIdentifier = this.consumerIdentifier; + other.externalMessageId = this.externalMessageId; + other.producerKey = this.producerKey; + other.messagePart = this.messagePart; + other.numberOfParts = this.numberOfParts; + other.partNumber = this.partNumber; + other.parentMessageID = this.parentMessageID; + } + + + /** + * Gets the message ID. + *

+ *

The JMSMessageID header field contains a value that + * uniquely identifies each message sent by a provider. + *

+ *

When a message is sent, JMSMessageID can be ignored. + * When the send or publish method returns, it + * contains a provider-assigned value. + *

+ *

A JMSMessageID is a String value that + * should function as a + * unique key for identifying messages in a historical repository. + * The exact scope of uniqueness is provider-defined. It should at + * least cover all messages for a specific installation of a + * provider, where an installation is some connected set of message + * routers. + *

+ *

All JMSMessageID values must start with the prefix + * 'ID:'. + * Uniqueness of message ID values across different providers is + * not required. + *

+ *

Since message IDs take some effort to create and increase a + * message's size, some JMS providers may be able to optimize message + * overhead if they are given a hint that the message ID is not used by + * an application. By calling the + * MessageProducer.setDisableMessageID method, a JMS client + * enables this potential optimization for all messages sent by that + * message producer. If the JMS provider accepts this + * hint, these messages must have the message ID set to null; if the + * provider ignores the hint, the message ID must be set to its normal + * unique value. + * + * @return the message ID + * @see javax.jms.Message#setJMSMessageID(String) + * @see javax.jms.MessageProducer#setDisableMessageID(bool) + */ + + public String getJMSMessageID() + { + if (jmsMessageID == null && producerKey != null) + { + jmsMessageID = producerKey + sequenceNumber; + } + return jmsMessageID; + } + + + /** + * Sets the message ID. + *

+ *

JMS providers set this field when a message is sent. This method + * can be used to change the value for a message that has been received. + * + * @param id the ID of the message + * @see javax.jms.Message#getJMSMessageID() + */ + + public void setJMSMessageID(String id) + { + this.jmsMessageID = id; + this.jmsMessageIdentity = null; + } + + + /** + * Gets the message timestamp. + *

+ *

The JMSTimestamp header field contains the time a + * message was + * handed off to a provider to be sent. It is not the time the + * message was actually transmitted, because the actual send may occur + * later due to transactions or other client-side queueing of messages. + *

+ *

When a message is sent, JMSTimestamp is ignored. When + * the send or publish + * method returns, it contains a time value somewhere in the interval + * between the call and the return. The value is in the format of a normal + * millis time value in the Java programming language. + *

+ *

Since timestamps take some effort to create and increase a + * message's size, some JMS providers may be able to optimize message + * overhead if they are given a hint that the timestamp is not used by an + * application. By calling the + * MessageProducer.setDisableMessageTimestamp method, a JMS + * client enables this potential optimization for all messages sent by + * that message producer. If the JMS provider accepts this + * hint, these messages must have the timestamp set to zero; if the + * provider ignores the hint, the timestamp must be set to its normal + * value. + * + * @return the message timestamp + * @see javax.jms.Message#setJMSTimestamp(long) + * @see javax.jms.MessageProducer#setDisableMessageTimestamp(bool) + */ + + public long getJMSTimestamp() + { + return jmsTimestamp; + } + + + /** + * Sets the message timestamp. + *

+ *

JMS providers set this field when a message is sent. This method + * can be used to change the value for a message that has been received. + * + * @param timestamp the timestamp for this message + * @see javax.jms.Message#getJMSTimestamp() + */ + + public void setJMSTimestamp(long timestamp) + { + this.jmsTimestamp = timestamp; + } + + + /** + * Gets the correlation ID as an array of bytes for the message. + *

+ *

The use of a byte[] value for + * JMSCorrelationID is non-portable. + * + * @return the correlation ID of a message as an array of bytes + * @see javax.jms.Message#setJMSCorrelationID(String) + * @see javax.jms.Message#getJMSCorrelationID() + * @see javax.jms.Message#setJMSCorrelationIDAsBytes(byte[]) + */ + + public byte[] getJMSCorrelationIDAsBytes() + { + return this.jmsCorrelationID != null ? this.jmsCorrelationID.getBytes() : null; + } + + + /** + * Sets the correlation ID as an array of bytes for the message. + *

+ *

The array is copied before the method returns, so + * future modifications to the array will not alter this message header. + *

+ *

If a provider supports the native concept of correlation ID, a + * JMS client may need to assign specific JMSCorrelationID + * values to match those expected by native messaging clients. + * JMS providers without native correlation ID values are not required to + * support this method and its corresponding get method; their + * implementation may throw a + * java.lang.UnsupportedOperationException. + *

+ *

The use of a byte[] value for + * JMSCorrelationID is non-portable. + * + * @param correlationID the correlation ID value as an array of bytes + * @see javax.jms.Message#setJMSCorrelationID(String) + * @see javax.jms.Message#getJMSCorrelationID() + * @see javax.jms.Message#getJMSCorrelationIDAsBytes() + */ + + public void setJMSCorrelationIDAsBytes(byte[] correlationID) + { + if (correlationID == null) + { + this.jmsCorrelationID = null; + } + else + { + this.jmsCorrelationID = new String(correlationID); + } + } + + + /** + * Sets the correlation ID for the message. + *

+ *

A client can use the JMSCorrelationID header field to + * link one message with another. A typical use is to link a response + * message with its request message. + *

+ *

JMSCorrelationID can hold one of the following: + *

    + *
  • A provider-specific message ID + *
  • An application-specific String + *
  • A provider-native byte[] value + *
+ *

+ *

Since each message sent by a JMS provider is assigned a message ID + * value, it is convenient to link messages via message ID. All message ID + * values must start with the 'ID:' prefix. + *

+ *

In some cases, an application (made up of several clients) needs to + * use an application-specific value for linking messages. For instance, + * an application may use JMSCorrelationID to hold a value + * referencing some external information. Application-specified values + * must not start with the 'ID:' prefix; this is reserved for + * provider-generated message ID values. + *

+ *

If a provider supports the native concept of correlation ID, a JMS + * client may need to assign specific JMSCorrelationID values + * to match those expected by clients that do not use the JMS API. A + * byte[] value is used for this + * purpose. JMS providers without native correlation ID values are not + * required to support byte[] values. The use of a + * byte[] value for JMSCorrelationID is + * non-portable. + * + * @param correlationID the message ID of a message being referred to + * @see javax.jms.Message#getJMSCorrelationID() + * @see javax.jms.Message#getJMSCorrelationIDAsBytes() + * @see javax.jms.Message#setJMSCorrelationIDAsBytes(byte[]) + */ + + public void setJMSCorrelationID(String correlationID) + { + this.jmsCorrelationID = correlationID; + } + + + /** + * Gets the correlation ID for the message. + *

+ *

This method is used to return correlation ID values that are + * either provider-specific message IDs or application-specific + * String values. + * + * @return the correlation ID of a message as a String + * @see javax.jms.Message#setJMSCorrelationID(String) + * @see javax.jms.Message#getJMSCorrelationIDAsBytes() + * @see javax.jms.Message#setJMSCorrelationIDAsBytes(byte[]) + */ + + public String getJMSCorrelationID() + { + return this.jmsCorrelationID; + } + + + /** + * Gets the Destination object to which a reply to this + * message should be sent. + * + * @return Destination to which to send a response to this + * message + * @see javax.jms.Message#setJMSReplyTo(Destination) + */ + + public ActiveMQDestination getJMSReplyTo() + { + return this.jmsReplyTo; + + } + + + /** + * Sets the Destination object to which a reply to this + * message should be sent. + *

+ *

The JMSReplyTo header field contains the destination + * where a reply + * to the current message should be sent. If it is null, no reply is + * expected. The destination may be either a Queue object or + * a Topic object. + *

+ *

Messages sent with a null JMSReplyTo value may be a + * notification of some event, or they may just be some data the sender + * thinks is of interest. + *

+ *

Messages with a JMSReplyTo value typically expect a + * response. A response is optional; it is up to the client to decide. + * These messages are called requests. A message sent in response to a + * request is called a reply. + *

+ *

In some cases a client may wish to match a request it sent earlier + * with a reply it has just received. The client can use the + * JMSCorrelationID header field for this purpose. + * + * @param replyTo Destination to which to send a response to + * this message + * @see javax.jms.Message#getJMSReplyTo() + */ + + public void setJMSReplyTo(ActiveMQDestination replyTo) + { + this.jmsReplyTo = (ActiveMQDestination) replyTo; + } + + + /** + * Gets the Destination object for this message. + *

+ *

The JMSDestination header field contains the + * destination to which the message is being sent. + *

+ *

When a message is sent, this field is ignored. After completion + * of the send or publish method, the field + * holds the destination specified by the method. + *

+ *

When a message is received, its JMSDestination value + * must be equivalent to the value assigned when it was sent. + * + * @return the destination of this message + * @see javax.jms.Message#setJMSDestination(Destination) + */ + + public ActiveMQDestination getJMSDestination() + { + return this.jmsDestination; + } + + + /** + * Sets the Destination object for this message. + *

+ *

JMS providers set this field when a message is sent. This method + * can be used to change the value for a message that has been received. + * + * @param destination the destination for this message + * @see javax.jms.Message#getJMSDestination() + */ + + public void setJMSDestination(ActiveMQDestination destination) + { + this.jmsDestination = (ActiveMQDestination) destination; + } + + + /** + * Gets the DeliveryMode value specified for this message. + * + * @return the delivery mode for this message + * @see javax.jms.Message#setJMSDeliveryMode(int) + * @see javax.jms.DeliveryMode + */ + + public int getJMSDeliveryMode() + { + return this.jmsDeliveryMode; + } + + + /** + * Sets the DeliveryMode value for this message. + *

+ *

JMS providers set this field when a message is sent. This method + * can be used to change the value for a message that has been received. + * + * @param deliveryMode the delivery mode for this message + * @see javax.jms.Message#getJMSDeliveryMode() + * @see javax.jms.DeliveryMode + */ + + public void setJMSDeliveryMode(int deliveryMode) + { + this.jmsDeliveryMode = deliveryMode; + } + + + /** + * Gets an indication of whether this message is being redelivered. + *

+ *

If a client receives a message with the JMSRedelivered + * field set, + * it is likely, but not guaranteed, that this message was delivered + * earlier but that its receipt was not acknowledged + * at that time. + * + * @return true if this message is being redelivered + * @see javax.jms.Message#setJMSRedelivered(bool) + */ + + public bool getJMSRedelivered() + { + return this.jmsRedelivered; + } + + + /** + * Specifies whether this message is being redelivered. + *

+ *

This field is set at the time the message is delivered. This + * method can be used to change the value for a message that has + * been received. + * + * @param redelivered an indication of whether this message is being + * redelivered + * @see javax.jms.Message#getJMSRedelivered() + */ + + public void setJMSRedelivered(bool redelivered) + { + this.jmsRedelivered = redelivered; + } + + + /** + * Gets the message type identifier supplied by the client when the + * message was sent. + * + * @return the message type + * @see javax.jms.Message#setJMSType(String) + */ + + public String getJMSType() + { + return this.jmsType; + } + + /** + * Sets the message type. + *

+ *

Some JMS providers use a message repository that contains the + * definitions of messages sent by applications. The JMSType + * header field may reference a message's definition in the provider's + * repository. + *

+ *

The JMS API does not define a standard message definition repository, + * nor does it define a naming policy for the definitions it contains. + *

+ *

Some messaging systems require that a message type definition for + * each application message be created and that each message specify its + * type. In order to work with such JMS providers, JMS clients should + * assign a value to JMSType, whether the application makes + * use of it or not. This ensures that the field is properly set for those + * providers that require it. + *

+ *

To ensure portability, JMS clients should use symbolic values for + * JMSType that can be configured at installation time to the + * values defined in the current provider's message repository. If string + * literals are used, they may not be valid type names for some JMS + * providers. + * + * @param type the message type + * @see javax.jms.Message#getJMSType() + */ + + public void setJMSType(String type) + { + this.jmsType = type; + } + + + /** + * Gets the message's expiration value. + *

+ *

When a message is sent, the JMSExpiration header field + * is left unassigned. After completion of the send or + * publish method, it holds the expiration time of the + * message. This is the sum of the time-to-live value specified by the + * client and the GMT at the time of the send or + * publish. + *

+ *

If the time-to-live is specified as zero, JMSExpiration + * is set to zero to indicate that the message does not expire. + *

+ *

When a message's expiration time is reached, a provider should + * discard it. The JMS API does not define any form of notification of + * message expiration. + *

+ *

Clients should not receive messages that have expired; however, + * the JMS API does not guarantee that this will not happen. + * + * @return the time the message expires, which is the sum of the + * time-to-live value specified by the client and the GMT at the + * time of the send + * @see javax.jms.Message#setJMSExpiration(long) + */ + + public long getJMSExpiration() + { + return this.jmsExpiration; + } + + + /** + * Sets the message's expiration value. + *

+ *

JMS providers set this field when a message is sent. This method + * can be used to change the value for a message that has been received. + * + * @param expiration the message's expiration time + * @see javax.jms.Message#getJMSExpiration() + */ + + public void setJMSExpiration(long expiration) + { + this.jmsExpiration = expiration; + } + + /** + * Gets the message priority level. + *

+ *

The JMS API defines ten levels of priority value, with 0 as the + * lowest + * priority and 9 as the highest. In addition, clients should consider + * priorities 0-4 as gradations of normal priority and priorities 5-9 + * as gradations of expedited priority. + *

+ *

The JMS API does not require that a provider strictly implement + * priority + * ordering of messages; however, it should do its best to deliver + * expedited messages ahead of normal messages. + * + * @return the default message priority + * @see javax.jms.Message#setJMSPriority(int) + */ + + public int getJMSPriority() + { + return this.jmsPriority; + } + + + /** + * Sets the priority level for this message. + *

+ *

JMS providers set this field when a message is sent. This method + * can be used to change the value for a message that has been received. + * + * @param priority the priority of this message + * @see javax.jms.Message#getJMSPriority() + */ + + public void setJMSPriority(int priority) + { + this.jmsPriority = priority; + } + + /** + * Clears a message's properties. + *

+ *

The message's header fields and body are not cleared. + */ + + + /** + * Indicates whether a property value exists. + * + * @param name the name of the property to test + * @return true if the property exists + */ + + public bool propertyExists(String name) + { + return this.properties != null ? this.properties.containsKey(name) : false; + } + + + /** + * Returns the value of the bool property with the + * specified name. + * + * @param name the name of the bool property + * @return the bool property value for the specified name + * @ if the JMS provider fails to get the property + * value due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + + public bool getboolProperty(String name) + { + return vanillaTobool(this.properties, name); + } + + + /** + * Returns the value of the byte property with the specified + * name. + * + * @param name the name of the byte property + * @return the byte property value for the specified name + * @ if the JMS provider fails to get the property + * value due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + + public byte getByteProperty(String name) + { + return vanillaToByte(this.properties, name); + } + + + /** + * Returns the value of the short property with the specified + * name. + * + * @param name the name of the short property + * @return the short property value for the specified name + * @ if the JMS provider fails to get the property + * value due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + + public short getShortProperty(String name) + { + return vanillaToShort(this.properties, name); + } + + + /** + * Returns the value of the int property with the specified + * name. + * + * @param name the name of the int property + * @return the int property value for the specified name + * @ if the JMS provider fails to get the property + * value due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + + public int getIntProperty(String name) + { + return vanillaToInt(this.properties, name); + } + + + /** + * Returns the value of the long property with the specified + * name. + * + * @param name the name of the long property + * @return the long property value for the specified name + * @ if the JMS provider fails to get the property + * value due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + + public long getLongProperty(String name) + { + return vanillaToLong(this.properties, name); + } + + + /** + * Returns the value of the float property with the specified + * name. + * + * @param name the name of the float property + * @return the float property value for the specified name + * @ if the JMS provider fails to get the property + * value due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + + public float getFloatProperty(String name) + { + return vanillaToFloat(this.properties, name); + } + + + /** + * Returns the value of the double property with the specified + * name. + * + * @param name the name of the double property + * @return the double property value for the specified name + * @ if the JMS provider fails to get the property + * value due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + + public double getDoubleProperty(String name) + { + return vanillaToDouble(this.properties, name); + } + + + /** + * Returns the value of the String property with the specified + * name. + * + * @param name the name of the String property + * @return the String property value for the specified name; + * if there is no property by this name, a null value is returned + * @ if the JMS provider fails to get the property + * value due to some internal error. + * @throws MessageFormatException if this type conversion is invalid. + */ + + public String getStringProperty(String name) + { + return vanillaToString(this.properties, name); + } + + + /** + * Returns the value of the Java object property with the specified name. + *

+ *

This method can be used to return, in objectified format, + * an object that has been stored as a property in the message with the + * equivalent setObjectProperty method call, or its equivalent + * primitive settypeProperty method. + * + * @param name the name of the Java object property + * @return the Java object property value with the specified name, in + * objectified format (for example, if the property was set as an + * int, an Integer is + * returned); if there is no property by this name, a null value + * is returned + */ + + public Object getObjectProperty(String name) + { + return this.properties != null ? this.properties.get(name) : null; + } + + + /** + * Returns an Enumeration of all the property names. + *

+ *

Note that JMS standard header fields are not considered + * properties and are not returned in this enumeration. + * + * @return an enumeration of all the names of property values + */ + + public IDictionaryEnumerator getPropertyNames() + { + if (this.properties == null) + { + this.properties = new Hashtable(); + } + return this.properties.GetEnumerator(); + } + + /** + * Retrieve the message properties as a Hashtable + * + * @return the Hashtable representing the properties or null if not set or used + */ + + public Hashtable getProperties() + { + return this.properties; + } + + /** + * Set the Message's properties from an external source + * No checking on correct types is done by this method + * + * @param newProperties + */ + + public void setProperties(Hashtable newProperties) + { + this.properties = newProperties; + } + + + /** + * Sets a bool property value with the specified name into + * the message. + * + * @param name the name of the bool property + * @param value the bool property value to set + * @ if the JMS provider fails to set the property + * due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is + * an empty string. + * @throws MessageNotWriteableException if properties are read-only + */ + + public void setboolProperty(String name, bool value) + { + prepareProperty(name); + this.properties.put(name, (value) ? bool.TRUE : bool.FALSE); + } + + + /** + * Sets a byte property value with the specified name into + * the message. + * + * @param name the name of the byte property + * @param value the byte property value to set + * @ if the JMS provider fails to set the property + * due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is + * an empty string. + * @throws MessageNotWriteableException if properties are read-only + */ + + public void setByteProperty(String name, byte value) + { + prepareProperty(name); + this.properties.put(name, new Byte(value)); + } + + + /** + * Sets a short property value with the specified name into + * the message. + * + * @param name the name of the short property + * @param value the short property value to set + * @ if the JMS provider fails to set the property + * due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is + * an empty string. + * @throws MessageNotWriteableException if properties are read-only + */ + + public void setShortProperty(String name, short value) + { + prepareProperty(name); + this.properties.put(name, new Short(value)); + } + + + /** + * Sets an int property value with the specified name into + * the message. + * + * @param name the name of the int property + * @param value the int property value to set + * @ if the JMS provider fails to set the property + * due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is + * an empty string. + * @throws MessageNotWriteableException if properties are read-only + */ + + public void setIntProperty(String name, int value) + { + prepareProperty(name); + this.properties.put(name, new Integer(value)); + } + + + /** + * Sets a long property value with the specified name into + * the message. + * + * @param name the name of the long property + * @param value the long property value to set + * @ if the JMS provider fails to set the property + * due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is + * an empty string. + * @throws MessageNotWriteableException if properties are read-only + */ + + public void setLongProperty(String name, long value) + { + prepareProperty(name); + this.properties.put(name, new Long(value)); + } + + + /** + * Sets a float property value with the specified name into + * the message. + * + * @param name the name of the float property + * @param value the float property value to set + * @ if the JMS provider fails to set the property + * due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is + * an empty string. + * @throws MessageNotWriteableException if properties are read-only + */ + + public void setFloatProperty(String name, float value) + { + prepareProperty(name); + this.properties.put(name, new Float(value)); + + } + + + /** + * Sets a double property value with the specified name into + * the message. + * + * @param name the name of the double property + * @param value the double property value to set + * @ if the JMS provider fails to set the property + * due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is + * an empty string. + * @throws MessageNotWriteableException if properties are read-only + */ + + public void setDoubleProperty(String name, double value) + { + prepareProperty(name); + this.properties.put(name, new Double(value)); + } + + + /** + * Sets a String property value with the specified name into + * the message. + * + * @param name the name of the String property + * @param value the String property value to set + * @ if the JMS provider fails to set the property + * due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is + * an empty string. + * @throws MessageNotWriteableException if properties are read-only + */ + + public void setStringProperty(String name, String value) + { + prepareProperty(name); + if (value == null) + { + this.properties.remove(name); + } + else + { + this.properties.put(name, value); + } + } + + + /** + * Sets a Java object property value with the specified name into the + * message. + *

+ *

Note that this method works only for the objectified primitive + * object types (Integer, Double, + * Long ...) and String objects. + * + * @param name the name of the Java object property + * @param value the Java object property value to set + * @ if the JMS provider fails to set the property + * due to some internal error. + * @throws IllegalArgumentException if the name is null or if the name is + * an empty string. + * @throws MessageFormatException if the object is invalid + * @throws MessageNotWriteableException if properties are read-only + */ + + public void setObjectProperty(String name, Object value) + { + prepareProperty(name); + if (value == null) + { + this.properties.remove(name); + } + else + { + if (value is Number || + value is Character || + value is bool || + value is String) + { + this.properties.put(name, value); + } + else + { + throw new MessageFormatException("Cannot set property to type: " + value.getClass().getName()); + } + } + } + + + /** + * Acknowledges all consumed messages of the session of this consumed + * message. + *

+ *

All consumed JMS messages support the acknowledge + * method for use when a client has specified that its JMS session's + * consumed messages are to be explicitly acknowledged. By invoking + * acknowledge on a consumed message, a client acknowledges + * all messages consumed by the session that the message was delivered to. + *

+ *

Calls to acknowledge are ignored for both transacted + * sessions and sessions specified to use implicit acknowledgement modes. + *

+ *

A client may individually acknowledge each message as it is consumed, + * or it may choose to acknowledge messages as an application-defined group + * (which is done by calling acknowledge on the last received message of the group, + * thereby acknowledging all messages consumed by the session.) + *

+ *

Messages that have been received but not acknowledged may be + * redelivered. + * + * @ if the JMS provider fails to acknowledge the + * messages due to some internal error. + * @throws javax.jms.IllegalStateException + * if this method is called on a closed + * session. + * @see javax.jms.Session#CLIENT_ACKNOWLEDGE + */ + + public void acknowledge() + { + if (this.messageAcknowledge != null) + { + this.messageAcknowledge.acknowledge(this); + } + } + + + /** + * Clears out the message body. Clearing a message's body does not clear + * its header values or property entries. + *

+ *

If this message body was read-only, calling this method leaves + * the message body in the same state as an empty body in a newly + * created message. + * + * @ if the JMS provider fails to clear the message + * body due to some internal error. + */ + + public void clearBody() + { + this.readOnlyMessage = false; + this.bodyAsBytes = null; + } + + bool vanillaTobool(Hashtable table, String name) + { + bool result = false; + Object value = getVanillaProperty(table, name); + if (value != null) + { + if (value is bool) + { + result = ((bool) value).boolValue(); + } + else if (value is String) + { + // will throw a runtime exception if cannot convert + result = bool.valueOf((String) value).boolValue(); + } + else + { + throw new MessageFormatException(name + " not a bool type"); + } + } + return result; + } + + byte vanillaToByte(Hashtable table, String name) + { + byte result = 0; + Object value = getVanillaProperty(table, name); + if (value != null) + { + if (value is Byte) + { + result = ((Byte) value).byteValue(); + } + else if (value is String) + { + result = Byte.valueOf((String) value).byteValue(); + } + else + { + throw new MessageFormatException(name + " not a Byte type"); + } + } + else + { + //object doesn't exist - so treat as a null .. + throw new NumberFormatException("Cannot interpret null as a Byte"); + } + return result; + } + + short vanillaToShort(Hashtable table, String name) + { + short result = 0; + Object value = getVanillaProperty(table, name); + if (value != null) + { + if (value is Short) + { + result = ((Short) value).shortValue(); + } + else if (value is String) + { + return Short.valueOf((String) value).shortValue(); + } + else if (value is Byte) + { + result = ((Byte) value).byteValue(); + } + else + { + throw new MessageFormatException(name + " not a Short type"); + } + } + else + { + throw new NumberFormatException(name + " is null"); + } + return result; + } + + int vanillaToInt(Hashtable table, String name) + { + int result = 0; + Object value = getVanillaProperty(table, name); + if (value != null) + { + if (value is Integer) + { + result = ((Integer) value).intValue(); + } + else if (value is String) + { + result = Integer.valueOf((String) value).intValue(); + } + else if (value is Byte) + { + result = ((Byte) value).intValue(); + } + else if (value is Short) + { + result = ((Short) value).intValue(); + } + else + { + throw new MessageFormatException(name + " not an Integer type"); + } + } + else + { + throw new NumberFormatException(name + " is null"); + } + return result; + } + + long vanillaToLong(Hashtable table, String name) + { + long result = 0; + Object value = getVanillaProperty(table, name); + if (value != null) + { + if (value is Long) + { + result = ((Long) value).longValue(); + } + else if (value is String) + { + // will throw a runtime exception if cannot convert + result = Long.valueOf((String) value).longValue(); + } + else if (value is Byte) + { + result = ((Byte) value).byteValue(); + } + else if (value is Short) + { + result = ((Short) value).shortValue(); + } + else if (value is Integer) + { + result = ((Integer) value).intValue(); + } + else + { + throw new MessageFormatException(name + " not a Long type"); + } + } + else + { + throw new NumberFormatException(name + " is null"); + } + return result; + } + + float vanillaToFloat(Hashtable table, String name) + { + float result = 0.0f; + Object value = getVanillaProperty(table, name); + if (value != null) + { + if (value is Float) + { + result = ((Float) value).floatValue(); + } + else if (value is String) + { + result = Float.valueOf((String) value).floatValue(); + } + else + { + throw new MessageFormatException(name + " not a Float type: " + value.getClass()); + } + } + else + { + throw new NullPointerException(name + " is null"); + } + return result; + } + + double vanillaToDouble(Hashtable table, String name) + { + double result = 0.0d; + Object value = getVanillaProperty(table, name); + if (value != null) + { + if (value is Double) + { + result = ((Double) value).doubleValue(); + } + else if (value is String) + { + result = Double.valueOf((String) value).doubleValue(); + } + else if (value is Float) + { + result = ((Float) value).floatValue(); + } + else + { + throw new MessageFormatException(name + " not a Double type"); + } + } + else + { + throw new NullPointerException(name + " is null"); + } + return result; + } + + Object getVanillaProperty(Hashtable table, String name) + { + Object result = null; + if (name == null) + { + throw new NullPointerException("name supplied is null"); + } + result = getReservedProperty(name); + if (result == null && table != null) + { + result = table.get(name); + } + return result; + } + + Object getReservedProperty(String name) + { + Object result = null; + if (name != null && name.equals(DELIVERY_COUNT_NAME)) + { + result = new Integer(deliveryCount); + } + return result; + } + + + String vanillaToString(Hashtable table, String name) + { + String result = null; + if (table != null) + { + Object value = table.get(name); + if (value != null) + { + if (value is String || value is Number || value is bool) + { + result = value.toString(); + } + else + { + throw new MessageFormatException(name + " not a String type"); + } + } + } + return result; + } + + private void prepareProperty(String name) + { + if (name == null) + { + throw new IllegalArgumentException("Invalid property name: cannot be null"); + } + if (name.length() == 0) + { + throw new IllegalArgumentException("Invalid property name: cannot be empty"); + } + if (this.readOnlyProperties) + { + throw new MessageNotWriteableException("Properties are read-only"); + } + if (this.properties == null) + { + this.properties = new Hashtable(); + } + } + + /** + * @return Returns the entryBrokerName. + */ + public String getEntryBrokerName() + { + return this.entryBrokerName; + } + + /** + * @param newEntryBrokerName The entryBrokerName to set. + */ + public void setEntryBrokerName(String newEntryBrokerName) + { + this.entryBrokerName = newEntryBrokerName; + } + + /** + * @return Returns the entryClusterName. + */ + public String getEntryClusterName() + { + return this.entryClusterName; + } + + /** + * @param newEntryClusterName The entryClusterName to set. + */ + public void setEntryClusterName(String newEntryClusterName) + { + this.entryClusterName = newEntryClusterName; + } + + /** + * @return Returns the consumerNos. + */ + public int[] getConsumerNos() + { + return this.consumerNos; + } + + /** + * @param newConsumerNos The consumerIDs to set. + */ + public void setConsumerNos(int[] newConsumerNos) + { + this.consumerNos = newConsumerNos; + } + + /** + * @return Returns the jmsClientID. + */ + public String getJMSClientID() + { + return this.jmsClientID; + } + + /** + * @param newJmsClientID The jmsClientID to set. + */ + public void setJMSClientID(String newJmsClientID) + { + this.jmsClientID = newJmsClientID; + } + + + + /** + * @return Returns true if this message is part of a transaction + */ + + public bool isPartOfTransaction() + { + return this.transactionId != null; + } + + /** + * @return Returns the transactionId. + */ + public Object getTransactionId() + { + return this.transactionId; + } + + /** + * @param newTransactionId The transactionId to set. + */ + public void setTransactionId(Object newTransactionId) + { + this.transactionId = newTransactionId; + //this.xaTransacted = newTransactionId!=null && newTransactionId.getClass()==ActiveMQXid.class; + } + + /** + * @return Returns the consumerId. + */ + public String getConsumerIdentifer() + { + return consumerIdentifier; + } + + /** + * @param consId The consumerId to set. + */ + public void setConsumerIdentifer(String consId) + { + this.consumerIdentifier = consId; + } + + /** + * @return Returns the messageConsumed. + */ + public bool isMessageConsumed() + { + return messageConsumed; + } + + /** + * @param messageConsumed The messageConsumed to set. + */ + public void setMessageConsumed(bool messageConsumed) + { + this.messageConsumed = messageConsumed; + } + + + public void prepareMessageBody() + { + } + + public void convertBodyToBytes() + { + //TODO SERIALIZATION MECHANISM FUNKINESS HERE + } + + public void buildBodyFromBytes() + { + //TODO SERIALIZATION MECHANISM FUNKINESS HERE + } + + /** + + public void writeBody(DataOutput dataOut) { + + } + + + + public void readBody(DataInput dataIn) throws IOException { + + } + + /** + * @return Returns the bodyAsBytes. + * @throws IOException + */ + public byte[] getBodyAsBytes() + { + return this.bodyAsBytes; + } + + + /** + * @return true if the body is already stored as bytes + */ + public bool isBodyConvertedToBytes() + { + return bodyAsBytes != null; + } + + + public void setBodyAsBytes(byte[] data,int offset, int length) + { + this.bodyAsBytes = new ByteArray(data); + } + + /** + * set the body as bytes + * @param ba + */ + public void setBodyAsBytes(byte[] ba) + { + this.bodyAsBytes = ba; + } + + /** + * write map properties to an output stream + * + * @param table + * @param dataOut + * @throws IOException + */ + + public void writeMapProperties(Hashtable table, Object output) + { + //TODO SERIALIZATION MECHANISM + } + + /** + * @param dataIn + * @return + * @throws IOException + */ + public Hashtable readMapProperties(Object dataIn) + { + //TODO serilization stuff + } + + /** + * @return Returns the xaTransacted. + */ + public bool isXaTransacted() + { + return xaTransacted; + } + + /** + * @return the ActiveMQDestination + */ + public ActiveMQDestination getJMSActiveMQDestination() + { + return jmsDestination; + } + + + + /** + * Determine if the message originated in the network from the named broker + * @param brokerName + * @return true if entry point matches the brokerName + */ + public bool isEntryBroker(String brokerName) + { + bool result = entryBrokerName != null && brokerName != null && entryBrokerName.equals(brokerName); + return result; + } + + /** + * Determine if the message originated in the network from the named cluster + * @param clusterName + * @return true if the entry point matches the clusterName + */ + public bool isEntryCluster(String clusterName) + { + bool result = entryClusterName != null && clusterName != null && entryClusterName.equals(clusterName); + return result; + } + + /** + * @return Returns the transientConsumed. + */ + public bool isTransientConsumed() + { + return transientConsumed; + } + /** + * @param transientConsumed The transientConsumed to set. + */ + public void setTransientConsumed(bool transientConsumed) + { + this.transientConsumed = transientConsumed; + } + + /** + * @return Returns the sequenceNumber. + */ + public long getSequenceNumber() + { + return sequenceNumber; + } + /** + * @param sequenceNumber The sequenceNumber to set. + */ + public void setSequenceNumber(long sequenceNumber) + { + this.sequenceNumber = sequenceNumber; + } + /** + * @return Returns the deliveryCount. + */ + public int getDeliveryCount() + { + return deliveryCount; + } + /** + * @param deliveryCount The deliveredCount to set. + */ + public void setDeliveryCount(int deliveryCount) + { + this.deliveryCount = deliveryCount; + } + + /** + * Increment the delivery count + * @return the new value of the delivery count + */ + public int incrementDeliveryCount() + { + return ++this.deliveryCount; + } + + /** + * @return true if the delivery mode is persistent + */ + public bool isPersistent() + { + return jmsDeliveryMode == DeliveryMode.PERSISTENT; + } + + /** + * @return Returns the dispatchedFromDLQ. + */ + public bool isDispatchedFromDLQ() + { + return dispatchedFromDLQ; + } + /** + * @param dispatchedFromDLQ The dispatchedFromDLQ to set. + */ + public void setDispatchedFromDLQ(bool dispatchedFromDLQ) + { + this.dispatchedFromDLQ = dispatchedFromDLQ; + } + + /** + * @return Returns the messsageHandle. + */ + public short getMesssageHandle() + { + return messsageHandle; + } + /** + * @param messsageHandle The messsageHandle to set. + */ + public void setMesssageHandle(short messsageHandle) + { + this.messsageHandle = messsageHandle; + } + /** + * @return Returns the externalMessageId. + */ + public bool isExternalMessageId() + { + return externalMessageId; + } + /** + * @param externalMessageId The externalMessageId to set. + */ + public void setExternalMessageId(bool externalMessageId) + { + this.externalMessageId = externalMessageId; + } + /** + * @return Returns the producerKey. + */ + public String getProducerKey() + { + return producerKey; + } + /** + * @param producerKey The producerKey to set. + */ + public void setProducerKey(String producerKey) + { + this.producerKey = producerKey; + } + + /** + * reset message fragmentation infomation + * on this message + * + */ + public void resetMessagePart() + { + messagePart = false; + partNumber = 0; + parentMessageID = null; + } + /** + * @return Returns the messagePart. + */ + public bool isMessagePart() + { + return messagePart; + } + + /** + * @return true if this is the last part of a fragmented message + */ + public bool isLastMessagePart() + { + return numberOfParts -1 == partNumber; + } + /** + * @param messagePart The messagePart to set. + */ + public void setMessagePart(bool messagePart) + { + this.messagePart = messagePart; + } + /** + * @return Returns the numberOfParts. + */ + public short getNumberOfParts() + { + return numberOfParts; + } + /** + * @param numberOfParts The numberOfParts to set. + */ + public void setNumberOfParts(short numberOfParts) + { + this.numberOfParts = numberOfParts; + } + /** + * @return Returns the partNumber. + */ + public short getPartNumber() + { + return partNumber; + } + /** + * @param partNumber The partNumber to set. + */ + public void setPartNumber(short partNumber) + { + this.partNumber = partNumber; + } + /** + * @return Returns the parentMessageId. + */ + public String getParentMessageID() + { + return parentMessageID; + } + /** + * @param parentMessageId The parentMessageId to set. + */ + public void setParentMessageID(String parentMessageId) + { + this.parentMessageID = parentMessageId; + } + } +} diff --git a/openwire-dotnet/comms-library/amqnet/ActiveMQQueue.cs b/openwire-dotnet/comms-library/amqnet/ActiveMQQueue.cs new file mode 100755 index 0000000000..379b9e9632 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/ActiveMQQueue.cs @@ -0,0 +1,22 @@ +using System; + +namespace ActiveMQ +{ + ///

+ /// Summary description for ActiveMQQueue. + /// + public class ActiveMQQueue : ActiveMQDestination { + public ActiveMQQueue() : base(){} + public ActiveMQQueue(String name) : base(name){} + public String getQueueName() { + return base.getPhysicalName(); + } + public override int getDestinationType() { + return ACTIVEMQ_QUEUE; + } + + public override ActiveMQDestination createDestination(String name) { + return new ActiveMQQueue(name); + } + } +} \ No newline at end of file diff --git a/openwire-dotnet/comms-library/amqnet/ActiveMQTopic.cs b/openwire-dotnet/comms-library/amqnet/ActiveMQTopic.cs new file mode 100755 index 0000000000..da2f4b843a --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/ActiveMQTopic.cs @@ -0,0 +1,28 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for ActiveMQTopic. + /// + public class ActiveMQTopic : ActiveMQDestination + { + public ActiveMQTopic(): base() {} + public ActiveMQTopic(String name):base(name){} + public String getTopicName() + { + return super.getPhysicalName(); + } + public override int getDestinationType() + { + return ACTIVEMQ_TOPIC; + } + + + public override ActiveMQDestination createDestination(String name) + { + return new ActiveMQTopic(name); + } + + } +} diff --git a/openwire-dotnet/comms-library/amqnet/BrokerInfo.cs b/openwire-dotnet/comms-library/amqnet/BrokerInfo.cs new file mode 100755 index 0000000000..b65c253678 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/BrokerInfo.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections; +namespace ActiveMQ +{ + public class BrokerInfo : AbstractPacket { + + private String brokerName; + private String clusterName; + private long startTime; + private Hashtable properties; + private bool remote; + + + /** + * Return the type of Packet + * + * @return integer representation of the type of Packet + */ + + public new int getPacketType() + { + return ACTIVEMQ_BROKER_INFO; + } + + + /** + * @return Returns the brokerName. + */ + public String getBrokerName() + { + return this.brokerName; + } + + /** + * @param newBrokerName The brokerName to set. + */ + public void setBrokerName(String newBrokerName) + { + this.brokerName = newBrokerName; + } + + /** + * @return Returns the clusterName. + */ + public String getClusterName() + { + return this.clusterName; + } + + /** + * @param newClusterName The clusterName to set. + */ + public void setClusterName(String newClusterName) + { + this.clusterName = newClusterName; + } + + /** + * @return Returns the properties. + */ + public Hashtable getProperties() + { + return this.properties; + } + + /** + * @param newProperties The properties to set. + */ + public void setProperties(Hashtable newProperties) + { + this.properties = newProperties; + } + + /** + * @return Returns the startTime. + */ + public long getStartTime() + { + return this.startTime; + } + + /** + * @param newStartTime The startTime to set. + */ + public void setStartTime(long newStartTime) + { + this.startTime = newStartTime; + } + + /** + * @return Returns the boondocks. + */ + public bool isRemote() + { + return remote; + } + /** + * @param boondocks The boondocks to set. + */ + public void setRemote(bool boondocks) + { + this.remote = boondocks; + } + + + public override String ToString() + { + return base.ToString() + " BrokerInfo{ " + + "brokerName = '" + brokerName + "' " + + ", clusterName = '" + clusterName + "' " + + ", startTime = " + startTime + + ", properties = " + properties + + " }"; + } + } + +} diff --git a/openwire-dotnet/comms-library/amqnet/ConnectionInfo.cs b/openwire-dotnet/comms-library/amqnet/ConnectionInfo.cs new file mode 100755 index 0000000000..f522f88eb6 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/ConnectionInfo.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections; +namespace ActiveMQ +{ + public class ConnectionInfo : AbstractPacket + { + + public static String NO_DELAY_PROPERTY = "noDelay"; + String clientId; + String userName; + String password; + String hostName; + String clientVersion; + int wireFormatVersion; + long startTime; + bool started; + bool closed; + Hashtable properties = new Hashtable(); + + public override short getPacketType() + { + return PacketConstants.ACTIVEMQ_CONNECTION_INFO; + } + + public override bool Equals(Object obj) + { + bool result = false; + if (obj != null && obj is ConnectionInfo) + { + ConnectionInfo info = (ConnectionInfo) obj; + result = this.clientId == info.clientId; + } + return result; + } + public override int GetHashCode() + { + return this.clientId != null ? this.clientId.GetHashCode() : base.GetHashCode(); + } + + + public String getClientId() + { + return this.clientId; + } + + public void setClientId(String newClientId) + { + this.clientId = newClientId; + } + + public String getHostName() + { + return this.hostName; + } + + public void setHostName(String newHostName) + { + this.hostName = newHostName; + } + + public String getPassword() + { + return this.password; + } + + + public void setPassword(String newPassword) + { + this.password = newPassword; + } + + public Hashtable getProperties() + { + return this.properties; + } + + + public void setProperties(Hashtable newProperties) + { + this.properties = newProperties; + } + + public long getStartTime() + { + return this.startTime; + } + + public void setStartTime(long newStartTime) + { + this.startTime = newStartTime; + } + + public String getUserName() + { + return this.userName; + } + + public void setUserName(String newUserName) + { + this.userName = newUserName; + } + + public bool isStarted() + { + return started; + } + + public void setStarted(bool started) + { + this.started = started; + } + + public bool isClosed() + { + return closed; + } + + public void setClosed(bool closed) + { + this.closed = closed; + } + + public String getClientVersion() + { + return clientVersion; + } + + public void setClientVersion(String clientVersion) + { + this.clientVersion = clientVersion; + + } + + public int getWireFormatVersion() + { + return wireFormatVersion; + } + + public void setWireFormatVersion(int wireFormatVersion) + { + this.wireFormatVersion = wireFormatVersion; + } + + + public override String ToString() + { + return base.ToString() + " ConnectionInfo{ " + + "clientId = '" + clientId + "' " + + ", userName = '" + userName + "' " + + ", hostName = '" + hostName + "' " + + ", clientVersion = '" + clientVersion + "' " + + ", wireFormatVersion = " + wireFormatVersion + + ", startTime = " + startTime + + ", started = " + started + + ", closed = " + closed + + ", properties = " + properties + + " }"; + } + } +} + + + diff --git a/openwire-dotnet/comms-library/amqnet/ConsumerInfo.cs b/openwire-dotnet/comms-library/amqnet/ConsumerInfo.cs new file mode 100755 index 0000000000..6db0190794 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/ConsumerInfo.cs @@ -0,0 +1,325 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for ConsumerInfo. + /// + public class ConsumerInfo : AbstractPacket { + + private ActiveMQDestination destination; + private String clientId; + private short sessionId; + private String consumerName; + private String selector; + private long startTime; + private bool started; + private int consumerNo; + private bool noLocal; + private bool browser; + private int prefetchNumber = 100; + + private String consumerId; + + + + /** + * @return Returns the sessionId. + */ + public short getSessionId() + { + return sessionId; + } + + /** + * @param sessionId The sessionId to set. + */ + public void setSessionId(short sessionId) + { + this.sessionId = sessionId; + } + + /** + * Return the type of Packet + * + * @return integer representation of the type of Packet + */ + public new int getPacketType() + { + return CONSUMER_INFO; + } + + /** + * Test for equality + * + * @param obj object to test + * @return true if equivalent + */ + public bool equals(Object obj) + { + bool result = false; + if (obj != null && obj is ConsumerInfo) + { + ConsumerInfo that = (ConsumerInfo) obj; + result = this.getConsumerId().equals(that.getConsumerId()); + } + return result; + } + + /** + * @return hash code for instance + */ + public new int hashCode() + { + return getConsumerId().hashCode(); + } + + /** + * @return Returns the clientId. + */ + public String getClientId() + { + return this.clientId; + } + + /** + * @param newClientId The clientId to set. + */ + public void setClientId(String newClientId) + { + this.clientId = newClientId; + } + + /** + * @return Returns the destination. + */ + public ActiveMQDestination getDestination() + { + return this.destination; + } + + /** + * @param newDestination The destination to set. + */ + public void setDestination(ActiveMQDestination newDestination) + { + this.destination = newDestination; + } + + /** + * @return Returns the selector. + */ + public String getSelector() + { + return this.selector; + } + + /** + * @param newSelector The selector to set. + */ + public void setSelector(String newSelector) + { + this.selector = newSelector; + } + + /** + * @return Returns the started. + */ + public bool isStarted() + { + return this.started; + } + + /** + * @param flag to indicate if started + */ + public void setStarted(bool flag) + { + this.started = flag; + } + + /** + * @return Returns the startTime. + */ + public long getStartTime() + { + return this.startTime; + } + + /** + * @param newStartTime The startTime to set. + */ + public void setStartTime(long newStartTime) + { + this.startTime = newStartTime; + } + + /** + * @return Returns the consumerNo. + */ + public int getConsumerNo() + { + return this.consumerNo; + } + + /** + * @param newConsumerNo The consumerNo to set. + */ + public void setConsumerNo(int newConsumerNo) + { + this.consumerNo = newConsumerNo; + } + + /** + * @return Returns the consumer name. + */ + public String getConsumerName() + { + return this.consumerName; + } + + /** + * @param newconsumerName The consumerName to set. + */ + public void setConsumerName(String newconsumerName) + { + this.consumerName = newconsumerName; + } + + /** + * @return Returns true if the Consumer is a durable Topic subscriber + */ + public bool isDurableTopic() + { + return this.destination.isTopic() && !this.destination.isTemporary() && this.consumerName != null + && this.consumerName.length() > 0; + } + + /** + * @return Returns the noLocal. + */ + public bool isNoLocal() + { + return noLocal; + } + + /** + * @param noLocal The noLocal to set. + */ + public void setNoLocal(bool noLocal) + { + this.noLocal = noLocal; + } + + /** + * @return Returns the browser. + */ + public bool isBrowser() + { + return browser; + } + + /** + * @param browser The browser to set. + */ + public void setBrowser(bool browser) + { + this.browser = browser; + } + + /** + * @return Returns the prefetchNumber. + */ + public int getPrefetchNumber() + { + return prefetchNumber; + } + + /** + * @param prefetchNumber The prefetchNumber to set. + */ + public void setPrefetchNumber(int prefetchNumber) + { + this.prefetchNumber = prefetchNumber; + } + + + /** + * Creates a primary key for the consumer info which uniquely + * describes the consumer using a combination of clientID and + * consumerName + * + * @return the consumerKey + */ + public String getConsumerKey() + { + if (consumerKey == null) + { + consumerKey = generateConsumerKey(clientId,consumerName); + } + return consumerKey; + } + + /** + * @return Returns the consumerIdentifier. + */ + public String getConsumerId() + { + if (consumerId == null) + { + consumerId = clientId + "." + sessionId + "." + consumerNo; + } + return consumerId; + } + /** + * @param consumerIdentifier The consumerIdentifier to set. + */ + public void setConsumerId(String consumerIdentifier) + { + this.consumerId = consumerIdentifier; + } + + + /** + * Generate a primary key for a consumer from the clientId and consumerName + * @param clientId + * @param consumerName + * @return + */ + public static String generateConsumerKey(String clientId, String consumerName) + { + return "[" + clientId + ":" + consumerName + "]"; + } + + /** + * @return true if the consumer is interested in advisory messages + */ + public bool isAdvisory() + { + return destination != null && destination.isAdvisory(); + } + + /** + * @return a pretty print + */ + public override String ToString() + { + return super.toString() + " ConsumerInfo{ " + + "browser = " + browser + + ", destination = " + destination + + ", consumerIdentifier = '" + getConsumerId() + "' " + + ", clientId = '" + clientId + "' " + + ", sessionId = '" + sessionId + "' " + + ", consumerName = '" + consumerName + "' " + + ", selector = '" + selector + "' " + + ", startTime = " + startTime + + ", started = " + started + + ", consumerNo = " + consumerNo + + ", noLocal = " + noLocal + + ", prefetchNumber = " + prefetchNumber + + ", consumerKey = '" + getConsumerKey() + "' " + + " }"; + } + } + +} diff --git a/openwire-dotnet/comms-library/amqnet/DestinationFilter.cs b/openwire-dotnet/comms-library/amqnet/DestinationFilter.cs new file mode 100755 index 0000000000..280973a6d1 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/DestinationFilter.cs @@ -0,0 +1,23 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for DestinationFilter. + /// + public abstract class DestinationFilter + { + public const String ANY_DESCENDENT = ">"; + public const String ANY_CHILD = "*"; + + public bool matches(ActiveMQMessage message) + { + return matches(message.getJMSDestination()); + } + + public abstract bool matches(ActiveMQDestination destination); + + + } + +} diff --git a/openwire-dotnet/comms-library/amqnet/IntResponseReceipt.cs b/openwire-dotnet/comms-library/amqnet/IntResponseReceipt.cs new file mode 100755 index 0000000000..a603c8fca8 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/IntResponseReceipt.cs @@ -0,0 +1,43 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for IntResponseReceipt. + /// + public class IntResponseReceipt : Receipt { + + private int result; + + /** + * @see org.activemq.message.Receipt#getPacketType() + */ + public new int getPacketType() + { + return INT_RESPONSE_RECEIPT_INFO; + } + + /** + * @return Returns the result. + */ + public int getResult() + { + return result; + } + + /** + * @param result The result to set. + */ + public void setResult(int result) + { + this.result = result; + } + + public new String ToString() + { + return super.toString() + " IntResponseReceipt{ " + + "result = " + result + + " }"; + } + } +} diff --git a/openwire-dotnet/comms-library/amqnet/MessageAck.cs b/openwire-dotnet/comms-library/amqnet/MessageAck.cs new file mode 100755 index 0000000000..55b949db1f --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/MessageAck.cs @@ -0,0 +1,264 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for MessageAck. + /// + public class MessageAck : AbstractPacket { + + public const int MESSAGE_READ_INDEX = 2; + public const int XA_TRANS_INDEX = 3; + public const int PERSISTENT_INDEX = 4; + public const int EXPIRED_INDEX = 5; + public const int TRANSACTION_ID_INDEX = 6; + public const int EXTERNAL_MESSAGE_ID_INDEX = 7; + public const int CACHED_VALUES_INDEX = 8; + public const int LONG_SEQUENCE_INDEX = 9; + + private String consumerId; + private String messageID; + private ActiveMQDestination destination; + private Object transactionId; + private bool messageRead; + private bool xaTransacted; + private bool persistent; + private bool expired; + private short sessionId; + private long sequenceNumber; + private String producerKey; + private bool externalMessageId; + + + + /** + * Return the type of Packet + * + * @return integer representation of the type of Packet + */ + + public new int getPacketType() + { + return ACTIVEMQ_MSG_ACK; + } + + /** + * @return pretty print of this Packet + */ + public new String ToString() + { + return super.toString() + " MessageAck{ " + + "consumerId = '" + consumerId + "' " + + ", messageID = '" + messageID + "' " + + ", destination = " + destination + + ", transactionId = '" + transactionId + "' " + + ", messageRead = " + messageRead + + ", xaTransacted = " + xaTransacted + + ", persistent = " + persistent + + ", expired = " + expired + + ", messageIdentity = " + messageIdentity + + " }"; + } + + + /** + * @return Returns the transactionId. + */ + public Object getTransactionId() + { + return this.transactionId; + } + + /** + * @param newTransactionId The transactionId to set. + */ + public void setTransactionId(Object newTransactionId) + { + this.transactionId = newTransactionId; + //this.xaTransacted = newTransactionId!=null && newTransactionId.getClass()==ActiveMQXid.class; + } + + /** + * @return Returns true if this message is part of a transaction + */ + + public bool isPartOfTransaction() + { + return this.transactionId != null; + } + + + /** + * @return the messageId + */ + + public String getMessageID() + { + if (messageID == null && producerKey != null) + { + messageID = producerKey + sequenceNumber; + } + return messageID; + } + + /** + * @param messageID The messageID to set. + */ + public void setMessageID(String messageID) + { + this.messageID = messageID; + } + + /** + * @return Returns the messageRead. + */ + public bool isMessageRead() + { + return messageRead; + } + + /** + * @param messageRead The messageRead to set. + */ + public void setMessageRead(bool messageRead) + { + this.messageRead = messageRead; + } + + /** + * @return Returns the consumerId. + */ + public String getConsumerId() + { + return consumerId; + } + + /** + * @param consumerId The consumerId to set. + */ + public void setConsumerId(String consumerId) + { + this.consumerId = consumerId; + } + + /** + * @return Returns the xaTransacted. + */ + public bool isXaTransacted() + { + return xaTransacted; + } + + + /** + * @return Returns the destination. + */ + public ActiveMQDestination getDestination() + { + return destination; + } + /** + * @param destination The destination to set. + */ + public void setDestination(ActiveMQDestination destination) + { + this.destination = destination; + } + /** + * @return Returns the persistent. + */ + public bool isPersistent() + { + return persistent; + } + /** + * @param persistent The persistent to set. + */ + public void setPersistent(bool persistent) + { + this.persistent = persistent; + } + + /** + * @return true the delivered message was to a non-persistent destination + */ + public bool isTemporary() + { + return persistent == false || (destination != null && destination.isTemporary()); + } + + /** + * @return Returns the expired. + */ + public bool isExpired() + { + return expired; + } + /** + * @param expired The expired to set. + */ + public void setExpired(bool expired) + { + this.expired = expired; + } + + /** + * @return Returns the producerKey. + */ + public String getProducerKey() + { + return producerKey; + } + /** + * @param producerKey The producerKey to set. + */ + public void setProducerKey(String producerKey) + { + this.producerKey = producerKey; + } + + /** + * @return Returns the messageSequence. + */ + public long getSequenceNumber() + { + return sequenceNumber; + } + /** + * @param messageSequence The messageSequence to set. + */ + public void setSequenceNumber(long messageSequence) + { + this.sequenceNumber = messageSequence; + } + /** + * @return Returns the sessionId. + */ + public short getSessionId() + { + return sessionId; + } + /** + * @param sessionId The sessionId to set. + */ + public void setSessionId(short sessionId) + { + this.sessionId = sessionId; + } + /** + * @return Returns the externalMessageId. + */ + public bool isExternalMessageId() + { + return externalMessageId; + } + /** + * @param externalMessageId The externalMessageId to set. + */ + public void setExternalMessageId(bool externalMessageId) + { + this.externalMessageId = externalMessageId; + } + } + +} diff --git a/openwire-dotnet/comms-library/amqnet/MessageAcknowledge.cs b/openwire-dotnet/comms-library/amqnet/MessageAcknowledge.cs new file mode 100755 index 0000000000..5011591bf9 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/MessageAcknowledge.cs @@ -0,0 +1,13 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for MessageAcknowledge. + /// + public interface MessageAcknowledge + { + void acknowledge(ActiveMQMessage caller); + } + +} diff --git a/openwire-dotnet/comms-library/amqnet/PacketConstants.cs b/openwire-dotnet/comms-library/amqnet/PacketConstants.cs new file mode 100755 index 0000000000..4ac31fa26f --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/PacketConstants.cs @@ -0,0 +1,157 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for Packetpublic constants. + /// + class PacketConstants + { + PacketConstants() + {} + + public const int NOT_SET = 0; + + /** + * ActiveMQMessage object + */ + public const int ACTIVEMQ_MESSAGE = 6; + + /** + * ActiveMQTextMessage object + */ + + public const int ACTIVEMQ_TEXT_MESSAGE = 7; + + /** + * ActiveMQObjectMessage object + */ + + public const int ACTIVEMQ_OBJECT_MESSAGE = 8; + + /** + * ActiveMQBytesMessage + */ + + public const int ACTIVEMQ_BYTES_MESSAGE = 9; + + /** + * ActiveMQStreamMessage object + */ + + public const int ACTIVEMQ_STREAM_MESSAGE = 10; + + /** + * ActiveMQMapMessage object + */ + + public const int ACTIVEMQ_MAP_MESSAGE = 11; + + /** + * Message acknowledge + */ + public const int ACTIVEMQ_MSG_ACK = 15; + + /** + * Recipt message + */ + + public const int RECEIPT_INFO = 16; + + /** + * Consumer Infomation + */ + + public const int CONSUMER_INFO = 17; + + /** + * Producer Info + */ + + public const int PRODUCER_INFO = 18; + + /** + * Transaction info + */ + + public const int TRANSACTION_INFO = 19; + + /** + * XA Transaction info + */ + + public const int XA_TRANSACTION_INFO = 20; + + /** + * Broker infomation message + */ + + public const int ACTIVEMQ_BROKER_INFO = 21; + + /** + * Connection info message + */ + + public const int ACTIVEMQ_CONNECTION_INFO = 22; + + + /** + * Session Info message + */ + public const int SESSION_INFO = 23; + + /** + * Durable Unsubscribe message + */ + + public const int DURABLE_UNSUBSCRIBE = 24; + + + /** + * A receipt with an Object reponse. + */ + public const int RESPONSE_RECEIPT_INFO = 25; + + + /** + * A receipt with an Integer reponse. + */ + public const int INT_RESPONSE_RECEIPT_INFO = 26; + + /** + * Infomation about the Capacity for more Messages for either Connection/Broker + */ + public const int CAPACITY_INFO = 27; + + /** + * Request infomation about the current capacity + */ + public const int CAPACITY_INFO_REQUEST = 28; + + /** + * Infomation about the wire format expected + */ + public const int WIRE_FORMAT_INFO = 29; + + /** + * Keep-alive message + */ + public const int KEEP_ALIVE = 30; + + /** + * A command to the Broker Admin + */ + public const int BROKER_ADMIN_COMMAND = 31; + + /** + * transmit cached values for the wire format + */ + public const int CACHED_VALUE_COMMAND = 32; + + /** + * transmit cached values for the wire format + */ + public const int CLEANUP_CONNECTION_INFO = 33; + } + } + diff --git a/openwire-dotnet/comms-library/amqnet/ProducerInfo.cs b/openwire-dotnet/comms-library/amqnet/ProducerInfo.cs new file mode 100755 index 0000000000..b3de722a7b --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/ProducerInfo.cs @@ -0,0 +1,175 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for ProducerInfo. + /// + public class ProducerInfo : AbstractPacket + { + private ActiveMQDestination destination; + private short producerId; + private String clientId; + private short sessionId; + private long startTime; + private bool started; + + + /** + * Test for equality + * + * @param obj object to test + * @return true if equivalent + */ + public override bool Equals(Object obj) + { + bool result = false; + if (obj != null && obj is ProducerInfo) + { + ProducerInfo info = (ProducerInfo) obj; + result = this.producerId == info.producerId && + this.sessionId == info.sessionId && + this.clientId.equals(info.clientId); + } + return result; + } + + /** + * @return hash code for instance + */ + + public override int GetHashCode() + { + if (cachedHashCode == -1) + { + String hashCodeStr = clientId + sessionId + producerId; + cachedHashCode = hashCodeStr.hashCode(); + } + return cachedHashCode; + } + + + /** + * @return Returns the producerId. + */ + public short getProducerId() + { + return producerId; + } + + /** + * @param producerId The producerId to set. + */ + public void setProducerId(short producerId) + { + this.producerId = producerId; + } + + /** + * @return Returns the sessionId. + */ + public short getSessionId() + { + return sessionId; + } + + /** + * @param sessionId The sessionId to set. + */ + public void setSessionId(short sessionId) + { + this.sessionId = sessionId; + } + + /** + * Return the type of Packet + * + * @return integer representation of the type of Packet + */ + + public new int getPacketType() + { + return PRODUCER_INFO; + } + + + /** + * @return Returns the clientId. + */ + public String getClientId() + { + return this.clientId; + } + + /** + * @param newClientId The clientId to set. + */ + public void setClientId(String newClientId) + { + this.clientId = newClientId; + } + + + /** + * @return Returns the destination. + */ + public ActiveMQDestination getDestination() + { + return this.destination; + } + + /** + * @param newDestination The destination to set. + */ + public void setDestination(ActiveMQDestination newDestination) + { + this.destination = newDestination; + } + + + /** + * @return Returns the started. + */ + public bool isStarted() + { + return this.started; + } + + /** + * @param flag to indicate if started + */ + public void setStarted(bool flag) + { + this.started = flag; + } + + /** + * @return Returns the startTime. + */ + public long getStartTime() + { + return this.startTime; + } + + /** + * @param newStartTime The startTime to set. + */ + public void setStartTime(long newStartTime) + { + this.startTime = newStartTime; + } + + public override String ToString() + { + return super.toString() + " ProducerInfo{ " + + "clientId = '" + clientId + "' " + + ", destination = " + destination + + ", producerId = '" + producerId + "' " + + ", sessionId = '" + sessionId + "' " + + ", startTime = " + startTime + + ", started = " + started + + " }"; + } + + } +} diff --git a/openwire-dotnet/comms-library/amqnet/Receipt.cs b/openwire-dotnet/comms-library/amqnet/Receipt.cs new file mode 100755 index 0000000000..ed9540298f --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/Receipt.cs @@ -0,0 +1,144 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for Receipt. + /// + public class Receipt : AbstractPacket + { + + private short correlationId; + private String brokerName; + private String clusterName; + private String exception; + private bool failed; + private int brokerMessageCapacity = 100; + + + /** + * @return Returns the jmsException. + */ + public String getException() + { + return exception; + } + + /** + * @param exception The exception to set. + */ + public void setException(String exception) + { + this.exception = exception; + } + + /** + * Return the type of Packet + * + * @return integer representation of the type of Packet + */ + + public new int getPacketType() + { + return RECEIPT_INFO; + } + + /** + * @return true, this is a receipt packet + */ + public new bool isReceipt() + { + return true; + } + + /** + * @return Returns the correlationId. + */ + public short getCorrelationId() + { + return this.correlationId; + } + + /** + * @param newCorrelationId The correlationId to set. + */ + public void setCorrelationId(short newCorrelationId) + { + this.correlationId = newCorrelationId; + } + + /** + * @return Returns the failed. + */ + public bool isFailed() + { + return this.failed; + } + + /** + * @param newFailed The failed to set. + */ + public void setFailed(bool newFailed) + { + this.failed = newFailed; + } + + /** + * @return Returns the brokerMessageCapacity. + */ + public int getBrokerMessageCapacity() + { + return brokerMessageCapacity; + } + /** + * @param brokerMessageCapacity The brokerMessageCapacity to set. + */ + public void setBrokerMessageCapacity(int brokerMessageCapacity) + { + this.brokerMessageCapacity = brokerMessageCapacity; + } + /** + * @return Returns the brokerName. + */ + public String getBrokerName() + { + return brokerName; + } + /** + * @param brokerName The brokerName to set. + */ + public void setBrokerName(String brokerName) + { + this.brokerName = brokerName; + } + /** + * @return Returns the clusterName. + */ + public String getClusterName() + { + return clusterName; + } + /** + * @param clusterName The clusterName to set. + */ + public void setClusterName(String clusterName) + { + this.clusterName = clusterName; + } + + /** + * @return pretty print of a Receipt + */ + public override String ToString() + { + return super.toString() + " Receipt{ " + + "brokerMessageCapacity = " + brokerMessageCapacity + + ", correlationId = '" + correlationId + "' " + + ", brokerName = '" + brokerName + "' " + + ", clusterName = '" + clusterName + "' " + + ", exception = " + exception + + ", failed = " + failed + + " }"; + } + } +} diff --git a/openwire-dotnet/comms-library/amqnet/ResponseReceipt.cs b/openwire-dotnet/comms-library/amqnet/ResponseReceipt.cs new file mode 100755 index 0000000000..e5e5bf49a6 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/ResponseReceipt.cs @@ -0,0 +1,61 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for ResponseReceipt. + /// +public class ResponseReceipt : Receipt { + + private Object result; + private byte[] resultBytes; + + /** + * @see org.activemq.message.Receipt#getPacketType() + */ + public new int getPacketType() { + return RESPONSE_RECEIPT_INFO; + } + + /** + * @return Returns the result. + */ + public Object getResult() { + return null; + //TODO Serilization code + } + + /** + * @param result The result to set. + */ + public void setResult(Object result) { + this.result = result; + this.resultBytes = null; + } + + /** + * @param data + */ + public void setResultBytes(byte[] resultBytes) { + this.resultBytes = resultBytes; + this.result = null; + } + + /** + * @return Returns the resultBytes. + */ + public byte[] getResultBytes() { + + //TODO SERILIZATION + return null; + } + + public override String ToString() { + return base.toString() + " ResponseReceipt{ " + + "result = " + result + + ", resultBytes = " + resultBytes + + " }"; + } +} + +} diff --git a/openwire-dotnet/comms-library/amqnet/SessionInfo.cs b/openwire-dotnet/comms-library/amqnet/SessionInfo.cs new file mode 100755 index 0000000000..292087cbd8 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/SessionInfo.cs @@ -0,0 +1,150 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for SessionInfo. + /// + public class SessionInfo : AbstractPacket { + + private String clientId; + private short sessionId; + private long startTime; + private bool started; + private int sessionMode; + + /** + * @return Returns the sessionMode. + */ + public int getSessionMode() + { + return sessionMode; + } + /** + * @param sessionMode The sessionMode to set. + */ + public void setSessionMode(int sessionMode) + { + this.sessionMode = sessionMode; + } + /** + * Return the type of Packet + * + * @return integer representation of the type of Packet + */ + + public new int getPacketType() + { + return SESSION_INFO; + } + + + /** + * Test for equality + * + * @param obj object to test + * @return true if equivalent + */ + public override bool Equals(Object obj) + { + boolean result = false; + if (obj != null && obj is SessionInfo) { + SessionInfo info = (SessionInfo) obj; + result = this.clientId.equals(info.clientId) && this.sessionId == info.sessionId; + } + return result; + } + + /** + * @return hash code for instance + */ + public override int GetHashCode() + { + if (cachedHashCode == -1) + { + String hashCodeStr = clientId + sessionId; + cachedHashCode = hashCodeStr.hashCode(); + } + return cachedHashCode; + } + + + + /** + * @return Returns the sessionId. + */ + public short getSessionId() + { + return sessionId; + } + + /** + * @param sessionId The sessionId to set. + */ + public void setSessionId(short sessionId) + { + this.sessionId = sessionId; + } + + + /** + * @return Returns the clientId. + */ + public String getClientId() + { + return this.clientId; + } + + /** + * @param newClientId The clientId to set. + */ + public void setClientId(String newClientId) + { + this.clientId = newClientId; + } + + + /** + * @return Returns the started. + */ + public bool isStarted() + { + return this.started; + } + + /** + * @param flag to indicate if started + */ + public void setStarted(bool flag) + { + this.started = flag; + } + + /** + * @return Returns the startTime. + */ + public long getStartTime() + { + return this.startTime; + } + + /** + * @param newStartTime The startTime to set. + */ + public void setStartTime(long newStartTime) + { + this.startTime = newStartTime; + } + + public override String ToString() + { + return super.toString() + " SessionInfo{ " + + "clientId = '" + clientId + "' " + + ", sessionId = '" + sessionId + "' " + + ", startTime = " + startTime + + ", started = " + started + + " }"; + } + } + +} diff --git a/openwire-dotnet/comms-library/amqnet/TransactionConstants.cs b/openwire-dotnet/comms-library/amqnet/TransactionConstants.cs new file mode 100755 index 0000000000..5ce916416a --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/TransactionConstants.cs @@ -0,0 +1,68 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for TransactionConstants. + /// + public class TransactionType + { + + /** + * Transaction state not set + */ + const int NOT_SET = 0; + /** + * Start a transaction + */ + const int START = 101; + /** + * Pre-commit a transaction + */ + const int PRE_COMMIT = 102; + /** + * Commit a transaction + */ + const int COMMIT = 103; + /** + * Recover a transaction + */ + const int RECOVER = 104; + /** + * Rollback a transaction + */ + const int ROLLBACK = 105; + /** + * End a transaction + */ + const int END = 106; + /** + * Forget a transaction + */ + const int FORGET = 107; + /** + * Join a transaction + */ + const int JOIN = 108; + /** + * Do a one phase commit... No PRE COMMIT has been done. + */ + const int COMMIT_ONE_PHASE = 109; + /** + * Get a list of all the XIDs that are currently prepared. + */ + const int XA_RECOVER = 110; + /** + * Get a the transaction timeout for the RM + */ + const int GET_TX_TIMEOUT = 111; + /** + * Set a the transaction timeout for the RM + */ + const int SET_TX_TIMEOUT = 112; + /** + * Gets the unique id of the resource manager. + */ + const int GET_RM_ID = 113; + } +} diff --git a/openwire-dotnet/comms-library/amqnet/TransactionInfo.cs b/openwire-dotnet/comms-library/amqnet/TransactionInfo.cs new file mode 100755 index 0000000000..dd0faad7e1 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/TransactionInfo.cs @@ -0,0 +1,90 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for TransactionInfo. + /// + public class TransactionInfo : AbstractPacket { + + private int type; + private String transactionId; + + /** + * @return Returns the transactionId. + */ + public String getTransactionId() + { + return transactionId; + } + + /** + * @param transactionId The transactionId to set. + */ + public void setTransactionId(String transactionId) + { + this.transactionId = transactionId; + } + + /** + * Return the type of Packet + * + * @return integer representation of the type of Packet + */ + + public new int getPacketType() + { + return TRANSACTION_INFO; + } + + + /** + * Test for equality + * + * @param obj object to test + * @return true if equivalent + */ + public bool equals(Object obj) + { + boolean result = false; + if (obj != null && obj is TransactionInfo) { + TransactionInfo info = (TransactionInfo) obj; + result = this.transactionId == info.transactionId; + } + return result; + } + + /** + * @return hash code for instance + */ + public override int GetHashCode() + { + return this.transactionId != null ? this.transactionId.hashCode() : base.hashCode(); + } + + /** + * @return Returns the type of transacton command. + */ + public int getType() + { + return this.type; + } + + /** + * @param newType the type of transaction command The type to set. + */ + public void setType(int newType) + { + this.type = newType; + } + + public override String ToString() + { + return base.toString() + " TransactionInfo{ " + + "transactionId = '" + transactionId + "' " + + ", type = " + type + + " }"; + } + } + +} diff --git a/openwire-dotnet/comms-library/amqnet/WireFormatInfo.cs b/openwire-dotnet/comms-library/amqnet/WireFormatInfo.cs new file mode 100755 index 0000000000..92cde75974 --- /dev/null +++ b/openwire-dotnet/comms-library/amqnet/WireFormatInfo.cs @@ -0,0 +1,55 @@ +using System; + +namespace ActiveMQ +{ + /// + /// Summary description for WireFormatInfo. + /// + public class WireFormatInfo : AbstractPacket + { + + public char[] MAGIC = new char[]{'A','c','t','i','v','e','M','Q'}; + + int version; + + + /** + * Return the type of Packet + * + * @return integer representation of the type of Packet + */ + + public new int getPacketType() + { + return WIRE_FORMAT_INFO; + } + + /** + * @return pretty print + */ + public override String ToString() + { + return super.toString() + " WireFormatInfo{ " + + "version = " + version + + " }"; + } + + + + /** + * @return Returns the version. + */ + public int getVersion() + { + return version; + } + /** + * @param version The version to set. + */ + public void setVersion(int version) + { + this.version = version; + } + } + +} diff --git a/pom.xml b/pom.xml new file mode 100755 index 0000000000..c7aa111818 --- /dev/null +++ b/pom.xml @@ -0,0 +1,384 @@ + + + 4.0.0 + activemq + activemq + 4.0-SNAPSHOT + ActiveMQ JMS Broker Project + + + + + + + codehaus + Codehaus Repository + http://dist.codehaus.org + legacy + + + + true + + codehaus + Ibiblio Repository + http://www.ibiblio.org/maven + legacy + + + apache + Apche Repository + http://cvs.apache.org/repository + legacy + + + + true + + central + Maven Repository Switchboard + http://repo1.maven.org/maven2 + + + + + + snapshots + Maven Central Plugins Development Repository + http://snapshots.maven.codehaus.org/maven2 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.4 + 1.4 + + + + org.apache.maven.plugins + maven-eclipse-plugin + + bin + + + + org.apache.maven.plugins + maven-surefire-plugin + + pertest + + + + + + + activemq-core + activemq-ra + activemq-jaas + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + scm:svn:http://svn.apache.org/repos/asf/maven/components/tags/maven-2.0-beta-1/ + scm:svn:https://svn.apache.org/repos/asf/maven/components/tags/maven-2.0-beta-1/ + http://svn.apache.org/viewcvs.cgi/maven/components/tags/maven-2.0-beta-1/ + + + + LogicBlaze Inc. + http://www.logicblaze.com/ + + + + + + + + + + activeio + activeio + 2.1 + + + + backport-util-concurrent + backport-util-concurrent + 2.0_01_pd + + + + commons-logging + commons-logging + 1.0.3 + + + + geronimo-spec + geronimo-spec-jms + 1.1-rc4 + + + geronimo-spec + geronimo-spec-jta + 1.0.1B-rc4 + + + geronimo-spec + geronimo-spec-j2ee-management + 1.0-rc4 + + + geronimo-spec + geronimo-spec-j2ee-jacc + 1.0-rc4 + + + geronimo-spec + geronimo-spec-j2ee-connector + 1.5-rc4 + + + + + + + + xbean + xbean-spring + 2.0-SNAPSHOT + + + + + log4j + log4j + 1.2.8 + runtime + + + + + + + commons-collections + commons-collections + 2.1 + + + + + springframework + spring + 1.2.4 + + + + + activecluster + activecluster + 1.1-SNAPSHOT + + + + + org.apache.derby + derby + 10.1.1.0 + + + + + axion + axion + 1.0-M3-dev + + + commons-primitives + commons-primitives + 1.0 + + + regexp + regexp + 1.3 + + + + + + + commons-pool + commons-pool + 1.2 + + + + + + + + activemq + smack + 1.5.0 + + + activemq + smackx + 1.5.0 + + + + + + + + xmlbeans + xbean + 2.0.0-beta1 + + + xmlbeans + xmlpublic + 2.0.0-beta1 + + + xmlbeans + xbean_xpath + 2.0.0-beta1 + + + + + stax + stax-api + 1.0 + + + stax + stax + 1.1.1-dev + + + + + activesoap + jaxp-api + 1.3 + + + + + xalan + xalan + 2.6.0 + + + + activemq + jmdns + 1.0-RC2 + + + mx4j + mx4j + 2.1.1 + + + mx4j + mx4j-jmx + 2.1.1 + + + mx4j + mx4j-remote + 2.1.1 + + + mx4j + mx4j-tools + 2.1.1 + + + mx4j + mx4j-impl + 2.1.1 + + + + activemq + activemq-jaas + ${project.version} + + + + + + + + + + + + junit + junit + 3.8.1 + test + + + jmock + jmock + 1.0.1 + test + + + jmock + jmock-cglib + 1.0.1 + test + + + cglib + cglib-full + 2.0 + test + + + + + diff --git a/project.properties b/project.properties new file mode 100755 index 0000000000..18a232a77e --- /dev/null +++ b/project.properties @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.repo.remote=\ +http://dist.codehaus.org,\ +http://www.ibiblio.org/maven,\ +http://cvs.apache.org/repository + +maven.multiproject.includes=\ +activemq-core/project.xml,\ +activemq-jaas/project.xml,\ +activemq-ra/project.xml,\ +activemq-web/project.xml,\ +assembly/project.xml +#activemq-systest/project.xml,\ + +maven.multiproject.excludes=\ +etc/project.xml + +maven.jar.mainclass=org.activemq.broker.impl.Main +maven.remote.group=activemq + +# ------------------------------------------------------------------- +# ChangeLog Properties (Reports) +# ------------------------------------------------------------------- +maven.changelog.factory=org.apache.maven.svnlib.SvnChangeLogFactory +maven.changelog.range=7 diff --git a/project.xml b/project.xml new file mode 100755 index 0000000000..a55547c760 --- /dev/null +++ b/project.xml @@ -0,0 +1,121 @@ + + + + + + + ActiveMQ + activemq + activemq + 4.0-SNAPSHOT + + + + + + + + + James Strachan + jstrachan + jstrachan@logicblaze.com + + + Hiram Chirino + chirino + hiram@logicblaze.com + + + Rob Davies + stewie + rajdavies@exist.com + + + Jonas Lim + jlim + jlim@exist.com + + + Frederick Oconer + foconer + foconer@exist.com + + + Joseph Gapuz + jgapuz + jgapuz@exist.com + + + Patrick Villacorta + pvillacorta + pvillacorta@exist.com + + + Darwin Flores + dflores + dflores@exist.com + + + Merwin Yap + myap + myap@exist.com + + + Adrian Co + aco + aco@exist.com + + + Dennis Cook + dcook + dennis@bevocal.com + + + Dag Liodden + daggerrz + dag.liodden@giantleap.no + + + Peter Brooke + pbrooke + + + Ramzi Saba + rsaba + rsaba@optaros.com + + + Brian McCallister + brianm + brianm@apache.org + + + + + maven-developer-activity-plugin + + + + + + scm:svn:svn://svn.activemq.org/activemq/scm/trunk/activemq/ + scm:svn:svn://svn.activemq.org/activemq/trunk/activemq/ + https://svn.activemq.org/activemq/trunk/activemq/ + + + diff --git a/sandbox/README b/sandbox/README new file mode 100755 index 0000000000..851262b56f --- /dev/null +++ b/sandbox/README @@ -0,0 +1 @@ +This directory tree contains experimental source code which one day could be integrated into the main branch \ No newline at end of file diff --git a/sandbox/jabber/.cvsignore b/sandbox/jabber/.cvsignore new file mode 100755 index 0000000000..fce77b76e5 --- /dev/null +++ b/sandbox/jabber/.cvsignore @@ -0,0 +1,8 @@ +target +.classpath +.project +*.iws +*.ipr +*.iml +build.properties + diff --git a/sandbox/jabber/project.properties b/sandbox/jabber/project.properties new file mode 100755 index 0000000000..413af37bb2 --- /dev/null +++ b/sandbox/jabber/project.properties @@ -0,0 +1,4 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=jar diff --git a/sandbox/jabber/project.xml b/sandbox/jabber/project.xml new file mode 100755 index 0000000000..4e6363c0a3 --- /dev/null +++ b/sandbox/jabber/project.xml @@ -0,0 +1,41 @@ + + + + 3 + ${basedir}/../../etc/project.xml + + ActiveMQ :: Transport :: Jabber + activemq-transport-jabber + ActiveMQ Jabber Message Transport + ActiveMQ Jabber Message Transport + + org.activemq.transport.jabber + + + Jabber Message Transport + org.activemq.transport.jabber + + + + + + + + + + activemq + activemq-core + ${pom.currentVersion} + + true + + + + activemq + activemq-core-test + ${pom.currentVersion} + + + + + diff --git a/sandbox/jabber/src/java/org/activemq/transport/jabber/JabberWireFormat.java b/sandbox/jabber/src/java/org/activemq/transport/jabber/JabberWireFormat.java new file mode 100755 index 0000000000..ac56565961 --- /dev/null +++ b/sandbox/jabber/src/java/org/activemq/transport/jabber/JabberWireFormat.java @@ -0,0 +1,193 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jabber; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.jms.JMSException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.activemq.io.AbstractWireFormat; +import org.activemq.io.WireFormat; +import org.activemq.message.ActiveMQBytesMessage; +import org.activemq.message.ActiveMQMessage; +import org.activemq.message.ActiveMQObjectMessage; +import org.activemq.message.ActiveMQTextMessage; +import org.activemq.message.Packet; +import org.activemq.io.util.ByteArray; + +/** + * A wire format which uses XMPP format of messages + * + * @version $Revision: 1.1 $ + */ +public class JabberWireFormat extends AbstractWireFormat { + private static final Log log = LogFactory.getLog(JabberWireFormat.class); + + public WireFormat copy() { + return new JabberWireFormat(); + } + + public Packet readPacket(DataInput in) throws IOException { + return null; /** TODO */ + } + + public Packet readPacket(int firstByte, DataInput in) throws IOException { + return null; /** TODO */ + } + + public Packet writePacket(Packet packet, DataOutput out) throws IOException, JMSException { + switch (packet.getPacketType()) { + case Packet.ACTIVEMQ_MESSAGE: + writeMessage((ActiveMQMessage) packet, "", out); + break; + + case Packet.ACTIVEMQ_TEXT_MESSAGE: + writeTextMessage((ActiveMQTextMessage) packet, out); + break; + + case Packet.ACTIVEMQ_BYTES_MESSAGE: + writeBytesMessage((ActiveMQBytesMessage) packet, out); + break; + + case Packet.ACTIVEMQ_OBJECT_MESSAGE: + writeObjectMessage((ActiveMQObjectMessage) packet, out); + break; + + case Packet.ACTIVEMQ_MAP_MESSAGE: + case Packet.ACTIVEMQ_STREAM_MESSAGE: + + + case Packet.ACTIVEMQ_BROKER_INFO: + case Packet.ACTIVEMQ_CONNECTION_INFO: + case Packet.ACTIVEMQ_MSG_ACK: + case Packet.CONSUMER_INFO: + case Packet.DURABLE_UNSUBSCRIBE: + case Packet.INT_RESPONSE_RECEIPT_INFO: + case Packet.PRODUCER_INFO: + case Packet.RECEIPT_INFO: + case Packet.RESPONSE_RECEIPT_INFO: + case Packet.SESSION_INFO: + case Packet.TRANSACTION_INFO: + case Packet.XA_TRANSACTION_INFO: + default: + log.warn("Ignoring message type: " + packet.getPacketType() + " packet: " + packet); + } + return null; + } + + /** + * Can this wireformat process packets of this version + * @param version the version number to test + * @return true if can accept the version + */ + public boolean canProcessWireFormatVersion(int version){ + return true; + } + + /** + * @return the current version of this wire format + */ + public int getCurrentWireFormatVersion(){ + return 1; + } + + // Implementation methods + //------------------------------------------------------------------------- + protected void writeObjectMessage(ActiveMQObjectMessage message, DataOutput out) throws JMSException, IOException { + Serializable object = message.getObject(); + String text = (object != null) ? object.toString() : ""; + writeMessage(message, text, out); + } + + protected void writeTextMessage(ActiveMQTextMessage message, DataOutput out) throws JMSException, IOException { + writeMessage(message, message.getText(), out); + } + + protected void writeBytesMessage(ActiveMQBytesMessage message, DataOutput out) throws IOException { + ByteArray data = message.getBodyAsBytes(); + String text = encodeBinary(data.getBuf(),data.getOffset(),data.getLength()); + writeMessage(message, text, out); + } + + protected void writeMessage(ActiveMQMessage message, String body, DataOutput out) throws IOException { + String type = getXmppType(message); + + StringBuffer buffer = new StringBuffer("<"); + buffer.append(type); + buffer.append(" to='"); + buffer.append(message.getJMSDestination().toString()); + buffer.append("' from='"); + buffer.append(message.getJMSReplyTo().toString()); + String messageID = message.getJMSMessageID(); + if (messageID != null) { + buffer.append("' id='"); + buffer.append(messageID); + } + + HashMap properties = message.getProperties(); + if (properties != null) { + for (Iterator iter = properties.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry = (Map.Entry) iter.next(); + Object key = entry.getKey(); + Object value = entry.getValue(); + if (value != null) { + buffer.append("' "); + buffer.append(key.toString()); + buffer.append("='"); + buffer.append(value.toString()); + } + } + } + + buffer.append("'>"); + + String id = message.getJMSCorrelationID(); + if (id != null) { + buffer.append(""); + buffer.append(id); + buffer.append(""); + } + buffer.append(body); + buffer.append(""); + + out.write(buffer.toString().getBytes()); + } + + protected String encodeBinary(byte[] data,int offset,int length) { + // TODO + throw new RuntimeException("Not implemented yet!"); + } + + protected String getXmppType(ActiveMQMessage message) { + String type = message.getJMSType(); + if (type == null) { + type = "message"; + } + return type; + } +} diff --git a/sandbox/jabber/src/test/org/activemq/transport/jabber/JabberWireFormatTest.java b/sandbox/jabber/src/test/org/activemq/transport/jabber/JabberWireFormatTest.java new file mode 100755 index 0000000000..072845d5d2 --- /dev/null +++ b/sandbox/jabber/src/test/org/activemq/transport/jabber/JabberWireFormatTest.java @@ -0,0 +1,50 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jabber; + +import junit.framework.TestCase; +import org.activemq.io.WireFormat; +import org.activemq.message.ActiveMQTextMessage; +import org.activemq.message.ActiveMQTopic; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; + +/** + * @version $Revision: 1.1 $ + */ +public class JabberWireFormatTest extends TestCase { + protected WireFormat format = new JabberWireFormat(); + + public void testWrite() throws Exception { + ActiveMQTextMessage message = new ActiveMQTextMessage(); + message.setJMSType("id"); + message.setJMSReplyTo(new ActiveMQTopic("my.source")); + message.setJMSDestination(new ActiveMQTopic("my.target")); + message.setJMSCorrelationID("abc123"); + message.setText("hello there "); + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(buffer); + format.writePacket(message, out); + out.close(); + + byte[] data = buffer.toByteArray(); + System.out.println(new String(data)); + } +} diff --git a/sandbox/jrms/.classpath b/sandbox/jrms/.classpath new file mode 100644 index 0000000000..b2101b07d3 --- /dev/null +++ b/sandbox/jrms/.classpath @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sandbox/jrms/.project b/sandbox/jrms/.project new file mode 100644 index 0000000000..2b1c079f5b --- /dev/null +++ b/sandbox/jrms/.project @@ -0,0 +1,19 @@ + + + + activemq-transport-jrms + ActiveMQ JRMS Message Transport + + activemq-core + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + \ No newline at end of file diff --git a/sandbox/jrms/project.properties b/sandbox/jrms/project.properties new file mode 100755 index 0000000000..f9e42ed3f3 --- /dev/null +++ b/sandbox/jrms/project.properties @@ -0,0 +1,5 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=jar +maven.eclipse.classpath.include=${basedir}/src/conf diff --git a/sandbox/jrms/project.xml b/sandbox/jrms/project.xml new file mode 100755 index 0000000000..350af1ae9a --- /dev/null +++ b/sandbox/jrms/project.xml @@ -0,0 +1,84 @@ + + + + 3 + ${basedir}/../../etc/project.xml + + ActiveMQ :: Transport :: JRMS + activemq-transport-jrms + ActiveMQ JRMS Message Transport + ActiveMQ JRMS Message Transport + + org.activemq.transport.jrms + + + JRMS Message Transport + org.activemq.transport.jrms + + + + + + + + + + activemq + activemq-core + ${pom.currentVersion} + + true + + + + activemq + activemq-core-test + ${pom.currentVersion} + + + + jrms + jrms + ${jrms_version} + + + + + + dev@activemq.codehaus.org + src/java + src/test + + + + + + + + src/test + + **/*.properties + **/*.xml + + + + + **/*Test.* + + + + **/jgroups/*Test.* + + + + + + src/conf + + **/* + + + + + + diff --git a/sandbox/jrms/src/java/org/activemq/transport/jrms/JRMSTransportChannel.java b/sandbox/jrms/src/java/org/activemq/transport/jrms/JRMSTransportChannel.java new file mode 100755 index 0000000000..5c34f99f74 --- /dev/null +++ b/sandbox/jrms/src/java/org/activemq/transport/jrms/JRMSTransportChannel.java @@ -0,0 +1,241 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jrms; + +import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean; +import com.sun.multicast.reliable.RMException; +import com.sun.multicast.reliable.transport.RMPacketSocket; +import com.sun.multicast.reliable.transport.SessionDoneException; +import com.sun.multicast.reliable.transport.TransportProfile; +import com.sun.multicast.reliable.transport.lrmp.LRMPTransportProfile; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.activemq.io.WireFormat; +import org.activemq.message.Packet; +import org.activemq.transport.TransportChannelSupport; +import org.activemq.util.IdGenerator; + +import javax.jms.JMSException; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.net.URI; + +/** + * A JRMS implementation of a TransportChannel + * + * @version $Revision$ + */ +public class JRMSTransportChannel extends TransportChannelSupport implements Runnable { + + private static final int SOCKET_BUFFER_SIZE = 32 * 1024; + private static final Log log = LogFactory.getLog(JRMSTransportChannel.class); + + private WireFormat wireFormat; + private SynchronizedBoolean closed; + private SynchronizedBoolean started; + private Thread thread; //need to change this - and use a thread pool + // need to see our own messages + private RMPacketSocket socket; + private IdGenerator idGenerator; + private String channelId; + private int port; + private InetAddress inetAddress; + private Object lock; + + /** + * Construct basic helpers + */ + protected JRMSTransportChannel(WireFormat wireFormat) { + this.wireFormat = wireFormat; + idGenerator = new IdGenerator(); + channelId = idGenerator.generateId(); + closed = new SynchronizedBoolean(false); + started = new SynchronizedBoolean(false); + lock = new Object(); + } + + /** + * Connect to a remote Node - e.g. a Broker + * + * @param remoteLocation + * @throws JMSException + */ + public JRMSTransportChannel(WireFormat wireFormat, URI remoteLocation) throws JMSException { + this(wireFormat); + try { + this.port = remoteLocation.getPort(); + this.inetAddress = InetAddress.getByName(remoteLocation.getHost()); + LRMPTransportProfile profile = new LRMPTransportProfile(inetAddress, port); + profile.setTTL((byte) 1); + profile.setOrdered(true); + this.socket = profile.createRMPacketSocket(TransportProfile.SEND_RECEIVE); + } + catch (Exception ioe) { + ioe.printStackTrace(); + JMSException jmsEx = new JMSException("Initialization of JRMSTransportChannel failed: " + ioe); + jmsEx.setLinkedException(ioe); + throw jmsEx; + } + } + + /** + * close the channel + */ + public void stop() { + if (closed.commit(false, true)) { + super.stop(); + try { + socket.close(); + } + catch (Exception e) { + log.trace(toString() + " now closed"); + } + } + } + + /** + * start listeneing for events + * + * @throws JMSException if an error occurs + */ + public void start() throws JMSException { + if (started.commit(false, true)) { + thread = new Thread(this, toString()); + if (isServerSide()) { + thread.setDaemon(true); + } + thread.start(); + } + } + + /** + * Asynchronously send a Packet + * + * @param packet + * @throws JMSException + */ + public void asyncSend(Packet packet) throws JMSException { + try { + DatagramPacket dpacket = createDatagramPacket(packet); + + // lets sync to avoid concurrent writes + //synchronized (lock) { + socket.send(dpacket); + //} + } + catch (RMException rme) { + JMSException jmsEx = new JMSException("syncSend failed " + rme.getMessage()); + jmsEx.setLinkedException(rme); + throw jmsEx; + } + catch (IOException e) { + JMSException jmsEx = new JMSException("asyncSend failed " + e.getMessage()); + jmsEx.setLinkedException(e); + throw jmsEx; + } + } + + + public boolean isMulticast() { + return true; + } + + /** + * reads packets from a Socket + */ + public void run() { + try { + while (!closed.get()) { + DatagramPacket dpacket = socket.receive(); + Packet packet = wireFormat.readPacket(channelId, dpacket); + if (packet != null) { + doConsumePacket(packet); + } + } + log.trace("The socket peer is now closed"); + //doClose(new IOException("Socket peer is now closed")); + stop(); + } + catch (SessionDoneException e) { + // this isn't really an exception, it just indicates + // that the socket has closed normally + log.trace("Session completed", e); + stop(); + } + catch (RMException ste) { + doClose(ste); + } + catch (IOException e) { + doClose(e); + } + } + + /** + * Can this wireformat process packets of this version + * @param version the version number to test + * @return true if can accept the version + */ + public boolean canProcessWireFormatVersion(int version){ + return wireFormat.canProcessWireFormatVersion(version); + } + + /** + * @return the current version of this wire format + */ + public int getCurrentWireFormatVersion(){ + return wireFormat.getCurrentWireFormatVersion(); + } + + protected DatagramPacket createDatagramPacket() { + DatagramPacket answer = new DatagramPacket(new byte[SOCKET_BUFFER_SIZE], SOCKET_BUFFER_SIZE); + answer.setPort(port); + answer.setAddress(inetAddress); + return answer; + } + + protected DatagramPacket createDatagramPacket(Packet packet) throws IOException, JMSException { + DatagramPacket answer = wireFormat.writePacket(channelId, packet); + answer.setPort(port); + answer.setAddress(inetAddress); + return answer; + } + + private void doClose(Exception ex) { + if (!closed.get()) { + JMSException jmsEx = new JMSException("Error reading socket: " + ex); + jmsEx.setLinkedException(ex); + onAsyncException(jmsEx); + stop(); + } + } + + /** + * pretty print for object + * + * @return String representation of this object + */ + public String toString() { + return "JRMSTransportChannel: " + socket; + } + + public void forceDisconnect() { + // TODO: implement me. + throw new RuntimeException("Not yet Implemented."); + } +} \ No newline at end of file diff --git a/sandbox/jrms/src/java/org/activemq/transport/jrms/JRMSTransportChannelFactory.java b/sandbox/jrms/src/java/org/activemq/transport/jrms/JRMSTransportChannelFactory.java new file mode 100755 index 0000000000..ef227145e4 --- /dev/null +++ b/sandbox/jrms/src/java/org/activemq/transport/jrms/JRMSTransportChannelFactory.java @@ -0,0 +1,64 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jrms; + +import org.activemq.io.WireFormat; +import org.activemq.transport.TransportChannel; +import org.activemq.transport.TransportChannelFactorySupport; + +import javax.jms.JMSException; +import java.net.URI; + +/** + * A JRMS implementation of a TransportChannelFactory + * + * @version $Revision$ + */ +public class JRMSTransportChannelFactory extends TransportChannelFactorySupport { + + /** + * Create a Channel to a remote Node - e.g. a Broker + * + * @param wireFormat + * @param remoteLocation + * @return the TransportChannel bound to the remote node + * @throws JMSException + */ + public TransportChannel create(WireFormat wireFormat, URI remoteLocation) throws JMSException { + return populateProperties(new JRMSTransportChannel(wireFormat, remoteLocation), remoteLocation); + } + + /** + * Create a Channel to a remote Node - e.g. a Broker + * + * @param wireFormat + * @param remoteLocation + * @param localLocation - + * e.g. local InetAddress and local port + * @return the TransportChannel bound to the remote node + * @throws JMSException + */ + public TransportChannel create(WireFormat wireFormat, URI remoteLocation, URI localLocation) throws JMSException { + return create(wireFormat, remoteLocation); + } + + public boolean requiresEmbeddedBroker() { + return true; + } + +} \ No newline at end of file diff --git a/sandbox/jrms/src/java/org/activemq/transport/jrms/JRMSTransportServerChannel.java b/sandbox/jrms/src/java/org/activemq/transport/jrms/JRMSTransportServerChannel.java new file mode 100755 index 0000000000..e11bc78ab7 --- /dev/null +++ b/sandbox/jrms/src/java/org/activemq/transport/jrms/JRMSTransportServerChannel.java @@ -0,0 +1,65 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jrms; + +import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.activemq.io.WireFormat; +import org.activemq.transport.TransportServerChannelSupport; + +import javax.jms.JMSException; +import java.net.URI; + +/** + * A JRMS implementation of TransportServerChannel + * + * @version $Revision$ + */ +public class JRMSTransportServerChannel extends TransportServerChannelSupport { + + private static final Log log = LogFactory.getLog(JRMSTransportServerChannel.class); + + private SynchronizedBoolean started; + + + public JRMSTransportServerChannel(WireFormat wireFormat, URI bindAddr) { + super(bindAddr); + started = new SynchronizedBoolean(false); + } + + + /** + * start listeneing for events + * + * @throws JMSException if an error occurs + */ + public void start() throws JMSException { + if (started.commit(false, true)) { + log.info("JRMS ServerChannel at: " + getUrl()); + } + } + + + /** + * @return pretty print of this + */ + public String toString() { + return "JRMSTransportServerChannel@" + getUrl(); + } +} \ No newline at end of file diff --git a/sandbox/jrms/src/java/org/activemq/transport/jrms/JRMSTransportServerChannelFactory.java b/sandbox/jrms/src/java/org/activemq/transport/jrms/JRMSTransportServerChannelFactory.java new file mode 100755 index 0000000000..3b5453b405 --- /dev/null +++ b/sandbox/jrms/src/java/org/activemq/transport/jrms/JRMSTransportServerChannelFactory.java @@ -0,0 +1,46 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jrms; + +import org.activemq.io.WireFormat; +import org.activemq.transport.TransportServerChannel; +import org.activemq.transport.TransportServerChannelFactory; + +import javax.jms.JMSException; +import java.net.URI; + +/** + * A multicast implementation of a TransportServerChannelFactory + * + * @version $Revision$ + */ +public class JRMSTransportServerChannelFactory implements TransportServerChannelFactory { + + /** + * Bind a ServerChannel to an address + * + * @param wireFormat + * @param bindAddress + * @return the TransportChannel bound to the remote node + * @throws JMSException + */ + public TransportServerChannel create(WireFormat wireFormat, URI bindAddress) throws JMSException { + return new JRMSTransportServerChannel(wireFormat, bindAddress); + } + +} \ No newline at end of file diff --git a/sandbox/jrms/src/java/org/activemq/transport/jrms/package.html b/sandbox/jrms/src/java/org/activemq/transport/jrms/package.html new file mode 100755 index 0000000000..158559f1ff --- /dev/null +++ b/sandbox/jrms/src/java/org/activemq/transport/jrms/package.html @@ -0,0 +1,11 @@ + + + + + +

+ An implementation of the transport layer using Sun's reliable multicast library +

+ + + diff --git a/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSQueueSendReceiveTest.java b/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSQueueSendReceiveTest.java new file mode 100755 index 0000000000..db77321d1f --- /dev/null +++ b/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSQueueSendReceiveTest.java @@ -0,0 +1,31 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jrms; + + +/** + * @version $Revision$ + */ +public class JRMSQueueSendReceiveTest extends JRMSTopicSendReceiveTest { + + protected void setUp() throws Exception { + topic = false; + super.setUp(); + } + +} \ No newline at end of file diff --git a/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSQueueSendReceiveTwoConnectionsTest.java b/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSQueueSendReceiveTwoConnectionsTest.java new file mode 100755 index 0000000000..3d40a51d88 --- /dev/null +++ b/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSQueueSendReceiveTwoConnectionsTest.java @@ -0,0 +1,31 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jrms; + + +/** + * @version $Revision$ + */ +public class JRMSQueueSendReceiveTwoConnectionsTest extends JRMSTopicSendReceiveTwoConnectionsTest { + + protected void setUp() throws Exception { + topic = false; + super.setUp(); + } + +} \ No newline at end of file diff --git a/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSTopicSendReceiveTest.java b/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSTopicSendReceiveTest.java new file mode 100755 index 0000000000..c00d54b347 --- /dev/null +++ b/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSTopicSendReceiveTest.java @@ -0,0 +1,34 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jrms; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.test.JmsTopicSendReceiveWithTwoConnectionsTest; + +/** + * @version $Revision$ + */ +public class JRMSTopicSendReceiveTest extends JmsTopicSendReceiveWithTwoConnectionsTest { + + protected ActiveMQConnectionFactory createConnectionFactory() { + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(); + factory.setBrokerURL("jrms://224.1.2.3:5123"); + return factory; + + } +} \ No newline at end of file diff --git a/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSTopicSendReceiveTwoConnectionsTest.java b/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSTopicSendReceiveTwoConnectionsTest.java new file mode 100755 index 0000000000..31f22ef455 --- /dev/null +++ b/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSTopicSendReceiveTwoConnectionsTest.java @@ -0,0 +1,34 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jrms; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.test.JmsTopicSendReceiveWithTwoConnectionsTest; + +/** + * @version $Revision$ + */ +public class JRMSTopicSendReceiveTwoConnectionsTest extends JmsTopicSendReceiveWithTwoConnectionsTest { + + protected ActiveMQConnectionFactory createConnectionFactory() { + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(); + factory.setBrokerURL("jrms://224.1.2.3:5123"); + return factory; + + } +} \ No newline at end of file diff --git a/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSTransportChannelTest.java b/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSTransportChannelTest.java new file mode 100755 index 0000000000..537877f2b7 --- /dev/null +++ b/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSTransportChannelTest.java @@ -0,0 +1,42 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jrms; + +import junit.textui.TestRunner; + +import org.activemq.transport.TransportChannelTestSupport; + +/** + * @version $Revision$ + */ +public class JRMSTransportChannelTest extends TransportChannelTestSupport { + + public static void main(String[] args) { + TestRunner.run(JRMSTransportChannelTest.class); + } + + public JRMSTransportChannelTest(String name) { + super(name); + } + + + protected void setUp() throws Exception { + createSenderAndReceiver("jrms://228.5.6.7:6677"); + super.setUp(); + } +} \ No newline at end of file diff --git a/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSTwoBrokerTest.java b/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSTwoBrokerTest.java new file mode 100755 index 0000000000..cf5fd410ac --- /dev/null +++ b/sandbox/jrms/src/test/org/activemq/transport/jrms/JRMSTwoBrokerTest.java @@ -0,0 +1,29 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jrms; + +import org.activemq.transport.multicast.MulticastTwoBrokerTest; + +/** + * @version $Revision$ + */ +public class JRMSTwoBrokerTest extends MulticastTwoBrokerTest { + protected String getBrokerURL() { + return "jrms://228.5.6.7:6677"; + } +} diff --git a/sandbox/jxta/.cvsignore b/sandbox/jxta/.cvsignore new file mode 100755 index 0000000000..fce77b76e5 --- /dev/null +++ b/sandbox/jxta/.cvsignore @@ -0,0 +1,8 @@ +target +.classpath +.project +*.iws +*.ipr +*.iml +build.properties + diff --git a/sandbox/jxta/project.properties b/sandbox/jxta/project.properties new file mode 100755 index 0000000000..f9e42ed3f3 --- /dev/null +++ b/sandbox/jxta/project.properties @@ -0,0 +1,5 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=jar +maven.eclipse.classpath.include=${basedir}/src/conf diff --git a/sandbox/jxta/project.xml b/sandbox/jxta/project.xml new file mode 100755 index 0000000000..ab81223bd4 --- /dev/null +++ b/sandbox/jxta/project.xml @@ -0,0 +1,53 @@ + + + + 3 + ${basedir}/../../etc/project.xml + + ActiveMQ :: Transport :: JXTA + activemq-transport-jxta + ActiveMQ JXTA Message Transport + ActiveMQ JXTA Message Transport + + org.activemq.transport.jxta + + + JXTA Message Transport + org.activemq.transport.jxta + + + + + + + + + + activemq + activemq-core + ${pom.currentVersion} + + true + + + + activemq + activemq-core-test + ${pom.currentVersion} + + + + p2psockets + p2psockets-core + ${p2psockets_core_version} + + + + jxta + jxta + ${jxta_version} + + + + + diff --git a/sandbox/jxta/src/conf/META-INF/services/org.apache.axis.components.jms.JMSVendorAdapter b/sandbox/jxta/src/conf/META-INF/services/org.apache.axis.components.jms.JMSVendorAdapter new file mode 100755 index 0000000000..c22d6d641b --- /dev/null +++ b/sandbox/jxta/src/conf/META-INF/services/org.apache.axis.components.jms.JMSVendorAdapter @@ -0,0 +1 @@ +org.activemq.axis.ActiveMQVendorAdapter diff --git a/sandbox/jxta/src/conf/META-INF/services/org/codehaus/activemq/transport/jxta b/sandbox/jxta/src/conf/META-INF/services/org/codehaus/activemq/transport/jxta new file mode 100755 index 0000000000..283fab7af1 --- /dev/null +++ b/sandbox/jxta/src/conf/META-INF/services/org/codehaus/activemq/transport/jxta @@ -0,0 +1 @@ +org.activemq.transport.jxta.JxtaTransportChannelFactory \ No newline at end of file diff --git a/sandbox/jxta/src/conf/META-INF/services/org/codehaus/activemq/transport/server/jxta b/sandbox/jxta/src/conf/META-INF/services/org/codehaus/activemq/transport/server/jxta new file mode 100755 index 0000000000..fe4b260956 --- /dev/null +++ b/sandbox/jxta/src/conf/META-INF/services/org/codehaus/activemq/transport/server/jxta @@ -0,0 +1 @@ +org.activemq.transport.jxta.JxtaTransportServerChannelFactory \ No newline at end of file diff --git a/sandbox/jxta/src/java/org/activemq/transport/jxta/JxtaTransportChannel.java b/sandbox/jxta/src/java/org/activemq/transport/jxta/JxtaTransportChannel.java new file mode 100755 index 0000000000..c64ec666d9 --- /dev/null +++ b/sandbox/jxta/src/java/org/activemq/transport/jxta/JxtaTransportChannel.java @@ -0,0 +1,82 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jxta; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.activemq.io.WireFormat; +import org.activemq.transport.tcp.TcpTransportChannel; +import org.p2psockets.P2PInetAddress; +import org.p2psockets.P2PSocket; + +import javax.jms.JMSException; +import java.io.IOException; +import java.net.Socket; +import java.net.URI; +import java.net.UnknownHostException; + +/** + * A JXTA implementation of a TransportChannel + * + * @version $Revision: 1.1 $ + */ +public class JxtaTransportChannel extends TcpTransportChannel { + + private static final Log log = LogFactory.getLog(JxtaTransportChannel.class); + + /** + * Connect to a remote Node - e.g. a Broker + * + * @param remoteLocation + * @throws JMSException + */ + public JxtaTransportChannel(WireFormat wireFormat, URI remoteLocation) throws JMSException { + super(wireFormat, remoteLocation); + } + + /** + * Connect to a remote Node - e.g. a Broker + * + * @param remoteLocation + * @param localLocation - + * e.g. local InetAddress and local port + * @throws JMSException + */ + public JxtaTransportChannel(WireFormat wireFormat, URI remoteLocation, URI localLocation) throws JMSException { + super(wireFormat, localLocation, remoteLocation); + } + + /** + * pretty print for object + * + * @return String representation of this object + */ + public String toString() { + return "P2pTransportChannel: " + socket; + } + + protected Socket createSocket(URI remoteLocation) throws UnknownHostException, IOException { + return new P2PSocket(remoteLocation.getHost(), remoteLocation.getPort()); + } + + protected Socket createSocket(URI remoteLocation, URI localLocation) throws IOException, UnknownHostException { + return new P2PSocket(remoteLocation.getHost(), remoteLocation.getPort(), P2PInetAddress + .getByName(localLocation.getHost()), localLocation.getPort()); + } + +} \ No newline at end of file diff --git a/sandbox/jxta/src/java/org/activemq/transport/jxta/JxtaTransportChannelFactory.java b/sandbox/jxta/src/java/org/activemq/transport/jxta/JxtaTransportChannelFactory.java new file mode 100755 index 0000000000..c4602bcf69 --- /dev/null +++ b/sandbox/jxta/src/java/org/activemq/transport/jxta/JxtaTransportChannelFactory.java @@ -0,0 +1,63 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jxta; + +import org.activemq.io.WireFormat; +import org.activemq.transport.TransportChannel; +import org.activemq.transport.TransportChannelFactorySupport; + +import javax.jms.JMSException; +import java.net.URI; + +/** + * A JXTA implementation of a TransportChannelFactory + * + * @version $Revision: 1.1 $ + */ +public class JxtaTransportChannelFactory extends TransportChannelFactorySupport { + + /** + * Create a Channel to a remote Node - e.g. a Broker + * + * @param wireFormat + * @param remoteLocation + * @return the TransportChannel bound to the remote node + * @throws JMSException + */ + public TransportChannel create(WireFormat wireFormat, URI remoteLocation) throws JMSException { + return populateProperties(new JxtaTransportChannel(wireFormat, remoteLocation), remoteLocation); + } + + /** + * Create a Channel to a remote Node - e.g. a Broker + * + * @param wireFormat + * @param remoteLocation + * @param localLocation - + * e.g. local InetAddress and local port + * @return the TransportChannel bound to the remote node + * @throws JMSException + */ + public TransportChannel create(WireFormat wireFormat, URI remoteLocation, URI localLocation) throws JMSException { + return populateProperties(new JxtaTransportChannel(wireFormat, remoteLocation, localLocation), remoteLocation); + } + + public boolean requiresEmbeddedBroker() { + return false; + } +} \ No newline at end of file diff --git a/sandbox/jxta/src/java/org/activemq/transport/jxta/JxtaTransportServerChannel.java b/sandbox/jxta/src/java/org/activemq/transport/jxta/JxtaTransportServerChannel.java new file mode 100755 index 0000000000..3b75fdce19 --- /dev/null +++ b/sandbox/jxta/src/java/org/activemq/transport/jxta/JxtaTransportServerChannel.java @@ -0,0 +1,88 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jxta; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.activemq.io.WireFormat; +import org.activemq.transport.tcp.TcpTransportServerChannel; +import org.p2psockets.P2PInetAddress; +import org.p2psockets.P2PServerSocket; + +import javax.jms.JMSException; +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.URI; +import java.net.UnknownHostException; + +/** + * Binds to a well known port and listens for Sockets ... + * + * @version $Revision: 1.1 $ + */ +public class JxtaTransportServerChannel extends TcpTransportServerChannel { + + private static final Log log = LogFactory.getLog(JxtaTransportServerChannel.class); + + /** + * Default Constructor + * + * @param bindAddr + * @throws JMSException + */ + public JxtaTransportServerChannel(WireFormat wireFormat, URI bindAddr) throws JMSException { + super(wireFormat, bindAddr); + } + + /** + * @return pretty print of this + */ + public String toString() { + return "P2pTransportServerChannel@" + getUrl(); + } + + protected ServerSocket createServerSocket(URI bind) throws UnknownHostException, IOException { + ServerSocket answer = null; + String host = bind.getHost(); + + +// host = (host == null || host.length() == 0) ? "localhost" : host; +// +// System.out.println("About to lookup host: " + host); + + if (host == null || host.length() == 0 || host.equals("localhost")) { + InetAddress addr = P2PInetAddress.getLocalHost(); + answer = new P2PServerSocket(bind.getPort(), getBacklog(), addr); + } + else { + InetAddress addr = P2PInetAddress.getByName(host); + answer = new P2PServerSocket(bind.getPort(), getBacklog(), addr); + } + /* + if (addr.equals(P2PInetAddress.getLocalHost())) { + answer = new P2PServerSocket(bind.getPort(), BACKLOG); + } + else { + answer = new P2PServerSocket(bind.getPort(), BACKLOG, addr); + } + */ + //answer = new P2PServerSocket(bind.toString(), BACKLOG); + return answer; + } +} \ No newline at end of file diff --git a/sandbox/jxta/src/java/org/activemq/transport/jxta/JxtaTransportServerChannelFactory.java b/sandbox/jxta/src/java/org/activemq/transport/jxta/JxtaTransportServerChannelFactory.java new file mode 100755 index 0000000000..cb0a3d4922 --- /dev/null +++ b/sandbox/jxta/src/java/org/activemq/transport/jxta/JxtaTransportServerChannelFactory.java @@ -0,0 +1,46 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.transport.jxta; + +import org.activemq.io.WireFormat; +import org.activemq.transport.TransportServerChannel; +import org.activemq.transport.TransportServerChannelFactory; + +import javax.jms.JMSException; +import java.net.URI; + +/** + * A JXTA implementation of a TransportServerChannelFactory + * + * @version $Revision: 1.1 $ + */ +public class JxtaTransportServerChannelFactory implements TransportServerChannelFactory { + + /** + * Bind a ServerChannel to an address + * + * @param wireFormat + * @param bindAddress + * @return the TransportChannel bound to the remote node + * @throws JMSException + */ + public TransportServerChannel create(WireFormat wireFormat, URI bindAddress) throws JMSException { + return new JxtaTransportServerChannel(wireFormat, bindAddress); + } + +} \ No newline at end of file diff --git a/sandbox/jxta/src/java/org/activemq/transport/jxta/package.html b/sandbox/jxta/src/java/org/activemq/transport/jxta/package.html new file mode 100755 index 0000000000..f7ef3e7550 --- /dev/null +++ b/sandbox/jxta/src/java/org/activemq/transport/jxta/package.html @@ -0,0 +1,11 @@ + + + + + +

+ An implementation of the transport layer using JXTA P2PSockets for navigating firewalls and NATs +

+ + + diff --git a/systest/bdb/.cvsignore b/systest/bdb/.cvsignore new file mode 100755 index 0000000000..e769e7f7b1 --- /dev/null +++ b/systest/bdb/.cvsignore @@ -0,0 +1,8 @@ +*.log +junit*.properties +target +*.iws +*.ipr +*.iml +build.properties + diff --git a/systest/bdb/maven.xml b/systest/bdb/maven.xml new file mode 100755 index 0000000000..9140b52618 --- /dev/null +++ b/systest/bdb/maven.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/systest/bdb/project.properties b/systest/bdb/project.properties new file mode 100755 index 0000000000..382d17bc18 --- /dev/null +++ b/systest/bdb/project.properties @@ -0,0 +1,9 @@ +# +# Testing system properties +# +maven.junit.sysproperties = activemq.persistenceAdapter activemq.store.dir + +activemq.persistenceAdapter = org.activemq.store.bdb.BDbPersistenceAdapter +activemq.store.dir = ${basedir}/target/MessageStore + +maven.test.search.classdir = true diff --git a/systest/bdb/project.xml b/systest/bdb/project.xml new file mode 100755 index 0000000000..1eb313bd6b --- /dev/null +++ b/systest/bdb/project.xml @@ -0,0 +1,55 @@ + + + 3 + ${basedir}/../../base-project.xml + + ActiveMQ :: Berkeley DB System Test + activemq-systest-bdb + 1.0-SNAPSHOT + + + + activemq + ${pom.currentVersion} + + + + + + src + ../../../src/test + + + + + + + **/*.properties + **/*.xml + + + + + **/*Test.* + + + **/Ssl*Test.* + + + **/bdbn/*Test.* + + + **/ember/*Test.* + **/gnet/*Test.* + + + **/jrms/* + **/multicast/* + **/jgroups/* + + + + + + + diff --git a/systest/bdb/src/README.txt b/systest/bdb/src/README.txt new file mode 100755 index 0000000000..8a953a6960 --- /dev/null +++ b/systest/bdb/src/README.txt @@ -0,0 +1 @@ +This module runs the system tests for Berkeley DB \ No newline at end of file diff --git a/systest/itests/client/maven.xml b/systest/itests/client/maven.xml new file mode 100755 index 0000000000..0e014f5482 --- /dev/null +++ b/systest/itests/client/maven.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/systest/itests/client/project.properties b/systest/itests/client/project.properties new file mode 100755 index 0000000000..24ac78c8c0 --- /dev/null +++ b/systest/itests/client/project.properties @@ -0,0 +1,10 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=jar +maven.itest.includes=**/*Test.* +#maven.itest.excludes=**/Abstract*.java + +enable.debug=false + +maven.eclipse.classpath.include=${basedir}/src/itest \ No newline at end of file diff --git a/systest/itests/client/project.xml b/systest/itests/client/project.xml new file mode 100755 index 0000000000..bb2d73b2e6 --- /dev/null +++ b/systest/itests/client/project.xml @@ -0,0 +1,99 @@ + + + + 3 + ${basedir}/../../etc/project.xml + + ActiveMQ :: Integration Tests :: Client + activemq-itest-client + ActiveMQ is a JMS 1.1 implementation designed for easy deployment in Geronimo + + ActiveMQ is an open source JMS 1.1 implementation designed for easy deployment in Geronimo + + + org.activemq.itests + + + Integration Tests + org.activemq.itest.* + + + + + + + + commons-jelly + commons-jelly-tags-velocity + ${commons_jelly_tags_velocity_version} + + + velocity + velocity + ${velocity_version} + + + + geronimo-spec + geronimo-spec-ejb + ${geronimo_spec_ejb_version} + + + activemq + activemq + ${pom.currentVersion} + jar + + + activemq + activemq-itest-ear + ${pom.currentVersion} + ear + + + activemq + activemq-itest-ejb + ${pom.currentVersion} + ejb + + + + + geronimo + geronimo-deployment-plugin + ${geronimo_deployment_plugin_version} + plugin + + + maven-itest-plugin + maven-itest-plugin + ${maven_itest_plugin_version} + plugin + + + + + openejb + openejb-core + ${openejb_core_version} + + true + + + + + geronimo + geronimo-security + ${geronimo_security_version} + + + + cglib + cglib-full + ${cglib_full_version} + http://cglib.sf.net + + + + + diff --git a/systest/itests/client/src/itest/org/activemq/itests/ResourceAdapterTest.java b/systest/itests/client/src/itest/org/activemq/itests/ResourceAdapterTest.java new file mode 100755 index 0000000000..03d8f13716 --- /dev/null +++ b/systest/itests/client/src/itest/org/activemq/itests/ResourceAdapterTest.java @@ -0,0 +1,108 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activemq.itests; + +import java.rmi.RemoteException; +import java.util.HashSet; +import java.util.Hashtable; + +import javax.ejb.CreateException; +import javax.jms.JMSException; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import junit.framework.TestCase; + +import org.activemq.itest.ejb.JMSToolHome; +import org.activemq.itest.ejb.JMSTool; + +/** + * @version $Revision: 1.1 $ + */ +public class ResourceAdapterTest extends TestCase { + + private static final String JMSBEAN_JNDI = "org/activemq/itest/JMSToolBean"; + + private static final String TEST_QUEUE = "TestQueue"; + + private static final String TRANSFER_MDB_INPUT_QUEUE = "MDBInQueue"; + private static final String TRANSFER_MDB_OUTPUT_QUEUE = "MDBOutQueue"; + + + private JMSTool jmsTool; + + + protected void setUp() throws Exception { + InitialContext ctx = createInitialContext(); + JMSToolHome home = (JMSToolHome) ctx.lookup(JMSBEAN_JNDI); + jmsTool = home.create(); + } + + protected void tearDown() throws Exception { + if( jmsTool != null ) { + try { + jmsTool.drain(TEST_QUEUE); + } catch (Throwable e) { + } + } + } + + public void testSendReceiveMultiple() throws CreateException, RemoteException, NamingException, JMSException { + for( int i=0; i < 5; i++) { + String msg1 = "Test Send Receive:"+i; + jmsTool.sendTextMessage(TEST_QUEUE, msg1); + String msg2 = jmsTool.receiveTextMessage(TEST_QUEUE, 1000); + assertEquals("Message are not the same a iteration: "+i, msg1, msg2); + } + } + + /** + * The MDBTransferBean should be moving message from the input queue to the output queue. + * Check to see if message sent to it's input get to the output queue. + */ + public void testSendReceiveFromMDB() throws CreateException, RemoteException, NamingException, JMSException { + HashSet a = new HashSet(); + HashSet b = new HashSet(); + + for( int i=0; i < 5; i++) { + String msg1 = "Test Send Receive From MDB:"+i; + a.add(msg1); + jmsTool.sendTextMessage(TRANSFER_MDB_INPUT_QUEUE, msg1); + } + + for( int i=0; i < 5; i++) { + String msg2 = jmsTool.receiveTextMessage(TRANSFER_MDB_OUTPUT_QUEUE, 1000); + b.add(msg2); + } + + // Compare the messages using sets since they may be received out of order since, + // the MDB runns concurrent threads. + + assertEquals(a,b); + } + + private InitialContext createInitialContext() throws NamingException { + Hashtable props = new Hashtable(); + props.put("java.naming.factory.initial", "org.openejb.client.RemoteInitialContextFactory"); + props.put("java.naming.provider.url", "127.0.0.1:4201"); + props.put("java.naming.security.principal", "testuser"); + props.put("java.naming.security.credentials", "testpassword"); + return new InitialContext(props); + } + +} diff --git a/systest/itests/client/src/itest/org/activemq/itests/log4j.properties b/systest/itests/client/src/itest/org/activemq/itests/log4j.properties new file mode 100755 index 0000000000..fa3583a119 --- /dev/null +++ b/systest/itests/client/src/itest/org/activemq/itests/log4j.properties @@ -0,0 +1,24 @@ +log4j.rootLogger=info, stdout + + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout + +# Pattern to output the caller's file name and line number. +log4j.appender.stdout.layout.ConversionPattern=%d [%t] %-5p %c - %m%n + +#log4j.appender.stdout.layout.ConversionPattern=%m [%t] (%F:%L) - %n +#log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n +#log4j.appender.stdout.layout.ConversionPattern=%5p [%c] %m%n +# detailed output +#log4j.appender.stdout.layout.ConversionPattern=%5p %t [%c] [%t] (%F:%L + + +# log4j.logger.pyrasun=DEBUG,STDOUT + +# log4j.logger.pyrasun.eio.EIOOutputBuffer=DEBUG +# log4j.additivity.pyrasun.eio.EIOOutputBuffer=false + +# log4j.logger.pyrasun.eio.EIOWorkerThread=DEBUG +# log4j.logger.pyrasun.eio.WorkerController=DEBUG +log4j.logger.pyrasun.eio.Endpoint_ready=INFO diff --git a/systest/itests/client/src/plan/activemq-itests-ear.ear.xml b/systest/itests/client/src/plan/activemq-itests-ear.ear.xml new file mode 100755 index 0000000000..184ccb56ec --- /dev/null +++ b/systest/itests/client/src/plan/activemq-itests-ear.ear.xml @@ -0,0 +1,97 @@ + + + + + activemq-itest-ejb-${pom.currentVersion}.jar + + + + JMSToolBean + org/activemq/itest/JMSToolBean + + + MDBTransferBean + + ITest ActiveMQ RA + + + + + + + + activemq-ra-${pom.currentVersion}.rar + + + + ITest ActiveMQ RA + tcp://localhost:61616 + + DefaultWorkManager + + + + + javax.jms.ConnectionFactory + + jms/Default + javax.jms.QueueConnectionFactory + javax.jms.TopicConnectionFactory + + + + + + 10 + 5000 + + + + javax.resource.spi.security.PasswordCredential + + + + + + + javax.jms.Queue + org.activemq.message.ActiveMQQueue + + MDBOutQueue + MDBOutQueue + + + + javax.jms.Queue + org.activemq.message.ActiveMQQueue + + MDBInQueue + MDBInQueue + + + + + javax.jms.Queue + org.activemq.message.ActiveMQQueue + + TestQueue + TestQueue + + + + + javax.jms.Topic + org.activemq.message.ActiveMQTopic + + TestTopic + TestTopic + + + + + + + + diff --git a/systest/itests/ear/.cvsignore b/systest/itests/ear/.cvsignore new file mode 100755 index 0000000000..eb5a316cbd --- /dev/null +++ b/systest/itests/ear/.cvsignore @@ -0,0 +1 @@ +target diff --git a/systest/itests/ear/maven.xml b/systest/itests/ear/maven.xml new file mode 100755 index 0000000000..e9b7e1286e --- /dev/null +++ b/systest/itests/ear/maven.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/systest/itests/ear/project.properties b/systest/itests/ear/project.properties new file mode 100755 index 0000000000..fc364329fe --- /dev/null +++ b/systest/itests/ear/project.properties @@ -0,0 +1,10 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=ear +maven.ear.appxml=target/app/application.xml + +maven.repo.remote=\ +http://www.ibiblio.org/maven,\ +http://dist.codehaus.org,\ +http://cvs.apache.org/repository diff --git a/systest/itests/ear/project.xml b/systest/itests/ear/project.xml new file mode 100755 index 0000000000..66d693034a --- /dev/null +++ b/systest/itests/ear/project.xml @@ -0,0 +1,52 @@ + + + + 3 + ${basedir}/../../../../etc/project.xml + + ActiveMQ :: Integration Tests :: Ear + activemq-itest-ear + ActiveMQ is a JMS 1.1 implementation designed for easy deployment in Geronimo + + ActiveMQ is an open source JMS 1.1 implementation designed for easy deployment in Geronimo + + + org.activemq.itest + + + + + + commons-jelly + commons-jelly-tags-velocity + ${commons_jelly_tags_velocity_version} + + + velocity + velocity + ${velocity_version} + + + + activemq + activemq-itest-ejb + ${pom.currentVersion} + ejb + + true + + + + + activemq + activemq-ra + ${pom.currentVersion} + rar + + true + + + + + + diff --git a/systest/itests/ear/src/application/META-INF/application.xml b/systest/itests/ear/src/application/META-INF/application.xml new file mode 100755 index 0000000000..14e8e05cf4 --- /dev/null +++ b/systest/itests/ear/src/application/META-INF/application.xml @@ -0,0 +1,11 @@ + + + activemq-ra-${pom.currentVersion}.rar + + + activemq-itest-ejb-${pom.currentVersion}.jar + + diff --git a/systest/itests/ejb/maven.xml b/systest/itests/ejb/maven.xml new file mode 100755 index 0000000000..1a9a28d929 --- /dev/null +++ b/systest/itests/ejb/maven.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/systest/itests/ejb/project.properties b/systest/itests/ejb/project.properties new file mode 100755 index 0000000000..193814554f --- /dev/null +++ b/systest/itests/ejb/project.properties @@ -0,0 +1,4 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=ejb diff --git a/systest/itests/ejb/project.xml b/systest/itests/ejb/project.xml new file mode 100755 index 0000000000..6c698ad097 --- /dev/null +++ b/systest/itests/ejb/project.xml @@ -0,0 +1,34 @@ + + + + 3 + ${basedir}/../../../../etc/project.xml + + ActiveMQ :: Integration Tests :: Ejb + activemq-itest-ejb + ActiveMQ is a JMS 1.1 implementation designed for easy deployment in Geronimo + + ActiveMQ is an open source JMS 1.1 implementation designed for easy deployment in Geronimo + + + org.activemq.itest + + + EJBs used by the Test Suite + org.activemq.itest.ejb.* + + + + + + geronimo-spec + geronimo-spec-ejb + ${geronimo_spec_ejb_version} + + false + true + + + + + diff --git a/systest/itests/ejb/src/ejb/META-INF/ejb-jar.xml b/systest/itests/ejb/src/ejb/META-INF/ejb-jar.xml new file mode 100755 index 0000000000..1b07903486 --- /dev/null +++ b/systest/itests/ejb/src/ejb/META-INF/ejb-jar.xml @@ -0,0 +1,121 @@ + + + + + + Stateless Session Bean used to test the ActiveMQ outbound RA. + + + + + A SSB that uses the JMS RA + + JMSToolBean + org.activemq.itest.ejb.JMSToolHome + org.activemq.itest.ejb.JMSTool + org.activemq.itest.ejb.JMSToolBean + Stateless + Container + + + + This is a reference to the ActiveMQ JMS provider. + + jms/Default + javax.jms.ConnectionFactory + Container + + + + TestQueue + javax.jms.Queue + Produces + TestQueue + + + MDBInQueue + javax.jms.Queue + Consumes + MDBInQueue + + + MDBOutQueue + javax.jms.Queue + Consumes + MDBOutQueue + + + + + + + A MDB that uses the JMS RA. Transfers messages from one queue to another. + + MDBTransferBean + org.activemq.itest.ejb.MDBTransferBean + javax.jms.MessageListener + Container + + + destination + MDBInQueue + + + destinationType + javax.jms.Queue + + + + + This is a reference to the ActiveMQ JMS provider. + + jms/Default + javax.jms.ConnectionFactory + Container + + + MDBOut + javax.jms.Queue + Produces + MDBOutQueue + + + + + + + + + + JMSToolBean + * + + Required + + + + + MDBTransferBean + Remote + * + + Required + + + + MDBInQueue + + + MDBOutQueue + + + TestQueue + + + + + diff --git a/systest/itests/ejb/src/ejb/META-INF/jboss.xml b/systest/itests/ejb/src/ejb/META-INF/jboss.xml new file mode 100644 index 0000000000..2e2eca5e9c --- /dev/null +++ b/systest/itests/ejb/src/ejb/META-INF/jboss.xml @@ -0,0 +1,95 @@ + + + + + + activemq.rar:name=iTests + + + + JMSToolBean + + jms/Default + default + + + + + MDBTransferBean + activemq-ra-3.1-SNAPSHOT.rar + ActiveMQ Message Driven Bean + + + + + + + MDBInQueue + activemq/queue/MDBInQueue + + + MDBOutQueue + activemq/queue/MDBOutQueue + + + TestQueue + activemq/queue/TestQueue + + + + + + default + java:/activemq/ConnectionFactory + + + + + + activemq-message-driven-bean + default + org.jboss.ejb.plugins.inflow.JBossMessageEndpointFactory + + + org.jboss.proxy.ClientMethodInterceptor + org.jboss.ejb.plugins.inflow.MessageEndpointInterceptor + org.jboss.proxy.TransactionInterceptor + org.jboss.invocation.InvokerInterceptor + + + + + + + + ActiveMQ Message Driven Bean + false + activemq-message-driven-bean + + org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor + org.jboss.ejb.plugins.LogInterceptor + org.jboss.ejb.plugins.RunAsSecurityInterceptor + + org.jboss.ejb.plugins.TxInterceptorCMT + org.jboss.ejb.plugins.CallValidationInterceptor + org.jboss.ejb.plugins.MetricsInterceptor + org.jboss.ejb.plugins.MessageDrivenInstanceInterceptor + + org.jboss.ejb.plugins.MessageDrivenInstanceInterceptor + org.jboss.ejb.plugins.MessageDrivenTxInterceptorBMT + org.jboss.ejb.plugins.CallValidationInterceptor + org.jboss.ejb.plugins.MetricsInterceptor + org.jboss.resource.connectionmanager.CachedConnectionInterceptor + + org.jboss.ejb.plugins.MessageDrivenInstancePool + + + + 100 + + + + + diff --git a/systest/itests/ejb/src/java/org/activemq/itest/ejb/JMSTool.java b/systest/itests/ejb/src/java/org/activemq/itest/ejb/JMSTool.java new file mode 100755 index 0000000000..646f44cfda --- /dev/null +++ b/systest/itests/ejb/src/java/org/activemq/itest/ejb/JMSTool.java @@ -0,0 +1,43 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activemq.itest.ejb; + +import java.rmi.RemoteException; + +import javax.ejb.EJBObject; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.naming.NamingException; + + +/** + * Provides the Remote interface of the MessengerBean. + * + * @version $Revision: 1.1 $ + */ +public interface JMSTool extends EJBObject { + + public void sendTextMessage(String dest, String text) throws RemoteException, JMSException, NamingException; + public void sendTextMessage(Destination dest, String text) throws RemoteException, JMSException, NamingException; + public String receiveTextMessage(String dest, long timeout) throws RemoteException, JMSException, NamingException; + public String receiveTextMessage(Destination dest, long timeout) throws RemoteException, JMSException, NamingException; + public int drain(String dest) throws RemoteException, JMSException, NamingException; + public int drain(Destination dest) throws RemoteException, JMSException, NamingException; + +} + diff --git a/systest/itests/ejb/src/java/org/activemq/itest/ejb/JMSToolBean.java b/systest/itests/ejb/src/java/org/activemq/itest/ejb/JMSToolBean.java new file mode 100755 index 0000000000..4ebd34e013 --- /dev/null +++ b/systest/itests/ejb/src/java/org/activemq/itest/ejb/JMSToolBean.java @@ -0,0 +1,142 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activemq.itest.ejb; + +import java.rmi.RemoteException; + +import javax.ejb.EJBException; +import javax.ejb.SessionBean; +import javax.ejb.SessionContext; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This is a SSB that uses an outbound JMS Resource Adapter. + * + * @version $Revision: 1.1 $ + */ +public class JMSToolBean implements SessionBean { + + private static final long serialVersionUID = 3834596495499474741L; + private static final Log log = LogFactory.getLog(JMSToolBean.class); + + private SessionContext sessionContext; + private Context envContext; + + public void ejbCreate() { + } + public void ejbRemove() { + } + public void ejbActivate() { + } + public void ejbPassivate() { + } + + public void setSessionContext(SessionContext sessionContext) { + try { + this.sessionContext = sessionContext; + envContext = (Context) new InitialContext().lookup("java:comp/env"); + } + catch (NamingException e) { + throw new EJBException(e); + } + } + + public void sendTextMessage(String dest, String text) throws RemoteException, JMSException, NamingException { + sendTextMessage(createDestination(dest), text); + } + + public void sendTextMessage(Destination dest, String text) throws RemoteException, JMSException, NamingException { + log.info("sendTextMessage start"); + Connection connection = createConnection(); + try { + Session session = connection.createSession(true, Session.SESSION_TRANSACTED); + MessageProducer producer = session.createProducer(dest); + producer.send(session.createTextMessage(text)); + } finally { + log.info("sendTextMessage end"); + connection.close(); + } + } + + public String receiveTextMessage(String dest, long timeout) throws RemoteException, JMSException, NamingException { + return receiveTextMessage(createDestination(dest), timeout); + } + + public String receiveTextMessage(Destination dest, long timeout) throws RemoteException, JMSException, NamingException { + log.info("receiveTextMessage start"); + Connection connection = createConnection(); + try { + connection.start(); + Session session = connection.createSession(true, Session.SESSION_TRANSACTED); + MessageConsumer consumer = session.createConsumer(dest); + TextMessage message = null; + message = (TextMessage) consumer.receive(timeout); + return message==null ? null : message.getText(); + } finally { + log.info("receiveTextMessage end"); + connection.close(); + } + } + + public int drain(String dest) throws RemoteException, JMSException, NamingException { + return drain(createDestination(dest)); + } + + public int drain(Destination dest) throws RemoteException, JMSException, NamingException { + log.info("drain start"); + Connection connection = createConnection(); + try { + + connection.start(); + Session session = connection.createSession(true, Session.SESSION_TRANSACTED); + MessageConsumer consumer = session.createConsumer(dest); + int counter=0; + while( consumer.receive(1000) != null) { + counter++; + } + return counter; + + } finally { + log.info("drain end"); + connection.close(); + } + } + + private Destination createDestination(String dest) throws NamingException { + return (Destination) envContext.lookup(dest); + } + private Connection createConnection() throws NamingException, JMSException { + ConnectionFactory cf = (ConnectionFactory) envContext.lookup("jms/Default"); + Connection con = cf.createConnection(); + return con; + } + +} diff --git a/systest/itests/ejb/src/java/org/activemq/itest/ejb/JMSToolHome.java b/systest/itests/ejb/src/java/org/activemq/itest/ejb/JMSToolHome.java new file mode 100755 index 0000000000..185537adae --- /dev/null +++ b/systest/itests/ejb/src/java/org/activemq/itest/ejb/JMSToolHome.java @@ -0,0 +1,33 @@ +/** + * + * Copyright 2004 Hiram Chirino + * + * Licensed 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.activemq.itest.ejb; + +import java.rmi.RemoteException; + +import javax.ejb.CreateException; +import javax.ejb.EJBHome; + +/** + * Provides the Home interface of the MessengerBean. + * + * @version $Revision: 1.1 $ + */ +public interface JMSToolHome extends EJBHome { + public JMSTool create() throws RemoteException, CreateException; +} + diff --git a/systest/itests/ejb/src/java/org/activemq/itest/ejb/MDBTransferBean.java b/systest/itests/ejb/src/java/org/activemq/itest/ejb/MDBTransferBean.java new file mode 100755 index 0000000000..1863a68c76 --- /dev/null +++ b/systest/itests/ejb/src/java/org/activemq/itest/ejb/MDBTransferBean.java @@ -0,0 +1,94 @@ +package org.activemq.itest.ejb; + +import javax.ejb.EJBException; +import javax.ejb.MessageDrivenBean; +import javax.ejb.MessageDrivenContext; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NameClassPair; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * + * @version $Revision: 1.1 $ $Date: 2005/03/14 02:57:53 $ + * + * */ +public class MDBTransferBean implements MessageDrivenBean, MessageListener { + private static final Log log = LogFactory.getLog(MDBTransferBean.class); + private MessageDrivenContext messageDrivenContext; + private Context envContext; + + public void ejbCreate() { + + } + + public void ejbRemove() throws EJBException { + } + + public void setMessageDrivenContext(MessageDrivenContext messageDrivenContext) throws EJBException { + try { + this.messageDrivenContext = messageDrivenContext; + envContext = (Context) new InitialContext().lookup("java:comp/env"); + } + catch (NamingException e) { + throw new EJBException(e); + } + } + + public void onMessage(Message message) { + System.out.println("entering onMessage"); + try { + ConnectionFactory cf = (ConnectionFactory) envContext.lookup("jms/Default"); + Connection con = cf.createConnection(); + try { + + Session session = con.createSession(true, 0); + Destination dest = (Destination) envContext.lookup("MDBOut"); + MessageProducer producer = session.createProducer(dest); + producer.setDeliveryMode(message.getJMSDeliveryMode()); + producer.send(message); + + } finally { + con.close(); + } + } catch (Throwable e) { + log.info(e); + } + System.out.println("leaving onMessage"); + } + + /** + * + */ + private void printCompEnv() throws NamingException { + log.warn("Printing java:comp/env/jms context: "); + Context c = (Context) new InitialContext().lookup("java:comp/env/jms"); + NamingEnumeration iter = c.list(""); + while (iter.hasMoreElements()) { + NameClassPair pair = (NameClassPair) iter.next(); + log.warn("'" + pair.getName() + "'"); + /* + * Object obj = ctx.lookup(node.getName()); if ( obj instanceof + * Context ){ node.type = Node.CONTEXT; + * buildNode(node,(Context)obj); } else if (obj instanceof + * java.rmi.Remote) { node.type = Node.BEAN; } else { node.type = + * Node.OTHER; } + */ + } + } + +} + + diff --git a/systest/jboss-test/maven.xml b/systest/jboss-test/maven.xml new file mode 100644 index 0000000000..7bada5eb47 --- /dev/null +++ b/systest/jboss-test/maven.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/systest/jboss-test/project.properties b/systest/jboss-test/project.properties new file mode 100644 index 0000000000..36b7674744 --- /dev/null +++ b/systest/jboss-test/project.properties @@ -0,0 +1,5 @@ +maven.multiproject.type=ejb +maven.ejb.src=src/main/ejb + +#activemq.jboss.home=C:/sandbox/cvs_master/trunk/jboss/server/default +activemq.jboss.home=/Users/chirino/Desktop/jboss-4.0.1sp1/server/default diff --git a/systest/jboss-test/project.xml b/systest/jboss-test/project.xml new file mode 100644 index 0000000000..85307cf6da --- /dev/null +++ b/systest/jboss-test/project.xml @@ -0,0 +1,88 @@ + + + 3 + panacya-mdb-test + Message Driven Bean Test + panacya-cicada + 1.0 + com.panacya.platform.service.bus + + + + + Michael Gaffney + mgaffney + mike@gaffney.cc + Panacya Inc. + + + + + + + activemq + activemq + 3.1-SNAPSHOT + http://activemq.codehaus.org/ + + + + geronimo-spec + geronimo-spec-j2ee + 1.4-rc4 + http://geronimo.apache.org/ + + + + concurrent + concurrent + 1.3.4 + + + + commons-logging + commons-logging + 1.0.3 + http://jakarta.apache.org/commons/logging/ + + true + true + + + + + springframework + spring + 1.1 + + + + log4j + log4j + 1.2.8 + + + + + src/main/java + src/test/java + + + **/*Test.java + + + **/Abstract*.java + + + + src/test/conf + + log4j.properties + + + + + + diff --git a/systest/jboss-test/src/main/ejb/META-INF/ejb-jar.xml b/systest/jboss-test/src/main/ejb/META-INF/ejb-jar.xml new file mode 100644 index 0000000000..a996bcf543 --- /dev/null +++ b/systest/jboss-test/src/main/ejb/META-INF/ejb-jar.xml @@ -0,0 +1,218 @@ + + + + + + A collection of simple EJBs for testing ActiveMQ integration with JBoss. + + + + + + A stateless session bean that sends a small text message to jms + Sender EJB + SenderEJB + com.panacya.platform.service.bus.sender.SenderHome + com.panacya.platform.service.bus.sender.Sender + com.panacya.platform.service.bus.sender.SenderBean + Stateless + Container + + jms/MyQueueConnectionFactory + javax.jms.QueueConnectionFactory + Container + Unshareable + + + jms/LogQueue + javax.jms.Queue + Produces + LoggingQueue + + + + + An MDB listening on a queue. + QueueListenerMDB + com.panacya.platform.service.bus.mdb.SimpleMessageReceiverBean + javax.jms.MessageListener + Container + + + destination + queue.testQueue + + + destinationType + javax.jms.Queue + + + acknowledgeMode + Auto-acknowledge + + + messageSelector + + + + maxSessions + 1 + + + useRAManagedTransaction + true + + + + ejb/Sender + Session + com.panacya.platform.service.bus.sender.SenderHome + com.panacya.platform.service.bus.sender.Sender + SenderEJB + + + + + An MDB listening on a topic with a non-durable subscription. + TopicNonDurableMDB + com.panacya.platform.service.bus.mdb.SimpleMessageReceiverBean + javax.jms.MessageListener + Container + + + destination + topic.testTopic + + + destinationType + javax.jms.Topic + + + acknowledgeMode + Auto-acknowledge + + + subscriptionDurability + NonDurable + + + messageSelector + + + + maxSessions + 1 + + + useRAManagedTransaction + true + + + + ejb/Sender + Session + com.panacya.platform.service.bus.sender.SenderHome + com.panacya.platform.service.bus.sender.Sender + SenderEJB + + + + + An MDB listening on a topic with a durable subscription. + TopicDurableMDB + com.panacya.platform.service.bus.mdb.SimpleMessageReceiverBean + javax.jms.MessageListener + Container + + + destination + topic.testTopic + + + destinationType + javax.jms.Topic + + + acknowledgeMode + Auto-acknowledge + + + subscriptionDurability + Durable + + + clientId + foo + + + subscriptionName + bar + + + messageSelector + + + + maxSessions + 1 + + + useRAManagedTransaction + true + + + + ejb/Sender + Session + com.panacya.platform.service.bus.sender.SenderHome + com.panacya.platform.service.bus.sender.Sender + SenderEJB + + + + + + + + + + QueueListenerMDB + * + + Required + + + + + TopicDurableMDB + * + + Required + + + + + TopicNonDurableMDB + * + + Required + + + + + SenderEJB + * + + Required + + + + LoggingQueue + + + + + diff --git a/systest/jboss-test/src/main/ejb/META-INF/jboss.xml b/systest/jboss-test/src/main/ejb/META-INF/jboss.xml new file mode 100644 index 0000000000..ffa43a7544 --- /dev/null +++ b/systest/jboss-test/src/main/ejb/META-INF/jboss.xml @@ -0,0 +1,103 @@ + + + + + + activemq.rar:name=iTests + + + + SenderEJB + + jms/MyQueueConnectionFactory + queuefactoryref + + + + + QueueListenerMDB + activemq-ra-4.0-SNAPSHOT.rar + ActiveMQ Message Driven Bean + + + + TopicNonDurableMDB + activemq-ra-4.0-SNAPSHOT.rar + ActiveMQ Message Driven Bean + + + + TopicDurableMDB + activemq-ra-4.0-SNAPSHOT.rar + ActiveMQ Message Driven Bean + + + + + + + LoggingQueue + activemq/queue/outbound + + + + + + queuefactoryref + java:/activemq/QueueConnectionFactory + + + topicfactoryref + java:/activemq/TopicConnectionFactory + + + + + + activemq-message-driven-bean + default + org.jboss.ejb.plugins.inflow.JBossMessageEndpointFactory + + + org.jboss.proxy.ClientMethodInterceptor + org.jboss.ejb.plugins.inflow.MessageEndpointInterceptor + org.jboss.proxy.TransactionInterceptor + org.jboss.invocation.InvokerInterceptor + + + + + + + + ActiveMQ Message Driven Bean + false + activemq-message-driven-bean + + org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor + org.jboss.ejb.plugins.LogInterceptor + org.jboss.ejb.plugins.RunAsSecurityInterceptor + + org.jboss.ejb.plugins.TxInterceptorCMT + org.jboss.ejb.plugins.CallValidationInterceptor + org.jboss.ejb.plugins.MetricsInterceptor + org.jboss.ejb.plugins.MessageDrivenInstanceInterceptor + + org.jboss.ejb.plugins.MessageDrivenInstanceInterceptor + org.jboss.ejb.plugins.MessageDrivenTxInterceptorBMT + org.jboss.ejb.plugins.CallValidationInterceptor + org.jboss.ejb.plugins.MetricsInterceptor + org.jboss.resource.connectionmanager.CachedConnectionInterceptor + + org.jboss.ejb.plugins.MessageDrivenInstancePool + + + + 100 + + + + + \ No newline at end of file diff --git a/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/client/ClientArgs.java b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/client/ClientArgs.java new file mode 100644 index 0000000000..d9dbf8951d --- /dev/null +++ b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/client/ClientArgs.java @@ -0,0 +1,92 @@ +/** + * + * Copyright 2004 Michael Gaffney + * + * Licensed 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 com.panacya.platform.service.bus.client; + +/** + * @author Michael Gaffney + */ +public class ClientArgs { + private long timeout = -1; + private String command; + private String destination; + + private int noOfMessages = 1; + + public ClientArgs(String[] args) { + /* + int argCount = args.length; + + System.out.println(argCount); + for(int i = 0 ; i < argCount; i++){ + System.out.println(args[i] + " .........."); + } + */ + switch (args.length) { + case 4: + setNoOfMessages(args[3]); + case 3: + setTimeout(args[2]); + case 2: + destination = args[1]; + command = args[0]; + break; + default : + printHelp(); + } + } + + public String getCommand() { + return command; + } + + public String getDestination() { + return destination; + } + + public long getTimeout() { + return timeout; + } + + private void setTimeout(String timout) { + if (!isEmpty(timout)) { + try { + timeout = Long.valueOf(timout).longValue(); + } catch (NumberFormatException e) { + System.err.println(e.toString()); + } + } + } + + public int getNoOfMessages() { + return noOfMessages; + } + + public void setNoOfMessages(String count) { + System.out.println("noOfMessage " + count); + this.noOfMessages = Integer.parseInt(count); + } + + private static boolean isEmpty(String value) { + return value == null || "".equals(value.trim()); + } + + private static void printHelp() { + System.out.println("JmsSimpleClient command(send | receive | send-receive) noOfMessages destination timeout"); + } +} + diff --git a/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/client/JmsSimpleClient.java b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/client/JmsSimpleClient.java new file mode 100644 index 0000000000..0abbbf4ce8 --- /dev/null +++ b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/client/JmsSimpleClient.java @@ -0,0 +1,124 @@ +package com.panacya.platform.service.bus.client; + +import org.activemq.ActiveMQConnectionFactory; +import org.springframework.jms.core.JmsTemplate; +import org.springframework.jms.core.MessageCreator; + +import javax.jms.Message; +import javax.jms.Session; +import javax.jms.JMSException; +import javax.naming.InitialContext; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.jms.ConnectionFactory; + +import org.activemq.util.IdGenerator; + +import java.util.Properties; +import java.lang.reflect.InvocationTargetException; + +/** + * @author Michael Gaffney + */ +public class JmsSimpleClient { + + private static final String BROKER_URL = "tcp://localhost:61616"; + //private static final String BROKER_URL = "jnp://localhost:1099"; + private static final String SEND_CMD = "send"; + private static final String RECEIVE_CMD = "receive"; + private static final String ENDLESS_RECEIVE_CMD = "receive-non-stop"; + private static final String SEND_RECEIVE_CMD = "send-receive"; + + public static final String NAMING_CONTEXT = "org.jnp.interfaces.NamingContextFactory"; + public static final String JNP_INTERFACES = "org.jnp.interfaces"; + + + public static void main(String[] args) { + execute(new ClientArgs(args)); + } + + private static void execute(ClientArgs args) { + try { + if (SEND_CMD.equals(args.getCommand())) { + JmsSimpleClient.sendToActiveMQ(args.getDestination(), args.getNoOfMessages()); + } else if (RECEIVE_CMD.equals(args.getCommand())) { + JmsSimpleClient.receiveFromActiveMQ(args.getDestination(), args.getTimeout()); + } else if (ENDLESS_RECEIVE_CMD.equals(args.getCommand())) { + JmsSimpleClient.receiveFromActiveMQ(args.getDestination()); + } else if (SEND_RECEIVE_CMD.equals(args.getCommand())) { + JmsSimpleClient.sendToActiveMQ(args.getDestination(), args.getNoOfMessages()); + JmsSimpleClient.receiveFromActiveMQ(args.getDestination(), args.getTimeout()); + } else { + System.err.println("Unknown command: " + args.getCommand()); + System.exit(-1); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void sendToActiveMQ(String destinationName, int count) throws Exception { + System.out.println("Sending to '" + destinationName + "' ..."); + JmsTemplate jt = createTemplate(destinationName); + + int i = 0; + for (; i < count; i++) { + jt.send(destinationName, new MessageCreator() { + public Message createMessage(Session session) throws JMSException { + return session.createTextMessage("hello ActiveMQ world "); + } + }); + } + + System.out.println("Done sending " + count + " message/s ........"); + } + + public static void receiveFromActiveMQ(String destinationName, long timeout) throws Exception { + System.out.println("Listening to '" + destinationName + "' ..."); + JmsTemplate jt = createTemplate(destinationName); + jt.setReceiveTimeout(timeout); + while (true) { + Message aMessage = jt.receive(destinationName); + System.out.println("...done"); + if (aMessage == null) { + System.out.println("No message received"); + } else { + System.out.println("Message Received: " + aMessage.toString()); + } + } + } + + public static void receiveFromActiveMQ(String destinationName) throws Exception { + System.out.println("Listening to '" + destinationName + "' ..."); + JmsTemplate jt = createTemplate(destinationName); + + while (true) { + Message aMessage = jt.receive(destinationName); + if (aMessage == null) { + System.out.println("No message received"); + } else { + int messageNumber = aMessage.getIntProperty("MessageNumber"); + System.out.println("Received MessageNumber: " + messageNumber); + } + } + } + + private static JmsTemplate createTemplate(String destinationName) { + + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(); + connectionFactory.setBrokerURL(BROKER_URL); + + IdGenerator idGenerator = new IdGenerator(); + connectionFactory.setClientID(idGenerator.generateId()); + + JmsTemplate jt = new JmsTemplate(connectionFactory); + if (destinationName.startsWith("topic")) { + jt.setPubSubDomain(true); + } + + return jt; + + } + + +} diff --git a/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/mdb/SimpleMessageReceiverBean.java b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/mdb/SimpleMessageReceiverBean.java new file mode 100644 index 0000000000..7684f693e8 --- /dev/null +++ b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/mdb/SimpleMessageReceiverBean.java @@ -0,0 +1,95 @@ +package com.panacya.platform.service.bus.mdb; + +import com.panacya.platform.service.bus.sender.SenderClient; +import com.panacya.platform.service.bus.sender.SenderException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.ejb.CreateException; +import javax.ejb.MessageDrivenBean; +import javax.ejb.MessageDrivenContext; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.TextMessage; +import javax.naming.NamingException; +import java.rmi.RemoteException; + +/** + * @author Michael Gaffney + */ + +public class SimpleMessageReceiverBean implements MessageDrivenBean, MessageListener { + + private static final String SENDER_NAME = "java:comp/env/ejb/Sender"; + private Log _log = LogFactory.getLog(SimpleMessageReceiverBean.class); + private MessageDrivenContext context; + + public SimpleMessageReceiverBean() { + if (_log.isInfoEnabled()) { + _log.info("SimpleMessageReceiverBean.SimpleMessageReceiverBean"); + } + } + + public void onMessage(Message message) { + if (_log.isInfoEnabled()) { + _log.info("SimpleMessageReceiverBean.onMessage"); + } + try { + handleMessage(message); + } catch (JMSException e) { + _log.error(e.toString(), e); + } catch (NamingException e) { + _log.error(e.toString(), e); + } catch (RemoteException e) { + _log.error(e.toString(), e); + } catch (CreateException e) { + _log.error(e.toString(), e); + } catch (SenderException e) { + _log.error(e.toString(), e); + } + } + + private void handleMessage(Message message) throws JMSException, NamingException, RemoteException, SenderException, CreateException { + if (message instanceof TextMessage) { + TextMessage textMessage = (TextMessage) message; + if (_log.isInfoEnabled()) { + _log.info("Message received: " + textMessage.getText()); + } + send(textMessage.getText()); + } else { + if (_log.isInfoEnabled()) { + _log.info("Unknown message type received: " + message.toString()); + } + send("Unknown message type: " + message.toString()); + } + } + + public void ejbRemove() { + if (_log.isInfoEnabled()) { + _log.info("SimpleMessageReceiverBean.ejbRemove"); + } + } + + public void setMessageDrivenContext(MessageDrivenContext messageDrivenContext) { + if (_log.isInfoEnabled()) { + _log.info("SimpleMessageReceiverBean.setMessageDrivenContext"); + } + context = messageDrivenContext; + } + + public void ejbCreate() { + if (_log.isInfoEnabled()) { + _log.info("SimpleMessageReceiverBean.ejbCreate"); + } + } + + private void send(String recMessage) throws NamingException, RemoteException, CreateException, SenderException { + sendToEJB(recMessage); + } + + private void sendToEJB(String recMessage) throws NamingException, RemoteException, CreateException, SenderException { + SenderClient senderClient = new SenderClient(SENDER_NAME); + senderClient.sendMessage(recMessage); + } +} diff --git a/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/Sender.java b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/Sender.java new file mode 100644 index 0000000000..46fee233fe --- /dev/null +++ b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/Sender.java @@ -0,0 +1,13 @@ +package com.panacya.platform.service.bus.sender; + +import javax.ejb.EJBObject; +import java.rmi.RemoteException; + +/** + * @author Michael Gaffney + */ + +public interface Sender extends EJBObject { + + public void sendMessage(String message) throws RemoteException, SenderException; +} diff --git a/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/SenderBean.java b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/SenderBean.java new file mode 100644 index 0000000000..9c2bc4a6c9 --- /dev/null +++ b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/SenderBean.java @@ -0,0 +1,72 @@ +package com.panacya.platform.service.bus.sender; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.ejb.CreateException; +import javax.ejb.EJBException; +import javax.ejb.SessionBean; +import javax.ejb.SessionContext; +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueConnectionFactory; +import javax.jms.QueueSender; +import javax.jms.QueueSession; +import javax.jms.TextMessage; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +/** + * @author Michael Gaffney + */ + +public class SenderBean implements SessionBean { + private Log _log = LogFactory.getLog(SenderBean.class); + + public SenderBean() { + } + + public void ejbCreate() throws CreateException { + } + + public void setSessionContext(SessionContext sessionContext) throws EJBException { + } + + public void ejbRemove() throws EJBException { + } + + public void ejbActivate() throws EJBException { + } + + public void ejbPassivate() throws EJBException { + } + + public void sendMessage(String message) throws SenderException { + try { + send(message); + } catch (NamingException e) { + _log.error(e.toString(), e); + throw new SenderException(e); + } catch (JMSException e) { + _log.error(e.toString(), e); + throw new SenderException(e); + } + } + + private void send(String recMessage) throws NamingException, JMSException { + InitialContext initCtx = new InitialContext(); + QueueConnectionFactory qcf = (QueueConnectionFactory) initCtx.lookup("java:comp/env/jms/MyQueueConnectionFactory"); + QueueConnection qcon = qcf.createQueueConnection(); + QueueSession qsession = qcon.createQueueSession(true, 0); + Queue q = (Queue) initCtx.lookup("java:comp/env/jms/LogQueue"); + QueueSender qsender = qsession.createSender(q); + TextMessage message = qsession.createTextMessage(); + message.setText("Message Received: " + recMessage); + qsender.send(message); + qsender.close(); + qsession.close(); + qcon.close(); + } + +} diff --git a/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/SenderClient.java b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/SenderClient.java new file mode 100644 index 0000000000..81a36938d4 --- /dev/null +++ b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/SenderClient.java @@ -0,0 +1,65 @@ +package com.panacya.platform.service.bus.sender; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.ejb.CreateException; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.rmi.PortableRemoteObject; +import java.rmi.RemoteException; + +/** + * @author Michael Gaffney + */ +public class SenderClient { + private static final Log _log = LogFactory.getLog(SenderClient.class); + + private Sender sender; + private String ejbName; + + public SenderClient(String ejbName) throws NamingException, RemoteException, CreateException { + setEjbName(ejbName); + } + + public void sendMessage(final String message) throws RemoteException, SenderException { + if (_log.isInfoEnabled()) { + _log.info("Sending message: " + message); + } + sender.sendMessage(message); + if (_log.isInfoEnabled()) { + _log.info("Message sent"); + } + } + + public String getEjbName() { + return ejbName; + } + + private void setEjbName(final String ejbName) throws NamingException, RemoteException, CreateException { + this.ejbName = ejbName; + lookupSender(ejbName); + } + + private void lookupSender(final String ejbName) throws NamingException, RemoteException, CreateException { + if (_log.isInfoEnabled()) { + _log.info("Looking up Sender: " + ejbName); + } + Context context = new InitialContext(); + Object objectRef = context.lookup(ejbName); + SenderHome senderHome = (SenderHome) PortableRemoteObject.narrow(objectRef, SenderHome.class); + sender = senderHome.create(); + } + + public static void main(String[] args) { + + try { + SenderClient client = new SenderClient("SenderEJB"); + client.sendMessage("Hello ActiveMQ"); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/SenderException.java b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/SenderException.java new file mode 100644 index 0000000000..91fe89d6e8 --- /dev/null +++ b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/SenderException.java @@ -0,0 +1,62 @@ +package com.panacya.platform.service.bus.sender; + +/** + * @author Michael Gaffney + */ +public class SenderException extends Exception { + + /** + * Constructs a new exception with null as its detail message. + * The cause is not initialized, and may subsequently be initialized by a + * call to {@link #initCause}. + */ + public SenderException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. The + * cause is not initialized, and may subsequently be initialized by + * a call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for + * later retrieval by the {@link #getMessage()} method. + */ + public SenderException(final String message) { + super(message); + } + + /** + * Constructs a new exception with the specified detail message and + * cause.

Note that the detail message associated with + * cause is not automatically incorporated in + * this exception's detail message. + * + * @param message the detail message (which is saved for later retrieval + * by the {@link #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A null value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + */ + public SenderException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail + * message of (cause==null ? null : cause.toString()) (which + * typically contains the class and detail message of cause). + * This constructor is useful for exceptions that are little more than + * wrappers for other throwables (for example, {@link + * java.security.PrivilegedActionException}). + * + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A null value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + */ + public SenderException(final Throwable cause) { + super(cause); + } +} diff --git a/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/SenderHome.java b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/SenderHome.java new file mode 100644 index 0000000000..925e4b6df0 --- /dev/null +++ b/systest/jboss-test/src/main/java/com/panacya/platform/service/bus/sender/SenderHome.java @@ -0,0 +1,15 @@ +package com.panacya.platform.service.bus.sender; + +import javax.ejb.CreateException; +import javax.ejb.EJBHome; +import java.rmi.RemoteException; + + +/** + * @author Michael Gaffney + */ + +public interface SenderHome extends EJBHome { + + com.panacya.platform.service.bus.sender.Sender create() throws RemoteException, CreateException; +} diff --git a/systest/jboss-test/src/main/jboss-datasource/panacya-jms-ds.xml b/systest/jboss-test/src/main/jboss-datasource/panacya-jms-ds.xml new file mode 100644 index 0000000000..2e7e1761aa --- /dev/null +++ b/systest/jboss-test/src/main/jboss-datasource/panacya-jms-ds.xml @@ -0,0 +1,38 @@ + + + + + + + + activemq/QueueConnectionFactory + + + activemq-ra-3.1-SNAPSHOT.rar + javax.jms.QueueConnectionFactory + JmsXARealm + true + + + + activemq/TopicConnectionFactory + + + activemq-ra-3.1-SNAPSHOT.rar + javax.jms.TopicConnectionFactory + JmsXARealm + true + + + + activemq/queue/outbound + jboss.jca:service=RARDeployment,name='activemq-ra-3.1-SNAPSHOT.rar' + javax.jms.Queue + + PhysicalName=queue.outbound + + + + diff --git a/systest/jboss-test/src/test/conf/log4j.properties b/systest/jboss-test/src/test/conf/log4j.properties new file mode 100644 index 0000000000..b8c2661341 --- /dev/null +++ b/systest/jboss-test/src/test/conf/log4j.properties @@ -0,0 +1,7 @@ +log4j.rootLogger=debug, stdout + +log4j.appender.stdout.threshold=ERROR +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +# Pattern to output the caller's file name and line number. +log4j.appender.stdout.layout.ConversionPattern=[%20.20t] %m%n diff --git a/systest/jboss-test/src/test/java/com/panacya/platform/service/bus/client/ClientArgsTest.java b/systest/jboss-test/src/test/java/com/panacya/platform/service/bus/client/ClientArgsTest.java new file mode 100644 index 0000000000..357081df64 --- /dev/null +++ b/systest/jboss-test/src/test/java/com/panacya/platform/service/bus/client/ClientArgsTest.java @@ -0,0 +1,49 @@ +/** + * + * Copyright 2004 Michael Gaffney + * + * Licensed 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 com.panacya.platform.service.bus.client; + +import junit.framework.TestCase; + +/** + * @author Michael Gaffney + */ +public class ClientArgsTest extends TestCase { + + public ClientArgsTest(String name) { + super(name); + } + + public void testThreeArgs() { + Long timeout = new Long(14999); + + String[] args = { "send", "topic.testTopic", timeout.toString()}; + ClientArgs c = new ClientArgs(args); + assertEquals(args[0], c.getCommand()); + assertEquals(args[1], c.getDestination()); + assertEquals(timeout.longValue(), c.getTimeout()); + } + + public void testTwoArgs() { + String[] args = { "send", "topic.testTopic"}; + ClientArgs c = new ClientArgs(args); + assertEquals(args[0], c.getCommand()); + assertEquals(args[1], c.getDestination()); + assertEquals(-1, c.getTimeout()); + } + +} diff --git a/systest/jdbc/.cvsignore b/systest/jdbc/.cvsignore new file mode 100755 index 0000000000..e769e7f7b1 --- /dev/null +++ b/systest/jdbc/.cvsignore @@ -0,0 +1,8 @@ +*.log +junit*.properties +target +*.iws +*.ipr +*.iml +build.properties + diff --git a/systest/jdbc/maven.xml b/systest/jdbc/maven.xml new file mode 100755 index 0000000000..8d3a52db44 --- /dev/null +++ b/systest/jdbc/maven.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/systest/jdbc/project.properties b/systest/jdbc/project.properties new file mode 100755 index 0000000000..10ef167654 --- /dev/null +++ b/systest/jdbc/project.properties @@ -0,0 +1,6 @@ +# +# Testing system properties +# +maven.junit.sysproperties = activemq.persistenceAdapter activemq.store.dir +activemq.persistenceAdapter = org.activemq.store.jdbc.JDBCPersistenceAdapter +activemq.store.dir = target/MessageStore diff --git a/systest/jdbc/project.xml b/systest/jdbc/project.xml new file mode 100755 index 0000000000..eed778fb36 --- /dev/null +++ b/systest/jdbc/project.xml @@ -0,0 +1,56 @@ + + + 3 + ${basedir}/../../base-project.xml + + ActiveMQ :: JDBC System Test + activemq-systest-jdbc + 1.0-SNAPSHOT + + + + activemq + ${pom.currentVersion} + + + + + + src + ../../../src/test + + + + + + ../../../src/test + + **/*.properties + **/*.xml + + + + + **/*Test.* + + + **/Ssl*Test.* + + + **/bdbn/*Test.* + + + **/ember/*Test.* + **/gnet/*Test.* + + + **/jrms/* + **/multicast/* + **/jgroups/* + + + + + + + diff --git a/systest/jdbc/src/README.txt b/systest/jdbc/src/README.txt new file mode 100755 index 0000000000..10dae99d24 --- /dev/null +++ b/systest/jdbc/src/README.txt @@ -0,0 +1 @@ +This module runs the system tests for Axion based JDBC \ No newline at end of file diff --git a/systest/jmscts/.cvsignore b/systest/jmscts/.cvsignore new file mode 100755 index 0000000000..bfdce30c25 --- /dev/null +++ b/systest/jmscts/.cvsignore @@ -0,0 +1,10 @@ +*.log +junit*.properties +target +*.iws +*.ipr +*.iml +build.properties + +.classpath +.project diff --git a/systest/jmscts/config/activemq.xml b/systest/jmscts/config/activemq.xml new file mode 100755 index 0000000000..5d6be0f70e --- /dev/null +++ b/systest/jmscts/config/activemq.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/systest/jmscts/config/jmscts.policy b/systest/jmscts/config/jmscts.policy new file mode 100755 index 0000000000..6acd889dc4 --- /dev/null +++ b/systest/jmscts/config/jmscts.policy @@ -0,0 +1,5 @@ +// grant all users all permissions. This is only for test cases +// and should be modified for deployment +grant { + permission java.security.AllPermission; +}; diff --git a/systest/jmscts/config/jmscts.properties b/systest/jmscts/config/jmscts.properties new file mode 100755 index 0000000000..71f65b655b --- /dev/null +++ b/systest/jmscts/config/jmscts.properties @@ -0,0 +1,52 @@ +# ============================================================================= +# General properties +# ----------------------------------------------------------------------------- + +# +# Username & password +# A user name and password for creating Connection instances via +# TopicConnectionFactory.createTopicConnection(...) etc +# +valid.username=CHANGE_ME +valid.password=CHANGE_ME + +# +# Invalid user name and password +# As above, but guaranteed to fail. +# +invalid.username=CHANGE_ME +invalid.password=CHANGE_ME + +# +# Message receipt timeout +# The default time to wait for messages, in milliseconds +# +org.exolab.jmscts.core.MessagingBehaviour.timeout=2000 + + +# ============================================================================= +# Compliance test properties +# ----------------------------------------------------------------------------- + +# +# Expiration interval +# Time in milliseconds to wait for the JMS provider to collect expired +# messages. +# This can be set for providers which collect expired messages periodically, +# rather than at the moment they expire. +# NOTE: for OpenJMS 0.7.6, this should be set to 5000 +org.exolab.jmscts.test.producer.ttl.ExpirationTest.expirationInterval=0 + + +# ============================================================================= +# Stress test properties +# ----------------------------------------------------------------------------- + +# +# Each of the following properties determines the no. of messages that +# will be sent by stress tests +# +org.exolab.jmscts.stress.Send0KTest.count=1000 +org.exolab.jmscts.stress.ReceiveSize0KTest.count=1000 +org.exolab.jmscts.stress.SendReceive0KTest.count=1000 +org.exolab.jmscts.stress.SendReceive2Size0KTest.count=1000 diff --git a/systest/jmscts/config/log4j.xml b/systest/jmscts/config/log4j.xml new file mode 100755 index 0000000000..16ee719c25 --- /dev/null +++ b/systest/jmscts/config/log4j.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/systest/jmscts/config/providers.xml b/systest/jmscts/config/providers.xml new file mode 100755 index 0000000000..d22d698224 --- /dev/null +++ b/systest/jmscts/config/providers.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + ActiveMQ + org.exolab.jmscts.activemq.ActiveMQProvider + + + + activemq.xml + + + + diff --git a/systest/jmscts/maven.xml b/systest/jmscts/maven.xml new file mode 100755 index 0000000000..b0ccf1da16 --- /dev/null +++ b/systest/jmscts/maven.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/systest/jmscts/project.xml b/systest/jmscts/project.xml new file mode 100755 index 0000000000..9d40651953 --- /dev/null +++ b/systest/jmscts/project.xml @@ -0,0 +1,135 @@ + + + 3 + ${basedir}/../../../etc/project.xml + + JMSCTS :: ActiveMQ Provider + jmscts-activemq + + org.exolab.jmscts.activemq + + ActiveMQ provider for the JMSCTS tests + + + + + + + jmscts + jmscts + 0.5-b2 + + + + exolabcore + exolabcore + 0.3.7 + http://openjms.sourceforge.net/ + + true + + + + + commons-cli + commons-cli + 1.0 + http://jakarta.apache.org/commons/cli/ + + true + + + + + castor + castor + 0.9.5 + http://castor.exolab.org/ + + true + + + + + log4j + log4j + 1.2.8 + + + + xalan + xalan + 2.5.1 + http://xml.apache.org/xalan-j/ + + root + true + + + + xerces + xerces + 2.3.0 + + true + + + + xml-apis + xml-apis + 1.0.b2 + + true + + + + + + activemq + activemq + ${pom.currentVersion} + + true + + + + + activeio + activeio + ${activeio_version} + + true + + + + + springframework + spring + ${spring_version} + http://www.springframework.org + + true + + + + + jdbm + jdbm + ${jdbm_version} + + true + + + + + + + + dev@activemq.codehaus.org + src/java + + + + maven-javadoc-plugin + + diff --git a/systest/jmscts/resources/coverage-requirements.xsl b/systest/jmscts/resources/coverage-requirements.xsl new file mode 100755 index 0000000000..0294e49a75 --- /dev/null +++ b/systest/jmscts/resources/coverage-requirements.xsl @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + <xsl:value-of select="concat('Requirement: ',$reqId)"/> + + + +

+ + + + + +
+ + + + + + + + + + +

+ +

+

+ See: +

    + + +
+

+ +

+ + + + + + + + + + + + + + + +
Test CaseTestsFailures
+ + + + + + + + + + + +
+

+
+
+ + + + + + + +
  • + + + + Section , + + + + Table + + + + + + +
  • +
    + + diff --git a/systest/jmscts/resources/coverage-tests.xsl b/systest/jmscts/resources/coverage-tests.xsl new file mode 100755 index 0000000000..8b3b642792 --- /dev/null +++ b/systest/jmscts/resources/coverage-tests.xsl @@ -0,0 +1,193 @@ + + + + + + + + + + + + +
    + + + + + +
    + +
    +
    +
    + + + + + + + + + + + + + + + +

    + +

    +

    + Requirements: +

    +

    +
    + + + +

    + + + + + + + + + + + + +
    RunConnectionFactoryDestinationDeliverySessionConsumerMessagePass
    +

    +
    +
    + + + +

    + + + + + + + + + + + + + + + + +
    RunDescription
    + + + + + + + +
    + +
    +
    +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No + + Yes + + + + + + + + + + + AUTO + CLIENT + DUPS_OK + + + + + + + + + + durable synchronous + + + durable asynchronous + + + + + + + +
    + diff --git a/systest/jmscts/resources/coverage.xsl b/systest/jmscts/resources/coverage.xsl new file mode 100755 index 0000000000..41a95f15ed --- /dev/null +++ b/systest/jmscts/resources/coverage.xsl @@ -0,0 +1,266 @@ + + + + + + + + + + + + + + + + + +
    + + +
    + +
    + + + +
    + +
    + + +
    + +
    + + + +

    + + + + + + + + + + + + + + + + + +
    Passed
    Failed
    Untested
    Total
    +

    +
    +
    + + + +

    + + + + + + + + + + + + + +
    Passed
    Failed
    Total
    +

    +
    +
    + + + +

    + + + + + + + + +
    RequirementTests
    +

    +
    +
    + + + +

    + + + + + + + + + +
    RequirementTestsFailures
    +

    +
    +
    + + + +

    + + + + + +
    Requirement
    +

    +
    +
    + + + +

    + + + + + + + + +
    Test CaseTests
    +

    +
    +
    + + + +

    + + + + + + + + + +
    Test CaseTestsFailures
    +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/systest/jmscts/resources/maven.css b/systest/jmscts/resources/maven.css new file mode 100755 index 0000000000..8ab262e542 --- /dev/null +++ b/systest/jmscts/resources/maven.css @@ -0,0 +1,96 @@ +body { + background: #fff; + color: #000; + } + +.app h3 { + color: #fff; + background-color: #036; + } + +.app h4 { + color: #fff; + background-color: #888; + } + +.a td { + background: #ddd; + color: #000; + } + +.b td { + background: #efefef; + color: #000; + } + +.app th { + background-color: #bbb; + color: #fff; + } + +div#banner { + border-top: 1px solid #369; + border-bottom: 1px solid #003; + } + +#banner, #banner td { + background: #fff; + color: #fff; + } + +#leftcol { + background: #eee; + color: #000; + border-right: 1px solid #aaa; + border-bottom: 1px solid #aaa; + border-top: 1px solid #fff; +} + +#navcolumn { + background: #eee; + color: #000; + border-right: none; + border-bottom: none; + border-top: none; + } + +#breadcrumbs { + background-color: #ddd; + color: #000; + border-top: 1px solid #fff; + border-bottom: 1px solid #aaa; + } + +#source { + background-color: #fff; + color: #000; + border-right: 1px solid #888; + border-left: 1px solid #888; + border-top: 1px solid #888; + border-bottom: 1px solid #888; + margin-right: 7px; + margin-left: 7px; + margin-top: 1em; + } + +#source pre { + margin-right: 7px; + margin-left: 7px; + } + +a:link, #breadcrumbs a:visited, #navcolumn a:visited, .app a:visited, .tasknav a:visited { + color: blue; + } + +a:active, a:hover, #leftcol a:active, #leftcol a:hover { + color: #f30 !important; + } + +a:link.selfref, a:visited.selfref { + color: #555 !important; + } + +h3, h4 { + margin-top: 1em; + margin-bottom: 0; + } diff --git a/systest/jmscts/resources/metadata.xml b/systest/jmscts/resources/metadata.xml new file mode 100755 index 0000000000..10224de455 --- /dev/null +++ b/systest/jmscts/resources/metadata.xml @@ -0,0 +1,4250 @@ + + + + + + org.exolab.jmscts.test.message.properties.PropertyTypeTest + + This class tests message property types + + + suite + + Sets up the test suite + + + + testObjectPropertyTypes + + Verifies valid and invalid object property types. + + + + + testPropertyTypes + + Verifies the supported primitive property types. + + + + + + + org.exolab.jmscts.test.session.ReceiverCloseTest + + This class tests the behaviour of closing a session while a receiver is active. + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + share + + Returns if this test can share resources with other test cases. This implementation always returns false, to ensure that a new connection is created for each test. + + + + startConnection + + Returns if the connection should be started prior to running the test. This implementation always returns false to avoid conflicts with test cases + + + + suite + + Sets up the test suite + + + + testSessionClose + + Verifies that the receive timers for a closed session continue to advance, so receives may time out and return a null message while the session is stopped. + + + + + + + org.exolab.jmscts.test.session.DupsAckTest + + This class tests the behaviour of consumers on sessions created with the Session.DUPS_OK_ACKNOWLEDGE message acknowledgment mode.
    +
    + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite. + + + + testDupsOKAcknowledgement + + Verifies dups ok acknowledgement functionality. Creates a consumer, send n messages, receives them, and closes the consumer. Creates another consumer and verifies that 0-n messages are be received. + + + +
    + + + org.exolab.jmscts.test.session.clientack.SubscriberRedeliveredTest + + This class tests the behaviour of the JMSRedelivered flag when multiple topic subscribers subscribe to the same topic, and one of the CLIENT_ACKNOWLEDGE sessions is recovered. + + + + + + checkJMSRedelivered + + Verifies that the JMSRedelivered property matches that expected + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + main + + The main line used to execute this test + + + + suite + + Sets up the test suite. + + + + testJMSRedelivered + + Verifies that messages received after TopicSession.recover() have their JMSRedelivered flag set to true, and that the same messages received via another TopicSession have their JMSRedelivered flag set to false. + + + + + + + org.exolab.jmscts.stress.SendReceive0KTest + + Performs a stress test using one producer, one consumer and empty messages. + + + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + getMessagePopulator + + Get the message populator. This implementation returns null + + + + suite + + Sets up the test suite + + + + test + + Performs a stress test using:
    • one producer
    • one consumer
    • one connection
    • one destination
    The producer and consumer run concurrently.
    For CLIENT_ACKNOWLEDGE sessions, each message is acknowledged. +
    +
    +
    + + + org.exolab.jmscts.test.selector.BooleanTest + + This class tests selectors containing boolean literals. + + + + suite + + Sets up the test suite + + + + testEquals1 + + Verifies that the selector true = true selects all messages + + + + + + testEquals2 + + Verifies that the selector true = false selects no messages + + + + + + testFalse + + Verifies that the selector false selects no messages + + + + + + + testFalseCase + + Verifies that the selector FALSE selects all messages + + + + + + + testGreaterEquals + + Verifies that the selector false >= true throws InvalidSelectorException + + + + + + testGreaterThan + + Verifies that the selector false > true throws InvalidSelectorException + + + + + + testLessEquals + + Verifies that the selector false <= true throws InvalidSelectorException + + + + + + testLessThan + + Verifies that the selector false < true throws InvalidSelectorException + + + + + + testNotEquals1 + + Verifies that the selector false <> true selects all messages + + + + + + testNotEquals2 + + Verifies that the selector false <> false selects no messages + + + + + + testTrue + + Verifies that the selector true selects all messages + + + + + + + testTrueCase + + Verifies that the selector TrUe selects all messages + + + + + + + + + org.exolab.jmscts.test.session.clientack.ClientAcknowledgeTest + + This class tests the behaviour of sessions created with the Session.CLIENT_ACKNOWLEDGE message acknowledgment mode. + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite. + + + + testClientAcknowledge + + Verifies client acknowledgement functionality. For each destination, send n messages, and then receive them. Acknowledge the last message received, recover the session, and verify that no other messages are received. + + + + + + testPartialClientAcknowledge + + Verifies client acknowledgement functionality. For each destination, send n messages, and then receive them. Acknowledge the middle message received for each destination, recover the session, and verify that the messages can be received again. The messages received after recovery should have their JMSRedelivered flag set to true. + + + + + + + testRecover + + Verifies session recovery behaviour. For each destination, send n messages, and then receive them. Recover the session, and verify that the messages can be received again. The messages received after recovery should have their JMSRedelivered flag set to true. + + + + + + + + org.exolab.jmscts.test.session.ListenerCloseTest + + This class tests the behaviour of stopping and closing a connection while a listener is active. NOTE: Temporary destinations cannot be used as a separate connection is required to verify that the test is successful. + + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + share + + Returns if this test can share resources with other test cases. This implementation always returns false, to ensure that a new connection is created for each test. + + + + startConnection + + Returns if the connection should be started prior to running the test. This implementation always returns false to avoid conflicts with test cases + + + + suite + + Sets up the test suite + + + + testSessionClose + + Verify that running MessageListeners have full access to session if the session is closed. + + + + + + + org.exolab.jmscts.test.session.clientack.CloseTest + + This class tests the behaviour of closing sessions created with the Session.CLIENT_ACKNOWLEDGE message acknowledgment mode. + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite. + + + + testConnectionClose + + Verifies that closing a connection does not force an acknowledgement of client-acknowledged sessions. + + + + + testSessionClose + + Verifies that closing a session does not force an acknowledgement of client-acknowledged sessions. + + + + + + + org.exolab.jmscts.test.message.properties.PropertyConversionTest + + This class tests message property conversion + + + checkDouble + + Checks that a double property equals the expected value + + + + checkFloat + + Checks that a float property equals the expected value + + + + expectConversionException + + For each class 'A' listed in classes, converts the range of values associated with 'A' to a String property, and then verifies that the conversion to an instance of the primitive fails + + + + expectConversionException + + For each value in an array, convert them to a string, and attempt conversion using the supplied primitive's message accessor method. The operation is expected to fail. + + + + expectException + + Expect an exception when converting from one primitive type to another + + + + suite + + Sets up the test suite + + + + testBoolean + + Verifies boolean property conversion + + + + + testByte + + Verifies byte property conversion + + + + + testDouble + + Verifies double property conversion + + + + + testFloat + + Verifies float property conversion + + + + + testInt + + Verifies int property conversion + + + + + testLong + + Verifies long property conversion + + + + + testNullToPrimitive + + Verifies that attempting to read a null as a primitive type is equivalent to calling the primitive's corresponding valueOf(String) conversion method with a null value. + + + + + testShort + + Verifies short property conversion + + + + + testStringBoolean + + Verifies string <-> boolean property conversions + + + + + testStringByte + + Verifies string <-> byte property conversions + + + + + testStringDouble + + Verifies string <-> double property conversions + + + + + testStringFloat + + Verifies string <-> float property conversions + + + + + testStringInt + + Verifies string <-> int property conversions + + + + + testStringLong + + Verifies string <-> long property conversions + + + + + testStringObject + + Verifies string <-> object property conversions + + + + + testStringShort + + Verifies string <-> short property conversions + + + + + + + org.exolab.jmscts.test.session.NonTransactedTest + + This class verifies that methods used only for transacted sessions throw IllegalStateException if invoked + + + + + + suite + + Sets up the test suite + + + + testCommit + + Verifies that an IllegalStateException is thrown if Session.commit() is invoked for a non-transacted sesssion + + + + + testRollback + + Verifies that an IllegalStateException is thrown if Session.rollback() is invoked for a non-transacted sesssion + + + + + + + org.exolab.jmscts.test.message.readwrite.ReadWriteTest + + This class tests that message properties and message bodies may be read and written on creation of a new message + + + checkBytesStreamReadableOnCreation + + Verifies that attempting to read a BytesMessage or StreamMessage on creation MessageNotReadableException, until reset() is invoked, and that subsequent reads throw MessageEOFException + + + + getMessagePopulator + + Get the message populator. This implementation always returns null + + + + suite + + Sets up the test suite + + + + testBytesReadableOnCreation + + Verifies that attempting to read a BytesMessage on creation throws MessageNotReadableException, until BytesMessage.reset() is invoked, and that subsequent reads throw MessageEOFException + + + + + + + + testReadableOnCreation + + Verifies that a message is readable on creation. + + + + + + + + + testStreamReadableOnCreation + + Verifies that attempting to read a StreamMessage on creation throws MessageNotReadableException, until StreamMessage.reset() is invoked, and that subsequent reads throw MessageEOFException + + + + + + + + testWriteableOnClear + + Verifies that a message is writeable after clearing its body. + + + + + testWriteableOnCreation + + Verifies that a message is writable on creation. + + + + + + + + org.exolab.jmscts.test.message.clear.RecoverClearTest + + This class tests the behaviour of Message.clearBody() and Message.clearProperties() for CLIENT_ACKNOWLEDGE sessions where the session is recovered. + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + testRecover + + Verifies that clearing the properties and bodies of received messages doesn't affect the messages received after recovering the session + + + + + + verify + + Receive messages, and verify them + + + + + + org.exolab.jmscts.test.connection.ReceiverTest + + This class tests the behaviour of stopping and closing a connection while a receiver is active. + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + share + + Returns if this test can share resources with other test cases. This implementation always returns false, to ensure that a new connection is created for each test. + + + + startConnection + + Returns if the connection should be started prior to running the test. This implementation always returns false to avoid conflicts with test cases + + + + suite + + Sets up the test suite + + + + testConnectionClose + + Verifies that the receive timers for a closed connection continue to advance, so receives may time out and return a null message as the connection is closed. + + + + + testConnectionRestart + + Verifies that the receive timers for a stopped connection continue to advance, so that when the connection is stopped, a waiting receiver can subsequently receive a message when the connection is started again + + + + + testConnectionStop + + Verifies that the receive timers for a stopped connection continue to advance, so receives may time out and return a null message while the connection is stopped. + + + + + + + org.exolab.jmscts.test.connection.AuthTest + + This class tests connection authorisation + + + suite + + Sets up the test suite + + + + testInvalidQueueAuth + + Verifies that a QueueConnection cannot be created, when using an invalid username and password + + + + + + testInvalidTopicAuth + + Verifies that a TopicConnection cannot be created, when using an invalid username and password + + + + + + testQueueAuth + + Verifies that a QueueConnection can be created, using a valid username and password + + + + + + testTopicAuth + + Verifies that a TopicConnection can be created, using a valid username and password + + + + + + + + org.exolab.jmscts.test.topic.DuplicateDurableSubscriberTest + + This class tests the behaviour of durable TopicSubscribers
    +
    + + + + suite + + Sets up the test suite + + + + testDuplicateSubscriber + + Verifies that creating a duplicate subscriber in a different session but same connection throws JMSException + + + + + testDuplicateSubscriberPerSession + + Verifies that creating a duplicate durable subscriber in the same session throws JMSException + + + +
    + + + org.exolab.jmscts.stress.SendReceive2Size0KTest + + Performs a stress test using one producer, two consumers and empty messages. + + + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + getMessagePopulator + + Get the message populator. This implementation returns null + + + + suite + + Sets up the test suite + + + + test + + Performs a stress test using:
    • one producer
    • two consumers
    • one connection
    • one destination
    The producer and consumers run concurrently.
    For CLIENT_ACKNOWLEDGE sessions, each message is acknowledged. +
    +
    +
    + + + org.exolab.jmscts.test.message.readwrite.SendReceiveReadWriteTest + + This class tests that message properties and message bodies are writeable on send, and readable on receipt + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + getMessagePopulator + + Get the message populator. This implementation always returns null + + + + suite + + Sets up the test suite + + + + testReadOnlyOnReceipt + + Verifies that a message's user properties and body are read-only on receipt, and that they may be subsequently cleared. + + + + + testWriteableOnSend + + Verifies that the same message may be populated and sent more than once. + + + + + + + org.exolab.jmscts.stress.SendReceive50Size0KTest + + Performs a stress test using one producer, fifty consumers and empty messages. + + + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + getMessagePopulator + + Get the message populator. This implementation returns null + + + + suite + + Sets up the test suite + + + + test + + Performs a stress test using:
    • one producer
    • fifty consumers
    • one connection
    • one destination
    The producer and consumers run concurrently.
    For CLIENT_ACKNOWLEDGE sessions, each message is acknowledged. +
    +
    +
    + + + org.exolab.jmscts.test.message.stream.StreamMessageTest + + This class tests the StreamMessage message type + + + + getMessagePopulator + + Get the message populator. This implementation always returns null + + + + populateByteArray + + Helper to return a byte array of the specified length, populated with an incrementing sequence of values + + + + read + + Read an object of the specified type + + + + readNull + + Attempt to read a null as a particular type + + + + suite + + Sets up the test suite + + + + testConversion + + Verifies valid conversions for all types except String (this is handled by testStringConversion()). + + + + + testFullReadBytes + + Verifies that readBytes can read an entire byte array, and returns -1 on the subsequent call. + + + + + testIncrementalReadBytes + + Verifies that readBytes can read be invoked incrementally. + + + + + testInvalidNumericConversion + + Verifies invalid string to numeric conversions. + + + + + testInvalidObject + + Verifies that an invalid object being written using the writeObject() method throws MessageFormatException + + + + + testNull + + Verifies null conversions. + + + + + testPartialReadBytes + + Verifies that invoking any read method when a partial byte array has been read throws MessageFormatException. + + + + + testReadBytesClearBody1 + + Verifies that invoking readBytes() followed by clearBody() followed by readBytes() returns the expected result, when the first readBytes() call has not completed reading the array field. This verifies that clearBody() correctly clears the state of the message. + + + + + testReadBytesClearBody2 + + Verifies that invoking readBytes() followed by clearBody() followed by readObject() returns the expected result, when the readBytes() call has not completed reading the array field. This ensures that clearBody() correctly clears the state of the message. + + + + + testReadBytesReset1 + + Verifies that invoking readBytes() followed by reset() followed by readBytes() returns the expected result, when the first readBytes() call has not completed reading the array field. This verifies that reset() correctly resets the state of the message. + + + + + testReadBytesReset2 + + Verifies that invoking readBytes() followed by reset() followed by readObject() returns the expected result, when the readBytes() call has not completed reading the array field. This verifies that reset() correctly resets the state of the message. + + + + + testReadFailure + + Verifies that if a read method throws MessageFormatException the current position of the read pointer is not incremented, and that a subsequent read is capable of recovering from the exception by re-reading the data as a different type. + + + + + testReadWriteBytes + + Verifies that readBytes returns that written by the writeBytes methods. + + + + + + testStringConversion + + Verifies valid string conversions. + + + + + testWriteBytes + + Verifies that invoking writeBytes does not modify the source array + + + + + + testWriteObject + + Verifies that writeObject() can handle all supported types + + + + + write + + Write an object using the appropriate write<Object>() method + + + + + + org.exolab.jmscts.test.connection.ClientIdentifierTest + + This class verifies that the client identifier can be set, but only on creation of a connection. + + + expectFailure + + Expect the setClientID operation to fail + + + + startConnection + + Returns true if the connection should be started prior to running the test. This implementation returns false, as it would affect the behaviour of {@link #testSetOnCreation} + + + + suite + + Sets up the test suite + + + + testDuplicateClientID + + Verifies that two connections may not have the same client identifier + + + + + testSetAfterClose + + Verifies that the client identifier cannot be set on a closed connection + + + + + testSetAfterCreateSession + + Verifies that the client identifier cannot be set after a session has been created + + + + + testSetAfterListenerRegistration + + Verifies that the client identifier cannot be set after an exception listener has been registered + + + + + testSetAfterStart + + Verifies that the client identifier cannot be set after the connection has been started + + + + + testSetAfterStop + + Verifies that the client identifier cannot be set after the connection has stopped + + + + + testSetOnCreation + + Verifies that the client identifier can be set on a connection, just after it is created, but not subsequently. + + + + + + + + org.exolab.jmscts.test.message.copy.MapMessageTest + + This class tests that MapMessage copies byte array content + + + + getMessagePopulator + + Get the message populator. This implementation always returns null + + + + suite + + Sets up the test suite + + + + testByteArrayCopy + + Verifies that MapMessage.setObject() copies byte arrays + + + + + testPartialSetBytesCopy + + Verifies that MapMessage.setBytes(byte[], int, int) takes a copy of the byte array. + + + + + testSetBytesCopy + + Verifies that MapMessage.setBytes(byte[]) takes a copy of the byte array. + + + + + + + org.exolab.jmscts.test.asf.ConnectionConsumerTest + + This class tests the behaviour of ConnectionConsumers + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + test + + Verifies that a ConnectionConsumer can be created + + + + + + + org.exolab.jmscts.test.message.clear.RollbackClearTest + + This class tests the behaviour of Message.clearBody() and Message.clearProperties() for transacted sessions where the session is rolled back. + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + testRollback + + Verifies that clearing the properties and bodies of received messages doesn't affect the messages received after rolling back the session + + + + + + verify + + Receive messages, and verify them + + + + + + org.exolab.jmscts.test.session.MessageListenerTest + + This class tests that MessageListeners are invoked serially. + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite. + + + + testSerialInvocation + + Verifies that message listeners are invoked serially. + + + + + + + org.exolab.jmscts.test.message.header.JMSDestinationTest + + This class tests the JMSDestination message property + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + testJMSDestination + + Verifies that the JMSDestination is assigned when a message is sent, and that the received message has the same JMSDestination value as that sent + + + + + testJMSDestinationOnResend + + Verifies that the JMSDestination is assigned when a message is resent to a different destination + + + + + + + org.exolab.jmscts.test.connection.StopStartTest + + This class tests the behaviour of Connection.stop and Connection.start + + + share + + Returns if this test can share resources with other test cases. This implementation always returns false, to ensure that a new connection is created for each test. + + + + startConnection + + Returns if the connection should be started prior to running the test. This implementation always returns false to avoid conflicts with test cases + + + + suite + + Sets up the test suite + + + + testStartForStartedConnection + + Verifies that invoking Connection.start() for a started connection has no effect. + + + + + testStopForStoppedConnection + + Verifies that invoking Connection.stop() for a stopped connection has no effect. + + + + + + + org.exolab.jmscts.test.session.transacted.CommitTest + + This class tests session commit functionality + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite. + + + + testCommit + + Verifies session commit behaviour + + + + + testCommitForClosedEndpoint + + Verifies that messages are sent on commit even if their producer has been closed, and that consumed messages are acknowledged even if the consumer that received them has been closed prior to commit + + + + + + + org.exolab.jmscts.test.connection.ConnectionCloseTest + + This class tests the behaviour of Connection.close + + + share + + Returns if this test can share resources with other test cases. This implementation always returns false, to ensure that a new connection is created for each test. + + + + suite + + Sets up the test suite + + + + testCloseForClosedConnection + + Verifies that closing a closed connection does not produce an exception. + + + + + testExceptionOnClose + + Verifies that a IllegalStateException is thrown for any Connection method (except Connection.close()), if the connection has been closed + + + + + + + org.exolab.jmscts.test.message.object.ObjectMessageTest + + This class tests the ObjectMessage message type + + + + getMessagePopulator + + Get the message populator. This implementation always returns null + + + + suite + + Sets up the test suite + + + + testNull + + Verifies that ObjectMessage supports nulls + + + + + + + org.exolab.jmscts.test.producer.ttl.TopicPublisherTest + + This class tests the behaviour of the time-to-live methods and their affect on the JMSExpiration, for TopicPublishers + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + testDefaultTTL + + Verifies that the default time-to-live for a publisher equals 0, and that the JMSExpiration property is set to 0 on send and receive when the default time-to-live is used. + + + + + + + + + testDefaultTTLForAnonTopic + + Verifies that the default time-to-live for a publisher constructed with no destination equals 0, and that the JMSExpiration property is set to 0 on send and receive when the default time-to-live is used. + + + + + + + + + testPublishWithTTL + + Verifies that the time-to-live set at publication is used when a message is sent, and that the JMSExpiration property on the received message equals that sent. + + + + + + + testPublishWithTTLAndAnonTopic + + Verifies that the time-to-live set at publication is used when a message is sent, using a TopicPublisher created with no default topic, and that the JMSExpiration property on the received message equals that sent. + + + + + + + testSetTTL + + Verifies that the time-to-live can be set on a publisher, that the value is used when no time-to-live is provided when a message is sent, and that the JMSExpiration property on the received message equals that sent. + + + + + + + + testSetTTLWithAnonTopic + + Verifies that the time-to-live can be set on a publisher created with no default topic, that the value is used when no time-to-live is provided when a message is sent, and that the JMSExpiration property on the received message equals that sent. + + + + + + + + verifyPublish + + Publishes a message using the TopicPublisher.publish(Message) method and verifies that the JMSExpiration property is set correctly on publish, and is the same on receipt + + + + verifyPublishWithTopic + + Publishes a message using the TopicPublisher.publish(Topic, Message) method and verifies that the JMSExpiration property is set correctly + + + + + + org.exolab.jmscts.test.session.transacted.RollbackTest + + This class tests session rollback functionality. + + + + + checkRedelivered + + Helper to compare the JMSRedelivered property of a list of messages against that expected + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite. + + + + testClose + + Verifies that calling Session.rollback() for a closed session throws IllegalStateException + + + + + testRollback + + Verifies session rollback behaviour + + + + + + + org.exolab.jmscts.stress.Send0KTest + + Performs a stress test using one producer and empty messages. + + + + + + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + getMessagePopulator + + Get the message populator. This implementation returns null + + + + shouldCreateMessage + + Determines if messages should be pre-created and populated for the test. + + + + suite + + Sets up the test suite + + + + test + + Performs a stress test using:
    • a single producer
    • a single destination
    • empty messages
    +
    +
    +
    + + + org.exolab.jmscts.test.message.copy.MessageCopyTest + + This class tests that message properties and message bodies are copied on send and receive. + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + getMessagePopulator + + Get the message populator. This implementation always returns null + + + + suite + + Sets up the test suite + + + + testCopyOnSend + + Verifies that messages are copied by the provider on send. + + + + + + + org.exolab.jmscts.test.producer.ttl.QueueSenderTest + + This class tests the behaviour of the time-to-live methods and their affect on the JMSExpiration, for QueueSenders + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + testDefaultTTL + + Verifies that the default time-to-live for a sender equals 0, and that the JMSExpiration property is set to 0 on send and receive when the default time-to-live is used. + + + + + + + + + testDefaultTTLForAnonQueue + + Verifies that the default time-to-live for a sender constructed with no destination equals 0, and that the JMSExpiration property is set to 0 on send and receive when the default time-to-live is used. + + + + + + + + + testSendWithTTL + + Verifies that the time-to-live set at send is used when a message is sent, and that the JMSExpiration property on the received message equals that sent + + + + + + + testSendWithTTLAndAnonQueue + + Verifies that the time-to-live set at send is used when a message is sent, using a QueueSender created with no default queue, and that the JMSExpiration property on the received message equals that sent + + + + + + + testSetTTL + + Verifies that the time-to-live can be set on a sender, that the value is used when no time-to-live is provided when a message is sent, and that the JMSExpiration message property is set correctly on send and receive + + + + + + + + testSetTTLWithAnonQueue + + Verifies that the time-to-live can be set on a sender created with no default queue, that the value is used when no time-to-live is provided when a message is sent, and that the JMSExpiration property on the received message equals that sent + + + + + + + + verifySend + + Sendes a message using the QueueSender.send(Message) method and verifies that the JMSExpiration property is set correctly on send, and is the same on receipt + + + + verifySendWithQueue + + Sendes a message using the QueueSender.send(Queue, Message) method and verifies that the JMSExpiration property is set correctly + + + + + + org.exolab.jmscts.test.session.CloseTest + + This class tests the behaviour of Session.close + + + invokeSessionMethods + + Invoke session methods + + + + share + + Returns if this test can share resources with other test cases. This implementation always returns false, to ensure that a new session is created for each test. + + + + suite + + Sets up the test suite + + + + testCloseForClosedSession + + Verifies that closing a closed session has no effect. + + + + + testExceptionOnClose + + Verifies that IllegalStateException is thrown for any Session method (except Session.close()) when invoking methods on a closed session. + + + + + + + org.exolab.jmscts.test.session.transacted.RecoverTest + + This class verifies that methods used only for transacted sessions throw IllegalStateException if invoked + + + suite + + Sets up the test suite + + + + testRecover + + Verifies that an IllegalStateException is thrown if Session.recover() is invoked for a transacted sesssion + + + + + + + + org.exolab.jmscts.test.message.map.MapMessageTest + + This class tests the MapMessage message type. + + + + checkEmpty + + Verifies that a getMapNames() returns an empty Enumeration for an empty message + + + + compare + + Compares two objects, failing if they are not the same + + + + get + + Helper to get a message property, invoking the appropriate get() method based on the type of the property + + + + getMessagePopulator + + Get the message populator. This implementation always returns null + + + + getNull + + Helper to attempt to convert a null property + + + + populate + + Populates a message with values + + + + set + + Helper to set a message property, invoking the appropriate set() method based on the type of the property + + + + suite + + Sets up the test suite + + + + testCase + + Verifies that field names are case sensitive NOTE: this is not explicitly mentioned in the specification, but is in keeping with property name requirements + + + + + testConversion + + Verifies valid conversions for all types except String (this is handled by testStringConversion()). + + + + + testGetMapNames + + Verifies that getMapNames() returns a name for each element in the set + + + + + testInvalidConversion + + Verifies that attempting to perform invalid invalid conversions throws MessageFormatException. + + + + + testInvalidNumericConversion + + Verifies that attempting to perform invalid invalid string to numeric conversions throws NumberFormatException + + + + + testInvalidObject + + Verifies that attempting to set an invalid object using the setObject() method throws MessageFormatException + + + + + testItemExists + + Verifies the behaviour of the itemExists() method + + + + + testMap + + Verifies that elements populated in a map are identical to those retrieved + + + + + testNullConversion + + Verifies behaviour of conversions from null + + + + + testSetObject + + Verifies that setObject() can handle all supported types + + + + + testStringConversion + + Verifies valid string conversions + + + + + testUnset + + Verifies that getting a MapMessage field for a field name that has not been set is handled as if the field exists with a null value. + + + + + + + org.exolab.jmscts.test.message.properties.IdentifierTest + + This class tests message property identifiers. + + + checkIdentifiers + + Check that a list of identifiers can/can't be used as property names + + + + expectFailure + + Expect the set property methods to fail for an invalid identifier + + + + expectSuccess + + Expect the set property methods to succeed for a valid identifier + + + + suite + + Sets up the test suite + + + + testIdentifierCase + + Verifies that property names are case sensitive + + + + + testInvalidIdentifiers + + Verifies that invalid identifiers can't be used as property names + + + + + testJMSXProviderIdentifiers + + Tests that JMSX prefixed identifiers set by the provider cannot be set by clients. This should only apply to those identifiers that the provider supports + + + + + testReservedWords + + Verifies that using a reserved word as a property name throws an exception. + + + + + testValidIdentifiers + + Verifies that valid identifiers may be used as property names + + + + + toLowerCase + + Returns a lowercase version of an array of Strings + + + + + + org.exolab.jmscts.test.message.header.JMSReplyToTest + + This class tests the JMSReplyTo message property. + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + testReplyTo + + Verifies that the reply-to address on a received message can be used to send and subsequently receive a message. + + + + + + + org.exolab.jmscts.test.session.AutoAckTest + + This class tests the behaviour of consumers on sessions created with the Session.AUTO_ACKNOWLEDGE message acknowledgment mode. + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite. + + + + testAutoAcknowledgement + + Verifies auto acknowledgement functionality. Creates a consumer, send n messages, receives them, and closes the consumer. Creates another consumer and verifies that no messages can be received. + + + + + + + org.exolab.jmscts.test.message.clear.ClearTest + + This class tests the behaviour of Message.clearBody() and Message.clearProperties() + + + checkClearBody + + Verifies that Message.clearBody() leaves the message body empty, and doesn't clear the message properties. + + + + checkClearBodyOnCreation + + Verifies that Message.clearBody() can be invoked for a new message. + + + + getMessagePopulator + + Get the message populator. This implementation always returns null + + + + suite + + Sets up the test suite + + + + testBytesClearBody + + Verifies that BytesMessage.clearBody() leaves the message body empty, and doesn't clear the message properties. + + + + + + + testBytesClearBodyOnCreation + + Verifies that BytesMessage.clearBody() can be invoked for a new message + + + + + + + testClearBody + + Verifies that Message.clearBody() leaves the message body empty, and doesn't clear the message properties. + + + + + + + + + testClearBodyOnCreation + + Verifies that Message.clearBody() can be invoked for a new message. + + + + + + + + + testClearProperties + + Verifies that Message.clearProperties() leaves the message properties empty, and doesn't clear the message body. + + + + + testClearPropertiesOnCreation + + Verifies that Message.clearProperties() can be invoked for a new message. + + + + + testStreamClearBody + + Verifies that StreamMessage.clearBody() leaves the message body empty, and doesn't clear the message properties. + + + + + + + testStreamClearBodyOnCreation + + Verifies that StreamMessage.clearBody() can be invoked for a new message + + + + + + + + + org.exolab.jmscts.test.message.header.JMSMessageIDTest + + This class tests the JMSMessageID message property + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + testJMSMessageID + + Verifies that the JMSMessageID is assigned when a message is sent, that the received message has the same JMSMessageID value, as that sent, and that a new JMSMessageID is assigned when the message is resent + + + + + + testJMSMessageIDAssignment + + Verifies that the JMSMessageID is assigned each time a message is sent + + + + + testJMSMessageIDPrefix + + Verifies that JMSMessageID is prefixed with 'ID:' + + + + + + + + org.exolab.jmscts.test.message.header.JMSCorrelationIDTest + + This class tests the JMSCorrelationID message property + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + testCorrelationID + + Verifies that the JMSCorrelationID header property can be set, and that the received message has the same JMSCorrelationID value as that sent + + + + + testCorrelationIDAsBytes + + Verifies the behaviour of the JMSCorrelationIDAsBytes methods. + + + + + testNullCorrelationID + + Verifies that a null can be assigned to JMSCorrelationID when:
    • a message is intially constructed
    • a message is received with a non-null JMSCorrelationID
    +
    + + +
    +
    + + + org.exolab.jmscts.test.message.copy.BytesMessageTest + + This class tests that BytesMessage copies content + + + + getMessagePopulator + + Get the message populator. This implementation always returns null + + + + suite + + Sets up the test suite + + + + testPartialWriteBytesCopy + + Verifies that BytesMessage.writeBytes(byte[], int, int) takes a copy of the byte array. + + + + + testWriteBytesCopy + + Verifies that BytesMessage.writeBytes(byte[]) takes a copy of the byte array. + + + + + + + org.exolab.jmscts.test.connection.SendReceiveStopTest + + This class tests the behaviour of Connection.stop and Connection.start + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + share + + Returns if this test can share resources with other test cases. This implementation always returns false, to ensure that a new connection is created for each test. + + + + startConnection + + Returns if the connection should be started prior to running the test. This implementation always returns false to avoid conflicts with test cases + + + + suite + + Sets up the test suite + + + + testStop + + Verifies that a connection doesn't receive any messages after starting a connection, sending messages, and then stopping it. + + + + + testStoppedOnCreation + + Verifies that a connection does not receive any messages on creation. + + + + + + + + + org.exolab.jmscts.test.connection.MetaDataTest + + This class tests that connections support JMSXGroupID and JMSXGroupSeq properties. + + + suite + + Sets up the test suite + + + + testMetaData + + Verifies that the connection supports JMSXGroupID and JMSXGroupSeq properties. + + + + + + + org.exolab.jmscts.test.message.close.CloseTest + + This class tests the behaviour of messages when the associated connection or session is closed. + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + getMessagePopulator + + Get the message populator. This implementation always returns null + + + + share + + Returns if this test can share resources with other test cases. This implementation always returns false, to ensure that a new session is created for each test. + + + + suite + + Sets up the test suite + + + + testCloseConnection + + Verifies that all the methods for a message may be invoked for a closed connection, with the exception of Message.acknowledge(), which should throw IllegalStateException. + + + + + testCloseSession + + Verifies that all the methods for a message may be invoked for a closed session, with the exception of Message.acknowledge(), which should throw IllegalStateException. + + + + + + + org.exolab.jmscts.test.message.bytes.BytesMessageTest + + This class tests the BytesMessage message type. + + + + checkEndOfStream1 + + Helper to test that readBytes(byte[]) returns -1 to indicate end-of-stream + + + + checkEndOfStream2 + + Helper to test that readBytes(byte[], int) returns -1 to indicate end-of-stream + + + + checkReadBytes1 + + Helper to invoke readBytes(byte[]) and verify that the result equals that expected + + + + checkReadBytes2 + + Helper to invoke readBytes(byte[], int) + + + + getMessagePopulator + + Get the message populator. This implementation always returns null + + + + populateByteArray + + Helper to return a byte array of the specified length, populated with an incrementing sequence of values + + + + suite + + Sets up the test suite + + + + testInvalidObject + + Verifies that attempting to write an invalid object using the BytesMessage.writeObject() method throws MessageFormatException + + + + + testPartialReadBytes1 + + Verifies that the BytesMessage.readBytes(byte[], int) method can be called multiple times passing a byte array less than the length of the byte stream, and that the result matches that expected. + + + + + testPartialReadBytes2 + + Verifies that the BytesMessage.readBytes(byte[], int) method can be called multiple times passing a length parameter less than the length of the array, and that the result matches that expected. + + + + + testReadBytes + + Verifies the behaviour of the BytesMessage.readBytes(byte[]) method. + + + + + testReadBytesForEmptyStream1 + + Verifies that the BytesMessage.readBytes(byte[]) method returns -1 to indicate end-of-stream, for an empty message body. + + + + + testReadBytesForEmptyStream2 + + Verifies that the BytesMessage.readBytes(byte[], int) method returns -1 to indicate end-of-stream, for an empty message body. + + + + + testReadBytesIndexException + + Verifies that the BytesMessage.readBytes(byte[], int) method throws IndexOutOfBoundsException for invalid length parameters + + + + + testReadFailure + + Verifies that if a read method throws MessageFormatException or NumberFormatException, the current position of the read pointer is not be incremented, and that a subsequent read is capable of recovering from the exception by re-reading the data as a different type.
    NOTE: With the exception of the readUTF() method, it is difficult to conceive test cases for read methods.
    • A provider that implements BytesMessage using a DataInputStream or equivalent, is likely to only throw MessageFormatException for the readUTF() method.
      The other likely exceptions are MessageEOFException for stream overruns, and JMSException for any other error.
    • As BytesMessage does not support conversion, NumberFormatException is unlikely to be thrown.
    +
    + +
    + + testWriteObject + + Verfies that all objectified primitives can be written using the BytesMessage.writeObject() method + + + +
    + + + org.exolab.jmscts.test.connection.ListenerTest + + This class tests the behaviour of stopping and closing a connection while a listener is active. NOTE: Temporary destinations cannot be used as a separate connection is required to verify that the test is successful. + + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + runTest + + Run the test + + + + share + + Returns if this test can share resources with other test cases. This implementation always returns false, to ensure that a new connection is created for each test. + + + + startConnection + + Returns if the connection should be started prior to running the test. This implementation always returns false to avoid conflicts with test cases + + + + suite + + Sets up the test suite + + + + testConnectionClose + + Verifies that running MessageListeners have full access to connection if the connection is closed - the close invocation must wait until they complete. + + + + + testConnectionStop + + Verifies that running MessageListeners have full access to the connection, if the connection is stopped. + + + + + + + org.exolab.jmscts.test.message.properties.PropertyTest + + This class tests that message properties set before a message is sent are identical to those received. + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + testPropertyIntegrity + + Verifies that user properties are not modified when a message is sent and subsequently received. + + + + + + testPropertyNames + + Verifies that JMS standard header fields are not returned by getPropertyNames(). + + + + + + + org.exolab.jmscts.test.selector.JMSTimestampTest + + This class tests selectors containing JMSTimestamp + + + + suite + + Sets up the test suite + + + + testInvalid1 + + Verifies that the selector JMSTimestamp >= '0' throws InvalidSelectorException + + + + + + testInvalid2 + + Verifies that the selector '0' <= JMSTimestamp throws InvalidSelectorException + + + + + + testInvalid3 + + Verifies that the selector JMSTimestamp = '2001/1/1 0:0' throws InvalidSelectorException + + + + + + testJMSTimestamp1 + + Verifies that the selector JMSTimestamp >= 0 selects all messages + + + + + + + testJMSTimestamp2 + + Verifies that the selector JMSTimestamp >= {time} selects all messages, where {time} is a literal value derived from System.currentTimeMillis() + + + + + + + testJMSTimestamp3 + + Verifies that the selector JMSTimestamp < {time} selects no messages, where {time} is a literal value derived from System.currentTimeMillis() + + + + + + + + + org.exolab.jmscts.test.selector.JMSDeliveryModeTest + + This class tests selectors containing JMSDeliveryMode + + + + suite + + Sets up the test suite + + + + testInvalid1 + + Verifies that the selector JMSDeliveryMode = {non-persistent} or JMSDeliveryMode = {persistent} throws InvalidSelectorException, where {non-persistent} and {persistent} are the values of DeliveryMode.NON_PERSISTENT and DeliveryMode.PERSISTENT respectively + + + + + + testInvalid2 + + Verifies that the selector JMSDeliveryMode = 'non_persistent' throws InvalidSelectorException. + + + + + + testJMSDeliveryMode + + Verifies that messages can be selected on JMSDeliveryMode, using the selector JMSDeliveryMode = 'PERSISTENT' or JMSDeliveryMode = 'NON_PERSISTENT'. This should select all messages + + + + + + + + + org.exolab.jmscts.test.selector.AndOperatorTest + + This class tests selectors containing the AND operator. + + + + suite + + Sets up the test suite + + + + testAnd1 + + Verifies that the selector true and true selects all messages + + + + + + testAnd2 + + Verifies that the selector true and false selects no messages + + + + + + testAnd3 + + Verifies that the selector false and true selects no messages + + + + + + testAnd4 + + Verifies that the selector false and false selects no messages + + + + + + testAndCase + + Verifies that the selector true AND true selects all messages + + + + + + + testInvalidAnd1 + + Verifies that the selector and throws InvalidSelectorException + + + + + testInvalidAnd2 + + Verifies that the selector true and throws InvalidSelectorException + + + + + testInvalidAnd3 + + Verifies that the selector false and throws InvalidSelectorException + + + + + testUnsetProperty1 + + Verifies that the selector true and dummy selects no messages, for the unset property 'dummy' + + + + + + + testUnsetProperty2 + + Verifies that the selector false and dummy selects no messages, for the unset property 'dummy' + + + + + + + testUnsetProperty3 + + Verifies that the selector dummy and true selects no messages, for the unset property 'dummy' + + + + + + + testUnsetProperty4 + + Verifies that the selector dummy and false selects no messages, for the unset property 'dummy' + + + + + + + testUnsetProperty5 + + Verifies that the selector dummy and dummy selects no messages, for the unset property 'dummy' + + + + + + + + + org.exolab.jmscts.test.session.clientack.RepublishTest + + This class tests the behaviour of republishing a received message, and then acknowledging it. + + + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + testRepublish + + Verify the behaviour of republishing a received message, and then acknowledging it. + + + + + + + org.exolab.jmscts.test.selector.FloatTest + + This class tests selectors containing floating point literals and objects. + + + + suite + + Sets up the test suite + + + + testDivisionByZero + + Verifies that the selector 10 / zero = 10 / zero selects all messages, when the double property 'zero' is set, with value 0.0 + + + + + testDoubleNaN1 + + Verifies that the selector doubleNaN = doubleNaN selects no messages, when the double property 'doubleNaN' is set, with value Double.NaN (as NaN != NaN) + + + + + testDoubleNaN2 + + Verifies that the selector doubleNaN <> doubleNaN selects all messages, when the double property 'doubleNaN' is set, with value Double.NaN + + + + + testEquals1 + + Verifies that the selector 0.0 = 0.0 selects all messages + + + + + + testEquals2 + + Verifies that the selector 0.0 = 1.0 selects no messages + + + + + + testEquals3 + + Verifies that the selector 0.2 = 0.2 selects all messages + + + + + + testEquals4 + + Verifies that the selector 0.2 = 0.0 selects no messages + + + + + + testEquals5 + + Verifies that the selector 92d = 92 selects all messages + + + + + + testEquals6 + + Verifies that the selector 93f = 93 selects all messages + + + + + + testEqualsProperty + + Verifies that the selector rate = 0.2 selects all messages, when the double property 'rate' is set, with value 0.2 + + + + + + testFloatNaN1 + + Verifies that the selector floatNaN = floatNaN selects no messages, when the float property 'floatNaN' is set, with value Float.NaN (as NaN != NaN) + + + + + testFloatNaN2 + + Verifies that the selector floatNaN <> floatNaN selects all messages, when the float property 'floatNaN' is set, with value Float.NaN + + + + + testGreaterEquals1 + + Verifies that the selector 2.0 >= 1.0 selects all messages + + + + + + testGreaterEquals2 + + Verifies that the selector 1.0 >= 2.0 selects no messages + + + + + + testGreaterThan1 + + Verifies that the selector 2.0 > 1.0 selects all messages + + + + + + testGreaterThan2 + + Verifies that the selector 1.0 > 2.0 selects no messages + + + + + + testInvalid1 + + Verifies that the selector 1.0 throws InvalidSelectorException + + + + + testInvalid2 + + Verifies that the selector -1.0 throws InvalidSelectorException + + + + + testInvalid3 + + Verifies that the selector 2.0 < '3.0' throws InvalidSelectorException + + + + + testInvalid4 + + Verifies that the selector 1.0 <> false throws InvalidSelectorException + + + + + testInvalid5 + + Verifies that the selector 1a.0 = 1a.0 throws InvalidSelectorException + + + + + testLessEquals1 + + Verifies that the selector 1.0 <= 2.0 selects all messages + + + + + + testLessEquals2 + + Verifies that the selector 2.0 <= 1.0 selects no messages + + + + + + testLessThan1 + + Verifies that the selector 1.0 < 2.0 selects all messages + + + + + + testLessThan2 + + Verifies that the selector 2.0 < 1.0 selects no messages + + + + + + testNotEquals1 + + Verifies that the selector 1.0 <> 2.0 selects all messages + + + + + + testNotEquals2 + + Verifies that the selector 1.0 <> 1.0 selects no messages + + + + + + testNotEquals3 + + Verifies that the selector 1.0 <> 1.0 selects all messages + + + + + + testNotEqualsProperty + + Verifies that the selector rate <> 0.2 selects no messages, when the double property 'rate' is set, with value 0.2 + + + + + + testNumericRange + + Verifies that selectors can have approximate numeric literals in the range Double.MIN_VALUE..Double.MAX_VALUE, using the selector {min-value}={min-value} and {max-value} = {max-value} where {min-value} and {max-value} are the values of Double.MIN_VALUE and Double.MAX_VALUE respectively. This should select all messages. + + + + + + testUnaryMinus1 + + Verifies that the selector -1.0 = -1.0 selects all messages + + + + + + testUnaryMinus2 + + Verifies that the selector -1.0 = 1.0 selects no messages + + + + + + testUnaryMinus3 + + Verifies that the selector --1.0 = 1.0 selects all messages + + + + + + testUnsetProperty1 + + Verifies that the selector dummy + 10.0 = 10.0 selects no messages, for the unset property 'dummy' + + + + + + testUnsetProperty2 + + Verifies that the selector dummy - 10.0 = -10.0 selects no messages, for the unset property 'dummy' + + + + + + testUnsetProperty3 + + Verifies that the selector 10.0 + dummy = 10.0 selects no messages, for the unset property 'dummy' + + + + + + testUnsetProperty4 + + Verifies that the selector 10.0 - dummy = 0.0 selects no messages, for the unset property 'dummy' + + + + + + testUnsetProperty5 + + Verifies that the selector dummy * 10.0 = 0.0 selects no messages, for the unset property 'dummy' + + + + + + testUnsetProperty6 + + Verifies that the selector 10.0 * dummy = 0.0 selects no messages, for the unset property 'dummy' + + + + + + testUnsetProperty7 + + Verifies that the selector dummy / 10.0 = 0.0 selects no messages, for the unset property 'dummy' + + + + + + testUnsetProperty8 + + Verifies that the selector 10.0 / dummy = 0.0 selects no messages, for the unset property 'dummy' + + + + + + + + org.exolab.jmscts.test.message.copy.ObjectMessageTest + + This class tests that ObjectMessage copies content + + + + getMessagePopulator + + Get the message populator. This implementation always returns null + + + + suite + + Sets up the test suite + + + + testByteArrayCopy + + Verifies that ObjectMessage.setObject() copies byte arrays + + + + + testCopyAtConstruction + + Verifies that ObjectMessage.getObject() returns a different reference to that set, when the object is supplied at construction + + + + + testCustomObjectCopy + + Verifies that ObjectMessage.setObject() copies user objects + + + + + + + org.exolab.jmscts.stress.ReceiveSize0KTest + + Performs a stress test using one producer, one consumer and empty messages. + + + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + getMessagePopulator + + Get the message populator. This implementation returns null + + + + shouldCreateMessage + + Determines if messages should be pre-created and populated for the test. + + + + suite + + Sets up the test suite + + + + test + + Performs a stress test using:
    • a single consumer
    • empty messages
    For CLIENT_ACKNOWLEDGE sessions, each message is acknowledged. +
    +
    +
    + + + org.exolab.jmscts.test.selector.IdentifierTest + + This class tests selector identifiers. + + + + suite + + Sets up the test suite + + + + testDollars + + Verifies that identifiers may contain '$' characters, using the selector $State = 'VIC' and property '$State' with value 'VIC'. This should select all messages. + + + + + + testInvalid1 + + Verifies that the selector ~ABC = 'foo' throws InvalidSelectorException + + + + + + testInvalid2 + + Verifies that the selector Country.name = 'Australia' throws InvalidSelectorException + + + + + + testUnderscores + + Verifies that identifiers may contain '_' characters, using the selector _postcode_ = '3001' and property '_postcode_' with value '3001'. This should select all messages. + + + + + + testUserIdentifierCase + + Verifies that user identifier names are case sensitive, using the selector country = 'Australia', and property 'Country' with value 'Australia'. This shouldn't select any messages. + + + + + + + + org.exolab.jmscts.test.selector.EmptySelectorTest + + This class tests the behaviour of empty selectors. + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + getMessagePopulator + + Get the message populator. This implementation always returns null + + + + suite + + Sets up the test suite + + + + testEmpty + + Verifies that consumer creation operations accept an empty string as a valid selector + + + + + testNull + + Verifies that consumer creation operations accept null as a valid selector + + + + + + + org.exolab.jmscts.test.message.copy.StreamMessageTest + + This class verifies that StreamMessage copies content + + + + getMessagePopulator + + Get the message populator. This implementation always returns null + + + + suite + + Sets up the test suite + + + + testByteArrayCopy + + Verifies that StreamMessage.setObject() copies byte arrays + + + + + testPartialWriteBytesCopy + + Verifies that StreamMessage.writeBytes(byte[], int, int) takes a copy of the byte array. + + + + + testWriteBytesCopy + + Verifies that StreamMessage.writeBytes(byte[]) takes a copy of the byte array. + + + + + + + org.exolab.jmscts.test.topic.DurableSubscriberTest + + This class tests the behaviour of durable TopicSubscribers + + + + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + testChangeSubscription + + Verifies that a client can change an existing durable subscription by creating a durable subscriber with the same name and a new topic. + + + + + testDurableSubscriber + + Verifies that a durable subscriber can be created for a session, closed, and recreated. + + + + + testUnsubscribe + + Verifies that a durable subscriber can be created for a session, unsubscribed, and recreated + + + + + + + org.exolab.jmscts.test.selector.JMSPriorityTest + + This class tests selectors containing JMSPriority + + + + suite + + Sets up the test suite + + + + testInvalid1 + + Verifies that the selector JMSPriority >= '0' throws InvalidSelectorException + + + + + testInvalid2 + + Verifies that the selector '0' <= JMSPriority throws InvalidSelectorException + + + + + + testJMSPriority + + Verifies that messages can be selected on JMSPriority, using the selector JMSPriority <> 0. This should select all messages + + + + + + testJMSPriorityCase + + Verifies that JMS identifier names are case sensitive, using the selector jmspriority between 1 and 9. This shouldn't select any messages. + + + + + + + + org.exolab.jmscts.test.selector.StringTest + + This class tests selectors containing string literals and objects + + + + suite + + Sets up the test suite + + + + testCaseComparison1 + + Verifies that string literals are case sensitive, using the selector 'abc' = 'ABC'. This shouldn't select any messages. + + + + + + + testCaseComparison2 + + Verifies that string literals are case sensitive, using the selector 'abc' <> 'ABC'. This should select all messages. + + + + + + + testCaseComparison3 + + Verifies that string literals are case sensitive when compared with string properties, using the selector Country = 'france', and the string property 'Country', with value 'France'. This should select no messages. + + + + + + + testCheckSingleQuotes1 + + Verifies that string literals can contain embedded quotes, using the selector 'it''s' = 'it''s'. This should select all messages + + + + + + + testCheckSingleQuotes2 + + Verifies that the quotes are preserved in string literals with embedded quotes, using the selector 'it''s' = 'its'. This should select no messages + + + + + + + testEquals + + Verifies that the selector 'abc' = 'abc' selects all messages + + + + + + + testEqualsProperty + + Verifies that the selector Country = 'France' selects all messages, when the string property 'Country' is set, with value 'France' + + + + + + + testGreaterEquals1 + + Verifies that the selector 'abc' >= 'abc' throws InvalidSelectorException + + + + + + testGreaterEquals2 + + Verifies that the selector dummy >= 'abc' throws InvalidSelectorException + + + + + + testGreaterEquals3 + + Verifies that the selector 'abc' >= dummy throws InvalidSelectorException + + + + + + testGreaterThan1 + + Verifies that the selector 'abc' > 'abc' throws InvalidSelectorException + + + + + + testGreaterThan2 + + Verifies that the selector dummy > 'abc' throws InvalidSelectorException + + + + + + testGreaterThan3 + + Verifies that the selector 'abc' > dummy throws InvalidSelectorException + + + + + + testInvalid1 + + Verifies that the selector 'abc' throws InvalidSelectorException + + + + + testInvalid2 + + Verifies that the selector 'abc' = 'abc throws InvalidSelectorException + + + + + testInvalid3 + + Verifies that the selector 'abc' = abc' throws InvalidSelectorException + + + + + testInvalid4 + + Verifies that the selector 'abc = 'abc' throws InvalidSelectorException + + + + + testInvalid5 + + Verifies that the selector 'abc'''' = 'abc' throws InvalidSelectorException + + + + + testInvalid6 + + Verifies that the selector "abc" = "abc" throws InvalidSelectorException + + + + + testLessEquals1 + + Verifies that the selector 'abc' <= 'abc' throws InvalidSelectorException + + + + + + testLessEquals2 + + Verifies that the selector dummy <= 'abc' throws InvalidSelectorException + + + + + + testLessEquals3 + + Verifies that the selector 'abc' <= dummy throws InvalidSelectorException + + + + + + testLessThan1 + + Verifies that the selector 'abc' < 'abc' throws InvalidSelectorException + + + + + + testLessThan2 + + Verifies that the selector dummy < 'abc' throws InvalidSelectorException + + + + + + testLessThan3 + + Verifies that the selector 'abc' < dummy throws InvalidSelectorException + + + + + + + + org.exolab.jmscts.test.selector.BetweenOperatorTest + + This class tests selector containing the BETWEEN operator. + + + + suite + + Sets up the test suite + + + + testBetween1 + + Verifies that the selector 17 between 16 and 18 selects all messages + + + + + + testBetween2 + + Verifies that the selector 17 between 18 and 19 selects no messages + + + + + + testBetween3 + + Verifies that the selector 17 Between 17 And 17 selects all messages + + + + + + + testBetween4 + + Verifies that the selector 17 between 4 * 4 and 10 + 8 selects all messages + + + + + + testBetween5 + + Verifies that the selector 17 between 4 * 5 and 10 + 12 selects no messages + + + + + + testBetween6 + + Verifies that the selector two between one and three selects all messages, where one, two and three are integer properties with with corresponding values + + + + + + testInvalid1 + + Verifies that the selector two between '1' and '3' throws InvalidSelectorException + + + + + testInvalid2 + + Verifies that the selector one between false and true throws InvalidSelectorException + + + + + testInvalid3 + + Verifies that the selector b' between 'a' and 'c' throws InvalidSelectorException + + + + + testInvalid4 + + Verifies that the selector between 1 and 3 throws InvalidSelectorException + + + + + testInvalid5 + + Verifies that the selector not between 1 and 3 throws InvalidSelectorException + + + + + testInvalid6 + + Verifies that the selector 2 between 1, 3 throws InvalidSelectorException + + + + + testInvalid7 + + Verifies that the selector 2 between 1 and throws InvalidSelectorException + + + + + testInvalid8 + + Verifies that the selector 2 between and 3 throws InvalidSelectorException + + + + + testInvalid9 + + Verifies that the selector JMSMessageID between 1 and 10 throws InvalidSelectorException + + + + + testNotBetween1 + + Verifies that the selector 17 not between 18 and 19 selects all messages + + + + + + testNotBetween2 + + Verifies that the selector 17 not between 16 and 18 selects no messages + + + + + + testNotBetween3 + + Verifies that the selector 17 not between 17 and 17 selects no messages + + + + + + testNotBetween4 + + Verifies that the selector 17 Not Between 4 * 5 And 20 / 1 selects all messages + + + + + + + testNotBetween5 + + Verifies that the selector two not between one and three selects all messages, where one, two and three are integer properties with with corresponding values + + + + + + testUnsetProperty1 + + Verifies that the selector dummy between 1 and 10 selects no messages, for the unset property 'dummy' + + + + + + + testUnsetProperty2 + + Verifies that the selector 1 between dummy and 10 selects no messages, for the unset property 'dummy' + + + + + + + testUnsetProperty3 + + Verifies that the selector 1 between 0 and dummy selects no messages, for the unset property 'dummy' + + + + + + + + + org.exolab.jmscts.test.message.properties.JMSXGroupTest + + This class tests that connections support JMSXGroupID and JMSXGroupSeq properties
    NOTE: the specification is not clear on the behaviour of null values for these properties. +
    + + checkProperty + + Verifies that atttempting to use an invalid property value throws MessageFormatException + + + + checkSequenceValue + + Verifies a value for the JMSXGroupSeq property + + + + checkSequenceValue + + Verifies a value for the JMSXGroupSeq property + + + + suite + + Sets up the test suite + + + + testJMSGroupSeq + + Verifies that the only allowed type for JMSXGroupID is an int. + + + + + testJMSGroupSeqRange + + Verifies that JMSXGroupSeq only handles ints > 0 + + + + + testJMSXGroupID + + Verifies that the only allowed type for JMSXGroupID is a String. + + + +
    + + + org.exolab.jmscts.test.topic.NoLocalTest + + This class tests the behaviour of the noLocal argument of TopicSubscriber + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + testNoLocal + + Verifies the behaviour of the subscriber noLocal attribute + + + + + + + org.exolab.jmscts.test.message.clear.SendReceiveClearTest + + This class tests the behaviour of Message.clearBody() and Message.clearProperties() on send and receipt, against all message, delivery, and transaction types + + + + checkClearBodyOnReceipt + + Verifies that Message.clearProperties() leaves the message properties empty, and doesn't clear the message body, on receipt of a message. + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + getMessagePopulator + + Get the message populator. This implementation returns null, as population is handled by the test cases. + + + + suite + + Sets up the test suite + + + + testBytesClearBodyOnReceipt + + Verifies that BytesMessage.clearBody() leaves the message body empty, and doesn't clear the message properties, on receipt of a message. + + + + + + + testClearBodyOnReceipt + + Verifies that Message.clearBody() leaves the message body empty, and doesn't clear the message properties, on receipt of a message. + + + + + + + + + testClearOnSend + + Verifies that Message.clearProperties() and Message.clearBody() on send doesn't affect the sent message + + + + + + testClearPropertiesOnReceipt + + Verifies that Message.clearProperties() leaves the message properties empty, and doesn't clear the message body, on receipt of a message. + + + + + testStreamClearBodyOnReceipt + + Verifies that StreamMessage.clearBody() leaves the message body empty, and doesn't clear the message properties, on receipt of a message. + + + + + + + + + org.exolab.jmscts.test.message.foreign.ForeignMessageTest + + This class tests the behaviour of sending 'foreign' messages. A 'foreign' message is one whose implementation is not one of those of the target provider. + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-create destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + test + + Verifies that 'foreign' messages are supported by the JMS provider + + + + + + + org.exolab.jmscts.test.session.transacted.CloseTest + + This class tests session rollback functionality. + + + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite. + + + + testRollbackForClosedConnection + + Verifies that closing a connection rolls back the transactions in progress on its transacted sessions. + + + + + testRollbackForClosedSession + + Verifies that closing a session rolls back the transaction in progress + + + + + + + org.exolab.jmscts.test.producer.ttl.ExpirationTest + + This class tests message expiration + + + checkExpiration + + Sends messageCount messages with time-to-live timeToLive, and messageCount messages which don't expire, and verifies that after sleeping timeToLive + EXPIRATION_INTERVAL milliseconds, only the non-expiring messages may be received + + + + checkExpiration + + Sends messageCount messages with time-to-live timeToLive, and messageCount messages which don't expire, and verifies that after sleeping timeToLive + EXPIRATION_INTERVAL milliseconds, only the non-expiring messages may be received + + + + getDestinations + + Returns the list of destination names used by this test case. These are used to pre-administer destinations prior to running the test case. + + + + suite + + Sets up the test suite + + + + testExpiration + + Verifies that messages expire.

    It sends two groups of messages - one group which expires after 1 second, and another which don't expire, and verifies that after the first group should have expired, only the non-expiring group can be received.

    If the JMS provider doesn't support immediate expiration of messages, the org.exolab.jmscts.test.producer.ttl.ExpirationTest.expirationInterval property should be configured in jmscts.properties to delay the test until the expired messages are collected. + + + + + + + testExpiration2DiffSubscribers + + Verifies that messages to topics expire, when one subscriber is durable, and the other non-durable.

    It sends two groups of messages - one group which expires after 1 second, and another which don't expire, and verifies that after the first group should have expired, only the non-expiring group can be received.

    If the JMS provider doesn't support immediate expiration of messages, the org.exolab.jmscts.test.producer.ttl.ExpirationTest.expirationInterval property should be configured in jmscts.properties to delay the test until the expired messages are collected. + + + + + + + + + + + testExpiration2Subscribers + + Verifies that messages to topics expire, when there is more than one subscriber.

    It sends two groups of messages - one group which expires after 1 second, and another which don't expire, and verifies that after the first group should have expired, only the non-expiring group can be received.

    If the JMS provider doesn't support immediate expiration of messages, the org.exolab.jmscts.test.producer.ttl.ExpirationTest.expirationInterval property should be configured in jmscts.properties to delay the test until the expired messages are collected. + + + + + + + + + testSingleMessageExpiration + + Verifies that a single message expires.

    It sends two messages - one which expires after 1 second, and another which doesn't expire, and verifies that after the first should have expired, only the non-expiring message can be received.

    If the JMS provider doesn't support immediate expiration of messages, the org.exolab.jmscts.test.producer.ttl.ExpirationTest.expirationInterval property should be configured in jmscts.properties to delay the test until the expired messages are collected. + + + + + + + + diff --git a/systest/jmscts/resources/requirements.xml b/systest/jmscts/resources/requirements.xml new file mode 100755 index 0000000000..687beee36f --- /dev/null +++ b/systest/jmscts/resources/requirements.xml @@ -0,0 +1,1858 @@ + + + + All JMS requirements as per 1.0.2b of the specification + + + + + + + + + + + + + + + + +

    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + + 3-2
    +
    + + +
    + + + + 3-3
    +
    + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + +
    + + + + 3-7
    +
    + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/BytesMessage.html + + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/StreamMessage.html + + + + + A null message selector indicates that there is no message selector for + the message consumer. + + section.3.8.1.1 + + + + + If the value of a message selector is an empty string, the value is + treated as a null and indicates that there is no message selector for + the message consumer. + + section.3.8.1.1 + + + + + The order of evaluation of a message selector is from left to right + within precedence level. Parentheses can be used to change this order. + + section.3.8.1.1 + + + + + A string literal is enclosed in single quotes, with an included single + quote represented by doubled single quote; for example, 'literal' and + 'literal''s'.
    + String literals use the Unicode character encoding. +
    + section.3.8.1.1 +
    + + + + An exact numeric literal is a numeric value without a decimal point, + such as 57, -957, +62; numbers in the range of Java long are supported. + Exact numeric literals use the Java integer literal syntax. + + section.3.8.1.1 + + + + + An approximate numeric literal is a numeric value in scientific notation, + such as 7E3 and -57.9E2, or a numeric value with a decimal, such as 7., + -95.7, and +6.2; numbers in the range of Java double are supported. + Approximate literals use the Java floating-point literal syntax. + + section.3.8.1.1 + + + + + A boolean literal is one of TRUE or FALSE. + + section.3.8.1.1 + + + + + An identifier is an unlimited-length character sequence that must begin + with a Java identifier start character; all following characters must be + Java identifier part characters. An identifier start character is any + character for which the method Character.isJavaIdentifierStart returns + true. This includes '_' and '$'. An identifier part character is any + character for which the method Character.isJavaIdentifierPart returns + true. + + section.3.8.1.1 + + + + + Identifiers are case sensitive. + + section.3.8.1.1 + + + + + Identifiers cannot be the words NULL, TRUE, FALSE, NOT, AND, OR, BETWEEN, + LIKE, IN, IS, or ESCAPE. These are case-insensitive reserved words. + + section.3.8.1.1 + + + + + Reserved words are case-insensitive. + + section.3.8.1.1 + + + + + The conversions that apply to the get methods for properties do not + apply when a property is used in a message selector expression. For + example, suppose you set a property as a string value, as in the + following:
    + myMessage.setStringProperty("NumberOfOrders", "2");
    + The following expression in a message selector would evaluate to false, + because a string cannot be used in an arithmetic expression: + "NumberOfOrders > 1" +
    + section.3.8.1.1 +
    + + + + Message header field references are restricted to JMSDeliveryMode, + JMSPriority, JMSMessageID, JMSTimestamp, JMSCorrelationID, and + JMSType. JMSMessageID, JMSCorrelationID, and JMSType values may be + null and if so are treated as a NULL value. + TODO - need to test some of these in code + + section.3.8.1.1 + + + + + Any name beginning with 'JMSX' is a JMS defined property name.
    + Any name beginning with 'JMS_' is a provider-specific property name.
    + Any name that does not begin with 'JMS' is an application-specific + property name.
    + NOTE: cannot realistically test JMS_ properties. +
    + section.3.8.1.1 +
    + + + + Whitespace is the same as that defined for Java: space, horizontal tab, + form feed and line terminator. + + section.3.8.1.1 + + + + + A selector is a conditional expression; a selector that evaluates to true + matches; a selector that evaluates to false or unknown does not match. + + section.3.8.1.1 + + + + + Arithmetic expressions are composed of themselves, arithmetic + operations, identifiers with numeric values, and numeric literals. + + section.3.8.1.1 + + + + + Conditional expressions are composed of themselves, comparison + operations, logical operations, identifiers with boolean values, and + boolean literals. + + section.3.8.1.1 + + + + + Logical operators in order of precedence: +
      +
    • NOT
    • +
    • AND
    • +
    • OR
    • +
    +
    + section.3.8.1.1 +
    + + + + Comparison operators in order of precedence: +
      +
    • =
    • +
    • >
    • +
    • >=
    • +
    • <
    • +
    • <=
    • +
    • <> (not equal)
    • +
    +
    + section.3.8.1.1 +
    + + + + Arithmetic operators in order of precedence are: +
      +
    • +, - (unary)
    • +
    • *, / (multiplication and division)
    • +
    • +, - (addition and subtraction)
    • +
    +
    + section.3.8.1.1 +
    + + + + Only like type values can be compared. One exception is that it is valid + to compare exact numeric values and approximate numeric values (the type + conversion required is defined by the rules of Java numeric promotion). + If the comparison of non-like type values is attempted, the value of the + operation is false. If either of the type values evaluates to NULL, the + value of the expression is unknown. + + section.3.8.1.1 + + + + + Boolean comparison is restricted to = and <> + + section.3.8.1.1 + + + + + String comparison is restricted to = and <> Two strings are + equal if and only if they contain the same sequence of characters. + + section.3.8.1.1 + + + + + BETWEEN expressions are of the form: + arithmetic-expr1 [NOT] BETWEEN arithmetic-expr2 and arithmetic-expr3 + + section.3.8.1.1 + + + + + IN expressions are of the form: + identifier [NOT] IN (string-literal1, string-literal2, ...) + where identifier has a String or NULL value. + If identifier is NULL, the value of the operation is unknown. + TODO - add tests where identifier is known to be a non-string value + + section.3.8.1.1 + + + + + LIKE expressions are of the form: + identifier [NOT] LIKE pattern-value [ESCAPE escape-character] + where identifier has a String value; pattern-value is a string literal + where '_' stands for any single character; '%' stands for any sequence of + characters, including the empty sequence, and all other characters stand + for themselves. The optional escape-character is a single-character + string literal whose character is used to escape the special meaning of + the '_' and '%' in pattern-value. + + section.3.8.1.1 + + + + + IS expressions are of the form: + identifier IS [NOT] NULL + The IS NULL form tests for a null header field value or a missing + property value. + The IS NOT NULL form tests for the existence of a non-null header field + value or property value + The IS NULL and IS NOT NULL operators convert an unknown header or + property value into the respective TRUE and FALSE values. + + section.3.8.1.1 + section.3.8.1.2 + + + + + JMS providers are required to verify the syntactic correctness of a + message selector at the time it is presented. A method providing a + syntactically incorrect selector must result in a JMS + InvalidSelectorException. + + section.3.8.1.1 + + + + + Arithmetic operations must use Java numeric promotion. + + section.3.8.1.1 + + + + + Header fields and property values may be NULL. Comparison or arithmetic + with an unknown value always yields an unknown value. + + section.3.8.1.2 + + + + + The AND operator evaluates as follows: + + + + + +
    ANDTFU
    TTFU
    FFFF
    UUFU
    +
    + + 3-4
    +
    + section.3.8.1.2 +
    + + + + The OR operator evaluates as follows: + + + + + +
    ORTFU
    TTTT
    FTFU
    UTUU
    +
    + + 3-5
    +
    + section.3.8.1.2 +
    + + + + The NOT operator evaluates as follows: + + + + + +
    NOT
    TF
    FT
    UU
    +
    + + 3-6
    +
    + section.3.8.1.2 +
    + + + + When used in a message selector JMSDeliveryMode is treated as having the + values 'PERSISTENT' and 'NON_PERSISTENT'. + + section.3.8.1.3 + + + + + Date and time values should use the standard Java long millis value. + When a date or time literal is included in a message selector, it should + be an integer literal for a millis value. + + section.3.8.1.3 + + + + + When creating a connection, a client may specify its credentials + as a name/password. + JMSSecurityException must be thrown when a provider rejects a + user name/password submitted by a client + + section.4.3.1 + section.7.3 + + + + + The facility to explicitly set a connection's client identifier is not a + mechanism for overriding the identifier that has been administratively + configured. It is provided for the case where no administratively + specified identifier exists. If one does exist, an attempt to change it + by setting it must throw an IllegalStateException. + + section.4.3.2 + + + + + If a client explicitly sets the client identifer it must do this + immediately after creating the connection and before any other action on + the connection is taken. After this point, setting the client identifier + is a programming error that should throw an IllegalStateException. + + section.4.3.2 + + + + + The client state identified by a client identifier can be in use by only + one client at a time. A JMS provider must prevent concurrently executing + clients from using it. + This prevention may take the form of JMSExceptions thrown when such use + is attempted; it may result in the offending client being blocked; or + some other solution. A JMS provider must insure that such attempted + 'sharing' of an individual client state does not result in messages + being lost or doubly processed. + TODO - don't handle the blocked scenario + + section.4.3.2 + + + + + When a Connection is created, it is in stopped mode. That means that no + messages are being delivered to it. + + section.4.3.3 + + + + + No messages are delivered by a connection to its client until it has been + started. + + section.4.3.3 + + + + + Stopping a connection has no effect on its ability to send messages. + + section.4.3.4 + + + + + A stop method call must not return until delivery of messages has paused. + This means a client can rely on the fact that none of its message + listeners will be called and all threads of control waiting for receive + to return will not return with a message until the connection is + restarted. + If MessageListeners are running when stop is invoked, stop must wait + until all of them have returned before it may return. While these + MessageListeners are completing, they must have the full services of the + connection available to them. + TODO - test behaviour of sending messages while a listener is active, + using a different session (i.e, not from the listener - undefined?) + TODO - add test where a thread stops a connection and another starts it + as the stop is in progress. + + section.4.3.5 + + + + + The receive timers for a stopped connection continue to advance, so + receives may time out and return a null message while the connection is + stopped. + + section.4.3.5 + + + + + Stopping a stopped connection is ignored. + + section.4.3.5 + + + + + Starting a started connection is ignored. + + section.4.3.5 + + + + + A close terminates all pending message receives on the connection's + session's consumers. The receives may return with a message or null + depending on whether or not there was a message available at the time of + the close. + + section.4.3.5 + + + + + If one or more of the connection's session's message listeners is + processing a message at the point when connection close is invoked, all + the facilities of the connection and its sessions must remain available + to those listeners until they return control to the JMS provider. + + section.4.3.5 + + + + + Once a connection has been closed, an attempt to use it or its sessions + or their message consumers and producers must throw an + IllegalStateException (calls to the close method of these objects must + be ignored). + + section.4.3.5 + + + + + If a connection is closed, there is no need to close its constituent + objects. The connection close is sufficient to signal the JMS provider + that all resources for the connection should be released. + TODO: probably the only meaningful way to test this in a provider + independent fashion is to attempt to use a temporary destination created + by the closed connection, on another connection. This should throw + InvalidDestinationException? + + section.4.3.5 + + + + + Closing a connection must roll back the transactions in progress on its + transacted sessions. + + section.4.3.5 + + + + + Closing a connection does NOT force an acknowledgement of + client-acknowledged sessions. + + section.4.3.5 + + + + + It is valid to continue to use message objects created or received after + a connection has been closed with the exception of a received message's + acknowledge method. + Invoking the acknowledge method of a received message must throw an + IllegalStateException. + + section.4.3.5 + + + + + Closing a closed connection must NOT throw an exception. + + section.4.3.5 + + + + + Closing a transacted session must roll back its transaction in progress. + + section.4.4.1 + + + + + Closing a client-acknowledged session does NOT force an acknowledge. + + section.4.4.1 + + + + + Once a session has been closed, an attempt to use it or its message + consumers and producers must throw an IllegalStateException (calls to + the close method of these objects must be ignored). + + section.4.4.1 + + + + + It is valid to continue to use message objects created or received via + the session, with the exception of a received message'acknowledge method. + Invoking the acknowledge method of a received message must throw an + IllegalStateException. + + section.4.4.1 + + + + + Closing a closed session must NOT throw an exception. + + section.4.4.1 + + + + + A close terminates all pending message receives on the connection's + session's consumers. The receives may return with a message or null + depending on whether or not there was a message available at the time of + the close. + + section.4.4.1 + + + + + If one or more of the connection's session's message listeners is + processing a message at the point when connection close is invoked, all + the facilities of the connection and its sessions must remain available + to those listeners until they return control to the JMS provider. + + section.4.4.1 + + + + + Each transacted session supports a single series of transactions. + Each transaction groups a set of produced messages and a set of consumed + messages into an atomic unit of work. + In effect, transactions organize a session's input message stream and + output message stream into series of atomic units. When a transaction + commits, its atomic unit of input is acknowledged and its associated + atomic unit of output is sent. + + section.4.4.7 + + + + + If a transacted session is rolled back, its produced messages are + destroyed and its consumed messages are automatically recovered. + + section.4.4.7 + + + + + With the DUPS_OK_ACKNOWLEDGE session acknowledgement mode, the session + lazily acknowledges the delivery of messages. This is likely to result + in the delivery of some duplicate messages if JMS fails, so it should be + used only by consumers that are tolerant of duplicate messages. Its + benefit is the reduction of session overhead achieved by minimizing the + work the session does to prevent duplicates. + + section.4.4.11 + + + + + With the AUTO_ACKNOWLEDGE session acknowledgement mode, the session + automatically acknowledges a client's receipt of a message when it has + either successfully returned from a call to receive or the + MessageListener it has called to process the message successfully + returns. + + section.4.4.11 + + + + + With the CLIENT_ACKNOWLEDGE session acknowledgement mode, a client + acknowledges a message by calling the message's acknowledge method. + Acknowledging a consumed message automatically acknowledges the receipt + of all messages that have been delivered by its session. + + section.4.4.11 + + + + + A session's recover method is used to stop a session and restart it with + its first unacknowledged message. In effect, the session's series of + delivered messages is reset to the point after its last acknowledged + message. The messages it now delivers may be different from those that + were originally delivered due to message expiration and the arrival of + higher-priority messages.
    + A session must set the redelivered flag of messages it redelivers due to + a recovery. +
    + section.4.4.11 + section.3.4.7 +
    + + + + A TransactionInProgressException exception is thrown when an operation + is invalid because a transaction is in progress. For instance, + attempting to call Session.commit() when a session is part of a + distributed transaction. + + section.7.3 + + + + + A TransactionRolledBackException exception must be thrown when a call to + Session.commit() results in a rollback of the current transaction. + + section.7.3 + + + + + A message can be acknowledged after its message consumer has closed as + message acknowledgement is performed at the session level + TODO - need to test this for single message acknowledgement via + CLIENT_ACKWNOWLEDGE etc + + + http://java.sun.com/products/jms/faq.html#msg_ack_close + + + + + + A session serializes all asynchronous delivery of messages. + While the session is busy executing one listener, all other messages to + be asynchronously delivered to the session must wait. + + +
    + + + + + + Invoking commit() on a non-transacted session should throw + IllegalStateException + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/Session.html#commit() + + section.7.3 + + + + + Invoking rollback() on a non-transacted session should throw + IllegalStateException + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/Session.html#rollback() + + + + + + Invoking recover() on a transacted session should throw + IllegalStateException + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/Session.html#recover() + + + + + + If not specified, the default time-to-live for a producer equals + 0 + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/MessageProducer.html#getTimeToLive() + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/MessageProducer.html#setTimeToLive() + + + + + + A client can specify a default time-to-live for messages sent by a + message producer, to be used when a time-to-live isn't specified per + message. +

    + Note: the specification doesn't mention min or max + values for time-to-live, or if any exceptions will be thrown if + an invalid time-to-live is specified. +

    +
    + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/MessageProducer.html + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/TopicPublisher.html#publish(javax.jms.Message) + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/TopicPublisher.html#publish(javax.jms.Topic,%20javax.jms.Message) + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/QueueSender.html#send(javax.jms.Message) + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/QueueSender.html#send(javax.jms.Queue,%20javax.jms.Message) + +
    + + + + When a message is sent, JMSDestination is ignored. After completion of + the send it holds the destination object specified by the sending method. + When a message is received, its destination value must be equivalent to + the value assigned when it was sent. + + section.3.4.1 + section.3.4.11 + + + + + When a message is sent, JMSMessageID is ignored. When the send method + returns it contains a provider-assigned value. + + section.3.4.3 + section.3.4.11 + + + + + All JMSMessageID values must start with the prefix 'ID:' + + section.3.4.3 + + + + + The JMSCorrelationID header field may be used to link one message with + another. It can contain a provider-specific message ID, an + application-specific string, or a provider-native byte[] value. + The application-specific string must not start with the 'ID:' prefix: + this is reserved for provider generated messages Ids. + + section.2.10 + section.3.4.5 + section.3.4.11 + + + + + If a provider supports the native concept of correlation ID, a JMS + client may need to assign specific JMSCorrelationID values to match + those expected by non-JMS clients. A byte[] value is used for this + purpose. JMS providers without native correlation ID values are not + required to support byte[] values - in this case + setJMSCorrelationIDAsBytes() and getJMSCorrelationIDAsBytes() may throw + java.lang.UnsupportedOperationException. + + section.3.4.5 + + + + + If a client receives a message with the JMSRedelivered indicator set, it + is likely, but not guaranteed, that this message was delivered but not + acknowledged in the past. In general, a provider must set the + JMSRedelivered message header field of a message whenever it is + redelivering a message. + + section.3.4.7 + section.3.4.11 + + + + + The JMSRedelivered header field has no meaning on send and is left + unassigned by the sending method. + + section.3.4.7 + + + + + When a message is sent, its expiration time is calculated as the sum of + the time-to-live value specified on the send method and the current GMT + value. On return from the send method, the message's JMSExpiration header + field contains this value. + + section.3.4.9 + + + + + When a message is received its JMSExpiration header field contains the + expiration as that on send. + + section.3.4.9 + + + + + If the time-to-live is specified as zero, expiration is set to zero to + indicate that the message does not expire. + + section.3.4.9 + + + + + When GMT is later than an undelivered message's expiration time, the + message should be destroyed. + Clients should not receive messages that have expired; however, JMS does + not guarantee that this will not happen. + + section.3.4.9 + + + + + The JMSReplyTo header field contains a Destination supplied by a client + when a message is sent. It is the destination where a reply to the + message should be sent.
    + Messages sent with a null JMSReplyTo value may be a notification of some + event or they may just be some data the sender thinks is of interest. +
    + section.2.10 + +
    + + + + + + When a message is created, its message body and properties may be set. + + +
    + + + + + + An identifier is an unlimited-length character sequence that must begin + with a Java identifier start character; all following characters must be + Java identifier part characters. An identifier start character is any + character for which the method Character.isJavaIdentifierStart returns + true. An identifier part character is any character for which the + method Character.isJavaIdentifierPart returns true. + + section.3.8.1.1 + + + + + Identifiers cannot be the words NULL, TRUE, FALSE, NOT, AND, OR, BETWEEN, + LIKE, IN, IS, or ESCAPE. These are case-insensitive reserved words. + + section.3.8.1.1 + + + + + Identifiers are case sensitive. + + section.3.8.1.1 + + + + + Property values can be boolean, byte, short, int, long, float, double, + and String. + + section.3.5.2 + + + + + The setObjectProperty method accepts values of Boolean, Byte, Short, + Integer, Long, Float, Double and String. An attempt to use any other + class must throw a JMS MessageFormatException.
    + The getObjectProperty method only returns values of null, Boolean, Byte, + Short, Integer, Long, Float, Double and String. A null value is returned + if a property by the specified name does not exist. +
    + section.3.5.5 + section.3.5.8 +
    + + + + User properties may not be modified by the provider + + implied + + + + + Properties support the following conversion table. The marked cases must + be supported. The unmarked cases must throw the JMS + MessageFormatException.
    + The String to numeric conversions must throw the + java.lang.NumberFormatException if the numeric's valueOf() method does + not accept the String value as a valid representation. + A value set as the row type can be read as the column type.
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     booleanbyteshortintlongfloatdoubleString
    booleanX      X
    byte XXXX  X
    short  XXX  X
    int   XX  X
    long    X  X
    float     XXX
    double      XX
    StringXXXXXXXX
    +
    + section.3.5.4 + table.3-2 +
    + + + + Attempting to read a null value as a Java primitive type must be treated + as calling the primitive's corresponding valueOf(String) conversion + method with a null value. + + section.3.5.4 + section.3.5.8 + + + + + The order of property values is not defined. To iterate through a + message's property values, use getPropertyNames to retrieve a property + name enumeration and then use the various property get methods to + retrieve their values. + The getPropertyNames method does not return the names of the JMS standard + header fields. + + section.3.5.6 + + + + + After sending a message, a client may retain and modify it without + affecting the message that has been sent. The same message object may be + sent multiple times. + + +
    + + + + + + When a message is received its body is read only. If an attempt is made + to change them, a MessageNotWriteableException must be thrown. + If its properties or body are subsequently cleared, they are in the same + state as a newly created message. + TODO - need to test in transaction context + + +
    + + +
    + + +
    + + + + + + The clearBody method of Message resets the value of the message body to + the empty initial message value as set by the message type's create method + provided by Session. Clearing a message's body does not clear its property + entries. When a message is cleared, its message body and properties may + be set. + + +
    + + + + + + Clearing a message's properties leaves the message with an empty set of + properties. New property entries can then be both created and read. + Clearing a message's property entries does not clear the value of its + body. + + +
    + + + + + + When the message is first created, the body of the message is in + write-only mode. If a client attempts to read a message in write-only + mode, a MessageNotReadableException is thrown. + + url.message.bytes + + + + + MessageEOFException must be thrown when an unexpected end of stream has + been reached when a message is being read. + + section.7.3 + + + + + The setObject method places a copy of the input in the message. + + +
    + + + + + + When the message is first created, the body of the message is in + write-only mode. If a client attempts to read a message in write-only + mode, a MessageNotReadableException is thrown. + + url.message.stream + + + + + MessageEOFException must be thrown when an unexpected end of stream has + been reached when a message is being read. + + section.7.3 + + + + + If a read method of a BytesMessage throws a MessageFormatException or + NumberFormatException, the current position of the read pointer must not + be incremented. A subsequent read must be capable of recovering from the + exception by re-reading the data as a different type.
    + NOTE: With the exception of the readUTF() method, it is difficult to + conceive test cases for read methods. +
      +
    • + A provider that implements BytesMessage using a DataInputStream + or equivalent, is likely to only throw MessageFormatException for + the readUTF() method.
      + The other likely exceptions are MessageEOFException for stream + overruns, and JMSException for any other error. +
    • +
    • + As BytesMessage does not support conversion, NumberFormatException + is unlikely to be thrown. +
    • +
    +
    + section.3.11.3 +
    + + + + If a read method of a StreamMessage throws a MessageFormatException or + NumberFormatException, the current position of the read pointer must not + be incremented. A subsequent read must be capable of recovering from the + exception by re-reading the data as a different type. + + section.3.11.3 + table.3-7 + + + + + A MapMessage is a message whose body contains a set of name-value pairs + where names are Strings and values are Java primitive types. The entries + can be accessed sequentially by enumerator or randomly by name. The + order of the entries is undefined. + + section.3.11 + + + + + MapMessage supports the following conversion table. + The marked cases must be supported. The unmarked cases must throw a JMS + MessageFormatException. The String to numeric conversions must throw a + java.lang.NumberFormatException if the numeric's valueOf() method does + not accept the String value as a valid representation. + MapMessage must implement the String to boolean conversion as + specified by the valueOf(String) method of Boolean as defined by the + Java language.
    + A value written as the row type can be read as the column type.
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     booleanbyteshortcharintlongfloatdoubleStringbyte[]
    booleanX       X 
    byte XX XX  X 
    short  X XX  X 
    char   X    X 
    int    XX  X 
    long     X  X 
    float      XXX 
    double       XX 
    StringXXX XXXXX 
    byte[]         X
    +
    + section.3.11.3 + table.3-7 +
    + + + + For MapMessage, attempting to read a null value as a Java primitive + type must be treated as calling the primitive's corresponding + valueOf(String) conversion method with a null value. Since char does not + support a String conversion, attempting to read a null value as a char + must throw NullPointerException. + + section.3.11.3 + + + + + Getting a MapMessage field for a field name that has not been set is + handled as if the field exists with a null value. + + section.3.11.3 + + + + + StreamMessage supports the following conversion table. + The marked cases must be supported. The unmarked cases must throw a JMS + MessageFormatException. The String to numeric conversions must throw a + java.lang.NumberFormatException if the numeric's valueOf() method does + not accept the String value as a valid representation. + StreamMessage must implement the String to boolean conversion as + specified by the valueOf(String) method of Boolean as defined by the + Java language.
    + A value written as the row type can be read as the column type.
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     booleanbyteshortcharintlongfloatdoubleStringbyte[]
    booleanX       X 
    byte XX XX  X 
    short  X XX  X 
    char   X    X 
    int    XX  X 
    long     X  X 
    float      XXX 
    double       XX 
    StringXXX XXXXX 
    byte[]         X
    +
    + section.3.11.3 + table.3-7 +
    + + + + MapMessage field names are case sensitive.
    + NOTE: the specification does not explicitly mention this requirement, + but it is in keeping with property name requirements. +
    + implied +
    + + + + For StreamMessage, attempting to read a null value as a Java primitive + type must be treated as calling the primitive's corresponding + valueOf(String) conversion method with a null value. Since char does not + support a String conversion, attempting to read a null value as a char + must throw NullPointerException.
    + NOTE: the specification is not explicit on the behaviour of invoking + writeBytes() or writeString() with a null value, but an acceptable policy + is to throw a NullPointerException. This is in keeping with the behaviour + of DataOutputStream. +
    + section.3.11.3 +
    + + + + A provider must be prepared to accept, from a client, a + message whose implementation is not one of its own. + A message with a 'foreign' implementation may not be handled as + efficiently as a provider's own implementation; however, it must be + handled.
    + Note that if the foreign message implementation contains a + JMSReplyTo header field that is set to a foreign destination + implementation, the provider is not required to handle or preserve the + value of this field. +
    + section.3.12 +
    + + + + The JMS message interfaces provide read/get methods for accessing + objects in a message body. All of these methods must be implemented to + return a copy of the accessed objects. + + section.3.12 + + + + + The JMS message interfaces provide read/get methods for accessing + message object properties. All of these methods must be implemented to + return a copy of the accessed objects. + + section.3.12 + + + + + When clearBody is called on a BytesMessage, the body of the message is in + write-only mode.
    + If clearBody is called on a message in read-only mode, the message body + is cleared and the message is in write-only mode. +
    + url.message.bytes +
    + + + + Refer to javadoc + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/BytesMessage.html#readBytes(byte[]) + + + + + + Refer to javadoc + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/BytesMessage.html#readBytes(byte[], int) + + + + + + Refer to javadoc + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/BytesMessage.html#writeObject() + + + + + + Refer to javadoc + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/MapMessage.html#setObject() + + + + + + Refer to javadoc + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/MapMessage.html#itemExists() + + + + + + Refer to javadoc + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/MapMessage.html#getMapNames() + + + + + + When clearBody is called on a StreamMessage, the body of the message is + in write-only mode.
    + If clearBody is called on a message in read-only mode, the message body + is cleared and the message is in write-only mode. +
    + url.message.stream +
    + + + + Refer to javadoc + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/StreamMessage.html#writeObject() + + + + + + Refer to javadoc + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/StreamMessage.html#writeBytes(byte[]) + + + + + + Refer to javadoc + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/StreamMessage.html#writeBytes(byte[], int, int) + + + + + + Refer to javadoc + + + http://java.sun.com/products/jms/javadoc-102a/javax/jms/StreamMessage.html#readBytes + + + + + + JMSXGroupID and JMSXGroupSeq are standard properties clients should use + if they want to group messages. All providers must support them. + JMSXGroupID is a string property. JMSXGroupSeq is an int property, + starting at 1. @todo - need to verify if the range is applicable + + section.3.5.9 + table.3-3 + + + + + JMS reserves the 'JMSX' property name prefix for JMS defined properties. + Unless noted otherwise, support for these properties is optional. The + Enumeration ConnectionMetaData.getJMSXPropertyNames() method returns the + names of the JMSX properties supported by a connection. + Properties that may only be set by the provider include JMSXUserID, + JMSXAppID, JMSXDeliveryCount, JMSXProducerTXID, JMSXConsumerTXID, + JMSXRcvTimestamp and JMSXState. + + section.3.5.9 + table.3-3 + + + + + ConnectionMetaData provides a list of the JMS defined property names + supported by the connection. This must include JMSXGroupID and + JMSXGroupSeq + + section.3.5.9 + + + + + In some cases, a connection may both publish and subscribe to a topic. + The subscriber NoLocal attribute allows a subscriber to inhibit the + delivery of messages published by its own connection. + + section.6.11 + + + + + If a client needs to receive all the messages published on a topic, + including the ones published while the subscriber is inactive, it uses a + durable TopicSubscriber. JMS retains a record of this durable + subscription and insures that all messages from the topic's publishers + are retained until either they are acknowledged by this durable + subscriber or they have expired. + + section.6.11.1 + section.6.3 + + + + + Sessions with durable subscribers must always provide the same client + identifier. In addition, each client must specify a name that uniquely + identifies (within client identifier) each durable subscription it + creates. + + section.6.11.1 + + + + + Only one session at a time can have a TopicSubscriber for a + particular durable subscription. + + section.6.11.1 + section.4.3.2 + + + + + A client can change an existing durable subscription by creating a + durable TopicSubscriber with the same name and a new topic and/or + message selector. Changing a durable subscription is equivalent to + deleting and recreating it. + + section.6.11.1 + + + + + TopicSessions provide the unsubscribe method for deleting a durable + subscription created by their client. This deletes the state being + maintained on behalf of the subscriber by its provider. + It is erroneous for a client to delete a durable subscription while it + has an active TopicSubscriber for it or while a message received by it + is part of a current transaction or has not been acknowledged in the + session. + (Note: this last sentence suggests that a provider doesn't need to check + that a client can remove a subscription) + + section.6.11.1 + + + + + For application servers, connections provide a special facility for + creating a ConnectionConsumer. The messages it is to consume are + specified by a destination and a message selector. In addition, a + ConnectionConsumer must be given a ServerSessionPool to use for + processing its messages. A maxMessages value is specified to limit the + number of messages a ConnectionConsumer may load at one time into a + ServerSession's session. + + section.8.2.4 + + + + + If a destination is removed via the provider's administration interface, + and subsequently recreated, all messages to that destination must also + be removed. + + implied + + + diff --git a/systest/jmscts/resources/statistics.xsl b/systest/jmscts/resources/statistics.xsl new file mode 100755 index 0000000000..3ae5922972 --- /dev/null +++ b/systest/jmscts/resources/statistics.xsl @@ -0,0 +1,228 @@ + + + + + + + + + + + Statistics + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + +

    + + + +

    +
    + + + + + + + + + + + + + + + + + + + + true + false + + + + +

    + + + + + + + + + + + + + + + + + + +
    RunDeliverySessionConsumerMessageTypeCountTimeMsgs/sec
    +

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AUTO + CLIENT + DUPS_OK + + + + + + + + + + durable synchronous + + + durable asynchronous + + + + + + + +
    diff --git a/systest/jmscts/resources/tigris.css b/systest/jmscts/resources/tigris.css new file mode 100755 index 0000000000..3cf368de88 --- /dev/null +++ b/systest/jmscts/resources/tigris.css @@ -0,0 +1,434 @@ +/* contains rules unsuitable for Netscape 4.x; simpler rules are in ns4_only.css. see */ + +/* colors, backgrounds, borders, link indication */ + +body { + background: #fff; + color: #000; + } +.app h3, .app h4, .app th, .tabs td, .tabs th, .functnbar { + background-image: url(../images/nw_min.gif); + background-repeat: no-repeat; + } +#navcolumn div div, body.docs #toc li li { + background-image: url(../images/strich.gif); + background-repeat: no-repeat; + background-position: .5em .5em; + } +#navcolumn div div.heading { + background-image: none; + } +.app h3, .app h4 { + color: #fff; + } +.app h3 { + background-color: #036; + } +.app h4 { + background-color: #888; + } +.a td { + background: #ddd; + } +.b td { + background: #efefef; + } +table, th, td { + border: none + } +.mtb { + border-top: solid 1px #ddd; + } +div.colbar { + background: #bbb; + } +div#banner { + border-top: 1px solid #369; + border-bottom: 1px solid #003; + } +div#helptext th { + border-bottom: 1px solid #996; + border-right: 1px solid #996; + } +div#helptext td { + border-bottom: 1px solid #cc9; + border-right: 1px solid #cc9; + } +.tabs { + border-bottom: .75em #888 solid; + } +.tabs th, .tabs td { + border-right: 1px solid #333; + } +.tabs td { + border-bottom: 1px solid #ddd; + } +#navcolumn { + background: #eee; + border-right: 1px solid #aaa; + border-bottom: 1px solid #aaa; + } +#breadcrumbs { + border-bottom: 1px solid #aaa; + background-color: #ddd; + } +#navcolumn, #breadcrumbs { + border-top: 1px solid #fff; + } +#rightcol div.www, #rightcol div.help { + border: 1px solid #ddd; + } +div#navcolumn div.focus { + border-top: 1px solid #aaa; + border-left: 1px solid #aaa; + background-color: #fff; + } +body.docs div.docs { + background: #fff; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + } +body.docs { + background: #eee url(../images/help_logo.gif) top right no-repeat !important; + } +.docs h3, .docs h4 { + border-top: solid 1px #000; + } +#alerterrormessage { + background: url(../images/icon_alert.gif) top left no-repeat !important; + } +.functnbar { + background-color: #aaa; + } +.functnbar2, .functnbar3 { + background: #aaa url(../images/sw_min.gif) no-repeat bottom left; + } +.functnbar3 { + background-color: #ddd; + } +.functnbar, .functnbar2, .functnbar3 { + color: #000; + } +.functnbar a, .functnbar2 a, .functnbar3 a { + color: #000; + text-decoration: underline; + } +#topmodule { + background: #ddd; + border-top: 1px solid #fff; + border-bottom: 1px solid #aaa; + border-right: 1px solid #aaa; + } +#topmodule #issueid { + border-right: 1px solid #aaa; + } +a:link, #navcolumn a:visited, .app a:visited, .tasknav a:visited { + color: blue; + } +a:active, a:hover, #leftcol a:active, #leftcol a:hover { + color: #f30 !important; + } +#login a:link, #login a:visited { + color: white; + text-decoration: underline; + } +#banner a:active, #banner a:hover { + color: #f90 !important; + } +#leftcol a, #breadcrumbs a { + text-decoration: none; + } +a:link.selfref, a:visited.selfref { + color: #555 !important; + text-decoration: none; + } +h2 .lastchild { + color: #777 + } +.tabs td, .tabs th { + background-color: #ddd; + } +.app th { + background-color: #bbb; + } +.tabs th { + background-color: #888; + color: #fff; + } +.axial th { + background-color: #ddd; + color: black + } +.tabs td { + background-color: #ddd; + } +.alert { + color: #c00; + } +.confirm { + color: green; + } +.info { + color: blue; + } +.selection { + background: #ffc; + } +#login { + color: #fff; + } +#helptext th { + background: #cc9; + } +#helptext td { + background: #ffc; + } +.tabs a { + text-decoration: none; + } +#navcolumn div strong { + color: #000; + } +#banner, #banner td { + background: #036; + color: #fff; + } +body #banner #login a { + color: #fff; + } + + +/* font and text properties, exclusive of link indication, alignment, text-indent */ + +body, th, td, input, select, textarea, h2 small { + font-family: Verdana, Helvetica, Arial, sans-serif; + } +code, pre { + font-family: 'Andale Mono', Courier, monospace; + } +html body, body th, body td, textarea, h2 small, .app h3, .app h4, #rightcol h3, #bodycol pre, #bodycol code { + font-size: x-small; + voice-family: "\"}\""; + voice-family: inherit; + font-size: small + } +html>body, html>body th, html>body td, html>body input, html>body select, html>body textarea, html>body h2 small, html>body .app h3, html>body .app h4, html>body #rightcol h3, html>body #bodycol pre, html>body #bodycol code { + font-size: small + } +small, div#footer td, div#login, div#helptext th, div#helptext td, div.tabs th, div.tabs td, input, select, .paginate, .functnbar, .functnbar2, .functnbar3, #breadcrumbs td, .courtesylinks, #rightcol div.help, .colbar, .tasknav, body.docs div#toc { + font-size: xx-small; + voice-family: "\"}\""; + voice-family: inherit; + font-size: x-small + } +html>body small, html>body div#footer td, html>body div#login, html>body div#helptext td, html>body div#helptext th, html>body div.tabs th, html>body div.tabs td, html>body input, html>body select, html>body .paginate, html>body .functnbar, html>body .functnbar2, html>body .functnbar3, html>body #breadcrumbs td, html>body .courtesylinks, html>body #rightcol div.help, html>body .colbar, html>body .tasknav, html>body.docs #toc { + font-size: x-small + } +#bodycol h2 { + font-family: Tahoma, Verdana, Helvetica, Arial, sans-serif; + font-size: 1.5em; + font-weight: normal; + } +h2 small { + font-weight: bold; + letter-spacing: .06em; + } +dt { + font-weight: bold + } +#login .username { + font-weight: bold; + } +h4 { + font-size: 1em; + } +#breadcrumbs td { + font-weight: bold; + } +.selection { + font-weight: bold + } + + +/* box properties (exclusive of borders), positioning, alignments, list types, text-indent */ + +#bodycol h2 { + margin-top: .3em; + margin-bottom: .5em; + } +p, ul, ol, dl { + margin-top: .67em; + margin-bottom: .67em; + } +h3, h4 { + margin-bottom: 0; + } +form { + margin-top: 0; + margin-bottom: 0; + } +#bodycol { + padding-left: 12px; + padding-right: 12px; + width: 100%; + voice-family: "\"}\""; + voice-family: inherit; + width: auto; + } +html>body #bodycol { + width: auto; + } +.docs { + line-height: 1.4; + } +.app h3, .app h4 { + padding: 5px; + margin-right: 2px; + margin-left: 2px; + } +.h3 p, .h4 p, .h3 dt, .h4 dt { + margin-right: 7px; + margin-left: 7px; + } +.tasknav { + margin-bottom: 1.33em + } +div.colbar { + padding: 4px; + margin: 2px 2px 0; + } +.tabs { + margin-top: .67em; + margin-right: 2px; + margin-left: 2px; + } +#leftcol { + padding-bottom: .5em; + } +#breadcrumbs td { + vertical-align: middle; + padding: 2px 8px; + } +#rightcol div.www, #rightcol div.help { + padding: 0 .5em + } +#navcolumn { + margin: -8px -8px 0 -8px; + padding: 4px; + } +#navcolumn div { + padding-left: 5px + } +div#navcolumn div div { + margin-top: .3em; + margin-bottom: .3em; + } +div#navcolumn div.focus { + margin-top: -.1em; + padding: .2em 4px; + } +body.docs #toc { + position: absolute; + top: 15px; + left: 0px; + width: 120px; + padding: 0 20px 0 0 + } +body.docs #toc ul, #toc ol { + margin-left: 0; + padding-left: 0; + } +body.docs #toc li { + margin-top: 7px; + padding-left: 10px; + list-style-type: none; + } +body.docs div.docs { + margin: 61px 0 0 150px; + padding: 1em 2em 1em 1em !important; + } +.docs p+p { + text-indent: 5%; + margin-top: -.67em + } +.docs h3, .docs h4 { + margin-bottom: .1em; + padding-top: .3em; + } +#alerterrormessage { + padding-left: 100px; + } +.functnbar, .functnbar2, .functnbar3 { + padding: 5px; + margin: .67em 2px; + } +#topmodule td { + vertical-align: middle; + padding: 2px 8px + } +body { + padding: 1em; + } +body.composite, body.docs { + margin: 0; + padding: 0; + } +th, td { + text-align: left; + vertical-align: top + } +.right { + text-align: right !important; + } +.center { + text-align: center !important; + } +.tabs td, .tabs th { + padding-left: 7px; + padding-right: 7px; + } +.axial th { + text-align: right; + } +.app .axial td th { + text-align: left; + } +body td .stb { + margin-top: 1em; + text-indent: 0; + } +body td .mtb { + margin-top: 2em; + text-indent: 0; + } +dd { + margin-bottom: .67em; + } +#footer { + margin: 4px + } +#helptext { + margin-top: 1em + } +#helptext td div { + margin: .5em + } +.courtesylinks { + margin-top: 1em; + padding-top: 1em + } +#navcolumn div { + margin-bottom: .5em; + } +#navcolumn div div { + margin-top: .3em + } +#navcolumn div div { + padding-left: 1em; + } +#banner, #banner td { + vertical-align: middle; + } +body.docs, body.nonav { + margin: 1em + } diff --git a/systest/jmscts/resources/xdoc2html.xsl b/systest/jmscts/resources/xdoc2html.xsl new file mode 100755 index 0000000000..2bdef81026 --- /dev/null +++ b/systest/jmscts/resources/xdoc2html.xsl @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + <xsl:value-of select="properties/title"/> + + + + + + + + + + + + + +
    +
    +
    + +
    +
    +
    + +
    + + + +
    + +

    + +

    +
    + +
    +
    + + +
    + +

    + +

    +
    + +
    +
    + + +
    +
    +
    +
    + + + + + + + + + + + + +
    +
    + + + + + a + b + + + + + + + + + + + + + + + + +
    diff --git a/systest/jmscts/src/java/org/exolab/jmscts/activemq/ActiveMQProvider.java b/systest/jmscts/src/java/org/exolab/jmscts/activemq/ActiveMQProvider.java new file mode 100755 index 0000000000..74a031f048 --- /dev/null +++ b/systest/jmscts/src/java/org/exolab/jmscts/activemq/ActiveMQProvider.java @@ -0,0 +1,202 @@ +/** + * Redistribution and use of this software and associated documentation + * ("Software"), with or without modification, are permitted provided + * that the following conditions are met: + * + * 1. Redistributions of source code must retain copyright + * statements and notices. Redistributions must also contain a + * copy of this document. + * + * 2. Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. The name "Exolab" must not be used to endorse or promote + * products derived from this Software without prior written + * permission of Exoffice Technologies. For written permission, + * please contact jima@intalio.com. + * + * 4. Products derived from this Software may not be called "Exolab" + * nor may "Exolab" appear in their names without prior written + * permission of Exoffice Technologies. Exolab is a registered + * trademark of Exoffice Technologies. + * + * 5. Due credit should be given to the Exolab Project + * (http://www.exolab.org/). + * + * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT + * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Copyright 2001, 2003 (C) Exoffice Technologies Inc. All Rights Reserved. + * Copyright 2005 (C) Hiram Chirino + * + * $Id: ActiveMQProvider.java,v 1.1.1.1 2005/03/11 21:15:21 jstrachan Exp $ + */ +package org.exolab.jmscts.activemq; + +import java.util.HashMap; + +import javax.jms.JMSException; +import javax.naming.NameNotFoundException; +import javax.naming.NamingException; + +import org.apache.commons.logging.LogFactory; +import org.activemq.ActiveMQConnection; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.ActiveMQXAConnectionFactory; +import org.activemq.message.ActiveMQDestination; +import org.activemq.message.ActiveMQQueue; +import org.activemq.message.ActiveMQTopic; +import org.exolab.jmscts.provider.Administrator; +import org.exolab.jmscts.provider.Provider; + + +/** + * This class provides methods for obtaining and manipulating administered + * objects managed by the ActiveMQ implementation of JMS + * + * @author James Strahcan + * @author Hiram Chirino + * @version $Revision: 1.1.1.1 $ $Date: 2005/03/11 21:15:21 $ + */ +public class ActiveMQProvider implements Provider, Administrator { + + private String brokerURL = System.getProperty("url", "vm://localhost"); + private HashMap directory = new HashMap(); + private ActiveMQConnection adminConnection = null; + + /** + * Initialises the administation interface + * + * @throws JMSException if the administration interface can't be + * initialised + */ + public void initialise(boolean start) throws JMSException { + + System.out.println("=================================="); + System.out.println("ActiveMQ provider starting up."); + System.out.println("=================================="); + + directory.put(getQueueConnectionFactory(), new ActiveMQConnectionFactory(brokerURL)); + directory.put(getTopicConnectionFactory(), new ActiveMQConnectionFactory(brokerURL)); + directory.put(getXAQueueConnectionFactory(), new ActiveMQXAConnectionFactory(brokerURL)); + directory.put(getXATopicConnectionFactory(), new ActiveMQXAConnectionFactory(brokerURL)); + + if( adminConnection!=null ) + throw new JMSException("Admin connection allready established."); + + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(brokerURL); + adminConnection = (ActiveMQConnection) factory.createConnection(); + adminConnection.setClientID("admin"); + adminConnection.start(); + } + + /** + * This method cleans up the administrator + * @throws JMSException + */ + public void cleanup(boolean stop) throws JMSException { + System.out.println("=================================="); + System.out.println("ActiveMQ provider sutting down."); + System.out.println("=================================="); + adminConnection.close(); + adminConnection=null; + directory.clear(); + } + + /** + * Returns the administration interface + */ + public Administrator getAdministrator() { + return this; + } + + /** + * Look up the named administered object + * + * @param name the name that the administered object is bound to + * @return the administered object bound to name + * @throws javax.naming.NamingException if the object is not bound, or the lookup fails + */ + public Object lookup(String name) throws NamingException { + Object result = directory.get(name); + if (result == null) { + throw new NameNotFoundException("Name not found: " + name); + } + return result; + } + + /** + * Create an administered destination + * + * @param name the destination name + * @param queue if true, create a queue, else create a topic + * @throws JMSException if the destination cannot be created + */ + public void createDestination(String name, boolean queue) + throws JMSException { + if (queue) { + directory.put(name, new ActiveMQQueue(name)); + } + else { + directory.put(name, new ActiveMQTopic(name)); + } + } + + /** + * Destroy an administered destination + * + * @param name the destination name + * @throws JMSException if the destination cannot be destroyed + */ + public void destroyDestination(String name) throws JMSException { + Object object = directory.remove(name); + if( object!=null && object instanceof ActiveMQDestination ) { + adminConnection.destroyDestination((ActiveMQDestination) object); + } + } + + /** + * Returns true if an administered destination exists + * + * @param name the destination name + * @throws JMSException for any internal JMS provider error + */ + public boolean destinationExists(String name) + throws JMSException { + return directory.containsKey(name); + } + + public String getQueueConnectionFactory() { + return "QueueConnectionFactory"; + } + public String getTopicConnectionFactory() { + return "TopicConnectionFactory"; + } + public String getXAQueueConnectionFactory() { + return "XAQueueConnectionFactory"; + } + public String getXATopicConnectionFactory() { + return "XATopicConnectionFactory"; + } + + public String getBrokerURL() { + return brokerURL; + } + + public void setBrokerURL(String brokerURL) { + LogFactory.getLog("TEST").info("!!!!!!!!!!!!!! setting url to "+brokerURL); + this.brokerURL = brokerURL; + } +} diff --git a/systest/usecases/maven.xml b/systest/usecases/maven.xml new file mode 100644 index 0000000000..2bd858ec01 --- /dev/null +++ b/systest/usecases/maven.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/systest/usecases/project.properties b/systest/usecases/project.properties new file mode 100644 index 0000000000..d14f36f329 --- /dev/null +++ b/systest/usecases/project.properties @@ -0,0 +1,4 @@ +# ------------------------------------------------------------------- +# Build Properties +# ------------------------------------------------------------------- +maven.multiproject.type=rar diff --git a/systest/usecases/project.xml b/systest/usecases/project.xml new file mode 100644 index 0000000000..ed6cf025b3 --- /dev/null +++ b/systest/usecases/project.xml @@ -0,0 +1,37 @@ + + + + 3 + ${basedir}/../../../etc/project.xml + + ActiveMQ :: Use Cases + activemq-usecases + + A collection of user supplied use cases which may be slow and so are outside of the core build + + + org.activemq.usecases + + + + + geronimo-spec + geronimo-spec-servlet + ${geronimo_spec_servlet_version} + + false + + + + + + activemq + activemq-core + ${pom.currentVersion} + + truex + + + + + diff --git a/systest/usecases/src/conf/activemq.xml b/systest/usecases/src/conf/activemq.xml new file mode 100644 index 0000000000..5a3113d494 --- /dev/null +++ b/systest/usecases/src/conf/activemq.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jdbc:axiondb:activemqdb:target/data/axion + + + + + + org.apache.derby.jdbc.EmbeddedDriver + + + + jdbc:derby:derbydb;create=true + + + + + + + + + true + + + + + + + com.mysql.jdbc.Driver + + + jdbc:mysql://localhost/activemq + + + myname + + + mypassword + + + true + + + + + + diff --git a/systest/usecases/src/test/org/activemq/usecases/ActiveMQQueueReceiver.java b/systest/usecases/src/test/org/activemq/usecases/ActiveMQQueueReceiver.java new file mode 100644 index 0000000000..83285306ba --- /dev/null +++ b/systest/usecases/src/test/org/activemq/usecases/ActiveMQQueueReceiver.java @@ -0,0 +1,83 @@ +package blah; + +import java.sql.Timestamp; +import java.util.Date; +import java.util.Properties; + +import javax.jms.*; +import javax.naming.*; + +public class ActiveMQQueueReceiver { + public static void main(String[] args) { + Queue queue = null; + QueueConnectionFactory queueConnectionFactory = null; + QueueConnection queueConnection = null; + + try { + Properties props = new Properties(); + //props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.evermind.server.rmi.RMIInitialContextFactory"); + //props.setProperty(Context.PROVIDER_URL, "ormi://10.1.0.99:3202/default"); + //props.setProperty(Context.SECURITY_PRINCIPAL, "dan"); + //props.setProperty(Context.SECURITY_CREDENTIALS, "abc123"); + + props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.activemq.jndi.ActiveMQInitialContextFactory"); + props.setProperty(Context.PROVIDER_URL, "tcp://hostname:61616"); + props.setProperty("queue.BlahQueue", "example.BlahQueue"); + + // use the following if you with to make the receiver a broker + //props.setProperty("useEmbeddedBroker", "true"); + + Context jndiContext = new InitialContext(props); + + //queueConnectionFactory = (QueueConnectionFactory) jndiContext.lookup("jms/QueueConnectionFactory"); + //queue = (Queue) jndiContext.lookup("jms/demoQueue"); + queueConnectionFactory = (QueueConnectionFactory) jndiContext.lookup("QueueConnectionFactory"); + queue = (Queue) jndiContext.lookup("BlahQueue"); + } + catch (NamingException e) { + System.out.println("---------------------------ERROR-----------------------------"); + e.printStackTrace(); + System.exit(-1); + } + + try { + queueConnection = queueConnectionFactory.createQueueConnection(); + QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + QueueReceiver queueReceiver = queueSession.createReceiver(queue); + queueConnection.start(); + + //while (true) + //{ + System.out.println("Starting to receive"); + + TextMessage message = (TextMessage) queueReceiver.receive(10000); + + if (message != null) { + Date timestamp = new Date(message.getJMSTimestamp()); + System.out.println("Blah: " + message.getStringProperty("Blah")); + System.out.println("Timestamp: " + timestamp); + System.out.println("Payload: " + message.getText()); + } + else { + System.out.println("NO MESSAGES"); + } + System.out.println(); + + //Thread.sleep(10000); + //} + } + catch (Exception e) { + System.out.println("SOMETHING WENT WRONG WHILE CONSUMING"); + e.printStackTrace(); + } + finally { + if (queueConnection != null) { + try { + queueConnection.close(); + } + catch (Exception ignored) { + } + } + } + } +} diff --git a/systest/usecases/src/test/org/activemq/usecases/ActiveMQQueueSender.java b/systest/usecases/src/test/org/activemq/usecases/ActiveMQQueueSender.java new file mode 100644 index 0000000000..b2074801fe --- /dev/null +++ b/systest/usecases/src/test/org/activemq/usecases/ActiveMQQueueSender.java @@ -0,0 +1,80 @@ +package blah; + +import java.util.Properties; + +import javax.jms.*; +import javax.naming.*; + +public class ActiveMQQueueSender +{ + public static void main(String[] args) + { + String msg = args.length < 1 ? "This is the default message" : args[0]; + + Queue queue = null; + QueueConnectionFactory queueConnectionFactory = null; + QueueConnection queueConnection = null; + + try + { + Properties props = new Properties(); + //props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.evermind.server.rmi.RMIInitialContextFactory"); + //props.setProperty(Context.PROVIDER_URL, "ormi://10.1.0.99:3202/default"); + //props.setProperty(Context.SECURITY_PRINCIPAL, "dan"); + //props.setProperty(Context.SECURITY_CREDENTIALS, "abc123"); + + props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.activemq.jndi.ActiveMQInitialContextFactory"); + props.setProperty(Context.PROVIDER_URL, "tcp://hostname:61616"); + props.setProperty("queue.BlahQueue", "example.BlahQueue"); + + Context jndiContext = new InitialContext(props); + + //queueConnectionFactory = (QueueConnectionFactory) jndiContext.lookup("jms/QueueConnectionFactory"); + //queue = (Queue) jndiContext.lookup("jms/demoQueue"); + + queueConnectionFactory = (QueueConnectionFactory) jndiContext.lookup("QueueConnectionFactory"); + queue = (Queue) jndiContext.lookup("BlahQueue"); + + } + catch (NamingException e) + { + System.out.println("---------------------------ERROR-----------------------------"); + e.printStackTrace(); + System.exit(-1); + } + + try + { + queueConnection = queueConnectionFactory.createQueueConnection(); + QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + QueueSender queueSender = queueSession.createSender(queue); + //queueSender.setDeliveryMode(DeliveryMode.PERSISTENT); + //queueSender.setTimeToLive(1000*60*60); + TextMessage message = queueSession.createTextMessage(); + + message.setText(msg); + message.setStringProperty("Blah", "Hello!"); + + queueSender.send(message); + System.out.println("Message sent"); + } + catch (JMSException e) + { + System.out.println("SOMETHING WENT WRONG WHILE SENDING"); + e.printStackTrace(); + } + finally + { + if (queueConnection != null) + { + try + { + queueConnection.close(); + } + catch (Exception ignored) + { + } + } + } + } +} diff --git a/systest/usecases/src/test/org/activemq/usecases/AvailableConsumerTest.java b/systest/usecases/src/test/org/activemq/usecases/AvailableConsumerTest.java new file mode 100644 index 0000000000..f758fa1787 --- /dev/null +++ b/systest/usecases/src/test/org/activemq/usecases/AvailableConsumerTest.java @@ -0,0 +1,400 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import junit.framework.TestCase; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.broker.BrokerContainer; +import org.activemq.broker.impl.BrokerContainerImpl; +import org.activemq.store.vm.VMPersistenceAdapter; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; + +/** + * TODO this test case relies on a perfect distribution of messages, dispatched one by one to each consumer. + * So this test case can only really work with an explicit setting of 'prefetch = 1' or something similar. + * The default out of the box dispatcher eagerly dispatches messages as quickly as possible. + * + * Ensures that if there is a network of brokers that a message is always dispatched to an available consumer * regardless of which broker it is on. + * + * @version $Revision: 1.1 $ + */ +public class AvailableConsumerTest extends TestCase { + private static final transient Log log = LogFactory.getLog(AvailableConsumerTest.class); + private static final long BROKER_INITIALIZATION_DELAY = 7000; + + public void testOneBroker() throws Throwable { + String[] urls = new String[]{"tcp://localhost:9000"}; + runSimulation(urls, 2, "QUEUE_NAME"); + //runSimulation(urls, 5, "QUEUE_NAME"); + //runSimulation(urls, 10, "QUEUE_NAME"); + } + + public void testTwoBrokers() throws Throwable { + String[] urls = new String[]{"tcp://localhost:9000", "tcp://localhost:9001"}; + runSimulation(urls, 2, "QUEUE_NAME"); + runSimulation(urls, 5, "QUEUE_NAME"); + runSimulation(urls, 10, "QUEUE_NAME"); + } + + public void testTenBrokers() throws Throwable { + String[] urls = new String[]{ + "tcp://localhost:9000", "tcp://localhost:9001", "tcp://localhost:9002", "tcp://localhost:9003", "tcp://localhost:9004", + "tcp://localhost:9005", "tcp://localhost:9006", "tcp://localhost:9007", "tcp://localhost:9008", "tcp://localhost:9009" + }; + runSimulation(urls, 2, "QUEUE_NAME"); + runSimulation(urls, 5, "QUEUE_NAME"); + runSimulation(urls, 10, "QUEUE_NAME"); + } + + private void runSimulation(String[] brokerURLs, int numConsumers, String queue) throws Throwable { + assertTrue("More than one consumer is required", numConsumers > 1); + + BrokerThread[] brokers = null; + BlockingConsumer[] consumers = null; + MessagePublisher[] publishers = null; + + try { + String reliableURL = createReliableURL(brokerURLs); + brokers = createBrokers(brokerURLs); + consumers = createConsumers(reliableURL, numConsumers, queue); + publishers = createPublishers(reliableURL, 1, queue); + + // Now get all of the consumers blocked except for one + { + for (int i = 0; i < consumers.length - 1; i++) { + publishers[0].sendMessage(); + } + waitUntilNumBlocked(consumers, consumers.length - 1); + } + + // Now send one more message which should cause all of the consumers to be blocked + { + publishers[0].sendMessage(); + waitUntilNumBlocked(consumers, consumers.length); + } + + // Unblock a consumer and make sure it is unblocked + { + synchronized (consumers[0]) { + consumers[0].notifyAll(); + } + waitUntilNumBlocked(consumers, consumers.length - 1); + } + + // Send another message and make sure it is blocked again + { + publishers[0].sendMessage(); + waitUntilNumBlocked(consumers, consumers.length); + } + + // Finally queue up a message for each consumer but one, then unblock them all and make sure one is still unblocked + { + for (int i = 0; i < consumers.length - 1; i++) { + publishers[0].sendMessage(); + } + + for (int i = 0; i < consumers.length; i++) { + synchronized (consumers[i]) { + consumers[i].notifyAll(); + } + } + + waitUntilNumBlocked(consumers, consumers.length - 1); + } + } + finally { + cleanupSimulation(brokers, publishers, consumers); + } + } + + private static void cleanupSimulation(BrokerThread[] brokers, MessagePublisher[] publishers, BlockingConsumer[] consumers) { + try { + for (int i = 0; i < publishers.length; i++) { + publishers[i].done(); + } + } + catch (Throwable t) { + log.warn("Non-fatal error during cleanup", t); + } + + try { + for (int i = 0; i < consumers.length; i++) { + // Unblock it in case it is blocked + synchronized (consumers[i]) { + consumers[i].notifyAll(); + } + + consumers[i].done(); + } + } + catch (Throwable t) { + log.warn("Non-fatal error during cleanup", t); + } + + try { + for (int i = 0; i < brokers.length; i++) { + brokers[i].done(); + } + } + catch (Throwable t) { + log.warn("Non-fatal error during cleanup", t); + } + } + + private static BrokerThread[] createBrokers(String[] brokerURLs) throws InterruptedException { + BrokerThread[] threads = new BrokerThread[brokerURLs.length]; + for (int i = 0; i < threads.length; i++) { + threads[i] = new BrokerThread(Integer.toString(i), brokerURLs[i], brokerURLs); + threads[i].start(); + } + +// Delay so that the brokers have a chance to come up fully and connect to each other + log.debug("Created " + threads.length + " brokers, giving them time to initialize..."); + Object temp = new Object(); + synchronized (temp) { + temp.wait(BROKER_INITIALIZATION_DELAY * brokerURLs.length); + } + log.debug("Brokers should be initialized now"); + + return threads; + } + + private static BlockingConsumer[] createConsumers(String brokerURL, int numConsumers, String queue) throws JMSException { + BlockingConsumer[] consumers = new BlockingConsumer[numConsumers]; + for (int i = 0; i < consumers.length; i++) { + consumers[i] = new BlockingConsumer(brokerURL, queue); + } + + return consumers; + } + + private static MessagePublisher[] createPublishers(String brokerURL, int numPublishers, String queue) throws JMSException { + MessagePublisher[] publishers = new MessagePublisher[numPublishers]; + for (int i = 0; i < publishers.length; i++) { + publishers[i] = new MessagePublisher(brokerURL, queue); + } + + return publishers; + } + + private static String createReliableURL(String[] brokerURLs) { + StringBuffer sb = new StringBuffer("reliable:"); + for (int i = 0; i < brokerURLs.length; i++) { + if (i != 0) { + sb.append(','); + } + + sb.append(brokerURLs[i]); + } + + return sb.toString(); + } + + private static void waitUntilNumBlocked(BlockingConsumer[] consumers, int expectedNumBlocked) throws InterruptedException { + boolean found = false; + int maxIterations = 50; + for (int iteration = 0; iteration < maxIterations; iteration++) { + int numBlocked = 0; + for (int i = 0; i < consumers.length; i++) { + numBlocked += consumers[i].isBlocked() ? 1 : 0; + } + + if (numBlocked == expectedNumBlocked) { + found = true; + break; + } + + log.debug("Waiting for " + expectedNumBlocked + " consumers to block, currently only " + numBlocked + " are blocked."); + Object temp = new Object(); + synchronized (temp) { + temp.wait(250); + } + } + + assertTrue("Never saw " + expectedNumBlocked + " consumers blocked", found); + } + + private static final class BrokerThread extends Thread { + private final String m_id; + private final String m_myURL; + private final String[] m_linkedURLs; + private BrokerContainer m_container; + + public BrokerThread(String id, String myURL, String[] linkedURLs) { + m_id = id; + m_myURL = myURL; + m_linkedURLs = linkedURLs; + } + + public void run() { + try { + m_container = new BrokerContainerImpl(m_id); + m_container.setPersistenceAdapter(new VMPersistenceAdapter()); + m_container.addConnector(m_myURL); + + for (int i = 0; i < m_linkedURLs.length; i++) { + if (!m_myURL.equals(m_linkedURLs[i])) { + m_container.addNetworkConnector("reliable:" + m_linkedURLs[i]); + } + } + + m_container.start(); + } + catch (JMSException e) { + log.error("Unexpected exception", e); + } + } + + public void done() { + try { + m_container.stop(); + } + catch (JMSException e) { + log.error("Unexpected exception", e); + } + } + } + + private static final class MessagePublisher { + private final String m_url; + private final Connection m_connection; + private final Session m_session; + private final Queue m_queue; + private final MessageProducer m_producer; + + public MessagePublisher(String url, String queue) throws JMSException { + this(url, queue, DeliveryMode.PERSISTENT); + } + + public MessagePublisher(String url, String queue, int deliveryMode) throws JMSException { + m_url = url; + + m_connection = new ActiveMQConnectionFactory(m_url).createConnection(); + m_connection.start(); + + m_session = m_connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + m_queue = m_session.createQueue(queue); + + m_producer = m_session.createProducer(m_queue); + m_producer.setDeliveryMode(deliveryMode); + } + + public void done() { + try { + m_producer.close(); + } + catch (Throwable ignored) { + } + + try { + m_session.close(); + } + catch (Throwable ignored) { + } + + try { + m_connection.close(); + } + catch (Throwable ignored) { + } + } + + public void sendMessage() throws JMSException { + Message message = m_session.createMessage(); + m_producer.send(message); + } + } + + private static final class BlockingConsumer implements MessageListener { + private final String m_url; + private final Connection m_connection; + private final Session m_session; + private final Queue m_queue; + private final MessageConsumer m_consumer; + private boolean m_blocked; + + public BlockingConsumer(String url, String queue) throws JMSException { + m_url = url; + + m_connection = new ActiveMQConnectionFactory(m_url).createConnection(); + m_connection.start(); + + m_session = m_connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + m_queue = m_session.createQueue(queue); + m_consumer = m_session.createConsumer(m_queue); + m_consumer.setMessageListener(this); + m_blocked = false; + } + + public boolean isBlocked() { + return m_blocked; + } + + public void done() { + try { + m_consumer.setMessageListener(null); + } + catch (Throwable ignored) { + } + + try { + m_consumer.close(); + } + catch (Throwable ignored) { + } + + try { + m_session.close(); + } + catch (Throwable ignored) { + } + + try { + m_connection.close(); + } + catch (Throwable ignored) { + } + } + + public void onMessage(Message message) { + m_blocked = true; + + synchronized (this) { + try { + wait(); + } + catch (InterruptedException e) { + log.error("Unexpected InterruptedException during onMessage", e); + } + } + + m_blocked = false; + } + } +} \ No newline at end of file diff --git a/systest/usecases/src/test/org/activemq/usecases/BecksNetworkTest.java b/systest/usecases/src/test/org/activemq/usecases/BecksNetworkTest.java new file mode 100644 index 0000000000..ef97711d01 --- /dev/null +++ b/systest/usecases/src/test/org/activemq/usecases/BecksNetworkTest.java @@ -0,0 +1,566 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import junit.framework.TestCase; +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.broker.BrokerContainer; +import org.activemq.broker.impl.BrokerContainerImpl; +import org.activemq.store.vm.VMPersistenceAdapter; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.jms.*; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * @author bbeck + * @version $Revision: 1.1 $ + */ +public class BecksNetworkTest extends TestCase { + private static final Log log = LogFactory.getLog(BecksNetworkTest.class); + + private static final int NUM_BROKERS = 10; + private static final int NUM_PRODUCERS = 10; + private static final int NUM_CONSUMERS = 1; + private static final int NUM_MESSAGES = 1; + private static final long MESSAGE_SEND_DELAY = 100; + private static final long MESSAGE_RECEIVE_DELAY = 50; + private static final int BASE_PORT = 9500; + private static final String QUEUE_NAME = "QUEUE"; + private static final String MESSAGE_PRODUCER_KEY = "PRODUCER"; + private static final String MESSAGE_BODY_KEY = "BODY"; + + public void testCase() throws Throwable { + main(new String[]{}); + } + + public static void main(String[] args) throws Throwable { + String[] addresses = new String[NUM_BROKERS]; + for (int i = 0; i < NUM_BROKERS; i++) { + addresses[i] = "tcp://localhost:" + (BASE_PORT + i); + } + + log.info("Starting brokers"); + BrokerContainer[] brokers = startBrokers(addresses); + String reliableURL = createAddressString(addresses, "reliable:", null); + + log.info("Creating simulation state"); + final SimulationState state = new SimulationState(NUM_PRODUCERS * NUM_MESSAGES); + Thread stateWatcher = new Thread("Simulation State Watcher Thread") { + public void run() { + while (state.getState() != SimulationState.FINISHED) { + log.info("State: " + state); + + synchronized (this) { + try { + wait(5000); + } + catch (InterruptedException ignored) { + } + } + } + } + }; + stateWatcher.setDaemon(true); + stateWatcher.start(); + + log.info("Starting components"); + MessageProducerComponent[] producers = new MessageProducerComponent[NUM_PRODUCERS]; + MessageConsumerComponent[] consumers = new MessageConsumerComponent[NUM_CONSUMERS]; + { + for (int i = 0; i < NUM_PRODUCERS; i++) { + producers[i] = new MessageProducerComponent(state, "MessageProducer[" + i + "]", reliableURL, NUM_MESSAGES); + producers[i].start(); + } + + for (int i = 0; i < NUM_CONSUMERS; i++) { + consumers[i] = new MessageConsumerComponent(state, "MessageConsumer[" + i + "]", reliableURL); + consumers[i].start(); + } + } + + // Start the simulation + log.info("##### Starting the simulation..."); + state.setState(SimulationState.RUNNING); + + for (int i = 0; i < producers.length; i++) { + producers[i].join(); + } + log.info("Producers finished"); + + for (int i = 0; i < consumers.length; i++) { + consumers[i].join(); + log.info(consumers[i].getId() + " consumed " + consumers[i].getNumberOfMessagesConsumed() + " messages."); + } + + log.info("Consumers finished"); + log.info("State: " + state); + + state.waitForSimulationState(SimulationState.FINISHED); + + log.info("Stopping brokers"); + for (int i = 0; i < brokers.length; i++) { + brokers[i].stop(); + } + } + + private static BrokerContainer[] startBrokers(String[] addresses) throws JMSException { + BrokerContainer[] containers = new BrokerContainer[addresses.length]; + for (int i = 0; i < containers.length; i++) { + containers[i] = new BrokerContainerImpl(Integer.toString(i)); + containers[i].setPersistenceAdapter(new VMPersistenceAdapter()); + containers[i].addConnector(addresses[i]); + + for (int j = 0; j < addresses.length; j++) { + if (i == j) { + continue; + } + + containers[i].addNetworkConnector("reliable:" + addresses[j]); + } + + containers[i].start(); + + log.debug("Created broker on " + addresses[i]); + } + + // Delay so this broker has a chance to come up fully... + try { + Thread.sleep(2000 * containers.length); + } + catch (InterruptedException ignored) { + } + + return containers; + } + + /* + private static BrokerContainer[] startBrokers(String[] addresses) throws JMSException, IOException + { + for(int i = 0; i < addresses.length; i++) { + File temp = File.createTempFile("broker_" + i + "_", ".xml"); + temp.deleteOnExit(); + + + PrintWriter fout = new PrintWriter(new FileWriter(temp)); + fout.println(""); + fout.println(""); + fout.println(""); + fout.println(" "); + fout.println(" "); + fout.println(" "); + fout.println(" "); + + if(addresses.length > 1) { + String otherAddresses = createAddressString(addresses, "list:", addresses[i]); + otherAddresses = "tcp://localhost:9000"; + + fout.println(" "); + fout.println(" "); + fout.println(" "); + } + + fout.println(" "); + fout.println(" "); + fout.println(" "); + fout.println(" "); + fout.println(""); + fout.close(); + + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("vm://" + i); + factory.setUseEmbeddedBroker(true); + factory.setBrokerXmlConfig("file:" + temp.getAbsolutePath()); + factory.setBrokerName("broker-" + addresses[i]); + factory.start(); + + Connection c = factory.createConnection(); + c.start(); + } + + // Delay so this broker has a chance to come up fully... + try { Thread.sleep(2000*addresses.length); } + catch(InterruptedException ignored) {} + + return null; + } + */ + + private static String createAddressString(String[] addresses, String prefix, String addressToSkip) { + StringBuffer sb = new StringBuffer(prefix); + boolean first = true; + + for (int i = 0; i < addresses.length; i++) { + if (addressToSkip != null && addressToSkip.equals(addresses[i])) { + continue; + } + + if (!first) { + sb.append(','); + } + + sb.append(addresses[i]); + first = false; + } + + return sb.toString(); + } + + private static final class SimulationState { + public static final int INITIALIZED = 1; + public static final int RUNNING = 2; + public static final int FINISHED = 3; + + private final Object m_stateLock; + private int m_state; + private final int m_numExpectedMessages; + private final Set m_messagesProduced; + private final Set m_messagesConsumed; + + public SimulationState(int numMessages) { + m_stateLock = new Object(); + synchronized (m_stateLock) { + m_state = INITIALIZED; + m_numExpectedMessages = numMessages; + m_messagesProduced = new HashSet(); + m_messagesConsumed = new HashSet(); + } + } + + public int getState() { + synchronized (m_stateLock) { + return m_state; + } + } + + public void setState(int newState) { + synchronized (m_stateLock) { + m_state = newState; + m_stateLock.notifyAll(); + } + } + + public void waitForSimulationState(int state) throws InterruptedException { + synchronized (m_stateLock) { + while (m_state != state) { + m_stateLock.wait(); + } + } + } + + public void onMessageProduced(String producerId, String messageBody) { + log.debug("-> onMessageProduced(" + producerId + ", " + messageBody + ")"); + + synchronized (m_stateLock) { + if (m_state == INITIALIZED) { + throw new RuntimeException("Message produced before the simulation has begun: messageBody=" + messageBody); + } + + if (m_state == FINISHED) { + throw new RuntimeException("Message produced after the simulation has finished: messageBody=" + messageBody); + } + + if (!m_messagesProduced.add(messageBody)) { + throw new RuntimeException("Duplicate message produced: messageBody=" + messageBody); + } + } + } + + public void onMessageConsumed(String consumerId, String messageBody) { + log.debug("<- onMessageConsumed(" + consumerId + ", " + messageBody + ")"); + + synchronized (m_stateLock) { + if (m_state != RUNNING) { + throw new RuntimeException("Message consumed while the simulation wasn't running: state = " + m_state + ", messageBody=" + messageBody); + } + + if (!m_messagesProduced.contains(messageBody)) { + throw new RuntimeException("Message consumed that wasn't produced: messageBody=" + messageBody); + } + + if (!m_messagesConsumed.add(messageBody)) { + throw new RuntimeException("Message consumed more than once: messageBody=" + messageBody); + } + + if (m_messagesConsumed.size() == m_numExpectedMessages) { + setState(FINISHED); + log.info("All expected messages have been consumed, finishing simulation."); + } + } + } + + public String toString() { + synchronized (m_stateLock) { + SortedMap unconsumed = new TreeMap(); + for (Iterator iter = m_messagesProduced.iterator(); iter.hasNext();) { + String message = (String) iter.next(); + int colonIndex = message.indexOf(':'); + String producerId = message.substring(0, colonIndex); + + Integer numMessages = (Integer) unconsumed.get(producerId); + numMessages = (numMessages == null) ? new Integer(1) : new Integer(numMessages.intValue() + 1); + unconsumed.put(producerId, numMessages); + } + + for (Iterator iter = m_messagesConsumed.iterator(); iter.hasNext();) { + String message = (String) iter.next(); + int colonIndex = message.indexOf(':'); + String producerId = message.substring(0, colonIndex); + + Integer numMessages = (Integer) unconsumed.get(producerId); + numMessages = (numMessages == null) ? new Integer(-1) : new Integer(numMessages.intValue() - 1); + if (numMessages.intValue() == 0) { + unconsumed.remove(producerId); + } + else { + unconsumed.put(producerId, numMessages); + } + } + + + return "SimulationState[" + + "state=" + m_state + " " + + "numExpectedMessages=" + m_numExpectedMessages + " " + + "numMessagesProduced=" + m_messagesProduced.size() + " " + + "numMessagesConsumed=" + m_messagesConsumed.size() + " " + + "unconsumed=" + unconsumed; + } + } + } + + private static abstract class SimulationComponent extends Thread { + protected final SimulationState m_simulationState; + protected final String m_id; + + protected abstract void _initialize() throws Throwable; + + protected abstract void _run() throws Throwable; + + protected abstract void _cleanup() throws Throwable; + + public SimulationComponent(SimulationState state, String id) { + super(id); + + m_simulationState = state; + m_id = id; + } + + public String getId() { + return m_id; + } + + public final void run() { + try { + try { + _initialize(); + } + catch (Throwable t) { + log.error("Error during initialization", t); + return; + } + + try { + if (m_simulationState.getState() == SimulationState.FINISHED) { + log.info(m_id + " : NO NEED TO WAIT FOR RUNNING - already FINISHED"); + } + else { + log.info(m_id + ": WAITING for RUNNING started"); + m_simulationState.waitForSimulationState(SimulationState.RUNNING); + log.info(m_id + ": WAITING for RUNNING finished"); + } + } + catch (InterruptedException e) { + log.error("Interrupted during wait for the simulation to begin", e); + return; + } + + try { + _run(); + } + catch (Throwable t) { + log.error("Error during running", t); + } + } + finally { + try { + _cleanup(); + } + catch (Throwable t) { + log.error("Error during cleanup", t); + } + } + + } + } + + private static abstract class JMSComponent extends SimulationComponent { + protected final String m_url; + protected Connection m_connection; + protected Session m_session; + protected Queue m_queue; + + public JMSComponent(SimulationState state, String id, String url) { + super(state, id); + + m_url = url; + } + + protected void _initialize() throws JMSException { + m_connection = new ActiveMQConnectionFactory(m_url).createConnection(); + m_connection.start(); + + m_session = m_connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + m_queue = m_session.createQueue(QUEUE_NAME); + } + + protected void _cleanup() throws JMSException { + if (m_session != null) { + m_session.close(); + } + + if (m_connection != null) { + m_connection.close(); + } + } + } + + private static final class MessageProducerComponent extends JMSComponent { + private final int m_numMessagesToSend; + private MessageProducer m_producer; + + public MessageProducerComponent(SimulationState state, String id, String url, int numMessages) { + super(state, id, url); + + m_numMessagesToSend = numMessages; + } + + protected void _initialize() throws JMSException { + super._initialize(); + + m_producer = m_session.createProducer(m_queue); + m_producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + } + + protected void _cleanup() throws JMSException { + if (m_producer != null) { + m_producer.close(); + } + + super._cleanup(); + } + + public void _run() throws JMSException, InterruptedException { + log.debug(m_id + ": started"); + for (int num = 0; num < m_numMessagesToSend; num++) { + String messageBody = createMessageBody(m_id, num); + + MapMessage message = m_session.createMapMessage(); + message.setString(MESSAGE_PRODUCER_KEY, m_id); + message.setString(MESSAGE_BODY_KEY, messageBody); + + // Pretend to be doing some work.... + Thread.sleep(MESSAGE_SEND_DELAY); + + m_simulationState.onMessageProduced(m_id, messageBody); + m_producer.send(message); + } + } + + private static String createMessageBody(String id, int num) { + return id + ":" + Integer.toString(num); + } + } + + private static final class MessageConsumerComponent extends JMSComponent implements MessageListener { + private final Object m_stateLock; + private boolean m_inOnMessage; + + private MessageConsumer m_consumer; + private int m_numMessagesConsumed; + + public MessageConsumerComponent(SimulationState state, String id, String url) { + super(state, id, url); + m_stateLock = new Object(); + m_inOnMessage = false; + } + + protected void _initialize() throws JMSException { + super._initialize(); + + m_consumer = m_session.createConsumer(m_queue); + m_consumer.setMessageListener(this); + + m_numMessagesConsumed = 0; + } + + protected void _cleanup() throws JMSException { + if (m_consumer != null) { + m_consumer.close(); + } + + super._cleanup(); + } + + public void _run() throws InterruptedException { + log.info(m_id + ": WAITING for FINISHED started"); + m_simulationState.waitForSimulationState(SimulationState.FINISHED); + log.info(m_id + ": WAITING for FINISHED finished"); + } + + public int getNumberOfMessagesConsumed() { + return m_numMessagesConsumed; + } + + public void onMessage(Message msg) { + synchronized (m_stateLock) { + if (m_inOnMessage) { + log.error("Already in onMessage!!!"); + } + + m_inOnMessage = true; + } + + try { + MapMessage message = (MapMessage) msg; + String messageBody = message.getString(MESSAGE_BODY_KEY); + + m_simulationState.onMessageConsumed(m_id, messageBody); + m_numMessagesConsumed++; + + // Pretend to be doing some work.... + Thread.sleep(MESSAGE_RECEIVE_DELAY); + } + catch (Throwable t) { + log.error("Unexpected error during onMessage: message=" + msg, t); + } + finally { + synchronized (m_stateLock) { + if (!m_inOnMessage) { + log.error("Not already in onMessage!!!"); + } + + m_inOnMessage = false; + } + } + } + } +} diff --git a/systest/usecases/src/test/org/activemq/usecases/EmbeddedRemoteConnector.xml b/systest/usecases/src/test/org/activemq/usecases/EmbeddedRemoteConnector.xml new file mode 100644 index 0000000000..f9a06b79bd --- /dev/null +++ b/systest/usecases/src/test/org/activemq/usecases/EmbeddedRemoteConnector.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.apache.derby.jdbc.EmbeddedDriver + + + + jdbc:derby:derbydb;create=true + + + + + + + + + true + + + + + + + com.mysql.jdbc.Driver + + + jdbc:mysql://localhost/activemq + + + myname + + + mypassword + + + true + + + + + \ No newline at end of file diff --git a/systest/usecases/src/test/org/activemq/usecases/EmbeddedRemoteConnectorTest.java b/systest/usecases/src/test/org/activemq/usecases/EmbeddedRemoteConnectorTest.java new file mode 100644 index 0000000000..9a68f97713 --- /dev/null +++ b/systest/usecases/src/test/org/activemq/usecases/EmbeddedRemoteConnectorTest.java @@ -0,0 +1,178 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases; + +import org.activemq.ActiveMQConnectionFactory; +import org.activemq.broker.BrokerContainer; +import org.activemq.broker.impl.BrokerContainerImpl; +import junit.framework.TestCase; + + +import org.activemq.spring.SpringBrokerContainerFactory; +import org.springframework.core.io.ClassPathResource; + +import javax.jms.JMSException; +import javax.jms.Session; +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.MessageProducer; +import javax.jms.MessageConsumer; +import javax.jms.Destination; +import javax.jms.TextMessage; +import javax.jms.Message; + +import org.activemq.io.impl.DefaultWireFormat; +import org.activemq.store.PersistenceAdapter; +import org.activemq.store.jdbc.JDBCPersistenceAdapter; +import org.apache.derby.jdbc.EmbeddedDataSource; + +/** + * @version $Revision: 1.1.1.1 $ + */ +public class EmbeddedRemoteConnectorTest extends TestCase{ + BrokerContainer receiveBroker; + String subject = "TOOL.DEFAULT"; + int messageCount = 10; + boolean init = false; + + + + protected ActiveMQConnectionFactory createReceiverConnectionFactory() throws JMSException { + receiveBroker = new BrokerContainerImpl("receiver"); + receiveBroker.addConnector("tcp://localhost:61616"); + receiveBroker.setPersistenceAdapter(createPersistenceAdapter()); + receiveBroker.start(); + + ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(receiveBroker, "tcp://localhost:61616"); + + return factory; + } + + protected ActiveMQConnectionFactory createEmbeddedRemoteBrokerConnectionFactory() throws JMSException { + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(); + connectionFactory.setUseEmbeddedBroker(true); + connectionFactory.setBrokerContainerFactory(new SpringBrokerContainerFactory(new ClassPathResource("org/activemq/usecases/EmbeddedRemoteConnector.xml"))); + + return connectionFactory; + } + + + + + public void testSendFromEmbeddedRemote() throws Exception { + + ActiveMQConnectionFactory embeddedRemoteFactory = createEmbeddedRemoteBrokerConnectionFactory(); + Connection conn = embeddedRemoteFactory.createConnection(); + Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination destination = session.createQueue(subject); + MessageProducer producer = session.createProducer(destination); + producer.setDeliveryMode(DeliveryMode.PERSISTENT); + + for (int i = 0; i < messageCount; i++) { + TextMessage message = session.createTextMessage("Message : " +i); + producer.send(message); + System.out.println("Sending " + message.getText()); + } + + + + } + + public void testReceiver() throws Exception { + ActiveMQConnectionFactory receiveFactory = createReceiverConnectionFactory(); + + Connection conn = receiveFactory.createConnection(); + Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination destination = session.createQueue(subject); + MessageConsumer consumer = session.createConsumer(destination); + conn.start(); + + + int counter =0; + + for (int i = 0; i < messageCount; i++) { + System.out.println("Before receive"); + Message message = consumer.receive(10000); + try { + if (message instanceof TextMessage) { + TextMessage txtMsg = (TextMessage) message; + System.out.println("Received : " + txtMsg.getText()); + + //increment counter only when a message is received + if(txtMsg.getText()!=null && txtMsg.getText().length()==0) { + counter++; + } + + + } + } + catch (Exception e) { + e.printStackTrace(); + } + + + } + + consumer.close(); + session.close(); + conn.close(); + + assertTrue("No messages were received",counter==messageCount ); + + } + + + + protected void tearDown() throws Exception { + + if (receiveBroker != null) { + receiveBroker.stop(); + } + } + + + + + + + /** + * Returns the persistence adapter. + * Sets up the testing database to be used when the messages are persistent. + * It attempts to recreate the tables everytime the test is executed. + * + * @return PersistenceAdapter - persistence adapter. + */ + protected PersistenceAdapter createPersistenceAdapter() { + EmbeddedDataSource ds = new EmbeddedDataSource(); + ds.setDatabaseName("testdb"); + if (!init) { + ds.setCreateDatabase("create"); + } + + JDBCPersistenceAdapter persistenceAdapter = new JDBCPersistenceAdapter(ds, new DefaultWireFormat()); + + if (!init) { + persistenceAdapter.setDropTablesOnStartup(true); + } + + init = true; + + return persistenceAdapter; + } + +} diff --git a/systest/usecases/src/test/org/activemq/usecases/lars/Server.java b/systest/usecases/src/test/org/activemq/usecases/lars/Server.java new file mode 100644 index 0000000000..6c13bae52a --- /dev/null +++ b/systest/usecases/src/test/org/activemq/usecases/lars/Server.java @@ -0,0 +1,145 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases.lars; + +import org.activemq.ActiveMQConnectionFactory; + +import javax.jms.*; + + +/** + * @version $Revision: 1.1 $ + */ +public class Server implements MessageListener { + private String configFile = "src/conf/activemq.xml"; + private QueueConnectionFactory cf; + + private QueueConnection queueConnection; + private QueueSession queueSession; + private Queue queue; + private MessageConsumer messageConsumer; + private QueueSender queueSender = null; + + private String messagePrefix = "X"; + private int messageMultiplier = 1; + + public Server() throws JMSException { + super(); + + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(); + connectionFactory.setUseEmbeddedBroker(true); + connectionFactory.setBrokerURL("vm://localhost"); + connectionFactory.setBrokerXmlConfig("file:" + configFile); + + cf = connectionFactory; + + initQueue(); + initListener(); + initSender(); + + queueConnection.start(); + } + + /** + * @param msgPfx May only contain one capital letter. + * @throws JMSException + */ + public Server(String msgPfx) throws JMSException { + this(); + messagePrefix = msgPfx; + + char letter = messagePrefix.charAt(0); + int pow = letter - 'A'; + + if (pow > 0 && pow <= 'Z' - 'A') { + messageMultiplier = (int) Math.pow(10, pow); + } + } + + private void initQueue() throws JMSException { + queueConnection = cf.createQueueConnection(); + queueSession = queueConnection.createQueueSession(false, javax.jms.Session.CLIENT_ACKNOWLEDGE); + queue = queueSession.createQueue("test_queue"); + } + + private void initSender() throws JMSException { + queueSender = queueSession.createSender(queue); + queueSender.setDeliveryMode(DeliveryMode.PERSISTENT); + queueSender.setTimeToLive(5000); + } + + private void initListener() throws JMSException { + messageConsumer = queueSession.createReceiver(queue); + messageConsumer.setMessageListener(this); + } + + public void onMessage(Message message) { + if (message instanceof MapMessage) { + try { + MapMessage msg = (MapMessage) message; + String command = msg.getStringProperty("cmd"); + + System.out.println(messagePrefix + command); + + msg.acknowledge(); + } + catch (JMSException e) { + e.printStackTrace(); + } + } + else { + System.out.println("Unknown message type"); + } + } + + public final void sendMessage(int i) throws JMSException { + MapMessage message = getMapMessage(); + + message.setStringProperty("cmd", Integer.toString(i * messageMultiplier)); + queueSender.send(message); + } + + public MapMessage getMapMessage() throws JMSException { + return queueSession.createMapMessage(); + } + + public void go() throws JMSException { + for (int i = 0; i < 20; i++) { + sendMessage(i); + + try { + Thread.sleep(500); + } + catch (InterruptedException e) { + } + } + } + + public static void main(String[] args) { + try { + Server s = new Server(); + + s.go(); + + //System.exit(0); + } + catch (JMSException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/systest/usecases/src/test/org/activemq/usecases/lars/ServerLoader.java b/systest/usecases/src/test/org/activemq/usecases/lars/ServerLoader.java new file mode 100644 index 0000000000..7e5296ef60 --- /dev/null +++ b/systest/usecases/src/test/org/activemq/usecases/lars/ServerLoader.java @@ -0,0 +1,72 @@ +/** + * + * Copyright 2004 Protique Ltd + * + * Licensed 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.activemq.usecases.lars; + +import java.lang.reflect.Constructor; + +/** + * @version $Revision: 1.1 $ + */ +public class ServerLoader { + + public static void main(String[] args) { + try { + ClassLoader cl1 = new ClassLoader() { + }; + ClassLoader cl2 = new ClassLoader() { + }; + + Class c1 = cl1.loadClass("org.activemq.usecases.lars.Server"); + Class c2 = cl2.loadClass("org.activemq.usecases.lars.Server"); + + final Constructor i1 = c1.getConstructor(new Class[]{String.class}); + final Constructor i2 = c2.getConstructor(new Class[]{String.class}); + + + Thread t1 = new Thread() { + public void run() { + try { + Server s1 = (Server) i1.newInstance(new Object[]{"A"}); + s1.go(); + } + catch (Exception e) { + e.printStackTrace(); + } + } + }; + Thread t2 = new Thread() { + public void run() { + try { + Server s2 = (Server) i2.newInstance(new Object[]{"B"}); + s2.go(); + } + catch (Exception e) { + e.printStackTrace(); + } + } + }; + + t1.start(); + t2.start(); + } + catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/systest/usecases/src/test/org/activemq/usecases/rest/RESTLoadTest.java b/systest/usecases/src/test/org/activemq/usecases/rest/RESTLoadTest.java new file mode 100644 index 0000000000..17086ef1d1 --- /dev/null +++ b/systest/usecases/src/test/org/activemq/usecases/rest/RESTLoadTest.java @@ -0,0 +1,23 @@ +package org.activemq.usecases.rest; + +import junit.framework.*; + +public class RESTLoadTest extends TestCase { + public volatile int counter = 0; + + public void testREST() { + int HowManyMessages = 60000; + TestConsumerThread consumer = new TestConsumerThread(this, HowManyMessages); + TestProducerThread producer = new TestProducerThread(this, HowManyMessages); + consumer.start(); + producer.start(); + while (counter > 0) { + } + System.out.println("Produced:" + producer.success + " Consumed:" + consumer.success); + } + + public static void main(String args[]) { + junit.textui.TestRunner.run(new TestSuite(RESTLoadTest.class)); + } +} + diff --git a/systest/usecases/src/test/org/activemq/usecases/rest/TestConsumerThread.java b/systest/usecases/src/test/org/activemq/usecases/rest/TestConsumerThread.java new file mode 100644 index 0000000000..d2ff925636 --- /dev/null +++ b/systest/usecases/src/test/org/activemq/usecases/rest/TestConsumerThread.java @@ -0,0 +1,68 @@ +package org.activemq.usecases.rest; + +import java.io.*; +import java.net.*; + +class TestConsumerThread extends Thread { + private RESTLoadTest c; + public int success; + private int messagecount; + + TestConsumerThread(RESTLoadTest p, int count) { + c = p; + success = 0; + c.counter++; + messagecount = count; + } + + private int performGet(String urlString) { + try { + URL url; + HttpURLConnection urlConn; + + DataOutputStream printout; + DataInputStream input; + + // URL of CGI-Bin script. + url = new URL(urlString); + + // URL connection channel. + urlConn = (HttpURLConnection) url.openConnection(); + urlConn.setDoInput(true); + urlConn.setDoOutput(true); + urlConn.setUseCaches(false); + + // Get response data. + input = new DataInputStream(urlConn.getInputStream()); + + String str; + while (null != ((str = input.readLine()))) { + System.out.println("CONSUME:" + str); + } + + input.close(); + success++; + return 0; + + } + catch (MalformedURLException me) { + System.err.println("MalformedURLException: " + me); + return 1; + } + catch (IOException ioe) { + System.err.println("IOException: " + ioe.getMessage()); + return 1; + } + } + + public void run() { + for (int i = 0; i < messagecount; i++) { + int e = performGet("http://127.0.0.1:8080/jms/FOO/BAR?id=1234&readTimeout=60000"); + if (e == 1) { + break; + } + } + c.counter--; + + } +} diff --git a/systest/usecases/src/test/org/activemq/usecases/rest/TestProducerThread.java b/systest/usecases/src/test/org/activemq/usecases/rest/TestProducerThread.java new file mode 100644 index 0000000000..c61fdebb41 --- /dev/null +++ b/systest/usecases/src/test/org/activemq/usecases/rest/TestProducerThread.java @@ -0,0 +1,78 @@ +package org.activemq.usecases.rest; + +import java.net.*; +import java.io.*; + +class TestProducerThread extends Thread { + RESTLoadTest c; + public int success; + private int messagecount; + + TestProducerThread(RESTLoadTest p, int count) { + c = p; + success = 0; + c.counter++; + messagecount = count; + + } + + private int performPost(String urlString, String id, String mesg) { + try { + URL url; + HttpURLConnection urlConn; + + DataOutputStream printout; + DataInputStream input; + + // URL of CGI-Bin script. + url = new URL(urlString); + + // URL connection channel. + urlConn = (HttpURLConnection) url.openConnection(); + urlConn.setRequestMethod("POST"); + urlConn.setDoInput(true); + urlConn.setDoOutput(true); + urlConn.setUseCaches(false); + urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + String content = "id=" + URLEncoder.encode(id) + + "&body=" + URLEncoder.encode(mesg); + urlConn.setRequestProperty("Content-length", Integer.toString(content.length())); + + printout = new DataOutputStream(urlConn.getOutputStream()); + printout.writeBytes(content); + printout.flush(); + printout.close(); + + // Get response data. + input = new DataInputStream(urlConn.getInputStream()); + + String str; + while (null != ((str = input.readLine()))) { + System.out.println("PRODUCE:" + str); + } + + input.close(); + success++; + return 0; + + } + catch (MalformedURLException me) { + System.err.println("MalformedURLException: " + me); + return 1; + } + catch (IOException ioe) { + System.err.println("IOException: " + ioe.getMessage()); + return 1; + } + } + + public void run() { + for (int i = 0; i < messagecount; i++) { + int e = performPost("http://127.0.0.1:8080/jms/FOO/BAR", "1234", "Test " + i); + if (e == 1) { + break; + } + } + c.counter--; + } +}