Rename modules {org.springframework.*=>spring-*}

This renaming more intuitively expresses the relationship between
subprojects and the JAR artifacts they produce.

Tracking history across these renames is possible, but it requires
use of the --follow flag to `git log`, for example

    $ git log spring-aop/src/main/java/org/springframework/aop/Advisor.java

will show history up until the renaming event, where

    $ git log --follow spring-aop/src/main/java/org/springframework/aop/Advisor.java

will show history for all changes to the file, before and after the
renaming.

See http://chrisbeams.com/git-diff-across-renamed-directories
This commit is contained in:
Chris Beams
2012-01-20 22:51:02 +01:00
parent b6cb514d38
commit 02a4473c62
5671 changed files with 20 additions and 32 deletions

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms;
/**
* Runtime exception mirroring the JMS IllegalStateException.
*
* @author Mark Pollack
* @since 1.1
* @see javax.jms.IllegalStateException
*/
public class IllegalStateException extends JmsException {
public IllegalStateException(javax.jms.IllegalStateException cause) {
super(cause);
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms;
/**
* Runtime exception mirroring the JMS InvalidClientIDException.
*
* @author Mark Pollack
* @since 1.1
* @see javax.jms.InvalidClientIDException
*/
public class InvalidClientIDException extends JmsException {
public InvalidClientIDException(javax.jms.InvalidClientIDException cause) {
super(cause);
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms;
/**
* Runtime exception mirroring the JMS InvalidDestinationException.
*
* @author Mark Pollack
* @since 1.1
* @see javax.jms.InvalidDestinationException
*/
public class InvalidDestinationException extends JmsException {
public InvalidDestinationException(javax.jms.InvalidDestinationException cause) {
super(cause);
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms;
/**
* Runtime exception mirroring the JMS InvalidSelectorException.
*
* @author Mark Pollack
* @since 1.1
* @see javax.jms.InvalidSelectorException
*/
public class InvalidSelectorException extends JmsException {
public InvalidSelectorException(javax.jms.InvalidSelectorException cause) {
super(cause);
}
}

View File

@@ -1,98 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms;
import javax.jms.JMSException;
import org.springframework.core.NestedRuntimeException;
/**
* Base class for exception thrown by the framework whenever it
* encounters a problem related to JMS.
*
* @author Mark Pollack
* @author Juergen Hoeller
* @since 1.1
*/
public abstract class JmsException extends NestedRuntimeException {
/**
* Constructor that takes a message.
* @param msg the detail message
*/
public JmsException(String msg) {
super(msg);
}
/**
* Constructor that takes a message and a root cause.
* @param msg the detail message
* @param cause the cause of the exception. This argument is generally
* expected to be a proper subclass of {@link javax.jms.JMSException},
* but can also be a JNDI NamingException or the like.
*/
public JmsException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Constructor that takes a plain root cause, intended for
* subclasses mirroring corresponding <code>javax.jms</code> exceptions.
* @param cause the cause of the exception. This argument is generally
* expected to be a proper subclass of {@link javax.jms.JMSException}.
*/
public JmsException(Throwable cause) {
super(cause != null ? cause.getMessage() : null, cause);
}
/**
* Convenience method to get the vendor specific error code if
* the root cause was an instance of JMSException.
* @return a string specifying the vendor-specific error code if the
* root cause is an instance of JMSException, or <code>null</code>
*/
public String getErrorCode() {
Throwable cause = getCause();
if (cause instanceof JMSException) {
return ((JMSException) cause).getErrorCode();
}
return null;
}
/**
* Return the detail message, including the message from the linked exception
* if there is one.
* @see javax.jms.JMSException#getLinkedException()
*/
public String getMessage() {
String message = super.getMessage();
Throwable cause = getCause();
if (cause instanceof JMSException) {
Exception linkedEx = ((JMSException) cause).getLinkedException();
if (linkedEx != null) {
String linkedMessage = linkedEx.getMessage();
String causeMessage = cause.getMessage();
if (linkedMessage != null && (causeMessage == null || !causeMessage.contains(linkedMessage))) {
message = message + "; nested exception is " + linkedEx;
}
}
}
return message;
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms;
/**
* Runtime exception mirroring the JMS JMSSecurityException.
*
* @author Mark Pollack
* @since 1.1
* @see javax.jms.JMSSecurityException
*/
public class JmsSecurityException extends JmsException {
public JmsSecurityException(javax.jms.JMSSecurityException cause) {
super(cause);
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms;
/**
* Runtime exception mirroring the JMS MessageEOFException.
*
* @author Mark Pollack
* @since 1.1
* @see javax.jms.MessageEOFException
*/
public class MessageEOFException extends JmsException {
public MessageEOFException(javax.jms.MessageEOFException cause) {
super(cause);
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms;
/**
* Runtime exception mirroring the JMS MessageFormatException.
*
* @author Mark Pollack
* @since 1.1
* @see javax.jms.MessageFormatException
*/
public class MessageFormatException extends JmsException {
public MessageFormatException(javax.jms.MessageFormatException cause) {
super(cause);
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms;
/**
* Runtime exception mirroring the JMS MessageNotReadableException.
*
* @author Mark Pollack
* @since 1.1
* @see javax.jms.MessageNotReadableException
*/
public class MessageNotReadableException extends JmsException {
public MessageNotReadableException(javax.jms.MessageNotReadableException cause) {
super(cause);
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms;
/**
* Runtime exception mirroring the JMS MessageNotWriteableException.
*
* @author Mark Pollack
* @since 1.1
* @see javax.jms.MessageNotWriteableException
*/
public class MessageNotWriteableException extends JmsException {
public MessageNotWriteableException(javax.jms.MessageNotWriteableException cause) {
super(cause);
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms;
/**
* Runtime exception mirroring the JMS ResourceAllocationException.
*
* @author Mark Pollack
* @since 1.1
* @see javax.jms.ResourceAllocationException
*/
public class ResourceAllocationException extends JmsException {
public ResourceAllocationException(javax.jms.ResourceAllocationException cause) {
super(cause);
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms;
/**
* Runtime exception mirroring the JMS TransactionInProgressException.
*
* @author Mark Pollack
* @since 1.1
* @see javax.jms.TransactionInProgressException
*/
public class TransactionInProgressException extends JmsException {
public TransactionInProgressException(javax.jms.TransactionInProgressException cause) {
super(cause);
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms;
/**
* Runtime exception mirroring the JMS TransactionRolledBackException.
*
* @author Mark Pollack
* @since 1.1
* @see javax.jms.TransactionRolledBackException
*/
public class TransactionRolledBackException extends JmsException {
public TransactionRolledBackException(javax.jms.TransactionRolledBackException cause) {
super(cause);
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms;
/**
* JmsException to be thrown when no other matching subclass found.
*
* @author Juergen Hoeller
* @since 1.1
*/
public class UncategorizedJmsException extends JmsException {
/**
* Constructor that takes a message.
* @param msg the detail message
*/
public UncategorizedJmsException(String msg) {
super(msg);
}
/**
* Constructor that takes a message and a root cause.
* @param msg the detail message
* @param cause the cause of the exception. This argument is generally
* expected to be a proper subclass of {@link javax.jms.JMSException},
* but can also be a JNDI NamingException or the like.
*/
public UncategorizedJmsException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Constructor that takes a root cause only.
* @param cause the cause of the exception. This argument is generally
* expected to be a proper subclass of {@link javax.jms.JMSException},
* but can also be a JNDI NamingException or the like.
*/
public UncategorizedJmsException(Throwable cause) {
super("Uncategorized exception occured during JMS processing", cause);
}
}

View File

@@ -1,273 +0,0 @@
/*
* Copyright 2002-2010 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.springframework.jms.config;
import javax.jms.Session;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Abstract parser for JMS listener container elements, providing support for
* common properties that are identical for all listener container variants.
*
* @author Juergen Hoeller
* @since 2.5
*/
abstract class AbstractListenerContainerParser implements BeanDefinitionParser {
protected static final String LISTENER_ELEMENT = "listener";
protected static final String ID_ATTRIBUTE = "id";
protected static final String DESTINATION_ATTRIBUTE = "destination";
protected static final String SUBSCRIPTION_ATTRIBUTE = "subscription";
protected static final String SELECTOR_ATTRIBUTE = "selector";
protected static final String REF_ATTRIBUTE = "ref";
protected static final String METHOD_ATTRIBUTE = "method";
protected static final String DESTINATION_RESOLVER_ATTRIBUTE = "destination-resolver";
protected static final String MESSAGE_CONVERTER_ATTRIBUTE = "message-converter";
protected static final String RESPONSE_DESTINATION_ATTRIBUTE = "response-destination";
protected static final String DESTINATION_TYPE_ATTRIBUTE = "destination-type";
protected static final String DESTINATION_TYPE_QUEUE = "queue";
protected static final String DESTINATION_TYPE_TOPIC = "topic";
protected static final String DESTINATION_TYPE_DURABLE_TOPIC = "durableTopic";
protected static final String CLIENT_ID_ATTRIBUTE = "client-id";
protected static final String ACKNOWLEDGE_ATTRIBUTE = "acknowledge";
protected static final String ACKNOWLEDGE_AUTO = "auto";
protected static final String ACKNOWLEDGE_CLIENT = "client";
protected static final String ACKNOWLEDGE_DUPS_OK = "dups-ok";
protected static final String ACKNOWLEDGE_TRANSACTED = "transacted";
protected static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";
protected static final String CONCURRENCY_ATTRIBUTE = "concurrency";
protected static final String PHASE_ATTRIBUTE = "phase";
protected static final String PREFETCH_ATTRIBUTE = "prefetch";
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node child = childNodes.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE) {
String localName = parserContext.getDelegate().getLocalName(child);
if (LISTENER_ELEMENT.equals(localName)) {
parseListener((Element) child, element, parserContext);
}
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
private void parseListener(Element listenerEle, Element containerEle, ParserContext parserContext) {
RootBeanDefinition listenerDef = new RootBeanDefinition();
listenerDef.setSource(parserContext.extractSource(listenerEle));
String ref = listenerEle.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(ref)) {
parserContext.getReaderContext().error(
"Listener 'ref' attribute contains empty value.", listenerEle);
}
else {
listenerDef.getPropertyValues().add("delegate", new RuntimeBeanReference(ref));
}
String method = null;
if (listenerEle.hasAttribute(METHOD_ATTRIBUTE)) {
method = listenerEle.getAttribute(METHOD_ATTRIBUTE);
if (!StringUtils.hasText(method)) {
parserContext.getReaderContext().error(
"Listener 'method' attribute contains empty value.", listenerEle);
}
}
listenerDef.getPropertyValues().add("defaultListenerMethod", method);
if (containerEle.hasAttribute(MESSAGE_CONVERTER_ATTRIBUTE)) {
String messageConverter = containerEle.getAttribute(MESSAGE_CONVERTER_ATTRIBUTE);
if (!StringUtils.hasText(messageConverter)) {
parserContext.getReaderContext().error(
"Listener container 'message-converter' attribute contains empty value.", containerEle);
}
else {
listenerDef.getPropertyValues().add("messageConverter",
new RuntimeBeanReference(messageConverter));
}
}
BeanDefinition containerDef = parseContainer(listenerEle, containerEle, parserContext);
if (listenerEle.hasAttribute(RESPONSE_DESTINATION_ATTRIBUTE)) {
String responseDestination = listenerEle.getAttribute(RESPONSE_DESTINATION_ATTRIBUTE);
boolean pubSubDomain = indicatesPubSub(containerDef);
listenerDef.getPropertyValues().add(
pubSubDomain ? "defaultResponseTopicName" : "defaultResponseQueueName", responseDestination);
if (containerDef.getPropertyValues().contains("destinationResolver")) {
listenerDef.getPropertyValues().add("destinationResolver",
containerDef.getPropertyValues().getPropertyValue("destinationResolver").getValue());
}
}
// Remain JMS 1.0.2 compatible for the adapter if the container class indicates this.
boolean jms102 = indicatesJms102(containerDef);
listenerDef.setBeanClassName(
"org.springframework.jms.listener.adapter.MessageListenerAdapter" + (jms102 ? "102" : ""));
containerDef.getPropertyValues().add("messageListener", listenerDef);
String containerBeanName = listenerEle.getAttribute(ID_ATTRIBUTE);
// If no bean id is given auto generate one using the ReaderContext's BeanNameGenerator
if (!StringUtils.hasText(containerBeanName)) {
containerBeanName = parserContext.getReaderContext().generateBeanName(containerDef);
}
// Register the listener and fire event
parserContext.registerBeanComponent(new BeanComponentDefinition(containerDef, containerBeanName));
}
protected abstract BeanDefinition parseContainer(
Element listenerEle, Element containerEle, ParserContext parserContext);
protected boolean indicatesPubSub(BeanDefinition containerDef) {
return false;
}
protected boolean indicatesJms102(BeanDefinition containerDef) {
return false;
}
protected void parseListenerConfiguration(Element ele, ParserContext parserContext, BeanDefinition configDef) {
String destination = ele.getAttribute(DESTINATION_ATTRIBUTE);
if (!StringUtils.hasText(destination)) {
parserContext.getReaderContext().error(
"Listener 'destination' attribute contains empty value.", ele);
}
configDef.getPropertyValues().add("destinationName", destination);
if (ele.hasAttribute(SUBSCRIPTION_ATTRIBUTE)) {
String subscription = ele.getAttribute(SUBSCRIPTION_ATTRIBUTE);
if (!StringUtils.hasText(subscription)) {
parserContext.getReaderContext().error(
"Listener 'subscription' attribute contains empty value.", ele);
}
configDef.getPropertyValues().add("durableSubscriptionName", subscription);
}
if (ele.hasAttribute(SELECTOR_ATTRIBUTE)) {
String selector = ele.getAttribute(SELECTOR_ATTRIBUTE);
if (!StringUtils.hasText(selector)) {
parserContext.getReaderContext().error(
"Listener 'selector' attribute contains empty value.", ele);
}
configDef.getPropertyValues().add("messageSelector", selector);
}
}
protected void parseContainerConfiguration(Element ele, ParserContext parserContext, BeanDefinition configDef) {
String destinationType = ele.getAttribute(DESTINATION_TYPE_ATTRIBUTE);
boolean pubSubDomain = false;
boolean subscriptionDurable = false;
if (DESTINATION_TYPE_DURABLE_TOPIC.equals(destinationType)) {
pubSubDomain = true;
subscriptionDurable = true;
}
else if (DESTINATION_TYPE_TOPIC.equals(destinationType)) {
pubSubDomain = true;
}
else if ("".equals(destinationType) || DESTINATION_TYPE_QUEUE.equals(destinationType)) {
// the default: queue
}
else {
parserContext.getReaderContext().error("Invalid listener container 'destination-type': " +
"only \"queue\", \"topic\" and \"durableTopic\" supported.", ele);
}
configDef.getPropertyValues().add("pubSubDomain", pubSubDomain);
configDef.getPropertyValues().add("subscriptionDurable", subscriptionDurable);
if (ele.hasAttribute(CLIENT_ID_ATTRIBUTE)) {
String clientId = ele.getAttribute(CLIENT_ID_ATTRIBUTE);
if (!StringUtils.hasText(clientId)) {
parserContext.getReaderContext().error(
"Listener 'client-id' attribute contains empty value.", ele);
}
configDef.getPropertyValues().add("clientId", clientId);
}
}
protected Integer parseAcknowledgeMode(Element ele, ParserContext parserContext) {
String acknowledge = ele.getAttribute(ACKNOWLEDGE_ATTRIBUTE);
if (StringUtils.hasText(acknowledge)) {
int acknowledgeMode = Session.AUTO_ACKNOWLEDGE;
if (ACKNOWLEDGE_TRANSACTED.equals(acknowledge)) {
acknowledgeMode = Session.SESSION_TRANSACTED;
}
else if (ACKNOWLEDGE_DUPS_OK.equals(acknowledge)) {
acknowledgeMode = Session.DUPS_OK_ACKNOWLEDGE;
}
else if (ACKNOWLEDGE_CLIENT.equals(acknowledge)) {
acknowledgeMode = Session.CLIENT_ACKNOWLEDGE;
}
else if (!ACKNOWLEDGE_AUTO.equals(acknowledge)) {
parserContext.getReaderContext().error("Invalid listener container 'acknowledge' setting [" +
acknowledge + "]: only \"auto\", \"client\", \"dups-ok\" and \"transacted\" supported.", ele);
}
return acknowledgeMode;
}
else {
return null;
}
}
protected boolean indicatesPubSubConfig(BeanDefinition configDef) {
return (Boolean) configDef.getPropertyValues().getPropertyValue("pubSubDomain").getValue();
}
}

View File

@@ -1,117 +0,0 @@
/*
* Copyright 2002-2010 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.springframework.jms.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
/**
* Parser for the JMS <code>&lt;jca-listener-container&gt;</code> element.
*
* @author Juergen Hoeller
* @since 2.5
*/
class JcaListenerContainerParser extends AbstractListenerContainerParser {
private static final String RESOURCE_ADAPTER_ATTRIBUTE = "resource-adapter";
private static final String ACTIVATION_SPEC_FACTORY_ATTRIBUTE = "activation-spec-factory";
protected BeanDefinition parseContainer(Element listenerEle, Element containerEle, ParserContext parserContext) {
RootBeanDefinition containerDef = new RootBeanDefinition();
containerDef.setSource(parserContext.extractSource(containerEle));
containerDef.setBeanClassName("org.springframework.jms.listener.endpoint.JmsMessageEndpointManager");
if (containerEle.hasAttribute(RESOURCE_ADAPTER_ATTRIBUTE)) {
String resourceAdapterBeanName = containerEle.getAttribute(RESOURCE_ADAPTER_ATTRIBUTE);
if (!StringUtils.hasText(resourceAdapterBeanName)) {
parserContext.getReaderContext().error(
"Listener container 'resource-adapter' attribute contains empty value.", containerEle);
}
else {
containerDef.getPropertyValues().add("resourceAdapter",
new RuntimeBeanReference(resourceAdapterBeanName));
}
}
String activationSpecFactoryBeanName = containerEle.getAttribute(ACTIVATION_SPEC_FACTORY_ATTRIBUTE);
String destinationResolverBeanName = containerEle.getAttribute(DESTINATION_RESOLVER_ATTRIBUTE);
if (StringUtils.hasText(activationSpecFactoryBeanName)) {
if (StringUtils.hasText(destinationResolverBeanName)) {
parserContext.getReaderContext().error("Specify either 'activation-spec-factory' or " +
"'destination-resolver', not both. If you define a dedicated JmsActivationSpecFactory bean, " +
"specify the custom DestinationResolver there (if possible).", containerEle);
}
containerDef.getPropertyValues().add("activationSpecFactory",
new RuntimeBeanReference(activationSpecFactoryBeanName));
}
if (StringUtils.hasText(destinationResolverBeanName)) {
containerDef.getPropertyValues().add("destinationResolver",
new RuntimeBeanReference(destinationResolverBeanName));
}
RootBeanDefinition configDef = new RootBeanDefinition();
configDef.setSource(parserContext.extractSource(configDef));
configDef.setBeanClassName("org.springframework.jms.listener.endpoint.JmsActivationSpecConfig");
parseListenerConfiguration(listenerEle, parserContext, configDef);
parseContainerConfiguration(containerEle, parserContext, configDef);
Integer acknowledgeMode = parseAcknowledgeMode(containerEle, parserContext);
if (acknowledgeMode != null) {
configDef.getPropertyValues().add("acknowledgeMode", acknowledgeMode);
}
String transactionManagerBeanName = containerEle.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE);
if (StringUtils.hasText(transactionManagerBeanName)) {
containerDef.getPropertyValues().add("transactionManager",
new RuntimeBeanReference(transactionManagerBeanName));
}
String concurrency = containerEle.getAttribute(CONCURRENCY_ATTRIBUTE);
if (StringUtils.hasText(concurrency)) {
configDef.getPropertyValues().add("concurrency", concurrency);
}
String prefetch = containerEle.getAttribute(PREFETCH_ATTRIBUTE);
if (StringUtils.hasText(prefetch)) {
configDef.getPropertyValues().add("prefetchSize", new Integer(prefetch));
}
String phase = containerEle.getAttribute(PHASE_ATTRIBUTE);
if (StringUtils.hasText(phase)) {
containerDef.getPropertyValues().add("phase", phase);
}
containerDef.getPropertyValues().add("activationSpecConfig", configDef);
return containerDef;
}
protected boolean indicatesPubSub(BeanDefinition containerDef) {
BeanDefinition configDef =
(BeanDefinition) containerDef.getPropertyValues().getPropertyValue("activationSpecConfig").getValue();
return indicatesPubSubConfig(configDef);
}
}

View File

@@ -1,175 +0,0 @@
/*
* Copyright 2002-2010 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.springframework.jms.config;
import javax.jms.Session;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
/**
* Parser for the JMS <code>&lt;listener-container&gt;</code> element.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 2.5
*/
class JmsListenerContainerParser extends AbstractListenerContainerParser {
private static final String CONTAINER_TYPE_ATTRIBUTE = "container-type";
private static final String CONTAINER_CLASS_ATTRIBUTE = "container-class";
private static final String CONNECTION_FACTORY_ATTRIBUTE = "connection-factory";
private static final String TASK_EXECUTOR_ATTRIBUTE = "task-executor";
private static final String ERROR_HANDLER_ATTRIBUTE = "error-handler";
private static final String CACHE_ATTRIBUTE = "cache";
protected BeanDefinition parseContainer(Element listenerEle, Element containerEle, ParserContext parserContext) {
RootBeanDefinition containerDef = new RootBeanDefinition();
containerDef.setSource(parserContext.extractSource(containerEle));
parseListenerConfiguration(listenerEle, parserContext, containerDef);
parseContainerConfiguration(containerEle, parserContext, containerDef);
String containerType = containerEle.getAttribute(CONTAINER_TYPE_ATTRIBUTE);
String containerClass = containerEle.getAttribute(CONTAINER_CLASS_ATTRIBUTE);
if (!"".equals(containerClass)) {
containerDef.setBeanClassName(containerClass);
}
else if ("".equals(containerType) || "default".equals(containerType)) {
containerDef.setBeanClassName("org.springframework.jms.listener.DefaultMessageListenerContainer");
}
else if ("default102".equals(containerType)) {
containerDef.setBeanClassName("org.springframework.jms.listener.DefaultMessageListenerContainer102");
}
else if ("simple".equals(containerType)) {
containerDef.setBeanClassName("org.springframework.jms.listener.SimpleMessageListenerContainer");
}
else if ("simple102".equals(containerType)) {
containerDef.setBeanClassName("org.springframework.jms.listener.SimpleMessageListenerContainer102");
}
else {
parserContext.getReaderContext().error(
"Invalid 'container-type' attribute: only \"default(102)\" and \"simple(102)\" supported.", containerEle);
}
String connectionFactoryBeanName = "connectionFactory";
if (containerEle.hasAttribute(CONNECTION_FACTORY_ATTRIBUTE)) {
connectionFactoryBeanName = containerEle.getAttribute(CONNECTION_FACTORY_ATTRIBUTE);
if (!StringUtils.hasText(connectionFactoryBeanName)) {
parserContext.getReaderContext().error(
"Listener container 'connection-factory' attribute contains empty value.", containerEle);
}
}
if (StringUtils.hasText(connectionFactoryBeanName)) {
containerDef.getPropertyValues().add("connectionFactory",
new RuntimeBeanReference(connectionFactoryBeanName));
}
String taskExecutorBeanName = containerEle.getAttribute(TASK_EXECUTOR_ATTRIBUTE);
if (StringUtils.hasText(taskExecutorBeanName)) {
containerDef.getPropertyValues().add("taskExecutor",
new RuntimeBeanReference(taskExecutorBeanName));
}
String errorHandlerBeanName = containerEle.getAttribute(ERROR_HANDLER_ATTRIBUTE);
if (StringUtils.hasText(errorHandlerBeanName)) {
containerDef.getPropertyValues().add("errorHandler",
new RuntimeBeanReference(errorHandlerBeanName));
}
String destinationResolverBeanName = containerEle.getAttribute(DESTINATION_RESOLVER_ATTRIBUTE);
if (StringUtils.hasText(destinationResolverBeanName)) {
containerDef.getPropertyValues().add("destinationResolver",
new RuntimeBeanReference(destinationResolverBeanName));
}
String cache = containerEle.getAttribute(CACHE_ATTRIBUTE);
if (StringUtils.hasText(cache)) {
if (containerType.startsWith("simple")) {
if (!("auto".equals(cache) || "consumer".equals(cache))) {
parserContext.getReaderContext().warning(
"'cache' attribute not actively supported for listener container of type \"simple\". " +
"Effective runtime behavior will be equivalent to \"consumer\" / \"auto\".", containerEle);
}
}
else {
containerDef.getPropertyValues().add("cacheLevelName", "CACHE_" + cache.toUpperCase());
}
}
Integer acknowledgeMode = parseAcknowledgeMode(containerEle, parserContext);
if (acknowledgeMode != null) {
if (acknowledgeMode == Session.SESSION_TRANSACTED) {
containerDef.getPropertyValues().add("sessionTransacted", Boolean.TRUE);
}
else {
containerDef.getPropertyValues().add("sessionAcknowledgeMode", acknowledgeMode);
}
}
String transactionManagerBeanName = containerEle.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE);
if (StringUtils.hasText(transactionManagerBeanName)) {
if (containerType.startsWith("simple")) {
parserContext.getReaderContext().error(
"'transaction-manager' attribute not supported for listener container of type \"simple\".", containerEle);
}
else {
containerDef.getPropertyValues().add("transactionManager",
new RuntimeBeanReference(transactionManagerBeanName));
}
}
String concurrency = containerEle.getAttribute(CONCURRENCY_ATTRIBUTE);
if (StringUtils.hasText(concurrency)) {
containerDef.getPropertyValues().add("concurrency", concurrency);
}
String prefetch = containerEle.getAttribute(PREFETCH_ATTRIBUTE);
if (StringUtils.hasText(prefetch)) {
if (containerType.startsWith("default")) {
containerDef.getPropertyValues().add("maxMessagesPerTask", new Integer(prefetch));
}
}
String phase = containerEle.getAttribute(PHASE_ATTRIBUTE);
if (StringUtils.hasText(phase)) {
containerDef.getPropertyValues().add("phase", phase);
}
return containerDef;
}
protected boolean indicatesPubSub(BeanDefinition containerDef) {
return indicatesPubSubConfig(containerDef);
}
protected boolean indicatesJms102(BeanDefinition containerDef) {
return containerDef.getBeanClassName().endsWith("102");
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms.config;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
* A {@link org.springframework.beans.factory.xml.NamespaceHandler}
* for the JMS namespace.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 2.5
*/
public class JmsNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("listener-container", new JmsListenerContainerParser());
registerBeanDefinitionParser("jca-listener-container", new JcaListenerContainerParser());
}
}

View File

@@ -1,9 +0,0 @@
/**
*
* Support package for declarative messaging configuration,
* with XML schema being the primary configuration format.
*
*/
package org.springframework.jms.config;

View File

@@ -1,90 +0,0 @@
/*
* Copyright 2002-2008 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.springframework.jms.connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.QueueReceiver;
import javax.jms.Topic;
import javax.jms.TopicSubscriber;
/**
* JMS MessageConsumer decorator that adapts all calls
* to a shared MessageConsumer instance underneath.
*
* @author Juergen Hoeller
* @since 2.5.6
*/
class CachedMessageConsumer implements MessageConsumer, QueueReceiver, TopicSubscriber {
protected final MessageConsumer target;
public CachedMessageConsumer(MessageConsumer target) {
this.target = target;
}
public String getMessageSelector() throws JMSException {
return this.target.getMessageSelector();
}
public Queue getQueue() throws JMSException {
return (this.target instanceof QueueReceiver ? ((QueueReceiver) this.target).getQueue() : null);
}
public Topic getTopic() throws JMSException {
return (this.target instanceof TopicSubscriber ? ((TopicSubscriber) this.target).getTopic() : null);
}
public boolean getNoLocal() throws JMSException {
return (this.target instanceof TopicSubscriber && ((TopicSubscriber) this.target).getNoLocal());
}
public MessageListener getMessageListener() throws JMSException {
return this.target.getMessageListener();
}
public void setMessageListener(MessageListener messageListener) throws JMSException {
this.target.setMessageListener(messageListener);
}
public Message receive() throws JMSException {
return this.target.receive();
}
public Message receive(long timeout) throws JMSException {
return this.target.receive(timeout);
}
public Message receiveNoWait() throws JMSException {
return this.target.receiveNoWait();
}
public void close() throws JMSException {
// It's a cached MessageConsumer...
}
public String toString() {
return "Cached JMS MessageConsumer: " + this.target;
}
}

View File

@@ -1,173 +0,0 @@
/*
* Copyright 2002-2008 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.springframework.jms.connection;
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;
/**
* JMS MessageProducer decorator that adapts calls to a shared MessageProducer
* instance underneath, managing QoS settings locally within the decorator.
*
* @author Juergen Hoeller
* @since 2.5.3
*/
class CachedMessageProducer implements MessageProducer, QueueSender, TopicPublisher {
private final MessageProducer target;
private Boolean originalDisableMessageID;
private Boolean originalDisableMessageTimestamp;
private int deliveryMode;
private int priority;
private long timeToLive;
public CachedMessageProducer(MessageProducer target) throws JMSException {
this.target = target;
this.deliveryMode = target.getDeliveryMode();
this.priority = target.getPriority();
this.timeToLive = target.getTimeToLive();
}
public void setDisableMessageID(boolean disableMessageID) throws JMSException {
if (this.originalDisableMessageID == null) {
this.originalDisableMessageID = Boolean.valueOf(this.target.getDisableMessageID());
}
this.target.setDisableMessageID(disableMessageID);
}
public boolean getDisableMessageID() throws JMSException {
return this.target.getDisableMessageID();
}
public void setDisableMessageTimestamp(boolean disableMessageTimestamp) throws JMSException {
if (this.originalDisableMessageTimestamp == null) {
this.originalDisableMessageTimestamp = Boolean.valueOf(this.target.getDisableMessageTimestamp());
}
this.target.setDisableMessageTimestamp(disableMessageTimestamp);
}
public boolean getDisableMessageTimestamp() throws JMSException {
return this.target.getDisableMessageTimestamp();
}
public void setDeliveryMode(int deliveryMode) {
this.deliveryMode = deliveryMode;
}
public int getDeliveryMode() {
return this.deliveryMode;
}
public void setPriority(int priority) {
this.priority = priority;
}
public int getPriority() {
return this.priority;
}
public void setTimeToLive(long timeToLive) {
this.timeToLive = timeToLive;
}
public long getTimeToLive() {
return this.timeToLive;
}
public Destination getDestination() throws JMSException {
return this.target.getDestination();
}
public Queue getQueue() throws JMSException {
return (Queue) this.target.getDestination();
}
public Topic getTopic() throws JMSException {
return (Topic) this.target.getDestination();
}
public void send(Message message) throws JMSException {
this.target.send(message, this.deliveryMode, this.priority, this.timeToLive);
}
public void send(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException {
this.target.send(message, deliveryMode, priority, timeToLive);
}
public void send(Destination destination, Message message) throws JMSException {
this.target.send(destination, message, this.deliveryMode, this.priority, this.timeToLive);
}
public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive) throws JMSException {
this.target.send(destination, message, deliveryMode, priority, timeToLive);
}
public void send(Queue queue, Message message) throws JMSException {
this.target.send(queue, message, this.deliveryMode, this.priority, this.timeToLive);
}
public void send(Queue queue, Message message, int deliveryMode, int priority, long timeToLive) throws JMSException {
this.target.send(queue, message, deliveryMode, priority, timeToLive);
}
public void publish(Message message) throws JMSException {
this.target.send(message, this.deliveryMode, this.priority, this.timeToLive);
}
public void publish(Message message, int deliveryMode, int priority, long timeToLive) throws JMSException {
this.target.send(message, deliveryMode, priority, timeToLive);
}
public void publish(Topic topic, Message message) throws JMSException {
this.target.send(topic, message, this.deliveryMode, this.priority, this.timeToLive);
}
public void publish(Topic topic, Message message, int deliveryMode, int priority, long timeToLive) throws JMSException {
this.target.send(topic, message, deliveryMode, priority, timeToLive);
}
public void close() throws JMSException {
// It's a cached MessageProducer... reset properties only.
if (this.originalDisableMessageID != null) {
this.target.setDisableMessageID(this.originalDisableMessageID.booleanValue());
this.originalDisableMessageID = null;
}
if (this.originalDisableMessageTimestamp != null) {
this.target.setDisableMessageTimestamp(this.originalDisableMessageTimestamp.booleanValue());
this.originalDisableMessageTimestamp = null;
}
}
public String toString() {
return "Cached JMS MessageProducer: " + this.target;
}
}

View File

@@ -1,525 +0,0 @@
/*
* Copyright 2002-2012 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.springframework.jms.connection;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
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.QueueSession;
import javax.jms.Session;
import javax.jms.TemporaryQueue;
import javax.jms.TemporaryTopic;
import javax.jms.Topic;
import javax.jms.TopicSession;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* {@link SingleConnectionFactory} subclass that adds {@link javax.jms.Session}
* caching as well {@link javax.jms.MessageProducer} caching. This ConnectionFactory
* also switches the {@link #setReconnectOnException "reconnectOnException" property}
* to "true" by default, allowing for automatic recovery of the underlying Connection.
*
* <p>By default, only one single Session will be cached, with further requested
* Sessions being created and disposed on demand. Consider raising the
* {@link #setSessionCacheSize "sessionCacheSize" value} in case of a
* high-concurrency environment.
*
* <p><b>NOTE: This ConnectionFactory decorator requires JMS 1.1 or higher.</b>
* You may use it through the JMS 1.0.2 API; however, the target JMS driver
* needs to be compliant with JMS 1.1.
*
* <p>When using the JMS 1.0.2 API, this ConnectionFactory will switch
* into queue/topic mode according to the JMS API methods used at runtime:
* <code>createQueueConnection</code> and <code>createTopicConnection</code> will
* lead to queue/topic mode, respectively; generic <code>createConnection</code>
* calls will lead to a JMS 1.1 connection which is able to serve both modes.
*
* <p><b>NOTE: This ConnectionFactory requires explicit closing of all Sessions
* obtained from its shared Connection.</b> This is the usual recommendation for
* native JMS access code anyway. However, with this ConnectionFactory, its use
* is mandatory in order to actually allow for Session reuse.
*
* <p>Note also that MessageConsumers obtained from a cached Session won't get
* closed until the Session will eventually be removed from the pool. This may
* lead to semantic side effects in some cases. For a durable subscriber, the
* logical <code>Session.close()</code> call will also close the subscription.
* Re-registering a durable consumer for the same subscription on the same
* Session handle is not supported; close and reobtain a cached Session first.
*
* @author Juergen Hoeller
* @since 2.5.3
*/
public class CachingConnectionFactory extends SingleConnectionFactory {
private int sessionCacheSize = 1;
private boolean cacheProducers = true;
private boolean cacheConsumers = true;
private volatile boolean active = true;
private final Map<Integer, LinkedList<Session>> cachedSessions =
new HashMap<Integer, LinkedList<Session>>();
/**
* Create a new CachingConnectionFactory for bean-style usage.
* @see #setTargetConnectionFactory
*/
public CachingConnectionFactory() {
super();
setReconnectOnException(true);
}
/**
* Create a new CachingConnectionFactory for the given target
* ConnectionFactory.
* @param targetConnectionFactory the target ConnectionFactory
*/
public CachingConnectionFactory(ConnectionFactory targetConnectionFactory) {
super(targetConnectionFactory);
setReconnectOnException(true);
}
/**
* Specify the desired size for the JMS Session cache (per JMS Session type).
* <p>This cache size is the maximum limit for the number of cached Sessions
* per session acknowledgement type (auto, client, dups_ok, transacted).
* As a consequence, the actual number of cached Sessions may be up to
* four times as high as the specified value - in the unlikely case
* of mixing and matching different acknowledgement types.
* <p>Default is 1: caching a single Session, (re-)creating further ones on
* demand. Specify a number like 10 if you'd like to raise the number of cached
* Sessions; that said, 1 may be sufficient for low-concurrency scenarios.
* @see #setCacheProducers
*/
public void setSessionCacheSize(int sessionCacheSize) {
Assert.isTrue(sessionCacheSize >= 1, "Session cache size must be 1 or higher");
this.sessionCacheSize = sessionCacheSize;
}
/**
* Return the desired size for the JMS Session cache (per JMS Session type).
*/
public int getSessionCacheSize() {
return this.sessionCacheSize;
}
/**
* Specify whether to cache JMS MessageProducers per JMS Session instance
* (more specifically: one MessageProducer per Destination and Session).
* <p>Default is "true". Switch this to "false" in order to always
* recreate MessageProducers on demand.
*/
public void setCacheProducers(boolean cacheProducers) {
this.cacheProducers = cacheProducers;
}
/**
* Return whether to cache JMS MessageProducers per JMS Session instance.
*/
public boolean isCacheProducers() {
return this.cacheProducers;
}
/**
* Specify whether to cache JMS MessageConsumers per JMS Session instance
* (more specifically: one MessageConsumer per Destination, selector String
* and Session). Note that durable subscribers will only be cached until
* logical closing of the Session handle.
* <p>Default is "true". Switch this to "false" in order to always
* recreate MessageConsumers on demand.
*/
public void setCacheConsumers(boolean cacheConsumers) {
this.cacheConsumers = cacheConsumers;
}
/**
* Return whether to cache JMS MessageConsumers per JMS Session instance.
*/
public boolean isCacheConsumers() {
return this.cacheConsumers;
}
/**
* Resets the Session cache as well.
*/
public void resetConnection() {
this.active = false;
synchronized (this.cachedSessions) {
for (LinkedList<Session> sessionList : this.cachedSessions.values()) {
synchronized (sessionList) {
for (Session session : sessionList) {
try {
session.close();
}
catch (Throwable ex) {
logger.trace("Could not close cached JMS Session", ex);
}
}
}
}
this.cachedSessions.clear();
}
this.active = true;
// Now proceed with actual closing of the shared Connection...
super.resetConnection();
}
/**
* Checks for a cached Session for the given mode.
*/
protected Session getSession(Connection con, Integer mode) throws JMSException {
LinkedList<Session> sessionList;
synchronized (this.cachedSessions) {
sessionList = this.cachedSessions.get(mode);
if (sessionList == null) {
sessionList = new LinkedList<Session>();
this.cachedSessions.put(mode, sessionList);
}
}
Session session = null;
synchronized (sessionList) {
if (!sessionList.isEmpty()) {
session = sessionList.removeFirst();
}
}
if (session != null) {
if (logger.isTraceEnabled()) {
logger.trace("Found cached JMS Session for mode " + mode + ": " +
(session instanceof SessionProxy ? ((SessionProxy) session).getTargetSession() : session));
}
}
else {
Session targetSession = createSession(con, mode);
if (logger.isDebugEnabled()) {
logger.debug("Creating cached JMS Session for mode " + mode + ": " + targetSession);
}
session = getCachedSessionProxy(targetSession, sessionList);
}
return session;
}
/**
* Wrap the given Session with a proxy that delegates every method call to it
* but adapts close calls. This is useful for allowing application code to
* handle a special framework Session just like an ordinary Session.
* @param target the original Session to wrap
* @param sessionList the List of cached Sessions that the given Session belongs to
* @return the wrapped Session
*/
protected Session getCachedSessionProxy(Session target, LinkedList<Session> sessionList) {
List<Class> classes = new ArrayList<Class>(3);
classes.add(SessionProxy.class);
if (target instanceof QueueSession) {
classes.add(QueueSession.class);
}
if (target instanceof TopicSession) {
classes.add(TopicSession.class);
}
return (Session) Proxy.newProxyInstance(
SessionProxy.class.getClassLoader(),
classes.toArray(new Class[classes.size()]),
new CachedSessionInvocationHandler(target, sessionList));
}
/**
* Invocation handler for a cached JMS Session proxy.
*/
private class CachedSessionInvocationHandler implements InvocationHandler {
private final Session target;
private final LinkedList<Session> sessionList;
private final Map<DestinationCacheKey, MessageProducer> cachedProducers =
new HashMap<DestinationCacheKey, MessageProducer>();
private final Map<ConsumerCacheKey, MessageConsumer> cachedConsumers =
new HashMap<ConsumerCacheKey, MessageConsumer>();
private boolean transactionOpen = false;
public CachedSessionInvocationHandler(Session target, LinkedList<Session> sessionList) {
this.target = target;
this.sessionList = sessionList;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (methodName.equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (methodName.equals("hashCode")) {
// Use hashCode of Session proxy.
return System.identityHashCode(proxy);
}
else if (methodName.equals("toString")) {
return "Cached JMS Session: " + this.target;
}
else if (methodName.equals("close")) {
// Handle close method: don't pass the call on.
if (active) {
synchronized (this.sessionList) {
if (this.sessionList.size() < getSessionCacheSize()) {
logicalClose((Session) proxy);
// Remain open in the session list.
return null;
}
}
}
// If we get here, we're supposed to shut down.
physicalClose();
return null;
}
else if (methodName.equals("getTargetSession")) {
// Handle getTargetSession method: return underlying Session.
return this.target;
}
else if (methodName.equals("commit") || methodName.equals("rollback")) {
this.transactionOpen = false;
}
else if (methodName.startsWith("create")) {
this.transactionOpen = true;
if (isCacheProducers() && (methodName.equals("createProducer") ||
methodName.equals("createSender") || methodName.equals("createPublisher"))) {
// Destination argument being null is ok for a producer
return getCachedProducer((Destination) args[0]);
}
else if (isCacheConsumers()) {
// let raw JMS invocation throw an exception if Destination (i.e. args[0]) is null
if ((methodName.equals("createConsumer") || methodName.equals("createReceiver") ||
methodName.equals("createSubscriber"))) {
Destination dest = (Destination) args[0];
if (dest != null && !(dest instanceof TemporaryQueue || dest instanceof TemporaryTopic)) {
return getCachedConsumer(dest,
(args.length > 1 ? (String) args[1] : null),
(args.length > 2 && (Boolean) args[2]),
null);
}
}
else if (methodName.equals("createDurableSubscriber")) {
Destination dest = (Destination) args[0];
if (dest != null) {
return getCachedConsumer(dest,
(args.length > 2 ? (String) args[2] : null),
(args.length > 3 && (Boolean) args[3]),
(String) args[1]);
}
}
}
}
try {
return method.invoke(this.target, args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
private MessageProducer getCachedProducer(Destination dest) throws JMSException {
DestinationCacheKey cacheKey = (dest != null ? new DestinationCacheKey(dest) : null);
MessageProducer producer = this.cachedProducers.get(cacheKey);
if (producer != null) {
if (logger.isTraceEnabled()) {
logger.trace("Found cached JMS MessageProducer for destination [" + dest + "]: " + producer);
}
}
else {
producer = this.target.createProducer(dest);
if (logger.isDebugEnabled()) {
logger.debug("Creating cached JMS MessageProducer for destination [" + dest + "]: " + producer);
}
this.cachedProducers.put(cacheKey, producer);
}
return new CachedMessageProducer(producer);
}
private MessageConsumer getCachedConsumer(
Destination dest, String selector, boolean noLocal, String subscription) throws JMSException {
ConsumerCacheKey cacheKey = new ConsumerCacheKey(dest, selector, noLocal, subscription);
MessageConsumer consumer = this.cachedConsumers.get(cacheKey);
if (consumer != null) {
if (logger.isTraceEnabled()) {
logger.trace("Found cached JMS MessageConsumer for destination [" + dest + "]: " + consumer);
}
}
else {
if (dest instanceof Topic) {
consumer = (subscription != null ?
this.target.createDurableSubscriber((Topic) dest, subscription, selector, noLocal) :
this.target.createConsumer(dest, selector, noLocal));
}
else {
consumer = this.target.createConsumer(dest, selector);
}
if (logger.isDebugEnabled()) {
logger.debug("Creating cached JMS MessageConsumer for destination [" + dest + "]: " + consumer);
}
this.cachedConsumers.put(cacheKey, consumer);
}
return new CachedMessageConsumer(consumer);
}
private void logicalClose(Session proxy) throws JMSException {
// Preserve rollback-on-close semantics.
if (this.transactionOpen && this.target.getTransacted()) {
this.transactionOpen = false;
this.target.rollback();
}
// Physically close durable subscribers at time of Session close call.
for (Iterator<Map.Entry<ConsumerCacheKey, MessageConsumer>> it = this.cachedConsumers.entrySet().iterator(); it.hasNext();) {
Map.Entry<ConsumerCacheKey, MessageConsumer> entry = it.next();
if (entry.getKey().subscription != null) {
entry.getValue().close();
it.remove();
}
}
// Allow for multiple close calls...
boolean returned = false;
synchronized (this.sessionList) {
if (!this.sessionList.contains(proxy)) {
this.sessionList.addLast(proxy);
returned = true;
}
}
if (returned && logger.isTraceEnabled()) {
logger.trace("Returned cached Session: " + this.target);
}
}
private void physicalClose() throws JMSException {
if (logger.isDebugEnabled()) {
logger.debug("Closing cached Session: " + this.target);
}
// Explicitly close all MessageProducers and MessageConsumers that
// this Session happens to cache...
try {
for (MessageProducer producer : this.cachedProducers.values()) {
producer.close();
}
for (MessageConsumer consumer : this.cachedConsumers.values()) {
consumer.close();
}
}
finally {
this.cachedProducers.clear();
this.cachedConsumers.clear();
// Now actually close the Session.
this.target.close();
}
}
}
/**
* Simple wrapper class around a Destination reference.
* Used as the cache key when caching MessageProducer objects.
*/
private static class DestinationCacheKey {
private final Destination destination;
private String destinationString;
public DestinationCacheKey(Destination destination) {
Assert.notNull(destination, "Destination must not be null");
this.destination = destination;
}
private String getDestinationString() {
if (this.destinationString == null) {
this.destinationString = this.destination.toString();
}
return this.destinationString;
}
protected boolean destinationEquals(DestinationCacheKey otherKey) {
return (this.destination.getClass().equals(otherKey.destination.getClass()) &&
(this.destination.equals(otherKey.destination) ||
getDestinationString().equals(otherKey.getDestinationString())));
}
public boolean equals(Object other) {
// Effectively checking object equality as well as toString equality.
// On WebSphere MQ, Destination objects do not implement equals...
return (other == this || destinationEquals((DestinationCacheKey) other));
}
public int hashCode() {
// Can't use a more specific hashCode since we can't rely on
// this.destination.hashCode() actually being the same value
// for equivalent destinations... Thanks a lot, WebSphere MQ!
return this.destination.getClass().hashCode();
}
}
/**
* Simple wrapper class around a Destination and other consumer attributes.
* Used as the cache key when caching MessageConsumer objects.
*/
private static class ConsumerCacheKey extends DestinationCacheKey {
private final String selector;
private final boolean noLocal;
private final String subscription;
public ConsumerCacheKey(Destination destination, String selector, boolean noLocal, String subscription) {
super(destination);
this.selector = selector;
this.noLocal = noLocal;
this.subscription = subscription;
}
public boolean equals(Object other) {
if (other == this) {
return true;
}
ConsumerCacheKey otherKey = (ConsumerCacheKey) other;
return (destinationEquals(otherKey) &&
ObjectUtils.nullSafeEquals(this.selector, otherKey.selector) &&
this.noLocal == otherKey.noLocal &&
ObjectUtils.nullSafeEquals(this.subscription, otherKey.subscription));
}
}
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2002-2008 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.springframework.jms.connection;
import java.util.ArrayList;
import java.util.List;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import org.springframework.util.Assert;
/**
* Implementation of the JMS ExceptionListener interface that supports chaining,
* allowing the addition of multiple ExceptionListener instances in order.
*
* @author Juergen Hoeller
* @since 2.0
*/
public class ChainedExceptionListener implements ExceptionListener {
/** List of ExceptionListeners */
private final List<ExceptionListener> delegates = new ArrayList<ExceptionListener>(2);
/**
* Add an ExceptionListener to the chained delegate list.
*/
public final void addDelegate(ExceptionListener listener) {
Assert.notNull(listener, "ExceptionListener must not be null");
this.delegates.add(listener);
}
/**
* Return all registered ExceptionListener delegates (as array).
*/
public final ExceptionListener[] getDelegates() {
return this.delegates.toArray(new ExceptionListener[this.delegates.size()]);
}
public void onException(JMSException ex) {
for (ExceptionListener listener : this.delegates) {
listener.onException(ex);
}
}
}

View File

@@ -1,416 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.connection;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.transaction.support.ResourceHolderSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
/**
* Helper class for managing a JMS {@link javax.jms.ConnectionFactory}, in particular
* for obtaining transactional JMS resources for a given ConnectionFactory.
*
* <p>Mainly for internal use within the framework. Used by
* {@link org.springframework.jms.core.JmsTemplate} as well as
* {@link org.springframework.jms.listener.DefaultMessageListenerContainer}.
*
* @author Juergen Hoeller
* @since 2.0
* @see SmartConnectionFactory
*/
public abstract class ConnectionFactoryUtils {
private static final Log logger = LogFactory.getLog(ConnectionFactoryUtils.class);
/**
* Release the given Connection, stopping it (if necessary) and eventually closing it.
* <p>Checks {@link SmartConnectionFactory#shouldStop}, if available.
* This is essentially a more sophisticated version of
* {@link org.springframework.jms.support.JmsUtils#closeConnection}.
* @param con the Connection to release
* (if this is <code>null</code>, the call will be ignored)
* @param cf the ConnectionFactory that the Connection was obtained from
* (may be <code>null</code>)
* @param started whether the Connection might have been started by the application
* @see SmartConnectionFactory#shouldStop
* @see org.springframework.jms.support.JmsUtils#closeConnection
*/
public static void releaseConnection(Connection con, ConnectionFactory cf, boolean started) {
if (con == null) {
return;
}
if (started && cf instanceof SmartConnectionFactory && ((SmartConnectionFactory) cf).shouldStop(con)) {
try {
con.stop();
}
catch (Throwable ex) {
logger.debug("Could not stop JMS Connection before closing it", ex);
}
}
try {
con.close();
}
catch (Throwable ex) {
logger.debug("Could not close JMS Connection", ex);
}
}
/**
* Return the innermost target Session of the given Session. If the given
* Session is a proxy, it will be unwrapped until a non-proxy Session is
* found. Otherwise, the passed-in Session will be returned as-is.
* @param session the Session proxy to unwrap
* @return the innermost target Session, or the passed-in one if no proxy
* @see SessionProxy#getTargetSession()
*/
public static Session getTargetSession(Session session) {
Session sessionToUse = session;
while (sessionToUse instanceof SessionProxy) {
sessionToUse = ((SessionProxy) sessionToUse).getTargetSession();
}
return sessionToUse;
}
/**
* Determine whether the given JMS Session is transactional, that is,
* bound to the current thread by Spring's transaction facilities.
* @param session the JMS Session to check
* @param cf the JMS ConnectionFactory that the Session originated from
* @return whether the Session is transactional
*/
public static boolean isSessionTransactional(Session session, ConnectionFactory cf) {
if (session == null || cf == null) {
return false;
}
JmsResourceHolder resourceHolder = (JmsResourceHolder) TransactionSynchronizationManager.getResource(cf);
return (resourceHolder != null && resourceHolder.containsSession(session));
}
/**
* Obtain a JMS Session that is synchronized with the current transaction, if any.
* @param cf the ConnectionFactory to obtain a Session for
* @param existingCon the existing JMS Connection to obtain a Session for
* (may be <code>null</code>)
* @param synchedLocalTransactionAllowed whether to allow for a local JMS transaction
* that is synchronized with a Spring-managed transaction (where the main transaction
* might be a JDBC-based one for a specific DataSource, for example), with the JMS
* transaction committing right after the main transaction. If not allowed, the given
* ConnectionFactory needs to handle transaction enlistment underneath the covers.
* @return the transactional Session, or <code>null</code> if none found
* @throws JMSException in case of JMS failure
*/
public static Session getTransactionalSession(
final ConnectionFactory cf, final Connection existingCon, final boolean synchedLocalTransactionAllowed)
throws JMSException {
return doGetTransactionalSession(cf, new ResourceFactory() {
public Session getSession(JmsResourceHolder holder) {
return holder.getSession(Session.class, existingCon);
}
public Connection getConnection(JmsResourceHolder holder) {
return (existingCon != null ? existingCon : holder.getConnection());
}
public Connection createConnection() throws JMSException {
return cf.createConnection();
}
public Session createSession(Connection con) throws JMSException {
return con.createSession(synchedLocalTransactionAllowed, Session.AUTO_ACKNOWLEDGE);
}
public boolean isSynchedLocalTransactionAllowed() {
return synchedLocalTransactionAllowed;
}
}, true);
}
/**
* Obtain a JMS QueueSession that is synchronized with the current transaction, if any.
* <p>Mainly intended for use with the JMS 1.0.2 API.
* @param cf the ConnectionFactory to obtain a Session for
* @param existingCon the existing JMS Connection to obtain a Session for
* (may be <code>null</code>)
* @param synchedLocalTransactionAllowed whether to allow for a local JMS transaction
* that is synchronized with a Spring-managed transaction (where the main transaction
* might be a JDBC-based one for a specific DataSource, for example), with the JMS
* transaction committing right after the main transaction. If not allowed, the given
* ConnectionFactory needs to handle transaction enlistment underneath the covers.
* @return the transactional Session, or <code>null</code> if none found
* @throws JMSException in case of JMS failure
*/
public static QueueSession getTransactionalQueueSession(
final QueueConnectionFactory cf, final QueueConnection existingCon, final boolean synchedLocalTransactionAllowed)
throws JMSException {
return (QueueSession) doGetTransactionalSession(cf, new ResourceFactory() {
public Session getSession(JmsResourceHolder holder) {
return holder.getSession(QueueSession.class, existingCon);
}
public Connection getConnection(JmsResourceHolder holder) {
return (existingCon != null ? existingCon : holder.getConnection(QueueConnection.class));
}
public Connection createConnection() throws JMSException {
return cf.createQueueConnection();
}
public Session createSession(Connection con) throws JMSException {
return ((QueueConnection) con).createQueueSession(synchedLocalTransactionAllowed, Session.AUTO_ACKNOWLEDGE);
}
public boolean isSynchedLocalTransactionAllowed() {
return synchedLocalTransactionAllowed;
}
}, true);
}
/**
* Obtain a JMS TopicSession that is synchronized with the current transaction, if any.
* <p>Mainly intended for use with the JMS 1.0.2 API.
* @param cf the ConnectionFactory to obtain a Session for
* @param existingCon the existing JMS Connection to obtain a Session for
* (may be <code>null</code>)
* @param synchedLocalTransactionAllowed whether to allow for a local JMS transaction
* that is synchronized with a Spring-managed transaction (where the main transaction
* might be a JDBC-based one for a specific DataSource, for example), with the JMS
* transaction committing right after the main transaction. If not allowed, the given
* ConnectionFactory needs to handle transaction enlistment underneath the covers.
* @return the transactional Session, or <code>null</code> if none found
* @throws JMSException in case of JMS failure
*/
public static TopicSession getTransactionalTopicSession(
final TopicConnectionFactory cf, final TopicConnection existingCon, final boolean synchedLocalTransactionAllowed)
throws JMSException {
return (TopicSession) doGetTransactionalSession(cf, new ResourceFactory() {
public Session getSession(JmsResourceHolder holder) {
return holder.getSession(TopicSession.class, existingCon);
}
public Connection getConnection(JmsResourceHolder holder) {
return (existingCon != null ? existingCon : holder.getConnection(TopicConnection.class));
}
public Connection createConnection() throws JMSException {
return cf.createTopicConnection();
}
public Session createSession(Connection con) throws JMSException {
return ((TopicConnection) con).createTopicSession(synchedLocalTransactionAllowed, Session.AUTO_ACKNOWLEDGE);
}
public boolean isSynchedLocalTransactionAllowed() {
return synchedLocalTransactionAllowed;
}
}, true);
}
/**
* Obtain a JMS Session that is synchronized with the current transaction, if any.
* <p>This <code>doGetTransactionalSession</code> variant always starts the underlying
* JMS Connection, assuming that the Session will be used for receiving messages.
* @param connectionFactory the JMS ConnectionFactory to bind for
* (used as TransactionSynchronizationManager key)
* @param resourceFactory the ResourceFactory to use for extracting or creating
* JMS resources
* @return the transactional Session, or <code>null</code> if none found
* @throws JMSException in case of JMS failure
* @see #doGetTransactionalSession(javax.jms.ConnectionFactory, ResourceFactory, boolean)
*/
public static Session doGetTransactionalSession(
ConnectionFactory connectionFactory, ResourceFactory resourceFactory) throws JMSException {
return doGetTransactionalSession(connectionFactory, resourceFactory, true);
}
/**
* Obtain a JMS Session that is synchronized with the current transaction, if any.
* @param connectionFactory the JMS ConnectionFactory to bind for
* (used as TransactionSynchronizationManager key)
* @param resourceFactory the ResourceFactory to use for extracting or creating
* JMS resources
* @param startConnection whether the underlying JMS Connection approach should be
* started in order to allow for receiving messages. Note that a reused Connection
* may already have been started before, even if this flag is <code>false</code>.
* @return the transactional Session, or <code>null</code> if none found
* @throws JMSException in case of JMS failure
*/
public static Session doGetTransactionalSession(
ConnectionFactory connectionFactory, ResourceFactory resourceFactory, boolean startConnection)
throws JMSException {
Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
Assert.notNull(resourceFactory, "ResourceFactory must not be null");
JmsResourceHolder resourceHolder =
(JmsResourceHolder) TransactionSynchronizationManager.getResource(connectionFactory);
if (resourceHolder != null) {
Session session = resourceFactory.getSession(resourceHolder);
if (session != null) {
if (startConnection) {
Connection con = resourceFactory.getConnection(resourceHolder);
if (con != null) {
con.start();
}
}
return session;
}
if (resourceHolder.isFrozen()) {
return null;
}
}
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
return null;
}
JmsResourceHolder resourceHolderToUse = resourceHolder;
if (resourceHolderToUse == null) {
resourceHolderToUse = new JmsResourceHolder(connectionFactory);
}
Connection con = resourceFactory.getConnection(resourceHolderToUse);
Session session = null;
try {
boolean isExistingCon = (con != null);
if (!isExistingCon) {
con = resourceFactory.createConnection();
resourceHolderToUse.addConnection(con);
}
session = resourceFactory.createSession(con);
resourceHolderToUse.addSession(session, con);
if (startConnection) {
con.start();
}
}
catch (JMSException ex) {
if (session != null) {
try {
session.close();
}
catch (Throwable ex2) {
// ignore
}
}
if (con != null) {
try {
con.close();
}
catch (Throwable ex2) {
// ignore
}
}
throw ex;
}
if (resourceHolderToUse != resourceHolder) {
TransactionSynchronizationManager.registerSynchronization(
new JmsResourceSynchronization(
resourceHolderToUse, connectionFactory, resourceFactory.isSynchedLocalTransactionAllowed()));
resourceHolderToUse.setSynchronizedWithTransaction(true);
TransactionSynchronizationManager.bindResource(connectionFactory, resourceHolderToUse);
}
return session;
}
/**
* Callback interface for resource creation.
* Serving as argument for the <code>doGetTransactionalSession</code> method.
*/
public interface ResourceFactory {
/**
* Fetch an appropriate Session from the given JmsResourceHolder.
* @param holder the JmsResourceHolder
* @return an appropriate Session fetched from the holder,
* or <code>null</code> if none found
*/
Session getSession(JmsResourceHolder holder);
/**
* Fetch an appropriate Connection from the given JmsResourceHolder.
* @param holder the JmsResourceHolder
* @return an appropriate Connection fetched from the holder,
* or <code>null</code> if none found
*/
Connection getConnection(JmsResourceHolder holder);
/**
* Create a new JMS Connection for registration with a JmsResourceHolder.
* @return the new JMS Connection
* @throws JMSException if thrown by JMS API methods
*/
Connection createConnection() throws JMSException;
/**
* Create a new JMS Session for registration with a JmsResourceHolder.
* @param con the JMS Connection to create a Session for
* @return the new JMS Session
* @throws JMSException if thrown by JMS API methods
*/
Session createSession(Connection con) throws JMSException;
/**
* Return whether to allow for a local JMS transaction that is synchronized with
* a Spring-managed transaction (where the main transaction might be a JDBC-based
* one for a specific DataSource, for example), with the JMS transaction
* committing right after the main transaction.
* @return whether to allow for synchronizing a local JMS transaction
*/
boolean isSynchedLocalTransactionAllowed();
}
/**
* Callback for resource cleanup at the end of a non-native JMS transaction
* (e.g. when participating in a JtaTransactionManager transaction).
* @see org.springframework.transaction.jta.JtaTransactionManager
*/
private static class JmsResourceSynchronization extends ResourceHolderSynchronization<JmsResourceHolder, Object> {
private final boolean transacted;
public JmsResourceSynchronization(JmsResourceHolder resourceHolder, Object resourceKey, boolean transacted) {
super(resourceHolder, resourceKey);
this.transacted = transacted;
}
protected boolean shouldReleaseBeforeCompletion() {
return !this.transacted;
}
protected void processResourceAfterCommit(JmsResourceHolder resourceHolder) {
try {
resourceHolder.commitAll();
}
catch (JMSException ex) {
throw new SynchedLocalTransactionFailedException("Local JMS transaction failed to commit", ex);
}
}
protected void releaseResource(JmsResourceHolder resourceHolder, Object resourceKey) {
resourceHolder.closeAll();
}
}
}

View File

@@ -1,163 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms.connection;
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 org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* {@link javax.jms.ConnectionFactory} implementation that delegates all calls
* to a given target {@link javax.jms.ConnectionFactory}, adapting specific
* <code>create(Queue/Topic)Connection</code> calls to the target ConnectionFactory
* if necessary (e.g. when running JMS 1.0.2 API based code against a generic
* JMS 1.1 ConnectionFactory, such as ActiveMQ's PooledConnectionFactory).
*
* <p>This class allows for being subclassed, with subclasses overriding only
* those methods (such as {@link #createConnection()}) that should not simply
* delegate to the target ConnectionFactory.
*
* <p>Can also be defined as-is, wrapping a specific target ConnectionFactory,
* using the "shouldStopConnections" flag to indicate whether Connections
* obtained from the target factory are supposed to be stopped before closed.
* The latter may be necessary for some connection pools that simply return
* released connections to the pool, not stopping them while they sit in the pool.
*
* @author Juergen Hoeller
* @since 2.0.2
* @see #createConnection()
* @see #setShouldStopConnections
* @see ConnectionFactoryUtils#releaseConnection
*/
public class DelegatingConnectionFactory
implements SmartConnectionFactory, QueueConnectionFactory, TopicConnectionFactory, InitializingBean {
private ConnectionFactory targetConnectionFactory;
private boolean shouldStopConnections = false;
/**
* Set the target ConnectionFactory that this ConnectionFactory should delegate to.
*/
public void setTargetConnectionFactory(ConnectionFactory targetConnectionFactory) {
Assert.notNull(targetConnectionFactory, "'targetConnectionFactory' must not be null");
this.targetConnectionFactory = targetConnectionFactory;
}
/**
* Return the target ConnectionFactory that this ConnectionFactory delegates to.
*/
public ConnectionFactory getTargetConnectionFactory() {
return this.targetConnectionFactory;
}
/**
* Indicate whether Connections obtained from the target factory are supposed
* to be stopped before closed ("true") or simply closed ("false").
* The latter may be necessary for some connection pools that simply return
* released connections to the pool, not stopping them while they sit in the pool.
* <p>Default is "false", simply closing Connections.
* @see ConnectionFactoryUtils#releaseConnection
*/
public void setShouldStopConnections(boolean shouldStopConnections) {
this.shouldStopConnections = shouldStopConnections;
}
public void afterPropertiesSet() {
if (getTargetConnectionFactory() == null) {
throw new IllegalArgumentException("'targetConnectionFactory' is required");
}
}
public Connection createConnection() throws JMSException {
return getTargetConnectionFactory().createConnection();
}
public Connection createConnection(String username, String password) throws JMSException {
return getTargetConnectionFactory().createConnection(username, password);
}
public QueueConnection createQueueConnection() throws JMSException {
ConnectionFactory cf = getTargetConnectionFactory();
if (cf instanceof QueueConnectionFactory) {
return ((QueueConnectionFactory) cf).createQueueConnection();
}
else {
Connection con = cf.createConnection();
if (!(con instanceof QueueConnection)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a QueueConnectionFactory");
}
return (QueueConnection) con;
}
}
public QueueConnection createQueueConnection(String username, String password) throws JMSException {
ConnectionFactory cf = getTargetConnectionFactory();
if (cf instanceof QueueConnectionFactory) {
return ((QueueConnectionFactory) cf).createQueueConnection(username, password);
}
else {
Connection con = cf.createConnection(username, password);
if (!(con instanceof QueueConnection)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a QueueConnectionFactory");
}
return (QueueConnection) con;
}
}
public TopicConnection createTopicConnection() throws JMSException {
ConnectionFactory cf = getTargetConnectionFactory();
if (cf instanceof TopicConnectionFactory) {
return ((TopicConnectionFactory) cf).createTopicConnection();
}
else {
Connection con = cf.createConnection();
if (!(con instanceof TopicConnection)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a TopicConnectionFactory");
}
return (TopicConnection) con;
}
}
public TopicConnection createTopicConnection(String username, String password) throws JMSException {
ConnectionFactory cf = getTargetConnectionFactory();
if (cf instanceof TopicConnectionFactory) {
return ((TopicConnectionFactory) cf).createTopicConnection(username, password);
}
else {
Connection con = cf.createConnection(username, password);
if (!(con instanceof TopicConnection)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a TopicConnectionFactory");
}
return (TopicConnection) con;
}
}
public boolean shouldStop(Connection con) {
return this.shouldStopConnections;
}
}

View File

@@ -1,207 +0,0 @@
/*
* Copyright 2002-2010 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.springframework.jms.connection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TransactionInProgressException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.transaction.support.ResourceHolderSupport;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* JMS resource holder, wrapping a JMS Connection and a JMS Session.
* JmsTransactionManager binds instances of this class to the thread,
* for a given JMS ConnectionFactory.
*
* <p>Note: This is an SPI class, not intended to be used by applications.
*
* @author Juergen Hoeller
* @since 1.1
* @see JmsTransactionManager
* @see org.springframework.jms.core.JmsTemplate
*/
public class JmsResourceHolder extends ResourceHolderSupport {
private static final Log logger = LogFactory.getLog(JmsResourceHolder.class);
private ConnectionFactory connectionFactory;
private boolean frozen = false;
private final List<Connection> connections = new LinkedList<Connection>();
private final List<Session> sessions = new LinkedList<Session>();
private final Map<Connection, List<Session>> sessionsPerConnection =
new HashMap<Connection, List<Session>>();
/**
* Create a new JmsResourceHolder that is open for resources to be added.
* @see #addConnection
* @see #addSession
*/
public JmsResourceHolder() {
}
/**
* Create a new JmsResourceHolder that is open for resources to be added.
* @param connectionFactory the JMS ConnectionFactory that this
* resource holder is associated with (may be <code>null</code>)
*/
public JmsResourceHolder(ConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
/**
* Create a new JmsResourceHolder for the given JMS Session.
* @param session the JMS Session
*/
public JmsResourceHolder(Session session) {
addSession(session);
this.frozen = true;
}
/**
* Create a new JmsResourceHolder for the given JMS resources.
* @param connection the JMS Connection
* @param session the JMS Session
*/
public JmsResourceHolder(Connection connection, Session session) {
addConnection(connection);
addSession(session, connection);
this.frozen = true;
}
/**
* Create a new JmsResourceHolder for the given JMS resources.
* @param connectionFactory the JMS ConnectionFactory that this
* resource holder is associated with (may be <code>null</code>)
* @param connection the JMS Connection
* @param session the JMS Session
*/
public JmsResourceHolder(ConnectionFactory connectionFactory, Connection connection, Session session) {
this.connectionFactory = connectionFactory;
addConnection(connection);
addSession(session, connection);
this.frozen = true;
}
public final boolean isFrozen() {
return this.frozen;
}
public final void addConnection(Connection connection) {
Assert.isTrue(!this.frozen, "Cannot add Connection because JmsResourceHolder is frozen");
Assert.notNull(connection, "Connection must not be null");
if (!this.connections.contains(connection)) {
this.connections.add(connection);
}
}
public final void addSession(Session session) {
addSession(session, null);
}
public final void addSession(Session session, Connection connection) {
Assert.isTrue(!this.frozen, "Cannot add Session because JmsResourceHolder is frozen");
Assert.notNull(session, "Session must not be null");
if (!this.sessions.contains(session)) {
this.sessions.add(session);
if (connection != null) {
List<Session> sessions = this.sessionsPerConnection.get(connection);
if (sessions == null) {
sessions = new LinkedList<Session>();
this.sessionsPerConnection.put(connection, sessions);
}
sessions.add(session);
}
}
}
public boolean containsSession(Session session) {
return this.sessions.contains(session);
}
public Connection getConnection() {
return (!this.connections.isEmpty() ? this.connections.get(0) : null);
}
public Connection getConnection(Class<? extends Connection> connectionType) {
return CollectionUtils.findValueOfType(this.connections, connectionType);
}
public Session getSession() {
return (!this.sessions.isEmpty() ? this.sessions.get(0) : null);
}
public Session getSession(Class<? extends Session> sessionType) {
return getSession(sessionType, null);
}
public Session getSession(Class<? extends Session> sessionType, Connection connection) {
List<Session> sessions = this.sessions;
if (connection != null) {
sessions = this.sessionsPerConnection.get(connection);
}
return CollectionUtils.findValueOfType(sessions, sessionType);
}
public void commitAll() throws JMSException {
for (Session session : this.sessions) {
try {
session.commit();
}
catch (TransactionInProgressException ex) {
// Ignore -> can only happen in case of a JTA transaction.
}
// Let IllegalStateException through: It might point out an unexpectedly closed session.
}
}
public void closeAll() {
for (Session session : this.sessions) {
try {
session.close();
}
catch (Throwable ex) {
logger.debug("Could not close synchronized JMS Session after transaction", ex);
}
}
for (Connection con : this.connections) {
ConnectionFactoryUtils.releaseConnection(con, this.connectionFactory, true);
}
this.connections.clear();
this.sessions.clear();
this.sessionsPerConnection.clear();
}
}

View File

@@ -1,321 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.connection;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TransactionRolledBackException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.InvalidIsolationLevelException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.transaction.UnexpectedRollbackException;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.ResourceTransactionManager;
import org.springframework.transaction.support.SmartTransactionObject;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* {@link org.springframework.transaction.PlatformTransactionManager} implementation
* for a single JMS {@link javax.jms.ConnectionFactory}. Binds a JMS
* Connection/Session pair from the specified ConnectionFactory to the thread,
* potentially allowing for one thread-bound Session per ConnectionFactory.
*
* <p>This local strategy is an alternative to executing JMS operations within
* JTA transactions. Its advantage is that it is able to work in any environment,
* for example a standalone application or a test suite, with any message broker
* as target. However, this strategy is <i>not</i> able to provide XA transactions,
* for example in order to share transactions between messaging and database access.
* A full JTA/XA setup is required for XA transactions, typically using Spring's
* {@link org.springframework.transaction.jta.JtaTransactionManager} as strategy.
*
* <p>Application code is required to retrieve the transactional JMS Session via
* {@link ConnectionFactoryUtils#getTransactionalSession} instead of a standard
* J2EE-style {@link ConnectionFactory#createConnection()} call with subsequent
* Session creation. Spring's {@link org.springframework.jms.core.JmsTemplate}
* will autodetect a thread-bound Session and automatically participate in it.
*
* <p>Alternatively, you can allow application code to work with the standard
* J2EE-style lookup pattern on a ConnectionFactory, for example for legacy code
* that is not aware of Spring at all. In that case, define a
* {@link TransactionAwareConnectionFactoryProxy} for your target ConnectionFactory,
* which will automatically participate in Spring-managed transactions.
*
* <p><b>The use of {@link CachingConnectionFactory} as a target for this
* transaction manager is strongly recommended.</b> CachingConnectionFactory
* uses a single JMS Connection for all JMS access in order to avoid the overhead
* of repeated Connection creation, as well as maintaining a cache of Sessions.
* Each transaction will then share the same JMS Connection, while still using
* its own individual JMS Session.
*
* <p>The use of a <i>raw</i> target ConnectionFactory would not only be inefficient
* because of the lack of resource reuse. It might also lead to strange effects
* when your JMS driver doesn't accept <code>MessageProducer.close()</code> calls
* and/or <code>MessageConsumer.close()</code> calls before <code>Session.commit()</code>,
* with the latter supposed to commit all the messages that have been sent through the
* producer handle and received through the consumer handle. As a safe general solution,
* always pass in a {@link CachingConnectionFactory} into this transaction manager's
* {@link #setConnectionFactory "connectionFactory"} property.
*
* <p>Transaction synchronization is turned off by default, as this manager might
* be used alongside a datastore-based Spring transaction manager such as the
* JDBC {@link org.springframework.jdbc.datasource.DataSourceTransactionManager},
* which has stronger needs for synchronization.
*
* @author Juergen Hoeller
* @since 1.1
* @see ConnectionFactoryUtils#getTransactionalSession
* @see TransactionAwareConnectionFactoryProxy
* @see org.springframework.jms.core.JmsTemplate
*/
public class JmsTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
private ConnectionFactory connectionFactory;
/**
* Create a new JmsTransactionManager for bean-style usage.
* <p>Note: The ConnectionFactory has to be set before using the instance.
* This constructor can be used to prepare a JmsTemplate via a BeanFactory,
* typically setting the ConnectionFactory via setConnectionFactory.
* <p>Turns off transaction synchronization by default, as this manager might
* be used alongside a datastore-based Spring transaction manager like
* DataSourceTransactionManager, which has stronger needs for synchronization.
* Only one manager is allowed to drive synchronization at any point of time.
* @see #setConnectionFactory
* @see #setTransactionSynchronization
*/
public JmsTransactionManager() {
setTransactionSynchronization(SYNCHRONIZATION_NEVER);
}
/**
* Create a new JmsTransactionManager, given a ConnectionFactory.
* @param connectionFactory the ConnectionFactory to obtain connections from
*/
public JmsTransactionManager(ConnectionFactory connectionFactory) {
this();
setConnectionFactory(connectionFactory);
afterPropertiesSet();
}
/**
* Set the JMS ConnectionFactory that this instance should manage transactions for.
*/
public void setConnectionFactory(ConnectionFactory cf) {
if (cf instanceof TransactionAwareConnectionFactoryProxy) {
// If we got a TransactionAwareConnectionFactoryProxy, we need to perform transactions
// for its underlying target ConnectionFactory, else JMS access code won't see
// properly exposed transactions (i.e. transactions for the target ConnectionFactory).
this.connectionFactory = ((TransactionAwareConnectionFactoryProxy) cf).getTargetConnectionFactory();
}
else {
this.connectionFactory = cf;
}
}
/**
* Return the JMS ConnectionFactory that this instance should manage transactions for.
*/
public ConnectionFactory getConnectionFactory() {
return this.connectionFactory;
}
/**
* Make sure the ConnectionFactory has been set.
*/
public void afterPropertiesSet() {
if (getConnectionFactory() == null) {
throw new IllegalArgumentException("Property 'connectionFactory' is required");
}
}
public Object getResourceFactory() {
return getConnectionFactory();
}
protected Object doGetTransaction() {
JmsTransactionObject txObject = new JmsTransactionObject();
txObject.setResourceHolder(
(JmsResourceHolder) TransactionSynchronizationManager.getResource(getConnectionFactory()));
return txObject;
}
protected boolean isExistingTransaction(Object transaction) {
JmsTransactionObject txObject = (JmsTransactionObject) transaction;
return (txObject.getResourceHolder() != null);
}
protected void doBegin(Object transaction, TransactionDefinition definition) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
throw new InvalidIsolationLevelException("JMS does not support an isolation level concept");
}
JmsTransactionObject txObject = (JmsTransactionObject) transaction;
Connection con = null;
Session session = null;
try {
con = createConnection();
session = createSession(con);
if (logger.isDebugEnabled()) {
logger.debug("Created JMS transaction on Session [" + session + "] from Connection [" + con + "]");
}
txObject.setResourceHolder(new JmsResourceHolder(getConnectionFactory(), con, session));
txObject.getResourceHolder().setSynchronizedWithTransaction(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getResourceHolder().setTimeoutInSeconds(timeout);
}
TransactionSynchronizationManager.bindResource(
getConnectionFactory(), txObject.getResourceHolder());
}
catch (JMSException ex) {
if (session != null) {
try {
session.close();
}
catch (Throwable ex2) {
// ignore
}
}
if (con != null) {
try {
con.close();
}
catch (Throwable ex2) {
// ignore
}
}
throw new CannotCreateTransactionException("Could not create JMS transaction", ex);
}
}
protected Object doSuspend(Object transaction) {
JmsTransactionObject txObject = (JmsTransactionObject) transaction;
txObject.setResourceHolder(null);
return TransactionSynchronizationManager.unbindResource(getConnectionFactory());
}
protected void doResume(Object transaction, Object suspendedResources) {
JmsResourceHolder conHolder = (JmsResourceHolder) suspendedResources;
TransactionSynchronizationManager.bindResource(getConnectionFactory(), conHolder);
}
protected void doCommit(DefaultTransactionStatus status) {
JmsTransactionObject txObject = (JmsTransactionObject) status.getTransaction();
Session session = txObject.getResourceHolder().getSession();
try {
if (status.isDebug()) {
logger.debug("Committing JMS transaction on Session [" + session + "]");
}
session.commit();
}
catch (TransactionRolledBackException ex) {
throw new UnexpectedRollbackException("JMS transaction rolled back", ex);
}
catch (JMSException ex) {
throw new TransactionSystemException("Could not commit JMS transaction", ex);
}
}
protected void doRollback(DefaultTransactionStatus status) {
JmsTransactionObject txObject = (JmsTransactionObject) status.getTransaction();
Session session = txObject.getResourceHolder().getSession();
try {
if (status.isDebug()) {
logger.debug("Rolling back JMS transaction on Session [" + session + "]");
}
session.rollback();
}
catch (JMSException ex) {
throw new TransactionSystemException("Could not roll back JMS transaction", ex);
}
}
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
JmsTransactionObject txObject = (JmsTransactionObject) status.getTransaction();
txObject.getResourceHolder().setRollbackOnly();
}
protected void doCleanupAfterCompletion(Object transaction) {
JmsTransactionObject txObject = (JmsTransactionObject) transaction;
TransactionSynchronizationManager.unbindResource(getConnectionFactory());
txObject.getResourceHolder().closeAll();
txObject.getResourceHolder().clear();
}
//-------------------------------------------------------------------------
// JMS 1.1 factory methods, potentially overridden for JMS 1.0.2
//-------------------------------------------------------------------------
/**
* Create a JMS Connection via this template's ConnectionFactory.
* <p>This implementation uses JMS 1.1 API.
* @return the new JMS Connection
* @throws javax.jms.JMSException if thrown by JMS API methods
*/
protected Connection createConnection() throws JMSException {
return getConnectionFactory().createConnection();
}
/**
* Create a JMS Session for the given Connection.
* <p>This implementation uses JMS 1.1 API.
* @param con the JMS Connection to create a Session for
* @return the new JMS Session
* @throws javax.jms.JMSException if thrown by JMS API methods
*/
protected Session createSession(Connection con) throws JMSException {
return con.createSession(true, Session.AUTO_ACKNOWLEDGE);
}
/**
* JMS transaction object, representing a JmsResourceHolder.
* Used as transaction object by JmsTransactionManager.
* @see JmsResourceHolder
*/
private static class JmsTransactionObject implements SmartTransactionObject {
private JmsResourceHolder resourceHolder;
public void setResourceHolder(JmsResourceHolder resourceHolder) {
this.resourceHolder = resourceHolder;
}
public JmsResourceHolder getResourceHolder() {
return this.resourceHolder;
}
public boolean isRollbackOnly() {
return this.resourceHolder.isRollbackOnly();
}
public void flush() {
// no-op
}
}
}

View File

@@ -1,150 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.connection;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.Session;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
/**
* A subclass of {@link JmsTransactionManager} for the JMS 1.0.2 specification,
* not relying on JMS 1.1 methods like JmsTransactionManager itself.
* This class can be used for JMS 1.0.2 providers, offering the same API as
* JmsTransactionManager does for JMS 1.1 providers.
*
* <p>You need to set the {@link #setPubSubDomain "pubSubDomain" property},
* since this class will always explicitly differentiate between a
* {@link javax.jms.QueueConnection} and a {@link javax.jms.TopicConnection}.
*
* @author Juergen Hoeller
* @since 1.1
* @see #setConnectionFactory
* @see #setPubSubDomain
* @deprecated as of Spring 3.0, in favor of the JMS 1.1 based {@link JmsTransactionManager}
*/
@Deprecated
public class JmsTransactionManager102 extends JmsTransactionManager {
private boolean pubSubDomain = false;
/**
* Create a new JmsTransactionManager102 for bean-style usage.
* <p>Note: The ConnectionFactory has to be set before using the instance.
* This constructor can be used to prepare a JmsTemplate via a BeanFactory,
* typically setting the ConnectionFactory via setConnectionFactory.
* @see #setConnectionFactory
*/
public JmsTransactionManager102() {
super();
}
/**
* Create a new JmsTransactionManager102, given a ConnectionFactory.
* @param connectionFactory the ConnectionFactory to manage transactions for
* @param pubSubDomain whether the Publish/Subscribe domain (Topics) or
* Point-to-Point domain (Queues) should be used
* @see #setPubSubDomain
*/
public JmsTransactionManager102(ConnectionFactory connectionFactory, boolean pubSubDomain) {
setConnectionFactory(connectionFactory);
this.pubSubDomain = pubSubDomain;
afterPropertiesSet();
}
/**
* Configure the transaction manager with knowledge of the JMS domain used.
* This tells the JMS 1.0.2 provider which class hierarchy to use for creating
* Connections and Sessions.
* <p>Default is Point-to-Point (Queues).
* @param pubSubDomain <code>true</code> for Publish/Subscribe domain (Topics),
* <code>false</code> for Point-to-Point domain (Queues)
*/
public void setPubSubDomain(boolean pubSubDomain) {
this.pubSubDomain = pubSubDomain;
}
/**
* Return whether the Publish/Subscribe domain (Topics) is used.
* Otherwise, the Point-to-Point domain (Queues) is used.
*/
public boolean isPubSubDomain() {
return this.pubSubDomain;
}
/**
* In addition to checking if the connection factory is set, make sure
* that the supplied connection factory is of the appropriate type for
* the specified destination type: QueueConnectionFactory for queues,
* and TopicConnectionFactory for topics.
*/
public void afterPropertiesSet() {
super.afterPropertiesSet();
// Make sure that the ConnectionFactory passed is consistent.
// Some provider implementations of the ConnectionFactory interface
// implement both domain interfaces under the cover, so just check if
// the selected domain is consistent with the type of connection factory.
if (isPubSubDomain()) {
if (!(getConnectionFactory() instanceof TopicConnectionFactory)) {
throw new IllegalArgumentException(
"Specified a Spring JMS 1.0.2 transaction manager for topics " +
"but did not supply an instance of TopicConnectionFactory");
}
}
else {
if (!(getConnectionFactory() instanceof QueueConnectionFactory)) {
throw new IllegalArgumentException(
"Specified a Spring JMS 1.0.2 transaction manager for queues " +
"but did not supply an instance of QueueConnectionFactory");
}
}
}
/**
* This implementation overrides the superclass method to use JMS 1.0.2 API.
*/
protected Connection createConnection() throws JMSException {
if (isPubSubDomain()) {
return ((TopicConnectionFactory) getConnectionFactory()).createTopicConnection();
}
else {
return ((QueueConnectionFactory) getConnectionFactory()).createQueueConnection();
}
}
/**
* This implementation overrides the superclass method to use JMS 1.0.2 API.
*/
protected Session createSession(Connection con) throws JMSException {
if (isPubSubDomain()) {
return ((TopicConnection) con).createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
}
else {
return ((QueueConnection) con).createQueueSession(true, Session.AUTO_ACKNOWLEDGE);
}
}
}

View File

@@ -1,41 +0,0 @@
/*
* Copyright 2002-2008 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.springframework.jms.connection;
import javax.jms.Session;
/**
* Subinterface of {@link javax.jms.Session} to be implemented by
* Session proxies. Allows access to the the underlying target Session.
*
* @author Juergen Hoeller
* @since 2.0.4
* @see TransactionAwareConnectionFactoryProxy
* @see CachingConnectionFactory
* @see ConnectionFactoryUtils#getTargetSession(javax.jms.Session)
*/
public interface SessionProxy extends Session {
/**
* Return the target Session of this proxy.
* <p>This will typically be the native provider Session
* or a wrapper from a session pool.
* @return the underlying Session (never <code>null</code>)
*/
Session getTargetSession();
}

View File

@@ -1,588 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.connection;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.Session;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* A JMS ConnectionFactory adapter that returns the same Connection
* from all {@link #createConnection()} calls, and ignores calls to
* {@link javax.jms.Connection#close()}. According to the JMS Connection
* model, this is perfectly thread-safe (in contrast to e.g. JDBC). The
* shared Connection can be automatically recovered in case of an Exception.
*
* <p>You can either pass in a specific JMS Connection directly or let this
* factory lazily create a Connection via a given target ConnectionFactory.
* This factory generally works with JMS 1.1 as well as the JMS 1.0.2 API.
*
* <p>Note that when using the JMS 1.0.2 API, this ConnectionFactory will switch
* into queue/topic mode according to the JMS API methods used at runtime:
* <code>createQueueConnection</code> and <code>createTopicConnection</code> will
* lead to queue/topic mode, respectively; generic <code>createConnection</code>
* calls will lead to a JMS 1.1 connection which is able to serve both modes.
*
* <p>Useful for testing and standalone environments in order to keep using the
* same Connection for multiple {@link org.springframework.jms.core.JmsTemplate}
* calls, without having a pooling ConnectionFactory underneath. This may span
* any number of transactions, even concurrently executing transactions.
*
* <p>Note that Spring's message listener containers support the use of
* a shared Connection within each listener container instance. Using
* SingleConnectionFactory in combination only really makes sense for
* sharing a single JMS Connection <i>across multiple listener containers</i>.
*
* @author Juergen Hoeller
* @author Mark Pollack
* @since 1.1
* @see org.springframework.jms.core.JmsTemplate
* @see org.springframework.jms.listener.SimpleMessageListenerContainer
* @see org.springframework.jms.listener.DefaultMessageListenerContainer#setCacheLevel
*/
public class SingleConnectionFactory
implements ConnectionFactory, QueueConnectionFactory, TopicConnectionFactory, ExceptionListener,
InitializingBean, DisposableBean {
protected final Log logger = LogFactory.getLog(getClass());
private ConnectionFactory targetConnectionFactory;
private String clientId;
private ExceptionListener exceptionListener;
private boolean reconnectOnException = false;
/** Wrapped Connection */
private Connection target;
/** Proxy Connection */
private Connection connection;
/** A hint whether to create a queue or topic connection */
private Boolean pubSubMode;
/** Whether the shared Connection has been started */
private boolean started = false;
/** Synchronization monitor for the shared Connection */
private final Object connectionMonitor = new Object();
/**
* Create a new SingleConnectionFactory for bean-style usage.
* @see #setTargetConnectionFactory
*/
public SingleConnectionFactory() {
}
/**
* Create a new SingleConnectionFactory that always returns the given Connection.
* @param target the single Connection
*/
public SingleConnectionFactory(Connection target) {
Assert.notNull(target, "Target Connection must not be null");
this.target = target;
this.connection = getSharedConnectionProxy(target);
}
/**
* Create a new SingleConnectionFactory that always returns a single
* Connection that it will lazily create via the given target
* ConnectionFactory.
* @param targetConnectionFactory the target ConnectionFactory
*/
public SingleConnectionFactory(ConnectionFactory targetConnectionFactory) {
Assert.notNull(targetConnectionFactory, "Target ConnectionFactory must not be null");
this.targetConnectionFactory = targetConnectionFactory;
}
/**
* Set the target ConnectionFactory which will be used to lazily
* create a single Connection.
*/
public void setTargetConnectionFactory(ConnectionFactory targetConnectionFactory) {
this.targetConnectionFactory = targetConnectionFactory;
}
/**
* Return the target ConnectionFactory which will be used to lazily
* create a single Connection, if any.
*/
public ConnectionFactory getTargetConnectionFactory() {
return this.targetConnectionFactory;
}
/**
* Specify a JMS client ID for the single Connection created and exposed
* by this ConnectionFactory.
* <p>Note that client IDs need to be unique among all active Connections
* of the underlying JMS provider. Furthermore, a client ID can only be
* assigned if the original ConnectionFactory hasn't already assigned one.
* @see javax.jms.Connection#setClientID
* @see #setTargetConnectionFactory
*/
public void setClientId(String clientId) {
this.clientId = clientId;
}
/**
* Return a JMS client ID for the single Connection created and exposed
* by this ConnectionFactory, if any.
*/
protected String getClientId() {
return this.clientId;
}
/**
* Specify an JMS ExceptionListener implementation that should be
* registered with with the single Connection created by this factory.
* @see #setReconnectOnException
*/
public void setExceptionListener(ExceptionListener exceptionListener) {
this.exceptionListener = exceptionListener;
}
/**
* Return the JMS ExceptionListener implementation that should be registered
* with with the single Connection created by this factory, if any.
*/
protected ExceptionListener getExceptionListener() {
return this.exceptionListener;
}
/**
* Specify whether the single Connection should be reset (to be subsequently renewed)
* when a JMSException is reported by the underlying Connection.
* <p>Default is "false". Switch this to "true" to automatically trigger
* recovery based on your JMS provider's exception notifications.
* <p>Internally, this will lead to a special JMS ExceptionListener
* (this SingleConnectionFactory itself) being registered with the
* underlying Connection. This can also be combined with a
* user-specified ExceptionListener, if desired.
* @see #setExceptionListener
*/
public void setReconnectOnException(boolean reconnectOnException) {
this.reconnectOnException = reconnectOnException;
}
/**
* Return whether the single Connection should be renewed when
* a JMSException is reported by the underlying Connection.
*/
protected boolean isReconnectOnException() {
return this.reconnectOnException;
}
/**
* Make sure a Connection or ConnectionFactory has been set.
*/
public void afterPropertiesSet() {
if (this.connection == null && getTargetConnectionFactory() == null) {
throw new IllegalArgumentException("Connection or 'targetConnectionFactory' is required");
}
}
public Connection createConnection() throws JMSException {
synchronized (this.connectionMonitor) {
if (this.connection == null) {
initConnection();
}
return this.connection;
}
}
public Connection createConnection(String username, String password) throws JMSException {
throw new javax.jms.IllegalStateException(
"SingleConnectionFactory does not support custom username and password");
}
public QueueConnection createQueueConnection() throws JMSException {
Connection con;
synchronized (this.connectionMonitor) {
this.pubSubMode = Boolean.FALSE;
con = createConnection();
}
if (!(con instanceof QueueConnection)) {
throw new javax.jms.IllegalStateException(
"This SingleConnectionFactory does not hold a QueueConnection but rather: " + con);
}
return ((QueueConnection) con);
}
public QueueConnection createQueueConnection(String username, String password) throws JMSException {
throw new javax.jms.IllegalStateException(
"SingleConnectionFactory does not support custom username and password");
}
public TopicConnection createTopicConnection() throws JMSException {
Connection con;
synchronized (this.connectionMonitor) {
this.pubSubMode = Boolean.TRUE;
con = createConnection();
}
if (!(con instanceof TopicConnection)) {
throw new javax.jms.IllegalStateException(
"This SingleConnectionFactory does not hold a TopicConnection but rather: " + con);
}
return ((TopicConnection) con);
}
public TopicConnection createTopicConnection(String username, String password) throws JMSException {
throw new javax.jms.IllegalStateException(
"SingleConnectionFactory does not support custom username and password");
}
/**
* Initialize the underlying shared Connection.
* <p>Closes and reinitializes the Connection if an underlying
* Connection is present already.
* @throws javax.jms.JMSException if thrown by JMS API methods
*/
public void initConnection() throws JMSException {
if (getTargetConnectionFactory() == null) {
throw new IllegalStateException(
"'targetConnectionFactory' is required for lazily initializing a Connection");
}
synchronized (this.connectionMonitor) {
if (this.target != null) {
closeConnection(this.target);
}
this.target = doCreateConnection();
prepareConnection(this.target);
if (logger.isInfoEnabled()) {
logger.info("Established shared JMS Connection: " + this.target);
}
this.connection = getSharedConnectionProxy(this.target);
}
}
/**
* Exception listener callback that renews the underlying single Connection.
*/
public void onException(JMSException ex) {
logger.warn("Encountered a JMSException - resetting the underlying JMS Connection", ex);
resetConnection();
}
/**
* Close the underlying shared connection.
* The provider of this ConnectionFactory needs to care for proper shutdown.
* <p>As this bean implements DisposableBean, a bean factory will
* automatically invoke this on destruction of its cached singletons.
*/
public void destroy() {
resetConnection();
}
/**
* Reset the underlying shared Connection, to be reinitialized on next access.
*/
public void resetConnection() {
synchronized (this.connectionMonitor) {
if (this.target != null) {
closeConnection(this.target);
}
this.target = null;
this.connection = null;
}
}
/**
* Create a JMS Connection via this template's ConnectionFactory.
* @return the new JMS Connection
* @throws javax.jms.JMSException if thrown by JMS API methods
*/
protected Connection doCreateConnection() throws JMSException {
ConnectionFactory cf = getTargetConnectionFactory();
if (Boolean.FALSE.equals(this.pubSubMode) && cf instanceof QueueConnectionFactory) {
return ((QueueConnectionFactory) cf).createQueueConnection();
}
else if (Boolean.TRUE.equals(this.pubSubMode) && cf instanceof TopicConnectionFactory) {
return ((TopicConnectionFactory) cf).createTopicConnection();
}
else {
return getTargetConnectionFactory().createConnection();
}
}
/**
* Prepare the given Connection before it is exposed.
* <p>The default implementation applies ExceptionListener and client id.
* Can be overridden in subclasses.
* @param con the Connection to prepare
* @throws JMSException if thrown by JMS API methods
* @see #setExceptionListener
* @see #setReconnectOnException
*/
protected void prepareConnection(Connection con) throws JMSException {
if (getClientId() != null) {
con.setClientID(getClientId());
}
if (getExceptionListener() != null || isReconnectOnException()) {
ExceptionListener listenerToUse = getExceptionListener();
if (isReconnectOnException()) {
listenerToUse = new InternalChainedExceptionListener(this, listenerToUse);
}
con.setExceptionListener(listenerToUse);
}
}
/**
* Template method for obtaining a (potentially cached) Session.
* <p>The default implementation always returns <code>null</code>.
* Subclasses may override this for exposing specific Session handles,
* possibly delegating to {@link #createSession} for the creation of raw
* Session objects that will then get wrapped and returned from here.
* @param con the JMS Connection to operate on
* @param mode the Session acknowledgement mode
* (<code>Session.TRANSACTED</code> or one of the common modes)
* @return the Session to use, or <code>null</code> to indicate
* creation of a raw standard Session
* @throws JMSException if thrown by the JMS API
*/
protected Session getSession(Connection con, Integer mode) throws JMSException {
return null;
}
/**
* Create a default Session for this ConnectionFactory,
* adaptign to JMS 1.0.2 style queue/topic mode if necessary.
* @param con the JMS Connection to operate on
* @param mode the Session acknowledgement mode
* (<code>Session.TRANSACTED</code> or one of the common modes)
* @return the newly created Session
* @throws JMSException if thrown by the JMS API
*/
protected Session createSession(Connection con, Integer mode) throws JMSException {
// Determine JMS API arguments...
boolean transacted = (mode == Session.SESSION_TRANSACTED);
int ackMode = (transacted ? Session.AUTO_ACKNOWLEDGE : mode);
// Now actually call the appropriate JMS factory method...
if (Boolean.FALSE.equals(this.pubSubMode) && con instanceof QueueConnection) {
return ((QueueConnection) con).createQueueSession(transacted, ackMode);
}
else if (Boolean.TRUE.equals(this.pubSubMode) && con instanceof TopicConnection) {
return ((TopicConnection) con).createTopicSession(transacted, ackMode);
}
else {
return con.createSession(transacted, ackMode);
}
}
/**
* Close the given Connection.
* @param con the Connection to close
*/
protected void closeConnection(Connection con) {
if (logger.isDebugEnabled()) {
logger.debug("Closing shared JMS Connection: " + this.target);
}
try {
try {
if (this.started) {
this.started = false;
con.stop();
}
}
finally {
con.close();
}
}
catch (javax.jms.IllegalStateException ex) {
logger.debug("Ignoring Connection state exception - assuming already closed: " + ex);
}
catch (Throwable ex) {
logger.debug("Could not close shared JMS Connection", ex);
}
}
/**
* Wrap the given Connection with a proxy that delegates every method call to it
* but suppresses close calls. This is useful for allowing application code to
* handle a special framework Connection just like an ordinary Connection from a
* JMS ConnectionFactory.
* @param target the original Connection to wrap
* @return the wrapped Connection
*/
protected Connection getSharedConnectionProxy(Connection target) {
List<Class> classes = new ArrayList<Class>(3);
classes.add(Connection.class);
if (target instanceof QueueConnection) {
classes.add(QueueConnection.class);
}
if (target instanceof TopicConnection) {
classes.add(TopicConnection.class);
}
return (Connection) Proxy.newProxyInstance(
Connection.class.getClassLoader(),
classes.toArray(new Class[classes.size()]),
new SharedConnectionInvocationHandler(target));
}
/**
* Invocation handler for a cached JMS Connection proxy.
*/
private class SharedConnectionInvocationHandler implements InvocationHandler {
private final Connection target;
public SharedConnectionInvocationHandler(Connection target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (method.getName().equals("hashCode")) {
// Use hashCode of Connection proxy.
return System.identityHashCode(proxy);
}
else if (method.getName().equals("toString")) {
return "Shared JMS Connection: " + this.target;
}
else if (method.getName().equals("setClientID")) {
// Handle setClientID method: throw exception if not compatible.
String currentClientId = this.target.getClientID();
if (currentClientId != null && currentClientId.equals(args[0])) {
return null;
}
else {
throw new javax.jms.IllegalStateException(
"setClientID call not supported on proxy for shared Connection. " +
"Set the 'clientId' property on the SingleConnectionFactory instead.");
}
}
else if (method.getName().equals("setExceptionListener")) {
// Handle setExceptionListener method: add to the chain.
ExceptionListener currentExceptionListener = this.target.getExceptionListener();
if (currentExceptionListener instanceof InternalChainedExceptionListener && args[0] != null) {
((InternalChainedExceptionListener) currentExceptionListener).addDelegate((ExceptionListener) args[0]);
return null;
}
else {
throw new javax.jms.IllegalStateException(
"setExceptionListener call not supported on proxy for shared Connection. " +
"Set the 'exceptionListener' property on the SingleConnectionFactory instead. " +
"Alternatively, activate SingleConnectionFactory's 'reconnectOnException' feature, " +
"which will allow for registering further ExceptionListeners to the recovery chain.");
}
}
else if (method.getName().equals("start")) {
// Handle start method: track started state.
synchronized (connectionMonitor) {
if (!started) {
this.target.start();
started = true;
}
}
return null;
}
else if (method.getName().equals("stop")) {
// Handle stop method: don't pass the call on.
return null;
}
else if (method.getName().equals("close")) {
// Handle close method: don't pass the call on.
return null;
}
else if (method.getName().equals("createSession") || method.getName().equals("createQueueSession") ||
method.getName().equals("createTopicSession")) {
boolean transacted = (Boolean) args[0];
Integer ackMode = (Integer) args[1];
Integer mode = (transacted ? Session.SESSION_TRANSACTED : ackMode);
Session session = getSession(this.target, mode);
if (session != null) {
if (!method.getReturnType().isInstance(session)) {
String msg = "JMS Session does not implement specific domain: " + session;
try {
session.close();
}
catch (Throwable ex) {
logger.trace("Failed to close newly obtained JMS Session", ex);
}
throw new javax.jms.IllegalStateException(msg);
}
return session;
}
}
try {
Object retVal = method.invoke(this.target, args);
if (method.getName().equals("getExceptionListener") && retVal instanceof InternalChainedExceptionListener) {
// Handle getExceptionListener method: hide internal chain.
InternalChainedExceptionListener listener = (InternalChainedExceptionListener) retVal;
return listener.getUserListener();
}
else {
return retVal;
}
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
/**
* Internal chained ExceptionListener for handling the internal recovery listener
* in combination with a user-specified listener.
*/
private static class InternalChainedExceptionListener extends ChainedExceptionListener {
private ExceptionListener userListener;
public InternalChainedExceptionListener(ExceptionListener internalListener, ExceptionListener userListener) {
addDelegate(internalListener);
if (userListener != null) {
addDelegate(userListener);
this.userListener = userListener;
}
}
public ExceptionListener getUserListener() {
return this.userListener;
}
}
}

View File

@@ -1,131 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.connection;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.QueueConnectionFactory;
import javax.jms.TopicConnectionFactory;
/**
* A subclass of {@link SingleConnectionFactory} for the JMS 1.0.2 specification,
* not relying on JMS 1.1 methods like SingleConnectionFactory itself.
* This class can be used for JMS 1.0.2 providers, offering the same API as
* SingleConnectionFactory does for JMS 1.1 providers.
*
* <p>You need to set the {@link #setPubSubDomain "pubSubDomain" property},
* since this class will always explicitly differentiate between a
* {@link javax.jms.QueueConnection} and a {@link javax.jms.TopicConnection}.
*
* @author Juergen Hoeller
* @since 1.1
* @see #setTargetConnectionFactory
* @see #setPubSubDomain
* @deprecated as of Spring 3.0, in favor of the JMS 1.1 based {@link SingleConnectionFactory}
*/
@Deprecated
public class SingleConnectionFactory102 extends SingleConnectionFactory {
private boolean pubSubDomain = false;
/**
* Create a new SingleConnectionFactory102 for bean-style usage.
*/
public SingleConnectionFactory102() {
super();
}
/**
* Create a new SingleConnectionFactory102 that always returns a single
* Connection that it will lazily create via the given target
* ConnectionFactory.
* @param connectionFactory the target ConnectionFactory
* @param pubSubDomain whether the Publish/Subscribe domain (Topics) or
* Point-to-Point domain (Queues) should be used
*/
public SingleConnectionFactory102(ConnectionFactory connectionFactory, boolean pubSubDomain) {
setTargetConnectionFactory(connectionFactory);
this.pubSubDomain = pubSubDomain;
afterPropertiesSet();
}
/**
* Configure the factory with knowledge of the JMS domain used.
* This tells the JMS 1.0.2 provider which class hierarchy to use for creating
* Connections and Sessions.
* <p>Default is Point-to-Point (Queues).
* @param pubSubDomain <code>true</code> for Publish/Subscribe domain (Topics),
* <code>false</code> for Point-to-Point domain (Queues)
*/
public void setPubSubDomain(boolean pubSubDomain) {
this.pubSubDomain = pubSubDomain;
}
/**
* Return whether the Publish/Subscribe domain (Topics) is used.
* Otherwise, the Point-to-Point domain (Queues) is used.
*/
public boolean isPubSubDomain() {
return this.pubSubDomain;
}
/**
* In addition to checking whether the target ConnectionFactory is set,
* make sure that the supplied factory is of the appropriate type for
* the specified destination type: QueueConnectionFactory for queues,
* TopicConnectionFactory for topics.
*/
public void afterPropertiesSet() {
super.afterPropertiesSet();
// Make sure that the ConnectionFactory passed is consistent.
// Some provider implementations of the ConnectionFactory interface
// implement both domain interfaces under the cover, so just check if
// the selected domain is consistent with the type of connection factory.
if (isPubSubDomain()) {
if (!(getTargetConnectionFactory() instanceof TopicConnectionFactory)) {
throw new IllegalArgumentException(
"Specified a Spring JMS 1.0.2 SingleConnectionFactory for topics " +
"but did not supply an instance of TopicConnectionFactory");
}
}
else {
if (!(getTargetConnectionFactory() instanceof QueueConnectionFactory)) {
throw new IllegalArgumentException(
"Specified a Spring JMS 1.0.2 SingleConnectionFactory for queues " +
"but did not supply an instance of QueueConnectionFactory");
}
}
}
/**
* This implementation overrides the superclass method to use JMS 1.0.2 API.
*/
protected Connection doCreateConnection() throws JMSException {
if (isPubSubDomain()) {
return ((TopicConnectionFactory) getTargetConnectionFactory()).createTopicConnection();
}
else {
return ((QueueConnectionFactory) getTargetConnectionFactory()).createQueueConnection();
}
}
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms.connection;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
/**
* Extension of the <code>javax.jms.ConnectionFactory</code> interface,
* indicating how to release Connections obtained from it.
*
* @author Juergen Hoeller
* @since 2.0.2
*/
public interface SmartConnectionFactory extends ConnectionFactory {
/**
* Should we stop the Connection, obtained from this ConnectionFactory?
* @param con the Connection to check
* @return whether a stop call is necessary
* @see javax.jms.Connection#stop()
*/
boolean shouldStop(Connection con);
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright 2002-2006 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.springframework.jms.connection;
import javax.jms.JMSException;
import org.springframework.jms.JmsException;
/**
* Exception thrown when a synchronized local transaction failed to complete
* (after the main transaction has already completed).
*
* @author Juergen Hoeller
* @since 2.0
* @see ConnectionFactoryUtils
*/
public class SynchedLocalTransactionFailedException extends JmsException {
/**
* Create a new SynchedLocalTransactionFailedException.
* @param msg the detail message
* @param cause the root cause
*/
public SynchedLocalTransactionFailedException(String msg, JMSException cause) {
super(msg, cause);
}
}

View File

@@ -1,327 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.connection;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;
import javax.jms.TransactionInProgressException;
import org.springframework.util.Assert;
/**
* Proxy for a target JMS {@link javax.jms.ConnectionFactory}, adding awareness of
* Spring-managed transactions. Similar to a transactional JNDI ConnectionFactory
* as provided by a J2EE server.
*
* <p>Messaging code which should remain unaware of Spring's JMS support can work with
* this proxy to seamlessly participate in Spring-managed transactions. Note that the
* transaction manager, for example {@link JmsTransactionManager}, still needs to work
* with the underlying ConnectionFactory, <i>not</i> with this proxy.
*
* <p><b>Make sure that TransactionAwareConnectionFactoryProxy is the outermost
* ConnectionFactory of a chain of ConnectionFactory proxies/adapters.</b>
* TransactionAwareConnectionFactoryProxy can delegate either directly to the
* target factory or to some intermediary adapter like
* {@link UserCredentialsConnectionFactoryAdapter}.
*
* <p>Delegates to {@link ConnectionFactoryUtils} for automatically participating
* in thread-bound transactions, for example managed by {@link JmsTransactionManager}.
* <code>createSession</code> calls and <code>close</code> calls on returned Sessions
* will behave properly within a transaction, that is, always work on the transactional
* Session. If not within a transaction, normal ConnectionFactory behavior applies.
*
* <p>Note that transactional JMS Sessions will be registered on a per-Connection
* basis. To share the same JMS Session across a transaction, make sure that you
* operate on the same JMS Connection handle - either through reusing the handle
* or through configuring a {@link SingleConnectionFactory} underneath.
*
* <p>Returned transactional Session proxies will implement the {@link SessionProxy}
* interface to allow for access to the underlying target Session. This is only
* intended for accessing vendor-specific Session API or for testing purposes
* (e.g. to perform manual transaction control). For typical application purposes,
* simply use the standard JMS Session interface.
*
* @author Juergen Hoeller
* @since 2.0
* @see UserCredentialsConnectionFactoryAdapter
* @see SingleConnectionFactory
*/
public class TransactionAwareConnectionFactoryProxy
implements ConnectionFactory, QueueConnectionFactory, TopicConnectionFactory {
private boolean synchedLocalTransactionAllowed = false;
private ConnectionFactory targetConnectionFactory;
/**
* Create a new TransactionAwareConnectionFactoryProxy.
*/
public TransactionAwareConnectionFactoryProxy() {
}
/**
* Create a new TransactionAwareConnectionFactoryProxy.
* @param targetConnectionFactory the target ConnectionFactory
*/
public TransactionAwareConnectionFactoryProxy(ConnectionFactory targetConnectionFactory) {
setTargetConnectionFactory(targetConnectionFactory);
}
/**
* Set the target ConnectionFactory that this ConnectionFactory should delegate to.
*/
public final void setTargetConnectionFactory(ConnectionFactory targetConnectionFactory) {
Assert.notNull(targetConnectionFactory, "targetConnectionFactory must not be nul");
this.targetConnectionFactory = targetConnectionFactory;
}
/**
* Return the target ConnectionFactory that this ConnectionFactory should delegate to.
*/
protected ConnectionFactory getTargetConnectionFactory() {
return this.targetConnectionFactory;
}
/**
* Set whether to allow for a local JMS transaction that is synchronized with a
* Spring-managed transaction (where the main transaction might be a JDBC-based
* one for a specific DataSource, for example), with the JMS transaction committing
* right after the main transaction. If not allowed, the given ConnectionFactory
* needs to handle transaction enlistment underneath the covers.
* <p>Default is "false": If not within a managed transaction that encompasses
* the underlying JMS ConnectionFactory, standard Sessions will be returned.
* Turn this flag on to allow participation in any Spring-managed transaction,
* with a local JMS transaction synchronized with the main transaction.
*/
public void setSynchedLocalTransactionAllowed(boolean synchedLocalTransactionAllowed) {
this.synchedLocalTransactionAllowed = synchedLocalTransactionAllowed;
}
/**
* Return whether to allow for a local JMS transaction that is synchronized
* with a Spring-managed transaction.
*/
protected boolean isSynchedLocalTransactionAllowed() {
return this.synchedLocalTransactionAllowed;
}
public Connection createConnection() throws JMSException {
Connection targetConnection = this.targetConnectionFactory.createConnection();
return getTransactionAwareConnectionProxy(targetConnection);
}
public Connection createConnection(String username, String password) throws JMSException {
Connection targetConnection = this.targetConnectionFactory.createConnection(username, password);
return getTransactionAwareConnectionProxy(targetConnection);
}
public QueueConnection createQueueConnection() throws JMSException {
if (!(this.targetConnectionFactory instanceof QueueConnectionFactory)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is no QueueConnectionFactory");
}
QueueConnection targetConnection =
((QueueConnectionFactory) this.targetConnectionFactory).createQueueConnection();
return (QueueConnection) getTransactionAwareConnectionProxy(targetConnection);
}
public QueueConnection createQueueConnection(String username, String password) throws JMSException {
if (!(this.targetConnectionFactory instanceof QueueConnectionFactory)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is no QueueConnectionFactory");
}
QueueConnection targetConnection =
((QueueConnectionFactory) this.targetConnectionFactory).createQueueConnection(username, password);
return (QueueConnection) getTransactionAwareConnectionProxy(targetConnection);
}
public TopicConnection createTopicConnection() throws JMSException {
if (!(this.targetConnectionFactory instanceof TopicConnectionFactory)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is no TopicConnectionFactory");
}
TopicConnection targetConnection =
((TopicConnectionFactory) this.targetConnectionFactory).createTopicConnection();
return (TopicConnection) getTransactionAwareConnectionProxy(targetConnection);
}
public TopicConnection createTopicConnection(String username, String password) throws JMSException {
if (!(this.targetConnectionFactory instanceof TopicConnectionFactory)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is no TopicConnectionFactory");
}
TopicConnection targetConnection =
((TopicConnectionFactory) this.targetConnectionFactory).createTopicConnection(username, password);
return (TopicConnection) getTransactionAwareConnectionProxy(targetConnection);
}
/**
* Wrap the given Connection with a proxy that delegates every method call to it
* but handles Session lookup in a transaction-aware fashion.
* @param target the original Connection to wrap
* @return the wrapped Connection
*/
protected Connection getTransactionAwareConnectionProxy(Connection target) {
List<Class> classes = new ArrayList<Class>(3);
classes.add(Connection.class);
if (target instanceof QueueConnection) {
classes.add(QueueConnection.class);
}
if (target instanceof TopicConnection) {
classes.add(TopicConnection.class);
}
return (Connection) Proxy.newProxyInstance(
Connection.class.getClassLoader(),
classes.toArray(new Class[classes.size()]),
new TransactionAwareConnectionInvocationHandler(target));
}
/**
* Invocation handler that exposes transactional Sessions for the underlying Connection.
*/
private class TransactionAwareConnectionInvocationHandler implements InvocationHandler {
private final Connection target;
public TransactionAwareConnectionInvocationHandler(Connection target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Invocation on ConnectionProxy interface coming in...
if (method.getName().equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (method.getName().equals("hashCode")) {
// Use hashCode of Connection proxy.
return System.identityHashCode(proxy);
}
else if (Session.class.equals(method.getReturnType())) {
Session session = ConnectionFactoryUtils.getTransactionalSession(
getTargetConnectionFactory(), this.target, isSynchedLocalTransactionAllowed());
if (session != null) {
return getCloseSuppressingSessionProxy(session);
}
}
else if (QueueSession.class.equals(method.getReturnType())) {
QueueSession session = ConnectionFactoryUtils.getTransactionalQueueSession(
(QueueConnectionFactory) getTargetConnectionFactory(), (QueueConnection) this.target,
isSynchedLocalTransactionAllowed());
if (session != null) {
return getCloseSuppressingSessionProxy(session);
}
}
else if (TopicSession.class.equals(method.getReturnType())) {
TopicSession session = ConnectionFactoryUtils.getTransactionalTopicSession(
(TopicConnectionFactory) getTargetConnectionFactory(), (TopicConnection) this.target,
isSynchedLocalTransactionAllowed());
if (session != null) {
return getCloseSuppressingSessionProxy(session);
}
}
// Invoke method on target Connection.
try {
return method.invoke(this.target, args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
private Session getCloseSuppressingSessionProxy(Session target) {
List<Class> classes = new ArrayList<Class>(3);
classes.add(SessionProxy.class);
if (target instanceof QueueSession) {
classes.add(QueueSession.class);
}
if (target instanceof TopicSession) {
classes.add(TopicSession.class);
}
return (Session) Proxy.newProxyInstance(
SessionProxy.class.getClassLoader(),
classes.toArray(new Class[classes.size()]),
new CloseSuppressingSessionInvocationHandler(target));
}
}
/**
* Invocation handler that suppresses close calls for a transactional JMS Session.
*/
private static class CloseSuppressingSessionInvocationHandler implements InvocationHandler {
private final Session target;
public CloseSuppressingSessionInvocationHandler(Session target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Invocation on SessionProxy interface coming in...
if (method.getName().equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (method.getName().equals("hashCode")) {
// Use hashCode of Connection proxy.
return System.identityHashCode(proxy);
}
else if (method.getName().equals("commit")) {
throw new TransactionInProgressException("Commit call not allowed within a managed transaction");
}
else if (method.getName().equals("rollback")) {
throw new TransactionInProgressException("Rollback call not allowed within a managed transaction");
}
else if (method.getName().equals("close")) {
// Handle close method: not to be closed within a transaction.
return null;
}
else if (method.getName().equals("getTargetSession")) {
// Handle getTargetSession method: return underlying Session.
return this.target;
}
// Invoke method on target Session.
try {
return method.invoke(this.target, args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
}

View File

@@ -1,300 +0,0 @@
/*
* Copyright 2002-2010 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.springframework.jms.connection;
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 org.springframework.beans.factory.InitializingBean;
import org.springframework.core.NamedThreadLocal;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* An adapter for a target JMS {@link javax.jms.ConnectionFactory}, applying the
* given user credentials to every standard <code>createConnection()</code> call,
* that is, implicitly invoking <code>createConnection(username, password)</code>
* on the target. All other methods simply delegate to the corresponding methods
* of the target ConnectionFactory.
*
* <p>Can be used to proxy a target JNDI ConnectionFactory that does not have user
* credentials configured. Client code can work with the ConnectionFactory without
* passing in username and password on every <code>createConnection()</code> call.
*
* <p>In the following example, client code can simply transparently work
* with the preconfigured "myConnectionFactory", implicitly accessing
* "myTargetConnectionFactory" with the specified user credentials.
*
* <pre class="code">
* &lt;bean id="myTargetConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"&gt;
* &lt;property name="jndiName" value="java:comp/env/jms/mycf"/&gt;
* &lt;/bean&gt;
*
* &lt;bean id="myConnectionFactory" class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter"&gt;
* &lt;property name="targetConnectionFactory" ref="myTargetConnectionFactory"/&gt;
* &lt;property name="username" value="myusername"/&gt;
* &lt;property name="password" value="mypassword"/&gt;
* &lt;/bean></pre>
*
* <p>If the "username" is empty, this proxy will simply delegate to the standard
* <code>createConnection()</code> method of the target ConnectionFactory.
* This can be used to keep a UserCredentialsConnectionFactoryAdapter bean
* definition just for the <i>option</i> of implicitly passing in user credentials
* if the particular target ConnectionFactory requires it.
*
* @author Juergen Hoeller
* @since 1.2
* @see #createConnection
* @see #createQueueConnection
* @see #createTopicConnection
*/
public class UserCredentialsConnectionFactoryAdapter
implements ConnectionFactory, QueueConnectionFactory, TopicConnectionFactory, InitializingBean {
private ConnectionFactory targetConnectionFactory;
private String username;
private String password;
private final ThreadLocal<JmsUserCredentials> threadBoundCredentials =
new NamedThreadLocal<JmsUserCredentials>("Current JMS user credentials");
/**
* Set the target ConnectionFactory that this ConnectionFactory should delegate to.
*/
public void setTargetConnectionFactory(ConnectionFactory targetConnectionFactory) {
Assert.notNull(targetConnectionFactory, "'targetConnectionFactory' must not be null");
this.targetConnectionFactory = targetConnectionFactory;
}
/**
* Set the username that this adapter should use for retrieving Connections.
* Default is no specific user.
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Set the password that this adapter should use for retrieving Connections.
* Default is no specific password.
*/
public void setPassword(String password) {
this.password = password;
}
public void afterPropertiesSet() {
if (this.targetConnectionFactory == null) {
throw new IllegalArgumentException("Property 'targetConnectionFactory' is required");
}
}
/**
* Set user credententials for this proxy and the current thread.
* The given username and password will be applied to all subsequent
* <code>createConnection()</code> calls on this ConnectionFactory proxy.
* <p>This will override any statically specified user credentials,
* that is, values of the "username" and "password" bean properties.
* @param username the username to apply
* @param password the password to apply
* @see #removeCredentialsFromCurrentThread
*/
public void setCredentialsForCurrentThread(String username, String password) {
this.threadBoundCredentials.set(new JmsUserCredentials(username, password));
}
/**
* Remove any user credentials for this proxy from the current thread.
* Statically specified user credentials apply again afterwards.
* @see #setCredentialsForCurrentThread
*/
public void removeCredentialsFromCurrentThread() {
this.threadBoundCredentials.remove();
}
/**
* Determine whether there are currently thread-bound credentials,
* using them if available, falling back to the statically specified
* username and password (i.e. values of the bean properties) else.
* @see #doCreateConnection
*/
public final Connection createConnection() throws JMSException {
JmsUserCredentials threadCredentials = this.threadBoundCredentials.get();
if (threadCredentials != null) {
return doCreateConnection(threadCredentials.username, threadCredentials.password);
}
else {
return doCreateConnection(this.username, this.password);
}
}
/**
* Delegate the call straight to the target ConnectionFactory.
*/
public Connection createConnection(String username, String password) throws JMSException {
return doCreateConnection(username, password);
}
/**
* This implementation delegates to the <code>createConnection(username, password)</code>
* method of the target ConnectionFactory, passing in the specified user credentials.
* If the specified username is empty, it will simply delegate to the standard
* <code>createConnection()</code> method of the target ConnectionFactory.
* @param username the username to use
* @param password the password to use
* @return the Connection
* @see javax.jms.ConnectionFactory#createConnection(String, String)
* @see javax.jms.ConnectionFactory#createConnection()
*/
protected Connection doCreateConnection(String username, String password) throws JMSException {
Assert.state(this.targetConnectionFactory != null, "'targetConnectionFactory' is required");
if (StringUtils.hasLength(username)) {
return this.targetConnectionFactory.createConnection(username, password);
}
else {
return this.targetConnectionFactory.createConnection();
}
}
/**
* Determine whether there are currently thread-bound credentials,
* using them if available, falling back to the statically specified
* username and password (i.e. values of the bean properties) else.
* @see #doCreateQueueConnection
*/
public final QueueConnection createQueueConnection() throws JMSException {
JmsUserCredentials threadCredentials = this.threadBoundCredentials.get();
if (threadCredentials != null) {
return doCreateQueueConnection(threadCredentials.username, threadCredentials.password);
}
else {
return doCreateQueueConnection(this.username, this.password);
}
}
/**
* Delegate the call straight to the target QueueConnectionFactory.
*/
public QueueConnection createQueueConnection(String username, String password) throws JMSException {
return doCreateQueueConnection(username, password);
}
/**
* This implementation delegates to the <code>createQueueConnection(username, password)</code>
* method of the target QueueConnectionFactory, passing in the specified user credentials.
* If the specified username is empty, it will simply delegate to the standard
* <code>createQueueConnection()</code> method of the target ConnectionFactory.
* @param username the username to use
* @param password the password to use
* @return the Connection
* @see javax.jms.QueueConnectionFactory#createQueueConnection(String, String)
* @see javax.jms.QueueConnectionFactory#createQueueConnection()
*/
protected QueueConnection doCreateQueueConnection(String username, String password) throws JMSException {
Assert.state(this.targetConnectionFactory != null, "'targetConnectionFactory' is required");
if (!(this.targetConnectionFactory instanceof QueueConnectionFactory)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a QueueConnectionFactory");
}
QueueConnectionFactory queueFactory = (QueueConnectionFactory) this.targetConnectionFactory;
if (StringUtils.hasLength(username)) {
return queueFactory.createQueueConnection(username, password);
}
else {
return queueFactory.createQueueConnection();
}
}
/**
* Determine whether there are currently thread-bound credentials,
* using them if available, falling back to the statically specified
* username and password (i.e. values of the bean properties) else.
* @see #doCreateTopicConnection
*/
public final TopicConnection createTopicConnection() throws JMSException {
JmsUserCredentials threadCredentials = this.threadBoundCredentials.get();
if (threadCredentials != null) {
return doCreateTopicConnection(threadCredentials.username, threadCredentials.password);
}
else {
return doCreateTopicConnection(this.username, this.password);
}
}
/**
* Delegate the call straight to the target TopicConnectionFactory.
*/
public TopicConnection createTopicConnection(String username, String password) throws JMSException {
return doCreateTopicConnection(username, password);
}
/**
* This implementation delegates to the <code>createTopicConnection(username, password)</code>
* method of the target TopicConnectionFactory, passing in the specified user credentials.
* If the specified username is empty, it will simply delegate to the standard
* <code>createTopicConnection()</code> method of the target ConnectionFactory.
* @param username the username to use
* @param password the password to use
* @return the Connection
* @see javax.jms.TopicConnectionFactory#createTopicConnection(String, String)
* @see javax.jms.TopicConnectionFactory#createTopicConnection()
*/
protected TopicConnection doCreateTopicConnection(String username, String password) throws JMSException {
Assert.state(this.targetConnectionFactory != null, "'targetConnectionFactory' is required");
if (!(this.targetConnectionFactory instanceof TopicConnectionFactory)) {
throw new javax.jms.IllegalStateException("'targetConnectionFactory' is not a TopicConnectionFactory");
}
TopicConnectionFactory queueFactory = (TopicConnectionFactory) this.targetConnectionFactory;
if (StringUtils.hasLength(username)) {
return queueFactory.createTopicConnection(username, password);
}
else {
return queueFactory.createTopicConnection();
}
}
/**
* Inner class used as ThreadLocal value.
*/
private static class JmsUserCredentials {
public final String username;
public final String password;
private JmsUserCredentials(String username, String password) {
this.username = username;
this.password = password;
}
public String toString() {
return "JmsUserCredentials[username='" + this.username + "',password='" + this.password + "']";
}
}
}

View File

@@ -1,9 +0,0 @@
/**
*
* Provides a PlatformTransactionManager implementation for a single
* JMS ConnectionFactory, and a SingleConnectionFactory adapter.
*
*/
package org.springframework.jms.connection;

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2002-2008 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.springframework.jms.core;
import javax.jms.JMSException;
import javax.jms.QueueBrowser;
import javax.jms.Session;
/**
* Callback for browsing the messages in a JMS queue.
*
* <p>To be used with JmsTemplate's callback methods that take a BrowserCallback
* argument, often implemented as an anonymous inner class.
*
* @author Juergen Hoeller
* @since 2.5.1
* @see JmsTemplate#browse(BrowserCallback)
* @see JmsTemplate#browseSelected(String, BrowserCallback)
*/
public interface BrowserCallback<T> {
/**
* Perform operations on the given {@link javax.jms.Session} and {@link javax.jms.QueueBrowser}.
* <p>The message producer is not associated with any destination.
* @param session the JMS <code>Session</code> object to use
* @param browser the JMS <code>QueueBrowser</code> object to use
* @return a result object from working with the <code>Session</code>, if any (can be <code>null</code>)
* @throws javax.jms.JMSException if thrown by JMS API methods
*/
T doInJms(Session session, QueueBrowser browser) throws JMSException;
}

View File

@@ -1,425 +0,0 @@
/*
* Copyright 2002-2008 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.springframework.jms.core;
import javax.jms.Destination;
import javax.jms.Message;
import javax.jms.Queue;
import org.springframework.jms.JmsException;
/**
* Specifies a basic set of JMS operations.
*
* <p>Implemented by {@link JmsTemplate}. Not often used but a useful option
* to enhance testability, as it can easily be mocked or stubbed.
*
* <p>Provides <code>JmsTemplate's</code> <code>send(..)</code> and
* <code>receive(..)</code> methods that mirror various JMS API methods.
* See the JMS specification and javadocs for details on those methods.
*
* @author Mark Pollack
* @author Juergen Hoeller
* @since 1.1
* @see JmsTemplate
* @see javax.jms.Destination
* @see javax.jms.Session
* @see javax.jms.MessageProducer
* @see javax.jms.MessageConsumer
*/
public interface JmsOperations {
/**
* Execute the action specified by the given action object within a JMS Session.
* <p>When used with a 1.0.2 provider, you may need to downcast
* to the appropriate domain implementation, either QueueSession or
* TopicSession in the action objects doInJms callback method.
* @param action callback object that exposes the session
* @return the result object from working with the session
* @throws JmsException if there is any problem
*/
<T> T execute(SessionCallback<T> action) throws JmsException;
/**
* Send messages to the default JMS destination (or one specified
* for each send operation). The callback gives access to the JMS Session
* and MessageProducer in order to perform complex send operations.
* @param action callback object that exposes the session/producer pair
* @return the result object from working with the session
* @throws JmsException checked JMSException converted to unchecked
*/
<T> T execute(ProducerCallback<T> action) throws JmsException;
/**
* Send messages to a JMS destination. The callback gives access to the JMS Session
* and MessageProducer in order to perform complex send operations.
* @param destination the destination to send messages to
* @param action callback object that exposes the session/producer pair
* @return the result object from working with the session
* @throws JmsException checked JMSException converted to unchecked
*/
<T> T execute(Destination destination, ProducerCallback<T> action) throws JmsException;
/**
* Send messages to a JMS destination. The callback gives access to the JMS Session
* and MessageProducer in order to perform complex send operations.
* @param destinationName the name of the destination to send messages to
* (to be resolved to an actual destination by a DestinationResolver)
* @param action callback object that exposes the session/producer pair
* @return the result object from working with the session
* @throws JmsException checked JMSException converted to unchecked
*/
<T> T execute(String destinationName, ProducerCallback<T> action) throws JmsException;
//-------------------------------------------------------------------------
// Convenience methods for sending messages
//-------------------------------------------------------------------------
/**
* Send a message to the default destination.
* <p>This will only work with a default destination specified!
* @param messageCreator callback to create a message
* @throws JmsException checked JMSException converted to unchecked
*/
void send(MessageCreator messageCreator) throws JmsException;
/**
* Send a message to the specified destination.
* The MessageCreator callback creates the message given a Session.
* @param destination the destination to send this message to
* @param messageCreator callback to create a message
* @throws JmsException checked JMSException converted to unchecked
*/
void send(Destination destination, MessageCreator messageCreator) throws JmsException;
/**
* Send a message to the specified destination.
* The MessageCreator callback creates the message given a Session.
* @param destinationName the name of the destination to send this message to
* (to be resolved to an actual destination by a DestinationResolver)
* @param messageCreator callback to create a message
* @throws JmsException checked JMSException converted to unchecked
*/
void send(String destinationName, MessageCreator messageCreator) throws JmsException;
//-------------------------------------------------------------------------
// Convenience methods for sending auto-converted messages
//-------------------------------------------------------------------------
/**
* Send the given object to the default destination, converting the object
* to a JMS message with a configured MessageConverter.
* <p>This will only work with a default destination specified!
* @param message the object to convert to a message
* @throws JmsException converted checked JMSException to unchecked
*/
void convertAndSend(Object message) throws JmsException;
/**
* Send the given object to the specified destination, converting the object
* to a JMS message with a configured MessageConverter.
* @param destination the destination to send this message to
* @param message the object to convert to a message
* @throws JmsException converted checked JMSException to unchecked
*/
void convertAndSend(Destination destination, Object message) throws JmsException;
/**
* Send the given object to the specified destination, converting the object
* to a JMS message with a configured MessageConverter.
* @param destinationName the name of the destination to send this message to
* (to be resolved to an actual destination by a DestinationResolver)
* @param message the object to convert to a message
* @throws JmsException checked JMSException converted to unchecked
*/
void convertAndSend(String destinationName, Object message) throws JmsException;
/**
* Send the given object to the default destination, converting the object
* to a JMS message with a configured MessageConverter. The MessagePostProcessor
* callback allows for modification of the message after conversion.
* <p>This will only work with a default destination specified!
* @param message the object to convert to a message
* @param postProcessor the callback to modify the message
* @throws JmsException checked JMSException converted to unchecked
*/
void convertAndSend(Object message, MessagePostProcessor postProcessor)
throws JmsException;
/**
* Send the given object to the specified destination, converting the object
* to a JMS message with a configured MessageConverter. The MessagePostProcessor
* callback allows for modification of the message after conversion.
* @param destination the destination to send this message to
* @param message the object to convert to a message
* @param postProcessor the callback to modify the message
* @throws JmsException checked JMSException converted to unchecked
*/
void convertAndSend(Destination destination, Object message, MessagePostProcessor postProcessor)
throws JmsException;
/**
* Send the given object to the specified destination, converting the object
* to a JMS message with a configured MessageConverter. The MessagePostProcessor
* callback allows for modification of the message after conversion.
* @param destinationName the name of the destination to send this message to
* (to be resolved to an actual destination by a DestinationResolver)
* @param message the object to convert to a message.
* @param postProcessor the callback to modify the message
* @throws JmsException checked JMSException converted to unchecked
*/
void convertAndSend(String destinationName, Object message, MessagePostProcessor postProcessor)
throws JmsException;
//-------------------------------------------------------------------------
// Convenience methods for receiving messages
//-------------------------------------------------------------------------
/**
* Receive a message synchronously from the default destination, but only
* wait up to a specified time for delivery.
* <p>This method should be used carefully, since it will block the thread
* until the message becomes available or until the timeout value is exceeded.
* <p>This will only work with a default destination specified!
* @return the message received by the consumer, or <code>null</code> if the timeout expires
* @throws JmsException checked JMSException converted to unchecked
*/
Message receive() throws JmsException;
/**
* Receive a message synchronously from the specified destination, but only
* wait up to a specified time for delivery.
* <p>This method should be used carefully, since it will block the thread
* until the message becomes available or until the timeout value is exceeded.
* @param destination the destination to receive a message from
* @return the message received by the consumer, or <code>null</code> if the timeout expires
* @throws JmsException checked JMSException converted to unchecked
*/
Message receive(Destination destination) throws JmsException;
/**
* Receive a message synchronously from the specified destination, but only
* wait up to a specified time for delivery.
* <p>This method should be used carefully, since it will block the thread
* until the message becomes available or until the timeout value is exceeded.
* @param destinationName the name of the destination to send this message to
* (to be resolved to an actual destination by a DestinationResolver)
* @return the message received by the consumer, or <code>null</code> if the timeout expires
* @throws JmsException checked JMSException converted to unchecked
*/
Message receive(String destinationName) throws JmsException;
/**
* Receive a message synchronously from the default destination, but only
* wait up to a specified time for delivery.
* <p>This method should be used carefully, since it will block the thread
* until the message becomes available or until the timeout value is exceeded.
* <p>This will only work with a default destination specified!
* @param messageSelector the JMS message selector expression (or <code>null</code> if none).
* See the JMS specification for a detailed definition of selector expressions.
* @return the message received by the consumer, or <code>null</code> if the timeout expires
* @throws JmsException checked JMSException converted to unchecked
*/
Message receiveSelected(String messageSelector) throws JmsException;
/**
* Receive a message synchronously from the specified destination, but only
* wait up to a specified time for delivery.
* <p>This method should be used carefully, since it will block the thread
* until the message becomes available or until the timeout value is exceeded.
* @param destination the destination to receive a message from
* @param messageSelector the JMS message selector expression (or <code>null</code> if none).
* See the JMS specification for a detailed definition of selector expressions.
* @return the message received by the consumer, or <code>null</code> if the timeout expires
* @throws JmsException checked JMSException converted to unchecked
*/
Message receiveSelected(Destination destination, String messageSelector) throws JmsException;
/**
* Receive a message synchronously from the specified destination, but only
* wait up to a specified time for delivery.
* <p>This method should be used carefully, since it will block the thread
* until the message becomes available or until the timeout value is exceeded.
* @param destinationName the name of the destination to send this message to
* (to be resolved to an actual destination by a DestinationResolver)
* @param messageSelector the JMS message selector expression (or <code>null</code> if none).
* See the JMS specification for a detailed definition of selector expressions.
* @return the message received by the consumer, or <code>null</code> if the timeout expires
* @throws JmsException checked JMSException converted to unchecked
*/
Message receiveSelected(String destinationName, String messageSelector) throws JmsException;
//-------------------------------------------------------------------------
// Convenience methods for receiving auto-converted messages
//-------------------------------------------------------------------------
/**
* Receive a message synchronously from the default destination, but only
* wait up to a specified time for delivery. Convert the message into an
* object with a configured MessageConverter.
* <p>This method should be used carefully, since it will block the thread
* until the message becomes available or until the timeout value is exceeded.
* <p>This will only work with a default destination specified!
* @return the message produced for the consumer or <code>null</code> if the timeout expires.
* @throws JmsException checked JMSException converted to unchecked
*/
Object receiveAndConvert() throws JmsException;
/**
* Receive a message synchronously from the specified destination, but only
* wait up to a specified time for delivery. Convert the message into an
* object with a configured MessageConverter.
* <p>This method should be used carefully, since it will block the thread
* until the message becomes available or until the timeout value is exceeded.
* @param destination the destination to receive a message from
* @return the message produced for the consumer or <code>null</code> if the timeout expires.
* @throws JmsException checked JMSException converted to unchecked
*/
Object receiveAndConvert(Destination destination) throws JmsException;
/**
* Receive a message synchronously from the specified destination, but only
* wait up to a specified time for delivery. Convert the message into an
* object with a configured MessageConverter.
* <p>This method should be used carefully, since it will block the thread
* until the message becomes available or until the timeout value is exceeded.
* @param destinationName the name of the destination to send this message to
* (to be resolved to an actual destination by a DestinationResolver)
* @return the message produced for the consumer or <code>null</code> if the timeout expires.
* @throws JmsException checked JMSException converted to unchecked
*/
Object receiveAndConvert(String destinationName) throws JmsException;
/**
* Receive a message synchronously from the default destination, but only
* wait up to a specified time for delivery. Convert the message into an
* object with a configured MessageConverter.
* <p>This method should be used carefully, since it will block the thread
* until the message becomes available or until the timeout value is exceeded.
* <p>This will only work with a default destination specified!
* @param messageSelector the JMS message selector expression (or <code>null</code> if none).
* See the JMS specification for a detailed definition of selector expressions.
* @return the message produced for the consumer or <code>null</code> if the timeout expires.
* @throws JmsException checked JMSException converted to unchecked
*/
Object receiveSelectedAndConvert(String messageSelector) throws JmsException;
/**
* Receive a message synchronously from the specified destination, but only
* wait up to a specified time for delivery. Convert the message into an
* object with a configured MessageConverter.
* <p>This method should be used carefully, since it will block the thread
* until the message becomes available or until the timeout value is exceeded.
* @param destination the destination to receive a message from
* @param messageSelector the JMS message selector expression (or <code>null</code> if none).
* See the JMS specification for a detailed definition of selector expressions.
* @return the message produced for the consumer or <code>null</code> if the timeout expires.
* @throws JmsException checked JMSException converted to unchecked
*/
Object receiveSelectedAndConvert(Destination destination, String messageSelector) throws JmsException;
/**
* Receive a message synchronously from the specified destination, but only
* wait up to a specified time for delivery. Convert the message into an
* object with a configured MessageConverter.
* <p>This method should be used carefully, since it will block the thread
* until the message becomes available or until the timeout value is exceeded.
* @param destinationName the name of the destination to send this message to
* (to be resolved to an actual destination by a DestinationResolver)
* @param messageSelector the JMS message selector expression (or <code>null</code> if none).
* See the JMS specification for a detailed definition of selector expressions.
* @return the message produced for the consumer or <code>null</code> if the timeout expires.
* @throws JmsException checked JMSException converted to unchecked
*/
Object receiveSelectedAndConvert(String destinationName, String messageSelector) throws JmsException;
//-------------------------------------------------------------------------
// Convenience methods for browsing messages
//-------------------------------------------------------------------------
/**
* Browse messages in the default JMS queue. The callback gives access to the JMS
* Session and QueueBrowser in order to browse the queue and react to the contents.
* @param action callback object that exposes the session/browser pair
* @return the result object from working with the session
* @throws JmsException checked JMSException converted to unchecked
*/
<T> T browse(BrowserCallback<T> action) throws JmsException;
/**
* Browse messages in a JMS queue. The callback gives access to the JMS Session
* and QueueBrowser in order to browse the queue and react to the contents.
* @param queue the queue to browse
* @param action callback object that exposes the session/browser pair
* @return the result object from working with the session
* @throws JmsException checked JMSException converted to unchecked
*/
<T> T browse(Queue queue, BrowserCallback<T> action) throws JmsException;
/**
* Browse messages in a JMS queue. The callback gives access to the JMS Session
* and QueueBrowser in order to browse the queue and react to the contents.
* @param queueName the name of the queue to browse
* (to be resolved to an actual destination by a DestinationResolver)
* @param action callback object that exposes the session/browser pair
* @return the result object from working with the session
* @throws JmsException checked JMSException converted to unchecked
*/
<T> T browse(String queueName, BrowserCallback<T> action) throws JmsException;
/**
* Browse selected messages in a JMS queue. The callback gives access to the JMS
* Session and QueueBrowser in order to browse the queue and react to the contents.
* @param messageSelector the JMS message selector expression (or <code>null</code> if none).
* See the JMS specification for a detailed definition of selector expressions.
* @param action callback object that exposes the session/browser pair
* @return the result object from working with the session
* @throws JmsException checked JMSException converted to unchecked
*/
<T> T browseSelected(String messageSelector, BrowserCallback<T> action) throws JmsException;
/**
* Browse selected messages in a JMS queue. The callback gives access to the JMS
* Session and QueueBrowser in order to browse the queue and react to the contents.
* @param queue the queue to browse
* @param messageSelector the JMS message selector expression (or <code>null</code> if none).
* See the JMS specification for a detailed definition of selector expressions.
* @param action callback object that exposes the session/browser pair
* @return the result object from working with the session
* @throws JmsException checked JMSException converted to unchecked
*/
<T> T browseSelected(Queue queue, String messageSelector, BrowserCallback<T> action) throws JmsException;
/**
* Browse selected messages in a JMS queue. The callback gives access to the JMS
* Session and QueueBrowser in order to browse the queue and react to the contents.
* @param queueName the name of the queue to browse
* (to be resolved to an actual destination by a DestinationResolver)
* @param messageSelector the JMS message selector expression (or <code>null</code> if none).
* See the JMS specification for a detailed definition of selector expressions.
* @param action callback object that exposes the session/browser pair
* @return the result object from working with the session
* @throws JmsException checked JMSException converted to unchecked
*/
<T> T browseSelected(String queueName, String messageSelector, BrowserCallback<T> action) throws JmsException;
}

View File

@@ -1,257 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.core;
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.Queue;
import javax.jms.QueueBrowser;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import org.springframework.jms.connection.JmsResourceHolder;
import org.springframework.jms.support.converter.SimpleMessageConverter102;
/**
* A subclass of {@link JmsTemplate} for the JMS 1.0.2 specification, not relying
* on JMS 1.1 methods like JmsTemplate itself. This class can be used for JMS
* 1.0.2 providers, offering the same API as JmsTemplate does for JMS 1.1 providers.
*
* <p>You must specify the domain (or style) of messaging to be either
* Point-to-Point (Queues) or Publish/Subscribe (Topics), using the
* {@link #setPubSubDomain "pubSubDomain" property}.
* Point-to-Point (Queues) is the default domain.
*
* <p>The "pubSubDomain" property is an important setting due to the use of similar
* but separate class hierarchies in the JMS 1.0.2 API. JMS 1.1 provides a new
* domain-independent API that allows for easy mix-and-match use of Point-to-Point
* and Publish/Subscribe styles.
*
* <p>This template uses a
* {@link org.springframework.jms.support.destination.DynamicDestinationResolver}
* and a {@link org.springframework.jms.support.converter.SimpleMessageConverter102}
* as default strategies for resolving a destination name and converting a message,
* respectively.
*
* @author Mark Pollack
* @author Juergen Hoeller
* @since 1.1
* @see #setConnectionFactory
* @see #setPubSubDomain
* @see javax.jms.Queue
* @see javax.jms.Topic
* @see javax.jms.QueueSession
* @see javax.jms.TopicSession
* @see javax.jms.QueueSender
* @see javax.jms.TopicPublisher
* @see javax.jms.QueueReceiver
* @see javax.jms.TopicSubscriber
* @deprecated as of Spring 3.0, in favor of the JMS 1.1 based {@link JmsTemplate}
*/
@Deprecated
public class JmsTemplate102 extends JmsTemplate {
/**
* Create a new JmsTemplate102 for bean-style usage.
* <p>Note: The ConnectionFactory has to be set before using the instance.
* This constructor can be used to prepare a JmsTemplate via a BeanFactory,
* typically setting the ConnectionFactory via setConnectionFactory.
* @see #setConnectionFactory
*/
public JmsTemplate102() {
super();
}
/**
* Create a new JmsTemplate102, given a ConnectionFactory.
* @param connectionFactory the ConnectionFactory to obtain Connections from
* @param pubSubDomain whether the Publish/Subscribe domain (Topics) or
* Point-to-Point domain (Queues) should be used
* @see #setPubSubDomain
*/
public JmsTemplate102(ConnectionFactory connectionFactory, boolean pubSubDomain) {
this();
setConnectionFactory(connectionFactory);
setPubSubDomain(pubSubDomain);
afterPropertiesSet();
}
/**
* Initialize the default implementations for the template's strategies:
* DynamicDestinationResolver and SimpleMessageConverter102.
* @see #setDestinationResolver
* @see #setMessageConverter
* @see org.springframework.jms.support.destination.DynamicDestinationResolver
* @see org.springframework.jms.support.converter.SimpleMessageConverter102
*/
protected void initDefaultStrategies() {
setMessageConverter(new SimpleMessageConverter102());
}
/**
* In addition to checking if the connection factory is set, make sure
* that the supplied connection factory is of the appropriate type for
* the specified destination type: QueueConnectionFactory for queues,
* and TopicConnectionFactory for topics.
*/
public void afterPropertiesSet() {
super.afterPropertiesSet();
// Make sure that the ConnectionFactory passed is consistent.
// Some provider implementations of the ConnectionFactory interface
// implement both domain interfaces under the cover, so just check if
// the selected domain is consistent with the type of connection factory.
if (isPubSubDomain()) {
if (!(getConnectionFactory() instanceof TopicConnectionFactory)) {
throw new IllegalArgumentException(
"Specified a Spring JMS 1.0.2 template for topics " +
"but did not supply an instance of TopicConnectionFactory");
}
}
else {
if (!(getConnectionFactory() instanceof QueueConnectionFactory)) {
throw new IllegalArgumentException(
"Specified a Spring JMS 1.0.2 template for queues " +
"but did not supply an instance of QueueConnectionFactory");
}
}
}
/**
* This implementation overrides the superclass method to accept either
* a QueueConnection or a TopicConnection, depending on the domain.
*/
protected Connection getConnection(JmsResourceHolder holder) {
return holder.getConnection(isPubSubDomain() ? (Class) TopicConnection.class : QueueConnection.class);
}
/**
* This implementation overrides the superclass method to accept either
* a QueueSession or a TopicSession, depending on the domain.
*/
protected Session getSession(JmsResourceHolder holder) {
return holder.getSession(isPubSubDomain() ? (Class) TopicSession.class : QueueSession.class);
}
/**
* This implementation overrides the superclass method to use JMS 1.0.2 API.
*/
protected Connection createConnection() throws JMSException {
if (isPubSubDomain()) {
return ((TopicConnectionFactory) getConnectionFactory()).createTopicConnection();
}
else {
return ((QueueConnectionFactory) getConnectionFactory()).createQueueConnection();
}
}
/**
* This implementation overrides the superclass method to use JMS 1.0.2 API.
*/
protected Session createSession(Connection con) throws JMSException {
if (isPubSubDomain()) {
return ((TopicConnection) con).createTopicSession(isSessionTransacted(), getSessionAcknowledgeMode());
}
else {
return ((QueueConnection) con).createQueueSession(isSessionTransacted(), getSessionAcknowledgeMode());
}
}
/**
* This implementation overrides the superclass method to use JMS 1.0.2 API.
*/
protected MessageProducer doCreateProducer(Session session, Destination destination) throws JMSException {
if (isPubSubDomain()) {
return ((TopicSession) session).createPublisher((Topic) destination);
}
else {
return ((QueueSession) session).createSender((Queue) destination);
}
}
/**
* This implementation overrides the superclass method to use JMS 1.0.2 API.
*/
protected MessageConsumer createConsumer(Session session, Destination destination, String messageSelector)
throws JMSException {
if (isPubSubDomain()) {
return ((TopicSession) session).createSubscriber((Topic) destination, messageSelector, isPubSubNoLocal());
}
else {
return ((QueueSession) session).createReceiver((Queue) destination, messageSelector);
}
}
protected QueueBrowser createBrowser(Session session, Queue queue, String messageSelector)
throws JMSException {
if (isPubSubDomain()) {
throw new javax.jms.IllegalStateException("Cannot create QueueBrowser for a TopicSession");
}
else {
return ((QueueSession) session).createBrowser(queue, messageSelector);
}
}
/**
* This implementation overrides the superclass method to use JMS 1.0.2 API.
*/
protected void doSend(MessageProducer producer, Message message) throws JMSException {
if (isPubSubDomain()) {
if (isExplicitQosEnabled()) {
((TopicPublisher) producer).publish(message, getDeliveryMode(), getPriority(), getTimeToLive());
}
else {
((TopicPublisher) producer).publish(message);
}
}
else {
if (isExplicitQosEnabled()) {
((QueueSender) producer).send(message, getDeliveryMode(), getPriority(), getTimeToLive());
}
else {
((QueueSender) producer).send(message);
}
}
}
/**
* This implementation overrides the superclass method to avoid using
* JMS 1.1's Session <code>getAcknowledgeMode()</code> method.
* The best we can do here is to check the setting on the template.
* @see #getSessionAcknowledgeMode()
*/
protected boolean isClientAcknowledge(Session session) throws JMSException {
return (getSessionAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE);
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2002-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.springframework.jms.core;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
/**
* Creates a JMS message given a {@link Session}.
*
* <p>The <code>Session</code> typically is provided by an instance
* of the {@link JmsTemplate} class.
*
* <p>Implementations <i>do not</i> need to concern themselves with
* checked <code>JMSExceptions</code> (from the '<code>javax.jms</code>'
* package) that may be thrown from operations they attempt. The
* <code>JmsTemplate</code> will catch and handle these
* <code>JMSExceptions</code> appropriately.
*
* @author Mark Pollack
* @since 1.1
*/
public interface MessageCreator {
/**
* Create a {@link Message} to be sent.
* @param session the JMS {@link Session} to be used to create the
* <code>Message</code> (never <code>null</code>)
* @return the <code>Message</code> to be sent
* @throws javax.jms.JMSException if thrown by JMS API methods
*/
Message createMessage(Session session) throws JMSException;
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2002-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.springframework.jms.core;
import javax.jms.JMSException;
import javax.jms.Message;
/**
* To be used with JmsTemplate's send method that convert an object to a message.
* It allows for further modification of the message after it has been processed
* by the converter. This is useful for setting of JMS Header and Properties.
*
* <p>This often as an anonymous class within a method implementation.
*
* @author Mark Pollack
* @since 1.1
* @see JmsTemplate#convertAndSend(String, Object, MessagePostProcessor)
* @see JmsTemplate#convertAndSend(javax.jms.Destination, Object, MessagePostProcessor)
* @see org.springframework.jms.support.converter.MessageConverter
*/
public interface MessagePostProcessor {
/**
* Apply a MessagePostProcessor to the message. The returned message is
* typically a modified version of the original.
* @param message the JMS message from the MessageConverter
* @return the modified version of the Message
* @throws javax.jms.JMSException if thrown by JMS API methods
*/
Message postProcessMessage(Message message) throws JMSException;
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright 2002-2008 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.springframework.jms.core;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
/**
* Callback for sending a message to a JMS destination.
*
* <p>To be used with JmsTemplate's callback methods that take a ProducerCallback
* argument, often implemented as an anonymous inner class.
*
* <p>The typical implementation will perform multiple operations on the
* supplied JMS {@link Session} and {@link MessageProducer}. When used with
* a 1.0.2 provider, you need to downcast to the appropriate domain
* implementation, either {@link javax.jms.QueueSender} or
* {@link javax.jms.TopicPublisher}, to actually send a message.
*
* @author Mark Pollack
* @since 1.1
* @see JmsTemplate#execute(ProducerCallback)
* @see JmsTemplate#execute(javax.jms.Destination, ProducerCallback)
* @see JmsTemplate#execute(String, ProducerCallback)
*/
public interface ProducerCallback<T> {
/**
* Perform operations on the given {@link Session} and {@link MessageProducer}.
* <p>The message producer is not associated with any destination unless
* when specified in the JmsTemplate call.
* @param session the JMS <code>Session</code> object to use
* @param producer the JMS <code>MessageProducer</code> object to use
* @return a result object from working with the <code>Session</code>, if any (can be <code>null</code>)
* @throws javax.jms.JMSException if thrown by JMS API methods
*/
T doInJms(Session session, MessageProducer producer) throws JMSException;
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2002-2008 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.springframework.jms.core;
import javax.jms.JMSException;
import javax.jms.Session;
/**
* Callback for executing any number of operations on a provided
* {@link Session}.
*
* <p>To be used with the {@link JmsTemplate#execute(SessionCallback)}
* method, often implemented as an anonymous inner class.
*
* @author Mark Pollack
* @since 1.1
* @see JmsTemplate#execute(SessionCallback)
*/
public interface SessionCallback<T> {
/**
* Execute any number of operations against the supplied JMS
* {@link Session}, possibly returning a result.
* @param session the JMS <code>Session</code>
* @return a result object from working with the <code>Session</code>, if any (so can be <code>null</code>)
* @throws javax.jms.JMSException if thrown by JMS API methods
*/
T doInJms(Session session) throws JMSException;
}

View File

@@ -1,9 +0,0 @@
/**
*
* Core package of the JMS support.
* Provides a JmsTemplate class and various callback interfaces.
*
*/
package org.springframework.jms.core;

View File

@@ -1,119 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.core.support;
import javax.jms.ConnectionFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jms.core.JmsTemplate;
/**
* Convenient super class for application classes that need JMS access.
*
* <p>Requires a ConnectionFactory or a JmsTemplate instance to be set.
* It will create its own JmsTemplate if a ConnectionFactory is passed in.
* A custom JmsTemplate instance can be created for a given ConnectionFactory
* through overriding the {@link #createJmsTemplate} method.
*
* @author Mark Pollack
* @since 1.1.1
* @see #setConnectionFactory
* @see #setJmsTemplate
* @see #createJmsTemplate
* @see org.springframework.jms.core.JmsTemplate
*/
public abstract class JmsGatewaySupport implements InitializingBean {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private JmsTemplate jmsTemplate;
/**
* Set the JMS connection factory to be used by the gateway.
* Will automatically create a JmsTemplate for the given ConnectionFactory.
* @see #createJmsTemplate
* @see #setConnectionFactory(javax.jms.ConnectionFactory)
* @param connectionFactory
*/
public final void setConnectionFactory(ConnectionFactory connectionFactory) {
this.jmsTemplate = createJmsTemplate(connectionFactory);
}
/**
* Create a JmsTemplate for the given ConnectionFactory.
* Only invoked if populating the gateway with a ConnectionFactory reference.
* <p>Can be overridden in subclasses to provide a JmsTemplate instance with
* a different configuration or the JMS 1.0.2 version, JmsTemplate102.
* @param connectionFactory the JMS ConnectionFactory to create a JmsTemplate for
* @return the new JmsTemplate instance
* @see #setConnectionFactory
* @see org.springframework.jms.core.JmsTemplate102
*/
protected JmsTemplate createJmsTemplate(ConnectionFactory connectionFactory) {
return new JmsTemplate(connectionFactory);
}
/**
* Return the JMS ConnectionFactory used by the gateway.
*/
public final ConnectionFactory getConnectionFactory() {
return (this.jmsTemplate != null ? this.jmsTemplate.getConnectionFactory() : null);
}
/**
* Set the JmsTemplate for the gateway.
* @param jmsTemplate
* @see #setConnectionFactory(javax.jms.ConnectionFactory)
*/
public final void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
/**
* Return the JmsTemplate for the gateway.
*/
public final JmsTemplate getJmsTemplate() {
return this.jmsTemplate;
}
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
if (this.jmsTemplate == null) {
throw new IllegalArgumentException("'connectionFactory' or 'jmsTemplate' is required");
}
try {
initGateway();
}
catch (Exception ex) {
throw new BeanInitializationException("Initialization of JMS gateway failed: " + ex.getMessage(), ex);
}
}
/**
* Subclasses can override this for custom initialization behavior.
* Gets called after population of this instance's bean properties.
* @throws java.lang.Exception if initialization fails
*/
protected void initGateway() throws Exception {
}
}

View File

@@ -1,9 +0,0 @@
/**
*
* Classes supporting the <code>org.springframework.jms.core</code> package.
* Contains a base class for JmsTemplate usage.
*
*/
package org.springframework.jms.core.support;

View File

@@ -1,639 +0,0 @@
/*
* Copyright 2002-2012 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.springframework.jms.listener;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.jms.Connection;
import javax.jms.JMSException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.SmartLifecycle;
import org.springframework.jms.JmsException;
import org.springframework.jms.connection.ConnectionFactoryUtils;
import org.springframework.jms.support.JmsUtils;
import org.springframework.jms.support.destination.JmsDestinationAccessor;
import org.springframework.util.ClassUtils;
/**
* Common base class for all containers which need to implement listening
* based on a JMS Connection (either shared or freshly obtained for each attempt).
* Inherits basic Connection and Session configuration handling from the
* {@link org.springframework.jms.support.JmsAccessor} base class.
*
* <p>This class provides basic lifecycle management, in particular management
* of a shared JMS Connection. Subclasses are supposed to plug into this
* lifecycle, implementing the {@link #sharedConnectionEnabled()} as well
* as the {@link #doInitialize()} and {@link #doShutdown()} template methods.
*
* <p>This base class does not assume any specific listener programming model
* or listener invoker mechanism. It just provides the general runtime
* lifecycle management needed for any kind of JMS-based listening mechanism
* that operates on a JMS Connection/Session.
*
* <p>For a concrete listener programming model, check out the
* {@link AbstractMessageListenerContainer} subclass. For a concrete listener
* invoker mechanism, check out the {@link DefaultMessageListenerContainer} class.
*
* @author Juergen Hoeller
* @since 2.0.3
* @see #sharedConnectionEnabled()
* @see #doInitialize()
* @see #doShutdown()
*/
public abstract class AbstractJmsListeningContainer extends JmsDestinationAccessor
implements SmartLifecycle, BeanNameAware, DisposableBean {
private String clientId;
private boolean autoStartup = true;
private int phase = Integer.MAX_VALUE;
private String beanName;
private Connection sharedConnection;
private boolean sharedConnectionStarted = false;
protected final Object sharedConnectionMonitor = new Object();
private boolean active = false;
private boolean running = false;
private final List<Object> pausedTasks = new LinkedList<Object>();
protected final Object lifecycleMonitor = new Object();
/**
* Specify the JMS client ID for a shared Connection created and used
* by this container.
* <p>Note that client IDs need to be unique among all active Connections
* of the underlying JMS provider. Furthermore, a client ID can only be
* assigned if the original ConnectionFactory hasn't already assigned one.
* @see javax.jms.Connection#setClientID
* @see #setConnectionFactory
*/
public void setClientId(String clientId) {
this.clientId = clientId;
}
/**
* Return the JMS client ID for the shared Connection created and used
* by this container, if any.
*/
public String getClientId() {
return this.clientId;
}
/**
* Set whether to automatically start the container after initialization.
* <p>Default is "true"; set this to "false" to allow for manual startup
* through the {@link #start()} method.
*/
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
public boolean isAutoStartup() {
return this.autoStartup;
}
/**
* Specify the phase in which this container should be started and
* stopped. The startup order proceeds from lowest to highest, and
* the shutdown order is the reverse of that. By default this value
* is Integer.MAX_VALUE meaning that this container starts as late
* as possible and stops as soon as possible.
*/
public void setPhase(int phase) {
this.phase = phase;
}
/**
* Return the phase in which this container will be started and stopped.
*/
public int getPhase() {
return this.phase;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
/**
* Return the bean name that this listener container has been assigned
* in its containing bean factory, if any.
*/
protected final String getBeanName() {
return this.beanName;
}
/**
* Delegates to {@link #validateConfiguration()} and {@link #initialize()}.
*/
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
validateConfiguration();
initialize();
}
/**
* Validate the configuration of this container.
* <p>The default implementation is empty. To be overridden in subclasses.
*/
protected void validateConfiguration() {
}
/**
* Calls {@link #shutdown()} when the BeanFactory destroys the container instance.
* @see #shutdown()
*/
public void destroy() {
shutdown();
}
//-------------------------------------------------------------------------
// Lifecycle methods for starting and stopping the container
//-------------------------------------------------------------------------
/**
* Initialize this container.
* <p>Creates a JMS Connection, starts the {@link javax.jms.Connection}
* (if {@link #setAutoStartup(boolean) "autoStartup"} hasn't been turned off),
* and calls {@link #doInitialize()}.
* @throws org.springframework.jms.JmsException if startup failed
*/
public void initialize() throws JmsException {
try {
synchronized (this.lifecycleMonitor) {
this.active = true;
this.lifecycleMonitor.notifyAll();
}
doInitialize();
}
catch (JMSException ex) {
synchronized (this.sharedConnectionMonitor) {
ConnectionFactoryUtils.releaseConnection(this.sharedConnection, getConnectionFactory(), this.autoStartup);
this.sharedConnection = null;
}
throw convertJmsAccessException(ex);
}
}
/**
* Stop the shared Connection, call {@link #doShutdown()},
* and close this container.
* @throws JmsException if shutdown failed
*/
public void shutdown() throws JmsException {
logger.debug("Shutting down JMS listener container");
boolean wasRunning;
synchronized (this.lifecycleMonitor) {
wasRunning = this.running;
this.running = false;
this.active = false;
this.lifecycleMonitor.notifyAll();
}
// Stop shared Connection early, if necessary.
if (wasRunning && sharedConnectionEnabled()) {
try {
stopSharedConnection();
}
catch (Throwable ex) {
logger.debug("Could not stop JMS Connection on shutdown", ex);
}
}
// Shut down the invokers.
try {
doShutdown();
}
catch (JMSException ex) {
throw convertJmsAccessException(ex);
}
finally {
if (sharedConnectionEnabled()) {
synchronized (this.sharedConnectionMonitor) {
ConnectionFactoryUtils.releaseConnection(this.sharedConnection, getConnectionFactory(), false);
this.sharedConnection = null;
}
}
}
}
/**
* Return whether this container is currently active,
* that is, whether it has been set up but not shut down yet.
*/
public final boolean isActive() {
synchronized (this.lifecycleMonitor) {
return this.active;
}
}
/**
* Start this container.
* @throws JmsException if starting failed
* @see #doStart
*/
public void start() throws JmsException {
try {
doStart();
}
catch (JMSException ex) {
throw convertJmsAccessException(ex);
}
}
/**
* Start the shared Connection, if any, and notify all invoker tasks.
* @throws JMSException if thrown by JMS API methods
* @see #startSharedConnection
*/
protected void doStart() throws JMSException {
// Lazily establish a shared Connection, if necessary.
if (sharedConnectionEnabled()) {
establishSharedConnection();
}
// Reschedule paused tasks, if any.
synchronized (this.lifecycleMonitor) {
this.running = true;
this.lifecycleMonitor.notifyAll();
resumePausedTasks();
}
// Start the shared Connection, if any.
if (sharedConnectionEnabled()) {
startSharedConnection();
}
}
/**
* Stop this container.
* @throws JmsException if stopping failed
* @see #doStop
*/
public void stop() throws JmsException {
try {
doStop();
}
catch (JMSException ex) {
throw convertJmsAccessException(ex);
}
}
public void stop(Runnable callback) {
this.stop();
callback.run();
}
/**
* Notify all invoker tasks and stop the shared Connection, if any.
* @throws JMSException if thrown by JMS API methods
* @see #stopSharedConnection
*/
protected void doStop() throws JMSException {
synchronized (this.lifecycleMonitor) {
this.running = false;
this.lifecycleMonitor.notifyAll();
}
if (sharedConnectionEnabled()) {
stopSharedConnection();
}
}
/**
* Determine whether this container is currently running,
* that is, whether it has been started and not stopped yet.
* @see #start()
* @see #stop()
* @see #runningAllowed()
*/
public final boolean isRunning() {
synchronized (this.lifecycleMonitor) {
return (this.running && runningAllowed());
}
}
/**
* Check whether this container's listeners are generally allowed to run.
* <p>This implementation always returns <code>true</code>; the default 'running'
* state is purely determined by {@link #start()} / {@link #stop()}.
* <p>Subclasses may override this method to check against temporary
* conditions that prevent listeners from actually running. In other words,
* they may apply further restrictions to the 'running' state, returning
* <code>false</code> if such a restriction prevents listeners from running.
*/
protected boolean runningAllowed() {
return true;
}
//-------------------------------------------------------------------------
// Management of a shared JMS Connection
//-------------------------------------------------------------------------
/**
* Establish a shared Connection for this container.
* <p>The default implementation delegates to {@link #createSharedConnection()},
* which does one immediate attempt and throws an exception if it fails.
* Can be overridden to have a recovery process in place, retrying
* until a Connection can be successfully established.
* @throws JMSException if thrown by JMS API methods
*/
protected void establishSharedConnection() throws JMSException {
synchronized (this.sharedConnectionMonitor) {
if (this.sharedConnection == null) {
this.sharedConnection = createSharedConnection();
logger.debug("Established shared JMS Connection");
}
}
}
/**
* Refresh the shared Connection that this container holds.
* <p>Called on startup and also after an infrastructure exception
* that occurred during invoker setup and/or execution.
* @throws JMSException if thrown by JMS API methods
*/
protected final void refreshSharedConnection() throws JMSException {
synchronized (this.sharedConnectionMonitor) {
ConnectionFactoryUtils.releaseConnection(
this.sharedConnection, getConnectionFactory(), this.sharedConnectionStarted);
this.sharedConnection = null;
this.sharedConnection = createSharedConnection();
if (this.sharedConnectionStarted) {
this.sharedConnection.start();
}
}
}
/**
* Create a shared Connection for this container.
* <p>The default implementation creates a standard Connection
* and prepares it through {@link #prepareSharedConnection}.
* @return the prepared Connection
* @throws JMSException if the creation failed
*/
protected Connection createSharedConnection() throws JMSException {
Connection con = createConnection();
try {
prepareSharedConnection(con);
return con;
}
catch (JMSException ex) {
JmsUtils.closeConnection(con);
throw ex;
}
}
/**
* Prepare the given Connection, which is about to be registered
* as shared Connection for this container.
* <p>The default implementation sets the specified client id, if any.
* Subclasses can override this to apply further settings.
* @param connection the Connection to prepare
* @throws JMSException if the preparation efforts failed
* @see #getClientId()
*/
protected void prepareSharedConnection(Connection connection) throws JMSException {
String clientId = getClientId();
if (clientId != null) {
connection.setClientID(clientId);
}
}
/**
* Start the shared Connection.
* @throws JMSException if thrown by JMS API methods
* @see javax.jms.Connection#start()
*/
protected void startSharedConnection() throws JMSException {
synchronized (this.sharedConnectionMonitor) {
this.sharedConnectionStarted = true;
if (this.sharedConnection != null) {
try {
this.sharedConnection.start();
}
catch (javax.jms.IllegalStateException ex) {
logger.debug("Ignoring Connection start exception - assuming already started: " + ex);
}
}
}
}
/**
* Stop the shared Connection.
* @throws JMSException if thrown by JMS API methods
* @see javax.jms.Connection#start()
*/
protected void stopSharedConnection() throws JMSException {
synchronized (this.sharedConnectionMonitor) {
this.sharedConnectionStarted = false;
if (this.sharedConnection != null) {
try {
this.sharedConnection.stop();
}
catch (javax.jms.IllegalStateException ex) {
logger.debug("Ignoring Connection stop exception - assuming already stopped: " + ex);
}
}
}
}
/**
* Return the shared JMS Connection maintained by this container.
* Available after initialization.
* @return the shared Connection (never <code>null</code>)
* @throws IllegalStateException if this container does not maintain a
* shared Connection, or if the Connection hasn't been initialized yet
* @see #sharedConnectionEnabled()
*/
protected final Connection getSharedConnection() {
if (!sharedConnectionEnabled()) {
throw new IllegalStateException(
"This listener container does not maintain a shared Connection");
}
synchronized (this.sharedConnectionMonitor) {
if (this.sharedConnection == null) {
throw new SharedConnectionNotInitializedException(
"This listener container's shared Connection has not been initialized yet");
}
return this.sharedConnection;
}
}
//-------------------------------------------------------------------------
// Management of paused tasks
//-------------------------------------------------------------------------
/**
* Take the given task object and reschedule it, either immediately if
* this container is currently running, or later once this container
* has been restarted.
* <p>If this container has already been shut down, the task will not
* get rescheduled at all.
* @param task the task object to reschedule
* @return whether the task has been rescheduled
* (either immediately or for a restart of this container)
* @see #doRescheduleTask
*/
protected final boolean rescheduleTaskIfNecessary(Object task) {
if (this.running) {
try {
doRescheduleTask(task);
}
catch (RuntimeException ex) {
logRejectedTask(task, ex);
this.pausedTasks.add(task);
}
return true;
}
else if (this.active) {
this.pausedTasks.add(task);
return true;
}
else {
return false;
}
}
/**
* Try to resume all paused tasks.
* Tasks for which rescheduling failed simply remain in paused mode.
*/
protected void resumePausedTasks() {
synchronized (this.lifecycleMonitor) {
if (!this.pausedTasks.isEmpty()) {
for (Iterator<?> it = this.pausedTasks.iterator(); it.hasNext();) {
Object task = it.next();
try {
doRescheduleTask(task);
it.remove();
if (logger.isDebugEnabled()) {
logger.debug("Resumed paused task: " + task);
}
}
catch (RuntimeException ex) {
logRejectedTask(task, ex);
// Keep the task in paused mode...
}
}
}
}
}
/**
* Determine the number of currently paused tasks, if any.
*/
public int getPausedTaskCount() {
synchronized (this.lifecycleMonitor) {
return this.pausedTasks.size();
}
}
/**
* Reschedule the given task object immediately.
* <p>To be implemented by subclasses if they ever call
* <code>rescheduleTaskIfNecessary</code>.
* This implementation throws an UnsupportedOperationException.
* @param task the task object to reschedule
* @see #rescheduleTaskIfNecessary
*/
protected void doRescheduleTask(Object task) {
throw new UnsupportedOperationException(
ClassUtils.getShortName(getClass()) + " does not support rescheduling of tasks");
}
/**
* Log a task that has been rejected by {@link #doRescheduleTask}.
* <p>The default implementation simply logs a corresponding message
* at debug level.
* @param task the rejected task object
* @param ex the exception thrown from {@link #doRescheduleTask}
*/
protected void logRejectedTask(Object task, RuntimeException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Listener container task [" + task + "] has been rejected and paused: " + ex);
}
}
//-------------------------------------------------------------------------
// Template methods to be implemented by subclasses
//-------------------------------------------------------------------------
/**
* Return whether a shared JMS Connection should be maintained
* by this container base class.
* @see #getSharedConnection()
*/
protected abstract boolean sharedConnectionEnabled();
/**
* Register any invokers within this container.
* <p>Subclasses need to implement this method for their specific
* invoker management process.
* <p>A shared JMS Connection, if any, will already have been
* started at this point.
* @throws JMSException if registration failed
* @see #getSharedConnection()
*/
protected abstract void doInitialize() throws JMSException;
/**
* Close the registered invokers.
* <p>Subclasses need to implement this method for their specific
* invoker management process.
* <p>A shared JMS Connection, if any, will automatically be closed
* <i>afterwards</i>.
* @throws JMSException if shutdown failed
* @see #shutdown()
*/
protected abstract void doShutdown() throws JMSException;
/**
* Exception that indicates that the initial setup of this container's
* shared JMS Connection failed. This is indicating to invokers that they need
* to establish the shared Connection themselves on first access.
*/
@SuppressWarnings("serial")
public static class SharedConnectionNotInitializedException extends RuntimeException {
/**
* Create a new SharedConnectionNotInitializedException.
* @param msg the detail message
*/
protected SharedConnectionNotInitializedException(String msg) {
super(msg);
}
}
}

View File

@@ -1,710 +0,0 @@
/*
* Copyright 2002-2012 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.springframework.jms.listener;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.Topic;
import org.springframework.jms.support.JmsUtils;
import org.springframework.util.Assert;
import org.springframework.util.ErrorHandler;
/**
* Abstract base class for message listener containers. Can either host
* a standard JMS {@link javax.jms.MessageListener} or a Spring-specific
* {@link SessionAwareMessageListener}.
*
* <p>Usually holds a single JMS {@link Connection} that all listeners are
* supposed to be registered on, which is the standard JMS way of managing
* listeners. Can alternatively also be used with a fresh Connection per
* listener, for J2EE-style XA-aware JMS messaging. The actual registration
* process is up to concrete subclasses.
*
* <p><b>NOTE:</b> The default behavior of this message listener container
* is to <b>never</b> propagate an exception thrown by a message listener up to
* the JMS provider. Instead, it will log any such exception at the error level.
* This means that from the perspective of the attendant JMS provider no such
* listener will ever fail. However, if error handling is necessary, then
* any implementation of the {@link ErrorHandler} strategy may be provided to
* the {@link #setErrorHandler(ErrorHandler)} method. Note that JMSExceptions
* <b>will</b> be passed to the ErrorHandler in addition to (but after) being
* passed to an {@link ExceptionListener}, if one has been provided.
*
* <p>The listener container offers the following message acknowledgment options:
* <ul>
* <li>"sessionAcknowledgeMode" set to "AUTO_ACKNOWLEDGE" (default):
* Automatic message acknowledgment <i>before</i> listener execution;
* no redelivery in case of exception thrown.
* <li>"sessionAcknowledgeMode" set to "CLIENT_ACKNOWLEDGE":
* Automatic message acknowledgment <i>after</i> successful listener execution;
* no redelivery in case of exception thrown.
* <li>"sessionAcknowledgeMode" set to "DUPS_OK_ACKNOWLEDGE":
* <i>Lazy</i> message acknowledgment during or after listener execution;
* <i>potential redelivery</i> in case of exception thrown.
* <li>"sessionTransacted" set to "true":
* Transactional acknowledgment after successful listener execution;
* <i>guaranteed redelivery</i> in case of exception thrown.
* </ul>
* The exact behavior might vary according to the concrete listener container
* and JMS provider used.
*
* <p>There are two solutions to the duplicate processing problem:
* <ul>
* <li>Either add <i>duplicate message detection</i> to your listener, in the
* form of a business entity existence check or a protocol table check. This
* usually just needs to be done in case of the JMSRedelivered flag being
* set on the incoming message (else just process straightforwardly).
* <li>Or wrap the <i>entire processing with an XA transaction</i>, covering the
* reception of the message as well as the execution of the message listener.
* This is only supported by {@link DefaultMessageListenerContainer}, through
* specifying a "transactionManager" (typically a
* {@link org.springframework.transaction.jta.JtaTransactionManager}, with
* a corresponding XA-aware JMS {@link javax.jms.ConnectionFactory} passed in as
* "connectionFactory").
* </ul>
* Note that XA transaction coordination adds significant runtime overhead,
* so it might be feasible to avoid it unless absolutely necessary.
*
* <p><b>Recommendations:</b>
* <ul>
* <li>The general recommendation is to set "sessionTransacted" to "true",
* typically in combination with local database transactions triggered by the
* listener implementation, through Spring's standard transaction facilities.
* This will work nicely in Tomcat or in a standalone environment, often
* combined with custom duplicate message detection (if it is unacceptable
* to ever process the same message twice).
* <li>Alternatively, specify a
* {@link org.springframework.transaction.jta.JtaTransactionManager} as
* "transactionManager" for a fully XA-aware JMS provider - typically when
* running on a J2EE server, but also for other environments with a JTA
* transaction manager present. This will give full "exactly-once" guarantees
* without custom duplicate message checks, at the price of additional
* runtime processing overhead.
* </ul>
*
* <p>Note that it is also possible to specify a
* {@link org.springframework.jms.connection.JmsTransactionManager} as external
* "transactionManager", providing fully synchronized Spring transactions based
* on local JMS transactions. The effect is similar to "sessionTransacted" set
* to "true", the difference being that this external transaction management
* will also affect independent JMS access code within the service layer
* (e.g. based on {@link org.springframework.jms.core.JmsTemplate} or
* {@link org.springframework.jms.connection.TransactionAwareConnectionFactoryProxy}),
* not just direct JMS Session usage in a {@link SessionAwareMessageListener}.
*
* @author Juergen Hoeller
* @since 2.0
* @see #setMessageListener
* @see javax.jms.MessageListener
* @see SessionAwareMessageListener
* @see #handleListenerException
* @see DefaultMessageListenerContainer
* @see SimpleMessageListenerContainer
* @see org.springframework.jms.listener.endpoint.JmsMessageEndpointManager
*/
public abstract class AbstractMessageListenerContainer extends AbstractJmsListeningContainer {
private volatile Object destination;
private volatile String messageSelector;
private volatile Object messageListener;
private boolean subscriptionDurable = false;
private String durableSubscriptionName;
private ExceptionListener exceptionListener;
private ErrorHandler errorHandler;
private boolean exposeListenerSession = true;
private boolean acceptMessagesWhileStopping = false;
/**
* Set the destination to receive messages from.
* <p>Alternatively, specify a "destinationName", to be dynamically
* resolved via the {@link org.springframework.jms.support.destination.DestinationResolver}.
* <p>Note: The destination may be replaced at runtime, with the listener
* container picking up the new destination immediately (works e.g. with
* DefaultMessageListenerContainer, as long as the cache level is less than
* CACHE_CONSUMER). However, this is considered advanced usage; use it with care!
* @see #setDestinationName(String)
*/
public void setDestination(Destination destination) {
Assert.notNull(destination, "'destination' must not be null");
this.destination = destination;
if (destination instanceof Topic && !(destination instanceof Queue)) {
// Clearly a Topic: let's set the "pubSubDomain" flag accordingly.
setPubSubDomain(true);
}
}
/**
* Return the destination to receive messages from. Will be <code>null</code>
* if the configured destination is not an actual {@link Destination} type;
* c.f. {@link #setDestinationName(String) when the destination is a String}.
*/
public Destination getDestination() {
return (this.destination instanceof Destination ? (Destination) this.destination : null);
}
/**
* Set the name of the destination to receive messages from.
* <p>The specified name will be dynamically resolved via the configured
* {@link #setDestinationResolver destination resolver}.
* <p>Alternatively, specify a JMS {@link Destination} object as "destination".
* <p>Note: The destination may be replaced at runtime, with the listener
* container picking up the new destination immediately (works e.g. with
* DefaultMessageListenerContainer, as long as the cache level is less than
* CACHE_CONSUMER). However, this is considered advanced usage; use it with care!
* @param destinationName the desired destination (can be <code>null</code>)
* @see #setDestination(javax.jms.Destination)
*/
public void setDestinationName(String destinationName) {
Assert.notNull(destinationName, "'destinationName' must not be null");
this.destination = destinationName;
}
/**
* Return the name of the destination to receive messages from.
* Will be <code>null</code> if the configured destination is not a
* {@link String} type; c.f. {@link #setDestination(Destination) when
* it is an actual Destination}.
*/
public String getDestinationName() {
return (this.destination instanceof String ? (String) this.destination : null);
}
/**
* Return a descriptive String for this container's JMS destination
* (never <code>null</code>).
*/
protected String getDestinationDescription() {
return this.destination.toString();
}
/**
* Set the JMS message selector expression (or <code>null</code> if none).
* Default is none.
* <p>See the JMS specification for a detailed definition of selector expressions.
* <p>Note: The message selector may be replaced at runtime, with the listener
* container picking up the new selector value immediately (works e.g. with
* DefaultMessageListenerContainer, as long as the cache level is less than
* CACHE_CONSUMER). However, this is considered advanced usage; use it with care!
*/
public void setMessageSelector(String messageSelector) {
this.messageSelector = messageSelector;
}
/**
* Return the JMS message selector expression (or <code>null</code> if none).
*/
public String getMessageSelector() {
return this.messageSelector;
}
/**
* Set the message listener implementation to register.
* This can be either a standard JMS {@link MessageListener} object
* or a Spring {@link SessionAwareMessageListener} object.
* <p>Note: The message listener may be replaced at runtime, with the listener
* container picking up the new listener object immediately (works e.g. with
* DefaultMessageListenerContainer, as long as the cache level is less than
* CACHE_CONSUMER). However, this is considered advanced usage; use it with care!
* @throws IllegalArgumentException if the supplied listener is not a
* {@link MessageListener} or a {@link SessionAwareMessageListener}
* @see javax.jms.MessageListener
* @see SessionAwareMessageListener
*/
public void setMessageListener(Object messageListener) {
checkMessageListener(messageListener);
this.messageListener = messageListener;
if (this.durableSubscriptionName == null) {
this.durableSubscriptionName = getDefaultSubscriptionName(messageListener);
}
}
/**
* Return the message listener object to register.
*/
public Object getMessageListener() {
return this.messageListener;
}
/**
* Check the given message listener, throwing an exception
* if it does not correspond to a supported listener type.
* <p>By default, only a standard JMS {@link MessageListener} object or a
* Spring {@link SessionAwareMessageListener} object will be accepted.
* @param messageListener the message listener object to check
* @throws IllegalArgumentException if the supplied listener is not a
* {@link MessageListener} or a {@link SessionAwareMessageListener}
* @see javax.jms.MessageListener
* @see SessionAwareMessageListener
*/
protected void checkMessageListener(Object messageListener) {
if (!(messageListener instanceof MessageListener ||
messageListener instanceof SessionAwareMessageListener)) {
throw new IllegalArgumentException(
"Message listener needs to be of type [" + MessageListener.class.getName() +
"] or [" + SessionAwareMessageListener.class.getName() + "]");
}
}
/**
* Determine the default subscription name for the given message listener.
* @param messageListener the message listener object to check
* @return the default subscription name
* @see SubscriptionNameProvider
*/
protected String getDefaultSubscriptionName(Object messageListener) {
if (messageListener instanceof SubscriptionNameProvider) {
return ((SubscriptionNameProvider) messageListener).getSubscriptionName();
}
else {
return messageListener.getClass().getName();
}
}
/**
* Set whether to make the subscription durable. The durable subscription name
* to be used can be specified through the "durableSubscriptionName" property.
* <p>Default is "false". Set this to "true" to register a durable subscription,
* typically in combination with a "durableSubscriptionName" value (unless
* your message listener class name is good enough as subscription name).
* <p>Only makes sense when listening to a topic (pub-sub domain).
* @see #setDurableSubscriptionName
*/
public void setSubscriptionDurable(boolean subscriptionDurable) {
this.subscriptionDurable = subscriptionDurable;
}
/**
* Return whether to make the subscription durable.
*/
public boolean isSubscriptionDurable() {
return this.subscriptionDurable;
}
/**
* Set the name of a durable subscription to create. To be applied in case
* of a topic (pub-sub domain) with subscription durability activated.
* <p>The durable subscription name needs to be unique within this client's
* JMS client id. Default is the class name of the specified message listener.
* <p>Note: Only 1 concurrent consumer (which is the default of this
* message listener container) is allowed for each durable subscription.
* @see #setSubscriptionDurable
* @see #setClientId
* @see #setMessageListener
*/
public void setDurableSubscriptionName(String durableSubscriptionName) {
this.durableSubscriptionName = durableSubscriptionName;
}
/**
* Return the name of a durable subscription to create, if any.
*/
public String getDurableSubscriptionName() {
return this.durableSubscriptionName;
}
/**
* Set the JMS ExceptionListener to notify in case of a JMSException thrown
* by the registered message listener or the invocation infrastructure.
*/
public void setExceptionListener(ExceptionListener exceptionListener) {
this.exceptionListener = exceptionListener;
}
/**
* Return the JMS ExceptionListener to notify in case of a JMSException thrown
* by the registered message listener or the invocation infrastructure, if any.
*/
public ExceptionListener getExceptionListener() {
return this.exceptionListener;
}
/**
* Set an ErrorHandler to be invoked in case of any uncaught exceptions thrown
* while processing a Message. By default there will be <b>no</b> ErrorHandler
* so that error-level logging is the only result.
*/
public void setErrorHandler(ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
/**
* Set whether to expose the listener JMS Session to a registered
* {@link SessionAwareMessageListener} as well as to
* {@link org.springframework.jms.core.JmsTemplate} calls.
* <p>Default is "true", reusing the listener's {@link Session}.
* Turn this off to expose a fresh JMS Session fetched from the same
* underlying JMS {@link Connection} instead, which might be necessary
* on some JMS providers.
* <p>Note that Sessions managed by an external transaction manager will
* always get exposed to {@link org.springframework.jms.core.JmsTemplate}
* calls. So in terms of JmsTemplate exposure, this setting only affects
* locally transacted Sessions.
* @see SessionAwareMessageListener
*/
public void setExposeListenerSession(boolean exposeListenerSession) {
this.exposeListenerSession = exposeListenerSession;
}
/**
* Return whether to expose the listener JMS {@link Session} to a
* registered {@link SessionAwareMessageListener}.
*/
public boolean isExposeListenerSession() {
return this.exposeListenerSession;
}
/**
* Set whether to accept received messages while the listener container
* in the process of stopping.
* <p>Default is "false", rejecting such messages through aborting the
* receive attempt. Switch this flag on to fully process such messages
* even in the stopping phase, with the drawback that even newly sent
* messages might still get processed (if coming in before all receive
* timeouts have expired).
* <p><b>NOTE:</b> Aborting receive attempts for such incoming messages
* might lead to the provider's retry count decreasing for the affected
* messages. If you have a high number of concurrent consumers, make sure
* that the number of retries is higher than the number of consumers,
* to be on the safe side for all potential stopping scenarios.
*/
public void setAcceptMessagesWhileStopping(boolean acceptMessagesWhileStopping) {
this.acceptMessagesWhileStopping = acceptMessagesWhileStopping;
}
/**
* Return whether to accept received messages while the listener container
* in the process of stopping.
*/
public boolean isAcceptMessagesWhileStopping() {
return this.acceptMessagesWhileStopping;
}
@Override
protected void validateConfiguration() {
if (this.destination == null) {
throw new IllegalArgumentException("Property 'destination' or 'destinationName' is required");
}
if (isSubscriptionDurable() && !isPubSubDomain()) {
throw new IllegalArgumentException("A durable subscription requires a topic (pub-sub domain)");
}
}
//-------------------------------------------------------------------------
// Template methods for listener execution
//-------------------------------------------------------------------------
/**
* Execute the specified listener,
* committing or rolling back the transaction afterwards (if necessary).
* @param session the JMS Session to operate on
* @param message the received JMS Message
* @see #invokeListener
* @see #commitIfNecessary
* @see #rollbackOnExceptionIfNecessary
* @see #handleListenerException
*/
protected void executeListener(Session session, Message message) {
try {
doExecuteListener(session, message);
}
catch (Throwable ex) {
handleListenerException(ex);
}
}
/**
* Execute the specified listener,
* committing or rolling back the transaction afterwards (if necessary).
* @param session the JMS Session to operate on
* @param message the received JMS Message
* @throws JMSException if thrown by JMS API methods
* @see #invokeListener
* @see #commitIfNecessary
* @see #rollbackOnExceptionIfNecessary
* @see #convertJmsAccessException
*/
protected void doExecuteListener(Session session, Message message) throws JMSException {
if (!isAcceptMessagesWhileStopping() && !isRunning()) {
if (logger.isWarnEnabled()) {
logger.warn("Rejecting received message because of the listener container " +
"having been stopped in the meantime: " + message);
}
rollbackIfNecessary(session);
throw new MessageRejectedWhileStoppingException();
}
try {
invokeListener(session, message);
}
catch (JMSException ex) {
rollbackOnExceptionIfNecessary(session, ex);
throw ex;
}
catch (RuntimeException ex) {
rollbackOnExceptionIfNecessary(session, ex);
throw ex;
}
catch (Error err) {
rollbackOnExceptionIfNecessary(session, err);
throw err;
}
commitIfNecessary(session, message);
}
/**
* Invoke the specified listener: either as standard JMS MessageListener
* or (preferably) as Spring SessionAwareMessageListener.
* @param session the JMS Session to operate on
* @param message the received JMS Message
* @throws JMSException if thrown by JMS API methods
* @see #setMessageListener
*/
@SuppressWarnings("rawtypes")
protected void invokeListener(Session session, Message message) throws JMSException {
Object listener = getMessageListener();
if (listener instanceof SessionAwareMessageListener) {
doInvokeListener((SessionAwareMessageListener) listener, session, message);
}
else if (listener instanceof MessageListener) {
doInvokeListener((MessageListener) listener, message);
}
else if (listener != null) {
throw new IllegalArgumentException(
"Only MessageListener and SessionAwareMessageListener supported: " + listener);
}
else {
throw new IllegalStateException("No message listener specified - see property 'messageListener'");
}
}
/**
* Invoke the specified listener as Spring SessionAwareMessageListener,
* exposing a new JMS Session (potentially with its own transaction)
* to the listener if demanded.
* @param listener the Spring SessionAwareMessageListener to invoke
* @param session the JMS Session to operate on
* @param message the received JMS Message
* @throws JMSException if thrown by JMS API methods
* @see SessionAwareMessageListener
* @see #setExposeListenerSession
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void doInvokeListener(SessionAwareMessageListener listener, Session session, Message message)
throws JMSException {
Connection conToClose = null;
Session sessionToClose = null;
try {
Session sessionToUse = session;
if (!isExposeListenerSession()) {
// We need to expose a separate Session.
conToClose = createConnection();
sessionToClose = createSession(conToClose);
sessionToUse = sessionToClose;
}
// Actually invoke the message listener...
listener.onMessage(message, sessionToUse);
// Clean up specially exposed Session, if any.
if (sessionToUse != session) {
if (sessionToUse.getTransacted() && isSessionLocallyTransacted(sessionToUse)) {
// Transacted session created by this container -> commit.
JmsUtils.commitIfNecessary(sessionToUse);
}
}
}
finally {
JmsUtils.closeSession(sessionToClose);
JmsUtils.closeConnection(conToClose);
}
}
/**
* Invoke the specified listener as standard JMS MessageListener.
* <p>Default implementation performs a plain invocation of the
* <code>onMessage</code> method.
* @param listener the JMS MessageListener to invoke
* @param message the received JMS Message
* @throws JMSException if thrown by JMS API methods
* @see javax.jms.MessageListener#onMessage
*/
protected void doInvokeListener(MessageListener listener, Message message) throws JMSException {
listener.onMessage(message);
}
/**
* Perform a commit or message acknowledgement, as appropriate.
* @param session the JMS Session to commit
* @param message the Message to acknowledge
* @throws javax.jms.JMSException in case of commit failure
*/
protected void commitIfNecessary(Session session, Message message) throws JMSException {
// Commit session or acknowledge message.
if (session.getTransacted()) {
// Commit necessary - but avoid commit call within a JTA transaction.
if (isSessionLocallyTransacted(session)) {
// Transacted session created by this container -> commit.
JmsUtils.commitIfNecessary(session);
}
}
else if (message != null && isClientAcknowledge(session)) {
message.acknowledge();
}
}
/**
* Perform a rollback, if appropriate.
* @param session the JMS Session to rollback
* @throws javax.jms.JMSException in case of a rollback error
*/
protected void rollbackIfNecessary(Session session) throws JMSException {
if (session.getTransacted() && isSessionLocallyTransacted(session)) {
// Transacted session created by this container -> rollback.
JmsUtils.rollbackIfNecessary(session);
}
}
/**
* Perform a rollback, handling rollback exceptions properly.
* @param session the JMS Session to rollback
* @param ex the thrown application exception or error
* @throws javax.jms.JMSException in case of a rollback error
*/
protected void rollbackOnExceptionIfNecessary(Session session, Throwable ex) throws JMSException {
try {
if (session.getTransacted() && isSessionLocallyTransacted(session)) {
// Transacted session created by this container -> rollback.
if (logger.isDebugEnabled()) {
logger.debug("Initiating transaction rollback on application exception", ex);
}
JmsUtils.rollbackIfNecessary(session);
}
}
catch (IllegalStateException ex2) {
logger.debug("Could not roll back because Session already closed", ex2);
}
catch (JMSException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by rollback error", ex);
throw err;
}
}
/**
* Check whether the given Session is locally transacted, that is, whether
* its transaction is managed by this listener container's Session handling
* and not by an external transaction coordinator.
* <p>Note: The Session's own transacted flag will already have been checked
* before. This method is about finding out whether the Session's transaction
* is local or externally coordinated.
* @param session the Session to check
* @return whether the given Session is locally transacted
* @see #isSessionTransacted()
* @see org.springframework.jms.connection.ConnectionFactoryUtils#isSessionTransactional
*/
protected boolean isSessionLocallyTransacted(Session session) {
return isSessionTransacted();
}
/**
* Handle the given exception that arose during listener execution.
* <p>The default implementation logs the exception at warn level,
* not propagating it to the JMS provider &mdash; assuming that all handling of
* acknowledgement and/or transactions is done by this listener container.
* This can be overridden in subclasses.
* @param ex the exception to handle
*/
protected void handleListenerException(Throwable ex) {
if (ex instanceof MessageRejectedWhileStoppingException) {
// Internal exception - has been handled before.
return;
}
if (ex instanceof JMSException) {
invokeExceptionListener((JMSException) ex);
}
if (isActive()) {
// Regular case: failed while active.
// Invoke ErrorHandler if available.
invokeErrorHandler(ex);
}
else {
// Rare case: listener thread failed after container shutdown.
// Log at debug level, to avoid spamming the shutdown log.
logger.debug("Listener exception after container shutdown", ex);
}
}
/**
* Invoke the registered JMS ExceptionListener, if any.
* @param ex the exception that arose during JMS processing
* @see #setExceptionListener
*/
protected void invokeExceptionListener(JMSException ex) {
ExceptionListener exceptionListener = getExceptionListener();
if (exceptionListener != null) {
exceptionListener.onException(ex);
}
}
/**
* Invoke the registered ErrorHandler, if any. Log at warn level otherwise.
* @param ex the uncaught error that arose during JMS processing.
* @see #setErrorHandler
*/
protected void invokeErrorHandler(Throwable ex) {
if (this.errorHandler != null) {
this.errorHandler.handleError(ex);
}
else if (logger.isWarnEnabled()) {
logger.warn("Execution of JMS message listener failed, and no ErrorHandler has been set.", ex);
}
}
/**
* Internal exception class that indicates a rejected message on shutdown.
* Used to trigger a rollback for an external transaction manager in that case.
*/
@SuppressWarnings("serial")
private static class MessageRejectedWhileStoppingException extends RuntimeException {
}
}

View File

@@ -1,540 +0,0 @@
/*
* Copyright 2002-2012 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.springframework.jms.listener;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.Topic;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.jms.connection.ConnectionFactoryUtils;
import org.springframework.jms.connection.JmsResourceHolder;
import org.springframework.jms.connection.SingleConnectionFactory;
import org.springframework.jms.support.JmsUtils;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.ResourceTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionSynchronizationUtils;
/**
* Base class for listener container implementations which are based on polling.
* Provides support for listener handling based on {@link javax.jms.MessageConsumer},
* optionally participating in externally managed transactions.
*
* <p>This listener container variant is built for repeated polling attempts,
* each invoking the {@link #receiveAndExecute} method. The MessageConsumer used
* may be reobtained fo reach attempt or cached inbetween attempts; this is up
* to the concrete implementation. The receive timeout for each attempt can be
* configured through the {@link #setReceiveTimeout "receiveTimeout"} property.
*
* <p>The underlying mechanism is based on standard JMS MessageConsumer handling,
* which is perfectly compatible with both native JMS and JMS in a J2EE environment.
* Neither the JMS <code>MessageConsumer.setMessageListener</code> facility
* nor the JMS ServerSessionPool facility is required. A further advantage
* of this approach is full control over the listening process, allowing for
* custom scaling and throttling and of concurrent message processing
* (which is up to concrete subclasses).
*
* <p>Message reception and listener execution can automatically be wrapped
* in transactions through passing a Spring
* {@link org.springframework.transaction.PlatformTransactionManager} into the
* {@link #setTransactionManager "transactionManager"} property. This will usually
* be a {@link org.springframework.transaction.jta.JtaTransactionManager} in a
* J2EE enviroment, in combination with a JTA-aware JMS ConnectionFactory obtained
* from JNDI (check your J2EE server's documentation).
*
* <p>This base class does not assume any specific mechanism for asynchronous
* execution of polling invokers. Check out {@link DefaultMessageListenerContainer}
* for a concrete implementation which is based on Spring's
* {@link org.springframework.core.task.TaskExecutor} abstraction,
* including dynamic scaling of concurrent consumers and automatic self recovery.
*
* @author Juergen Hoeller
* @since 2.0.3
* @see #createListenerConsumer
* @see #receiveAndExecute
* @see #setTransactionManager
*/
public abstract class AbstractPollingMessageListenerContainer extends AbstractMessageListenerContainer {
/**
* The default receive timeout: 1000 ms = 1 second.
*/
public static final long DEFAULT_RECEIVE_TIMEOUT = 1000;
private final MessageListenerContainerResourceFactory transactionalResourceFactory =
new MessageListenerContainerResourceFactory();
private boolean sessionTransactedCalled = false;
private boolean pubSubNoLocal = false;
private PlatformTransactionManager transactionManager;
private DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
private long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT;
private volatile Boolean commitAfterNoMessageReceived;
@Override
public void setSessionTransacted(boolean sessionTransacted) {
super.setSessionTransacted(sessionTransacted);
this.sessionTransactedCalled = true;
}
/**
* Set whether to inhibit the delivery of messages published by its own connection.
* Default is "false".
* @see javax.jms.TopicSession#createSubscriber(javax.jms.Topic, String, boolean)
*/
public void setPubSubNoLocal(boolean pubSubNoLocal) {
this.pubSubNoLocal = pubSubNoLocal;
}
/**
* Return whether to inhibit the delivery of messages published by its own connection.
*/
protected boolean isPubSubNoLocal() {
return this.pubSubNoLocal;
}
/**
* Specify the Spring {@link org.springframework.transaction.PlatformTransactionManager}
* to use for transactional wrapping of message reception plus listener execution.
* <p>Default is none, not performing any transactional wrapping.
* If specified, this will usually be a Spring
* {@link org.springframework.transaction.jta.JtaTransactionManager} or one
* of its subclasses, in combination with a JTA-aware ConnectionFactory that
* this message listener container obtains its Connections from.
* <p><b>Note: Consider the use of local JMS transactions instead.</b>
* Simply switch the {@link #setSessionTransacted "sessionTransacted"} flag
* to "true" in order to use a locally transacted JMS Session for the entire
* receive processing, including any Session operations performed by a
* {@link SessionAwareMessageListener} (e.g. sending a response message).
* Alternatively, a {@link org.springframework.jms.connection.JmsTransactionManager}
* may be used for fully synchronized Spring transactions based on local JMS
* transactions. Check {@link AbstractMessageListenerContainer}'s javadoc for
* a discussion of transaction choices and message redelivery scenarios.
* @see org.springframework.transaction.jta.JtaTransactionManager
* @see org.springframework.jms.connection.JmsTransactionManager
*/
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* Return the Spring PlatformTransactionManager to use for transactional
* wrapping of message reception plus listener execution.
*/
protected final PlatformTransactionManager getTransactionManager() {
return this.transactionManager;
}
/**
* Specify the transaction name to use for transactional wrapping.
* Default is the bean name of this listener container, if any.
* @see org.springframework.transaction.TransactionDefinition#getName()
*/
public void setTransactionName(String transactionName) {
this.transactionDefinition.setName(transactionName);
}
/**
* Specify the transaction timeout to use for transactional wrapping, in <b>seconds</b>.
* Default is none, using the transaction manager's default timeout.
* @see org.springframework.transaction.TransactionDefinition#getTimeout()
* @see #setReceiveTimeout
*/
public void setTransactionTimeout(int transactionTimeout) {
this.transactionDefinition.setTimeout(transactionTimeout);
}
/**
* Set the timeout to use for receive calls, in <b>milliseconds</b>.
* The default is 1000 ms, that is, 1 second.
* <p><b>NOTE:</b> This value needs to be smaller than the transaction
* timeout used by the transaction manager (in the appropriate unit,
* of course). -1 indicates no timeout at all; however, this is only
* feasible if not running within a transaction manager.
* @see javax.jms.MessageConsumer#receive(long)
* @see javax.jms.MessageConsumer#receive()
* @see #setTransactionTimeout
*/
public void setReceiveTimeout(long receiveTimeout) {
this.receiveTimeout = receiveTimeout;
}
@Override
public void initialize() {
// Set sessionTransacted=true in case of a non-JTA transaction manager.
if (!this.sessionTransactedCalled &&
this.transactionManager instanceof ResourceTransactionManager &&
!TransactionSynchronizationUtils.sameResourceFactory(
(ResourceTransactionManager) this.transactionManager, getConnectionFactory())) {
super.setSessionTransacted(true);
}
// Use bean name as default transaction name.
if (this.transactionDefinition.getName() == null) {
this.transactionDefinition.setName(getBeanName());
}
// Proceed with superclass initialization.
super.initialize();
}
/**
* Create a MessageConsumer for the given JMS Session,
* registering a MessageListener for the specified listener.
* @param session the JMS Session to work on
* @return the MessageConsumer
* @throws javax.jms.JMSException if thrown by JMS methods
* @see #receiveAndExecute
*/
protected MessageConsumer createListenerConsumer(Session session) throws JMSException {
Destination destination = getDestination();
if (destination == null) {
destination = resolveDestinationName(session, getDestinationName());
}
return createConsumer(session, destination);
}
/**
* Execute the listener for a message received from the given consumer,
* wrapping the entire operation in an external transaction if demanded.
* @param session the JMS Session to work on
* @param consumer the MessageConsumer to work on
* @return whether a message has been received
* @throws JMSException if thrown by JMS methods
* @see #doReceiveAndExecute
*/
protected boolean receiveAndExecute(Object invoker, Session session, MessageConsumer consumer)
throws JMSException {
if (this.transactionManager != null) {
// Execute receive within transaction.
TransactionStatus status = this.transactionManager.getTransaction(this.transactionDefinition);
boolean messageReceived;
try {
messageReceived = doReceiveAndExecute(invoker, session, consumer, status);
}
catch (JMSException ex) {
rollbackOnException(status, ex);
throw ex;
}
catch (RuntimeException ex) {
rollbackOnException(status, ex);
throw ex;
}
catch (Error err) {
rollbackOnException(status, err);
throw err;
}
this.transactionManager.commit(status);
return messageReceived;
}
else {
// Execute receive outside of transaction.
return doReceiveAndExecute(invoker, session, consumer, null);
}
}
/**
* Actually execute the listener for a message received from the given consumer,
* fetching all requires resources and invoking the listener.
* @param session the JMS Session to work on
* @param consumer the MessageConsumer to work on
* @param status the TransactionStatus (may be <code>null</code>)
* @return whether a message has been received
* @throws JMSException if thrown by JMS methods
* @see #doExecuteListener(javax.jms.Session, javax.jms.Message)
*/
protected boolean doReceiveAndExecute(
Object invoker, Session session, MessageConsumer consumer, TransactionStatus status)
throws JMSException {
Connection conToClose = null;
Session sessionToClose = null;
MessageConsumer consumerToClose = null;
try {
Session sessionToUse = session;
boolean transactional = false;
if (sessionToUse == null) {
sessionToUse = ConnectionFactoryUtils.doGetTransactionalSession(
getConnectionFactory(), this.transactionalResourceFactory, true);
transactional = (sessionToUse != null);
}
if (sessionToUse == null) {
Connection conToUse;
if (sharedConnectionEnabled()) {
conToUse = getSharedConnection();
}
else {
conToUse = createConnection();
conToClose = conToUse;
conToUse.start();
}
sessionToUse = createSession(conToUse);
sessionToClose = sessionToUse;
}
MessageConsumer consumerToUse = consumer;
if (consumerToUse == null) {
consumerToUse = createListenerConsumer(sessionToUse);
consumerToClose = consumerToUse;
}
Message message = receiveMessage(consumerToUse);
if (message != null) {
if (logger.isDebugEnabled()) {
logger.debug("Received message of type [" + message.getClass() + "] from consumer [" +
consumerToUse + "] of " + (transactional ? "transactional " : "") + "session [" +
sessionToUse + "]");
}
messageReceived(invoker, sessionToUse);
boolean exposeResource = (!transactional && isExposeListenerSession() &&
!TransactionSynchronizationManager.hasResource(getConnectionFactory()));
if (exposeResource) {
TransactionSynchronizationManager.bindResource(
getConnectionFactory(), new LocallyExposedJmsResourceHolder(sessionToUse));
}
try {
doExecuteListener(sessionToUse, message);
}
catch (Throwable ex) {
if (status != null) {
if (logger.isDebugEnabled()) {
logger.debug("Rolling back transaction because of listener exception thrown: " + ex);
}
status.setRollbackOnly();
}
handleListenerException(ex);
// Rethrow JMSException to indicate an infrastructure problem
// that may have to trigger recovery...
if (ex instanceof JMSException) {
throw (JMSException) ex;
}
}
finally {
if (exposeResource) {
TransactionSynchronizationManager.unbindResource(getConnectionFactory());
}
}
// Indicate that a message has been received.
return true;
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Consumer [" + consumerToUse + "] of " + (transactional ? "transactional " : "") +
"session [" + sessionToUse + "] did not receive a message");
}
noMessageReceived(invoker, sessionToUse);
// Nevertheless call commit, in order to reset the transaction timeout (if any).
// However, don't do this on Tibco since this may lead to a deadlock there.
if (shouldCommitAfterNoMessageReceived(sessionToUse)) {
commitIfNecessary(sessionToUse, message);
}
// Indicate that no message has been received.
return false;
}
}
finally {
JmsUtils.closeMessageConsumer(consumerToClose);
JmsUtils.closeSession(sessionToClose);
ConnectionFactoryUtils.releaseConnection(conToClose, getConnectionFactory(), true);
}
}
/**
* This implementation checks whether the Session is externally synchronized.
* In this case, the Session is not locally transacted, despite the listener
* container's "sessionTransacted" flag being set to "true".
* @see org.springframework.jms.connection.JmsResourceHolder
*/
@Override
protected boolean isSessionLocallyTransacted(Session session) {
if (!super.isSessionLocallyTransacted(session)) {
return false;
}
JmsResourceHolder resourceHolder =
(JmsResourceHolder) TransactionSynchronizationManager.getResource(getConnectionFactory());
return (resourceHolder == null || resourceHolder instanceof LocallyExposedJmsResourceHolder ||
!resourceHolder.containsSession(session));
}
/**
* Determine whether to trigger a commit after no message has been received.
* This is a good idea on any JMS provider other than Tibco, which is what
* this default implementation checks for.
* @param session the current JMS Session which received no message
* @return whether to call {@link #commitIfNecessary} on the given Session
*/
protected boolean shouldCommitAfterNoMessageReceived(Session session) {
if (this.commitAfterNoMessageReceived == null) {
Session target = ConnectionFactoryUtils.getTargetSession(session);
this.commitAfterNoMessageReceived = !target.getClass().getName().startsWith("com.tibco.tibjms.");
}
return this.commitAfterNoMessageReceived;
}
/**
* Perform a rollback, handling rollback exceptions properly.
* @param status object representing the transaction
* @param ex the thrown listener exception or error
*/
private void rollbackOnException(TransactionStatus status, Throwable ex) {
logger.debug("Initiating transaction rollback on listener exception", ex);
try {
this.transactionManager.rollback(status);
}
catch (RuntimeException ex2) {
logger.error("Listener exception overridden by rollback exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Listener exception overridden by rollback error", ex);
throw err;
}
}
/**
* Receive a message from the given consumer.
* @param consumer the MessageConsumer to use
* @return the Message, or <code>null</code> if none
* @throws JMSException if thrown by JMS methods
*/
protected Message receiveMessage(MessageConsumer consumer) throws JMSException {
return (this.receiveTimeout < 0 ? consumer.receive() : consumer.receive(this.receiveTimeout));
}
/**
* Template method that gets called right when a new message has been received,
* before attempting to process it. Allows subclasses to react to the event
* of an actual incoming message, for example adapting their consumer count.
* @param invoker the invoker object (passed through)
* @param session the receiving JMS Session
*/
protected void messageReceived(Object invoker, Session session) {
}
/**
* Template method that gets called when <i>no</i> message has been received,
* before returning to the receive loop again. Allows subclasses to react to
* the event of no incoming message, for example marking the invoker as idle.
* @param invoker the invoker object (passed through)
* @param session the receiving JMS Session
*/
protected void noMessageReceived(Object invoker, Session session) {
}
//-------------------------------------------------------------------------
// JMS 1.1 factory methods, potentially overridden for JMS 1.0.2
//-------------------------------------------------------------------------
/**
* Fetch an appropriate Connection from the given JmsResourceHolder.
* <p>This implementation accepts any JMS 1.1 Connection.
* @param holder the JmsResourceHolder
* @return an appropriate Connection fetched from the holder,
* or <code>null</code> if none found
*/
protected Connection getConnection(JmsResourceHolder holder) {
return holder.getConnection();
}
/**
* Fetch an appropriate Session from the given JmsResourceHolder.
* <p>This implementation accepts any JMS 1.1 Session.
* @param holder the JmsResourceHolder
* @return an appropriate Session fetched from the holder,
* or <code>null</code> if none found
*/
protected Session getSession(JmsResourceHolder holder) {
return holder.getSession();
}
/**
* Create a JMS MessageConsumer for the given Session and Destination.
* <p>This implementation uses JMS 1.1 API.
* @param session the JMS Session to create a MessageConsumer for
* @param destination the JMS Destination to create a MessageConsumer for
* @return the new JMS MessageConsumer
* @throws javax.jms.JMSException if thrown by JMS API methods
*/
protected MessageConsumer createConsumer(Session session, Destination destination) throws JMSException {
// Only pass in the NoLocal flag in case of a Topic:
// Some JMS providers, such as WebSphere MQ 6.0, throw IllegalStateException
// in case of the NoLocal flag being specified for a Queue.
if (isPubSubDomain()) {
if (isSubscriptionDurable() && destination instanceof Topic) {
return session.createDurableSubscriber(
(Topic) destination, getDurableSubscriptionName(), getMessageSelector(), isPubSubNoLocal());
}
else {
return session.createConsumer(destination, getMessageSelector(), isPubSubNoLocal());
}
}
else {
return session.createConsumer(destination, getMessageSelector());
}
}
/**
* ResourceFactory implementation that delegates to this listener container's protected callback methods.
*/
private class MessageListenerContainerResourceFactory implements ConnectionFactoryUtils.ResourceFactory {
public Connection getConnection(JmsResourceHolder holder) {
return AbstractPollingMessageListenerContainer.this.getConnection(holder);
}
public Session getSession(JmsResourceHolder holder) {
return AbstractPollingMessageListenerContainer.this.getSession(holder);
}
public Connection createConnection() throws JMSException {
if (AbstractPollingMessageListenerContainer.this.sharedConnectionEnabled()) {
Connection sharedCon = AbstractPollingMessageListenerContainer.this.getSharedConnection();
return new SingleConnectionFactory(sharedCon).createConnection();
}
else {
return AbstractPollingMessageListenerContainer.this.createConnection();
}
}
public Session createSession(Connection con) throws JMSException {
return AbstractPollingMessageListenerContainer.this.createSession(con);
}
public boolean isSynchedLocalTransactionAllowed() {
return AbstractPollingMessageListenerContainer.this.isSessionTransacted();
}
}
}

View File

@@ -1,117 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.listener;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;
import org.springframework.jms.connection.JmsResourceHolder;
/**
* A subclass of {@link DefaultMessageListenerContainer} for the JMS 1.0.2 specification,
* not relying on JMS 1.1 methods like SimpleMessageListenerContainer itself.
*
* <p>This class can be used for JMS 1.0.2 providers, offering the same facility as
* DefaultMessageListenerContainer does for JMS 1.1 providers.
*
* @author Juergen Hoeller
* @since 2.0
* @deprecated as of Spring 3.0, in favor of the JMS 1.1 based {@link DefaultMessageListenerContainer}
*/
@Deprecated
public class DefaultMessageListenerContainer102 extends DefaultMessageListenerContainer {
/**
* This implementation overrides the superclass method to accept either
* a QueueConnection or a TopicConnection, depending on the domain.
*/
protected Connection getConnection(JmsResourceHolder holder) {
return holder.getConnection(isPubSubDomain() ? TopicConnection.class : QueueConnection.class);
}
/**
* This implementation overrides the superclass method to accept either
* a QueueSession or a TopicSession, depending on the domain.
*/
protected Session getSession(JmsResourceHolder holder) {
return holder.getSession(isPubSubDomain() ? TopicSession.class : QueueSession.class);
}
/**
* This implementation overrides the superclass method to use JMS 1.0.2 API.
*/
protected Connection createConnection() throws JMSException {
if (isPubSubDomain()) {
return ((TopicConnectionFactory) getConnectionFactory()).createTopicConnection();
}
else {
return ((QueueConnectionFactory) getConnectionFactory()).createQueueConnection();
}
}
/**
* This implementation overrides the superclass method to use JMS 1.0.2 API.
*/
protected Session createSession(Connection con) throws JMSException {
if (isPubSubDomain()) {
return ((TopicConnection) con).createTopicSession(isSessionTransacted(), getSessionAcknowledgeMode());
}
else {
return ((QueueConnection) con).createQueueSession(isSessionTransacted(), getSessionAcknowledgeMode());
}
}
/**
* This implementation overrides the superclass method to use JMS 1.0.2 API.
*/
protected MessageConsumer createConsumer(Session session, Destination destination) throws JMSException {
if (isPubSubDomain()) {
if (isSubscriptionDurable()) {
return ((TopicSession) session).createDurableSubscriber(
(Topic) destination, getDurableSubscriptionName(), getMessageSelector(), isPubSubNoLocal());
}
else {
return ((TopicSession) session).createSubscriber(
(Topic) destination, getMessageSelector(), isPubSubNoLocal());
}
}
else {
return ((QueueSession) session).createReceiver((Queue) destination, getMessageSelector());
}
}
/**
* This implementation overrides the superclass method to avoid using
* JMS 1.1's Session <code>getAcknowledgeMode()</code> method.
* The best we can do here is to check the setting on the listener container.
*/
protected boolean isClientAcknowledge(Session session) throws JMSException {
return (getSessionAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE);
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright 2002-2008 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.springframework.jms.listener;
import javax.jms.Session;
import org.springframework.jms.connection.JmsResourceHolder;
/**
* JmsResourceHolder marker subclass that indicates local exposure,
* i.e. that does not indicate an externally managed transaction.
*
* @author Juergen Hoeller
* @since 2.5.2
*/
class LocallyExposedJmsResourceHolder extends JmsResourceHolder {
public LocallyExposedJmsResourceHolder(Session session) {
super(session);
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.listener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
/**
* Variant of the standard JMS {@link javax.jms.MessageListener} interface,
* offering not only the received Message but also the underlying
* JMS Session object. The latter can be used to send reply messages,
* without the need to access an external Connection/Session,
* i.e. without the need to access the underlying ConnectionFactory.
*
* <p>Supported by Spring's {@link DefaultMessageListenerContainer}
* and {@link SimpleMessageListenerContainer},
* as direct alternative to the standard JMS MessageListener interface.
* Typically <i>not</i> supported by JCA-based listener containers:
* For maximum compatibility, implement a standard JMS MessageListener instead.
*
* @author Juergen Hoeller
* @since 2.0
* @see AbstractMessageListenerContainer#setMessageListener
* @see DefaultMessageListenerContainer
* @see SimpleMessageListenerContainer
* @see org.springframework.jms.listener.endpoint.JmsMessageEndpointManager
* @see javax.jms.MessageListener
*/
public interface SessionAwareMessageListener<M extends Message> {
/**
* Callback for processing a received JMS message.
* <p>Implementors are supposed to process the given Message,
* typically sending reply messages through the given Session.
* @param message the received JMS message (never <code>null</code>)
* @param session the underlying JMS Session (never <code>null</code>)
* @throws JMSException if thrown by JMS methods
*/
void onMessage(M message, Session session) throws JMSException;
}

View File

@@ -1,395 +0,0 @@
/*
* Copyright 2002-2012 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.springframework.jms.listener;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executor;
import javax.jms.Connection;
import javax.jms.Destination;
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.Topic;
import org.springframework.jms.support.JmsUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
/**
* Message listener container that uses the plain JMS client API's
* <code>MessageConsumer.setMessageListener()</code> method to
* create concurrent MessageConsumers for the specified listeners.
*
* <p>This is the simplest form of a message listener container.
* It creates a fixed number of JMS Sessions to invoke the listener,
* not allowing for dynamic adaptation to runtime demands. Its main
* advantage is its low level of complexity and the minimum requirements
* on the JMS provider: Not even the ServerSessionPool facility is required.
*
* <p>See the {@link AbstractMessageListenerContainer} javadoc for details
* on acknowledge modes and transaction options.
*
* <p>For a different style of MessageListener handling, through looped
* <code>MessageConsumer.receive()</code> calls that also allow for
* transactional reception of messages (registering them with XA transactions),
* see {@link DefaultMessageListenerContainer}.
*
* @author Juergen Hoeller
* @since 2.0
* @see javax.jms.MessageConsumer#setMessageListener
* @see DefaultMessageListenerContainer
* @see org.springframework.jms.listener.endpoint.JmsMessageEndpointManager
*/
public class SimpleMessageListenerContainer extends AbstractMessageListenerContainer implements ExceptionListener {
private boolean pubSubNoLocal = false;
private boolean connectLazily = false;
private int concurrentConsumers = 1;
private Executor taskExecutor;
private Set<Session> sessions;
private Set<MessageConsumer> consumers;
private final Object consumersMonitor = new Object();
/**
* Set whether to inhibit the delivery of messages published by its own connection.
* Default is "false".
* @see javax.jms.TopicSession#createSubscriber(javax.jms.Topic, String, boolean)
*/
public void setPubSubNoLocal(boolean pubSubNoLocal) {
this.pubSubNoLocal = pubSubNoLocal;
}
/**
* Return whether to inhibit the delivery of messages published by its own connection.
*/
protected boolean isPubSubNoLocal() {
return this.pubSubNoLocal;
}
/**
* Specify whether to connect lazily, i.e. whether to establish the JMS Connection
* and the corresponding Sessions and MessageConsumers as late as possible -
* in the start phase of this container.
* <p>Default is "false": connecting early, i.e. during the bean initialization phase.
* Set this flag to "true" in order to switch to lazy connecting if your target broker
* is likely to not have started up yet and you prefer to not even try a connection.
* @see #start()
* @see #initialize()
*/
public void setConnectLazily(boolean connectLazily) {
this.connectLazily = connectLazily;
}
/**
* Specify concurrency limits via a "lower-upper" String, e.g. "5-10", or a simple
* upper limit String, e.g. "10".
* <p>This listener container will always hold on to the maximum number of
* consumers {@link #setConcurrentConsumers} since it is unable to scale.
* <p>This property is primarily supported for configuration compatibility with
* {@link DefaultMessageListenerContainer}. For this local listener container,
* generally use {@link #setConcurrentConsumers} instead.
*/
public void setConcurrency(String concurrency) {
try {
int separatorIndex = concurrency.indexOf('-');
if (separatorIndex != -1) {
setConcurrentConsumers(Integer.parseInt(concurrency.substring(separatorIndex + 1, concurrency.length())));
}
else {
setConcurrentConsumers(Integer.parseInt(concurrency));
}
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException("Invalid concurrency value [" + concurrency + "]: only " +
"single maximum integer (e.g. \"5\") and minimum-maximum combo (e.g. \"3-5\") supported. " +
"Note that SimpleMessageListenerContainer will effectively ignore the minimum value and " +
"always keep a fixed number of consumers according to the maximum value.");
}
}
/**
* Specify the number of concurrent consumers to create. Default is 1.
* <p>Raising the number of concurrent consumers is recommendable in order
* to scale the consumption of messages coming in from a queue. However,
* note that any ordering guarantees are lost once multiple consumers are
* registered. In general, stick with 1 consumer for low-volume queues.
* <p><b>Do not raise the number of concurrent consumers for a topic.</b>
* This would lead to concurrent consumption of the same message,
* which is hardly ever desirable.
*/
public void setConcurrentConsumers(int concurrentConsumers) {
Assert.isTrue(concurrentConsumers > 0, "'concurrentConsumers' value must be at least 1 (one)");
this.concurrentConsumers = concurrentConsumers;
}
/**
* Set the Spring TaskExecutor to use for executing the listener once
* a message has been received by the provider.
* <p>Default is none, that is, to run in the JMS provider's own receive thread,
* blocking the provider's receive endpoint while executing the listener.
* <p>Specify a TaskExecutor for executing the listener in a different thread,
* rather than blocking the JMS provider, usually integrating with an existing
* thread pool. This allows to keep the number of concurrent consumers low (1)
* while still processing messages concurrently (decoupled from receiving!).
* <p><b>NOTE: Specifying a TaskExecutor for listener execution affects
* acknowledgement semantics.</b> Messages will then always get acknowledged
* before listener execution, with the underlying Session immediately reused
* for receiving the next message. Using this in combination with a transacted
* session or with client acknowledgement will lead to unspecified results!
* <p><b>NOTE: Concurrent listener execution via a TaskExecutor will lead
* to concurrent processing of messages that have been received by the same
* underlying Session.</b> As a consequence, it is not recommended to use
* this setting with a {@link SessionAwareMessageListener}, at least not
* if the latter performs actual work on the given Session. A standard
* {@link javax.jms.MessageListener} will work fine, in general.
* @see #setConcurrentConsumers
* @see org.springframework.core.task.SimpleAsyncTaskExecutor
* @see org.springframework.scheduling.commonj.WorkManagerTaskExecutor
*/
public void setTaskExecutor(Executor taskExecutor) {
this.taskExecutor = taskExecutor;
}
@Override
protected void validateConfiguration() {
super.validateConfiguration();
if (isSubscriptionDurable() && this.concurrentConsumers != 1) {
throw new IllegalArgumentException("Only 1 concurrent consumer supported for durable subscription");
}
}
//-------------------------------------------------------------------------
// Implementation of AbstractMessageListenerContainer's template methods
//-------------------------------------------------------------------------
/**
* Always use a shared JMS Connection.
*/
@Override
protected final boolean sharedConnectionEnabled() {
return true;
}
/**
* Creates the specified number of concurrent consumers,
* in the form of a JMS Session plus associated MessageConsumer.
* @see #createListenerConsumer
*/
@Override
protected void doInitialize() throws JMSException {
if (!this.connectLazily) {
try {
establishSharedConnection();
}
catch (JMSException ex) {
logger.debug("Could not connect on initialization - registering message consumers lazily", ex);
return;
}
initializeConsumers();
}
}
/**
* Re-initializes this container's JMS message consumers,
* if not initialized already.
*/
@Override
protected void doStart() throws JMSException {
super.doStart();
initializeConsumers();
}
/**
* Registers this listener container as JMS ExceptionListener on the shared connection.
*/
@Override
protected void prepareSharedConnection(Connection connection) throws JMSException {
super.prepareSharedConnection(connection);
connection.setExceptionListener(this);
}
/**
* JMS ExceptionListener implementation, invoked by the JMS provider in
* case of connection failures. Re-initializes this listener container's
* shared connection and its sessions and consumers.
* @param ex the reported connection exception
*/
public void onException(JMSException ex) {
// First invoke the user-specific ExceptionListener, if any.
invokeExceptionListener(ex);
// Now try to recover the shared Connection and all consumers...
if (logger.isInfoEnabled()) {
logger.info("Trying to recover from JMS Connection exception: " + ex);
}
try {
synchronized (this.consumersMonitor) {
this.sessions = null;
this.consumers = null;
}
refreshSharedConnection();
initializeConsumers();
logger.info("Successfully refreshed JMS Connection");
}
catch (JMSException recoverEx) {
logger.debug("Failed to recover JMS Connection", recoverEx);
logger.error("Encountered non-recoverable JMSException", ex);
}
}
/**
* Initialize the JMS Sessions and MessageConsumers for this container.
* @throws JMSException in case of setup failure
*/
protected void initializeConsumers() throws JMSException {
// Register Sessions and MessageConsumers.
synchronized (this.consumersMonitor) {
if (this.consumers == null) {
this.sessions = new HashSet<Session>(this.concurrentConsumers);
this.consumers = new HashSet<MessageConsumer>(this.concurrentConsumers);
Connection con = getSharedConnection();
for (int i = 0; i < this.concurrentConsumers; i++) {
Session session = createSession(con);
MessageConsumer consumer = createListenerConsumer(session);
this.sessions.add(session);
this.consumers.add(consumer);
}
}
}
}
/**
* Create a MessageConsumer for the given JMS Session,
* registering a MessageListener for the specified listener.
* @param session the JMS Session to work on
* @return the MessageConsumer
* @throws JMSException if thrown by JMS methods
* @see #executeListener
*/
protected MessageConsumer createListenerConsumer(final Session session) throws JMSException {
Destination destination = getDestination();
if (destination == null) {
destination = resolveDestinationName(session, getDestinationName());
}
MessageConsumer consumer = createConsumer(session, destination);
if (this.taskExecutor != null) {
consumer.setMessageListener(new MessageListener() {
public void onMessage(final Message message) {
taskExecutor.execute(new Runnable() {
public void run() {
processMessage(message, session);
}
});
}
});
}
else {
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
processMessage(message, session);
}
});
}
return consumer;
}
/**
* Process a message received from the provider.
* <p>Executes the listener, exposing the current JMS Session as
* thread-bound resource (if "exposeListenerSession" is "true").
* @param message the received JMS Message
* @param session the JMS Session to operate on
* @see #executeListener
* @see #setExposeListenerSession
*/
protected void processMessage(Message message, Session session) {
boolean exposeResource = isExposeListenerSession();
if (exposeResource) {
TransactionSynchronizationManager.bindResource(
getConnectionFactory(), new LocallyExposedJmsResourceHolder(session));
}
try {
executeListener(session, message);
}
finally {
if (exposeResource) {
TransactionSynchronizationManager.unbindResource(getConnectionFactory());
}
}
}
/**
* Destroy the registered JMS Sessions and associated MessageConsumers.
*/
@Override
protected void doShutdown() throws JMSException {
logger.debug("Closing JMS MessageConsumers");
for (MessageConsumer consumer : this.consumers) {
JmsUtils.closeMessageConsumer(consumer);
}
logger.debug("Closing JMS Sessions");
for (Session session : this.sessions) {
JmsUtils.closeSession(session);
}
}
//-------------------------------------------------------------------------
// JMS 1.1 factory methods, potentially overridden for JMS 1.0.2
//-------------------------------------------------------------------------
/**
* Create a JMS MessageConsumer for the given Session and Destination.
* <p>This implementation uses JMS 1.1 API.
* @param session the JMS Session to create a MessageConsumer for
* @param destination the JMS Destination to create a MessageConsumer for
* @return the new JMS MessageConsumer
* @throws JMSException if thrown by JMS API methods
*/
protected MessageConsumer createConsumer(Session session, Destination destination) throws JMSException {
// Only pass in the NoLocal flag in case of a Topic:
// Some JMS providers, such as WebSphere MQ 6.0, throw IllegalStateException
// in case of the NoLocal flag being specified for a Queue.
if (isPubSubDomain()) {
if (isSubscriptionDurable() && destination instanceof Topic) {
return session.createDurableSubscriber(
(Topic) destination, getDurableSubscriptionName(), getMessageSelector(), isPubSubNoLocal());
}
else {
return session.createConsumer(destination, getMessageSelector(), isPubSubNoLocal());
}
}
else {
return session.createConsumer(destination, getMessageSelector());
}
}
}

View File

@@ -1,99 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.listener;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;
/**
* A subclass of {@link SimpleMessageListenerContainer} for the JMS 1.0.2 specification,
* not relying on JMS 1.1 methods like SimpleMessageListenerContainer itself.
*
* <p>This class can be used for JMS 1.0.2 providers, offering the same facility as
* SimpleMessageListenerContainer does for JMS 1.1 providers.
*
* @author Juergen Hoeller
* @since 2.0
* @deprecated as of Spring 3.0, in favor of the JMS 1.1 based {@link SimpleMessageListenerContainer}
*/
@Deprecated
public class SimpleMessageListenerContainer102 extends SimpleMessageListenerContainer {
/**
* This implementation overrides the superclass method to use JMS 1.0.2 API.
*/
protected Connection createConnection() throws JMSException {
if (isPubSubDomain()) {
return ((TopicConnectionFactory) getConnectionFactory()).createTopicConnection();
}
else {
return ((QueueConnectionFactory) getConnectionFactory()).createQueueConnection();
}
}
/**
* This implementation overrides the superclass method to use JMS 1.0.2 API.
*/
protected Session createSession(Connection con) throws JMSException {
if (isPubSubDomain()) {
return ((TopicConnection) con).createTopicSession(isSessionTransacted(), getSessionAcknowledgeMode());
}
else {
return ((QueueConnection) con).createQueueSession(isSessionTransacted(), getSessionAcknowledgeMode());
}
}
/**
* This implementation overrides the superclass method to use JMS 1.0.2 API.
*/
protected MessageConsumer createConsumer(Session session, Destination destination) throws JMSException {
if (isPubSubDomain()) {
if (isSubscriptionDurable()) {
return ((TopicSession) session).createDurableSubscriber(
(Topic) destination, getDurableSubscriptionName(), getMessageSelector(), isPubSubNoLocal());
}
else {
return ((TopicSession) session).createSubscriber(
(Topic) destination, getMessageSelector(), isPubSubNoLocal());
}
}
else {
return ((QueueSession) session).createReceiver((Queue) destination, getMessageSelector());
}
}
/**
* This implementation overrides the superclass method to avoid using
* JMS 1.1's Session <code>getAcknowledgeMode()</code> method.
* The best we can do here is to check the setting on the listener container.
*/
protected boolean isClientAcknowledge(Session session) throws JMSException {
return (getSessionAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE);
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2002-2008 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.springframework.jms.listener;
/**
* Interface to be implemented by message listener objects that suggest a specific
* name for a durable subscription that they might be registered with. Otherwise
* the listener class name will be used as a default subscription name.
*
* <p>Applies to {@link javax.jms.MessageListener} objects as well as to
* {@link SessionAwareMessageListener} objects and plain listener methods
* (as supported by {@link org.springframework.jms.listener.adapter.MessageListenerAdapter}.
*
* @author Juergen Hoeller
* @since 2.5.6
*/
public interface SubscriptionNameProvider {
/**
* Determine the subscription name for this message listener object.
*/
String getSubscriptionName();
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2002-2006 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.springframework.jms.listener.adapter;
import org.springframework.jms.JmsException;
/**
* Exception to be thrown when the execution of a listener method failed.
*
* @author Juergen Hoeller
* @since 2.0
* @see MessageListenerAdapter
*/
public class ListenerExecutionFailedException extends JmsException {
/**
* Constructor for ListenerExecutionFailedException.
* @param msg the detail message
* @param cause the exception thrown by the listener method
*/
public ListenerExecutionFailedException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,653 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.listener.adapter;
import java.lang.reflect.InvocationTargetException;
import javax.jms.Destination;
import javax.jms.InvalidDestinationException;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jms.listener.SessionAwareMessageListener;
import org.springframework.jms.listener.SubscriptionNameProvider;
import org.springframework.jms.support.JmsUtils;
import org.springframework.jms.support.converter.MessageConversionException;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.SimpleMessageConverter;
import org.springframework.jms.support.destination.DestinationResolver;
import org.springframework.jms.support.destination.DynamicDestinationResolver;
import org.springframework.util.Assert;
import org.springframework.util.MethodInvoker;
import org.springframework.util.ObjectUtils;
/**
* Message listener adapter that delegates the handling of messages to target
* listener methods via reflection, with flexible message type conversion.
* Allows listener methods to operate on message content types, completely
* independent from the JMS API.
*
* <p>By default, the content of incoming JMS messages gets extracted before
* being passed into the target listener method, to let the target method
* operate on message content types such as String or byte array instead of
* the raw {@link Message}. Message type conversion is delegated to a Spring
* JMS {@link MessageConverter}. By default, a {@link SimpleMessageConverter}
* will be used. (If you do not want such automatic message conversion taking
* place, then be sure to set the {@link #setMessageConverter MessageConverter}
* to <code>null</code>.)
*
* <p>If a target listener method returns a non-null object (typically of a
* message content type such as <code>String</code> or byte array), it will get
* wrapped in a JMS <code>Message</code> and sent to the response destination
* (either the JMS "reply-to" destination or a
* {@link #setDefaultResponseDestination(javax.jms.Destination) specified default
* destination}).
*
* <p><b>Note:</b> The sending of response messages is only available when
* using the {@link SessionAwareMessageListener} entry point (typically through a
* Spring message listener container). Usage as standard JMS {@link MessageListener}
* does <i>not</i> support the generation of response messages.
*
* <p>Find below some examples of method signatures compliant with this
* adapter class. This first example handles all <code>Message</code> types
* and gets passed the contents of each <code>Message</code> type as an
* argument. No <code>Message</code> will be sent back as all of these
* methods return <code>void</code>.
*
* <pre class="code">public interface MessageContentsDelegate {
* void handleMessage(String text);
* void handleMessage(Map map);
* void handleMessage(byte[] bytes);
* void handleMessage(Serializable obj);
* }</pre>
*
* This next example handles all <code>Message</code> types and gets
* passed the actual (raw) <code>Message</code> as an argument. Again, no
* <code>Message</code> will be sent back as all of these methods return
* <code>void</code>.
*
* <pre class="code">public interface RawMessageDelegate {
* void handleMessage(TextMessage message);
* void handleMessage(MapMessage message);
* void handleMessage(BytesMessage message);
* void handleMessage(ObjectMessage message);
* }</pre>
*
* This next example illustrates a <code>Message</code> delegate
* that just consumes the <code>String</code> contents of
* {@link javax.jms.TextMessage TextMessages}. Notice also how the
* name of the <code>Message</code> handling method is different from the
* {@link #ORIGINAL_DEFAULT_LISTENER_METHOD original} (this will have to
* be configured in the attandant bean definition). Again, no <code>Message</code>
* will be sent back as the method returns <code>void</code>.
*
* <pre class="code">public interface TextMessageContentDelegate {
* void onMessage(String text);
* }</pre>
*
* This final example illustrates a <code>Message</code> delegate
* that just consumes the <code>String</code> contents of
* {@link javax.jms.TextMessage TextMessages}. Notice how the return type
* of this method is <code>String</code>: This will result in the configured
* {@link MessageListenerAdapter} sending a {@link javax.jms.TextMessage} in response.
*
* <pre class="code">public interface ResponsiveTextMessageContentDelegate {
* String handleMessage(String text);
* }</pre>
*
* For further examples and discussion please do refer to the Spring
* reference documentation which describes this class (and it's attendant
* XML configuration) in detail.
*
* @author Juergen Hoeller
* @since 2.0
* @see #setDelegate
* @see #setDefaultListenerMethod
* @see #setDefaultResponseDestination
* @see #setMessageConverter
* @see org.springframework.jms.support.converter.SimpleMessageConverter
* @see org.springframework.jms.listener.SessionAwareMessageListener
* @see org.springframework.jms.listener.AbstractMessageListenerContainer#setMessageListener
*/
public class MessageListenerAdapter
implements MessageListener, SessionAwareMessageListener<Message>, SubscriptionNameProvider {
/**
* Out-of-the-box value for the default listener method: "handleMessage".
*/
public static final String ORIGINAL_DEFAULT_LISTENER_METHOD = "handleMessage";
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private Object delegate;
private String defaultListenerMethod = ORIGINAL_DEFAULT_LISTENER_METHOD;
private Object defaultResponseDestination;
private DestinationResolver destinationResolver = new DynamicDestinationResolver();
private MessageConverter messageConverter;
/**
* Create a new {@link MessageListenerAdapter} with default settings.
*/
public MessageListenerAdapter() {
initDefaultStrategies();
this.delegate = this;
}
/**
* Create a new {@link MessageListenerAdapter} for the given delegate.
* @param delegate the delegate object
*/
public MessageListenerAdapter(Object delegate) {
initDefaultStrategies();
setDelegate(delegate);
}
/**
* Set a target object to delegate message listening to.
* Specified listener methods have to be present on this target object.
* <p>If no explicit delegate object has been specified, listener
* methods are expected to present on this adapter instance, that is,
* on a custom subclass of this adapter, defining listener methods.
*/
public void setDelegate(Object delegate) {
Assert.notNull(delegate, "Delegate must not be null");
this.delegate = delegate;
}
/**
* Return the target object to delegate message listening to.
*/
protected Object getDelegate() {
return this.delegate;
}
/**
* Specify the name of the default listener method to delegate to,
* for the case where no specific listener method has been determined.
* Out-of-the-box value is {@link #ORIGINAL_DEFAULT_LISTENER_METHOD "handleMessage"}.
* @see #getListenerMethodName
*/
public void setDefaultListenerMethod(String defaultListenerMethod) {
this.defaultListenerMethod = defaultListenerMethod;
}
/**
* Return the name of the default listener method to delegate to.
*/
protected String getDefaultListenerMethod() {
return this.defaultListenerMethod;
}
/**
* Set the default destination to send response messages to. This will be applied
* in case of a request message that does not carry a "JMSReplyTo" field.
* <p>Response destinations are only relevant for listener methods that return
* result objects, which will be wrapped in a response message and sent to a
* response destination.
* <p>Alternatively, specify a "defaultResponseQueueName" or "defaultResponseTopicName",
* to be dynamically resolved via the DestinationResolver.
* @see #setDefaultResponseQueueName(String)
* @see #setDefaultResponseTopicName(String)
* @see #getResponseDestination
*/
public void setDefaultResponseDestination(Destination destination) {
this.defaultResponseDestination = destination;
}
/**
* Set the name of the default response queue to send response messages to.
* This will be applied in case of a request message that does not carry a
* "JMSReplyTo" field.
* <p>Alternatively, specify a JMS Destination object as "defaultResponseDestination".
* @see #setDestinationResolver
* @see #setDefaultResponseDestination(javax.jms.Destination)
*/
public void setDefaultResponseQueueName(String destinationName) {
this.defaultResponseDestination = new DestinationNameHolder(destinationName, false);
}
/**
* Set the name of the default response topic to send response messages to.
* This will be applied in case of a request message that does not carry a
* "JMSReplyTo" field.
* <p>Alternatively, specify a JMS Destination object as "defaultResponseDestination".
* @see #setDestinationResolver
* @see #setDefaultResponseDestination(javax.jms.Destination)
*/
public void setDefaultResponseTopicName(String destinationName) {
this.defaultResponseDestination = new DestinationNameHolder(destinationName, true);
}
/**
* Set the DestinationResolver that should be used to resolve response
* destination names for this adapter.
* <p>The default resolver is a DynamicDestinationResolver. Specify a
* JndiDestinationResolver for resolving destination names as JNDI locations.
* @see org.springframework.jms.support.destination.DynamicDestinationResolver
* @see org.springframework.jms.support.destination.JndiDestinationResolver
*/
public void setDestinationResolver(DestinationResolver destinationResolver) {
Assert.notNull(destinationResolver, "DestinationResolver must not be null");
this.destinationResolver = destinationResolver;
}
/**
* Return the DestinationResolver for this adapter.
*/
protected DestinationResolver getDestinationResolver() {
return this.destinationResolver;
}
/**
* Set the converter that will convert incoming JMS messages to
* listener method arguments, and objects returned from listener
* methods back to JMS messages.
* <p>The default converter is a {@link SimpleMessageConverter}, which is able
* to handle {@link javax.jms.BytesMessage BytesMessages},
* {@link javax.jms.TextMessage TextMessages} and
* {@link javax.jms.ObjectMessage ObjectMessages}.
*/
public void setMessageConverter(MessageConverter messageConverter) {
this.messageConverter = messageConverter;
}
/**
* Return the converter that will convert incoming JMS messages to
* listener method arguments, and objects returned from listener
* methods back to JMS messages.
*/
protected MessageConverter getMessageConverter() {
return this.messageConverter;
}
/**
* Standard JMS {@link MessageListener} entry point.
* <p>Delegates the message to the target listener method, with appropriate
* conversion of the message argument. In case of an exception, the
* {@link #handleListenerException(Throwable)} method will be invoked.
* <p><b>Note:</b> Does not support sending response messages based on
* result objects returned from listener methods. Use the
* {@link SessionAwareMessageListener} entry point (typically through a Spring
* message listener container) for handling result objects as well.
* @param message the incoming JMS message
* @see #handleListenerException
* @see #onMessage(javax.jms.Message, javax.jms.Session)
*/
public void onMessage(Message message) {
try {
onMessage(message, null);
}
catch (Throwable ex) {
handleListenerException(ex);
}
}
/**
* Spring {@link SessionAwareMessageListener} entry point.
* <p>Delegates the message to the target listener method, with appropriate
* conversion of the message argument. If the target method returns a
* non-null object, wrap in a JMS message and send it back.
* @param message the incoming JMS message
* @param session the JMS session to operate on
* @throws JMSException if thrown by JMS API methods
*/
@SuppressWarnings("unchecked")
public void onMessage(Message message, Session session) throws JMSException {
// Check whether the delegate is a MessageListener impl itself.
// In that case, the adapter will simply act as a pass-through.
Object delegate = getDelegate();
if (delegate != this) {
if (delegate instanceof SessionAwareMessageListener) {
if (session != null) {
((SessionAwareMessageListener) delegate).onMessage(message, session);
return;
}
else if (!(delegate instanceof MessageListener)) {
throw new javax.jms.IllegalStateException("MessageListenerAdapter cannot handle a " +
"SessionAwareMessageListener delegate if it hasn't been invoked with a Session itself");
}
}
if (delegate instanceof MessageListener) {
((MessageListener) delegate).onMessage(message);
return;
}
}
// Regular case: find a handler method reflectively.
Object convertedMessage = extractMessage(message);
String methodName = getListenerMethodName(message, convertedMessage);
if (methodName == null) {
throw new javax.jms.IllegalStateException("No default listener method specified: " +
"Either specify a non-null value for the 'defaultListenerMethod' property or " +
"override the 'getListenerMethodName' method.");
}
// Invoke the handler method with appropriate arguments.
Object[] listenerArguments = buildListenerArguments(convertedMessage);
Object result = invokeListenerMethod(methodName, listenerArguments);
if (result != null) {
handleResult(result, message, session);
}
else {
logger.trace("No result object given - no result to handle");
}
}
public String getSubscriptionName() {
Object delegate = getDelegate();
if (delegate != this && delegate instanceof SubscriptionNameProvider) {
return ((SubscriptionNameProvider) delegate).getSubscriptionName();
}
else {
return delegate.getClass().getName();
}
}
/**
* Initialize the default implementations for the adapter's strategies.
* @see #setMessageConverter
* @see org.springframework.jms.support.converter.SimpleMessageConverter
*/
protected void initDefaultStrategies() {
setMessageConverter(new SimpleMessageConverter());
}
/**
* Handle the given exception that arose during listener execution.
* The default implementation logs the exception at error level.
* <p>This method only applies when used as standard JMS {@link MessageListener}.
* In case of the Spring {@link SessionAwareMessageListener} mechanism,
* exceptions get handled by the caller instead.
* @param ex the exception to handle
* @see #onMessage(javax.jms.Message)
*/
protected void handleListenerException(Throwable ex) {
logger.error("Listener execution failed", ex);
}
/**
* Extract the message body from the given JMS message.
* @param message the JMS <code>Message</code>
* @return the content of the message, to be passed into the
* listener method as argument
* @throws JMSException if thrown by JMS API methods
*/
protected Object extractMessage(Message message) throws JMSException {
MessageConverter converter = getMessageConverter();
if (converter != null) {
return converter.fromMessage(message);
}
return message;
}
/**
* Determine the name of the listener method that is supposed to
* handle the given message.
* <p>The default implementation simply returns the configured
* default listener method, if any.
* @param originalMessage the JMS request message
* @param extractedMessage the converted JMS request message,
* to be passed into the listener method as argument
* @return the name of the listener method (never <code>null</code>)
* @throws JMSException if thrown by JMS API methods
* @see #setDefaultListenerMethod
*/
protected String getListenerMethodName(Message originalMessage, Object extractedMessage) throws JMSException {
return getDefaultListenerMethod();
}
/**
* Build an array of arguments to be passed into the target listener method.
* Allows for multiple method arguments to be built from a single message object.
* <p>The default implementation builds an array with the given message object
* as sole element. This means that the extracted message will always be passed
* into a <i>single</i> method argument, even if it is an array, with the target
* method having a corresponding single argument of the array's type declared.
* <p>This can be overridden to treat special message content such as arrays
* differently, for example passing in each element of the message array
* as distinct method argument.
* @param extractedMessage the content of the message
* @return the array of arguments to be passed into the
* listener method (each element of the array corresponding
* to a distinct method argument)
*/
protected Object[] buildListenerArguments(Object extractedMessage) {
return new Object[] {extractedMessage};
}
/**
* Invoke the specified listener method.
* @param methodName the name of the listener method
* @param arguments the message arguments to be passed in
* @return the result returned from the listener method
* @throws JMSException if thrown by JMS API methods
* @see #getListenerMethodName
* @see #buildListenerArguments
*/
protected Object invokeListenerMethod(String methodName, Object[] arguments) throws JMSException {
try {
MethodInvoker methodInvoker = new MethodInvoker();
methodInvoker.setTargetObject(getDelegate());
methodInvoker.setTargetMethod(methodName);
methodInvoker.setArguments(arguments);
methodInvoker.prepare();
return methodInvoker.invoke();
}
catch (InvocationTargetException ex) {
Throwable targetEx = ex.getTargetException();
if (targetEx instanceof JMSException) {
throw (JMSException) targetEx;
}
else {
throw new ListenerExecutionFailedException(
"Listener method '" + methodName + "' threw exception", targetEx);
}
}
catch (Throwable ex) {
throw new ListenerExecutionFailedException("Failed to invoke target method '" + methodName +
"' with arguments " + ObjectUtils.nullSafeToString(arguments), ex);
}
}
/**
* Handle the given result object returned from the listener method,
* sending a response message back.
* @param result the result object to handle (never <code>null</code>)
* @param request the original request message
* @param session the JMS Session to operate on (may be <code>null</code>)
* @throws JMSException if thrown by JMS API methods
* @see #buildMessage
* @see #postProcessResponse
* @see #getResponseDestination
* @see #sendResponse
*/
protected void handleResult(Object result, Message request, Session session) throws JMSException {
if (session != null) {
if (logger.isDebugEnabled()) {
logger.debug("Listener method returned result [" + result +
"] - generating response message for it");
}
Message response = buildMessage(session, result);
postProcessResponse(request, response);
Destination destination = getResponseDestination(request, response, session);
sendResponse(session, destination, response);
}
else {
if (logger.isWarnEnabled()) {
logger.warn("Listener method returned result [" + result +
"]: not generating response message for it because of no JMS Session given");
}
}
}
/**
* Build a JMS message to be sent as response based on the given result object.
* @param session the JMS Session to operate on
* @param result the content of the message, as returned from the listener method
* @return the JMS <code>Message</code> (never <code>null</code>)
* @throws JMSException if thrown by JMS API methods
* @see #setMessageConverter
*/
protected Message buildMessage(Session session, Object result) throws JMSException {
MessageConverter converter = getMessageConverter();
if (converter != null) {
return converter.toMessage(result, session);
}
else {
if (!(result instanceof Message)) {
throw new MessageConversionException(
"No MessageConverter specified - cannot handle message [" + result + "]");
}
return (Message) result;
}
}
/**
* Post-process the given response message before it will be sent.
* <p>The default implementation sets the response's correlation id
* to the request message's correlation id, if any; otherwise to the
* request message id.
* @param request the original incoming JMS message
* @param response the outgoing JMS message about to be sent
* @throws JMSException if thrown by JMS API methods
* @see javax.jms.Message#setJMSCorrelationID
*/
protected void postProcessResponse(Message request, Message response) throws JMSException {
String correlation = request.getJMSCorrelationID();
if (correlation == null) {
correlation = request.getJMSMessageID();
}
response.setJMSCorrelationID(correlation);
}
/**
* Determine a response destination for the given message.
* <p>The default implementation first checks the JMS Reply-To
* {@link Destination} of the supplied request; if that is not <code>null</code>
* it is returned; if it is <code>null</code>, then the configured
* {@link #resolveDefaultResponseDestination default response destination}
* is returned; if this too is <code>null</code>, then an
* {@link InvalidDestinationException} is thrown.
* @param request the original incoming JMS message
* @param response the outgoing JMS message about to be sent
* @param session the JMS Session to operate on
* @return the response destination (never <code>null</code>)
* @throws JMSException if thrown by JMS API methods
* @throws InvalidDestinationException if no {@link Destination} can be determined
* @see #setDefaultResponseDestination
* @see javax.jms.Message#getJMSReplyTo()
*/
protected Destination getResponseDestination(Message request, Message response, Session session)
throws JMSException {
Destination replyTo = request.getJMSReplyTo();
if (replyTo == null) {
replyTo = resolveDefaultResponseDestination(session);
if (replyTo == null) {
throw new InvalidDestinationException("Cannot determine response destination: " +
"Request message does not contain reply-to destination, and no default response destination set.");
}
}
return replyTo;
}
/**
* Resolve the default response destination into a JMS {@link Destination}, using this
* accessor's {@link DestinationResolver} in case of a destination name.
* @return the located {@link Destination}
* @throws javax.jms.JMSException if resolution failed
* @see #setDefaultResponseDestination
* @see #setDefaultResponseQueueName
* @see #setDefaultResponseTopicName
* @see #setDestinationResolver
*/
protected Destination resolveDefaultResponseDestination(Session session) throws JMSException {
if (this.defaultResponseDestination instanceof Destination) {
return (Destination) this.defaultResponseDestination;
}
if (this.defaultResponseDestination instanceof DestinationNameHolder) {
DestinationNameHolder nameHolder = (DestinationNameHolder) this.defaultResponseDestination;
return getDestinationResolver().resolveDestinationName(session, nameHolder.name, nameHolder.isTopic);
}
return null;
}
/**
* Send the given response message to the given destination.
* @param response the JMS message to send
* @param destination the JMS destination to send to
* @param session the JMS session to operate on
* @throws JMSException if thrown by JMS API methods
* @see #postProcessProducer
* @see javax.jms.Session#createProducer
* @see javax.jms.MessageProducer#send
*/
protected void sendResponse(Session session, Destination destination, Message response) throws JMSException {
MessageProducer producer = session.createProducer(destination);
try {
postProcessProducer(producer, response);
producer.send(response);
}
finally {
JmsUtils.closeMessageProducer(producer);
}
}
/**
* Post-process the given message producer before using it to send the response.
* <p>The default implementation is empty.
* @param producer the JMS message producer that will be used to send the message
* @param response the outgoing JMS message about to be sent
* @throws JMSException if thrown by JMS API methods
*/
protected void postProcessProducer(MessageProducer producer, Message response) throws JMSException {
}
/**
* Internal class combining a destination name
* and its target destination type (queue or topic).
*/
private static class DestinationNameHolder {
public final String name;
public final boolean isTopic;
public DestinationNameHolder(String name, boolean isTopic) {
this.name = name;
this.isTopic = isTopic;
}
}
}

View File

@@ -1,100 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.listener.adapter;
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.QueueSession;
import javax.jms.Session;
import javax.jms.Topic;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import org.springframework.jms.support.JmsUtils;
import org.springframework.jms.support.converter.SimpleMessageConverter102;
/**
* A {@link MessageListenerAdapter} subclass for the JMS 1.0.2 specification,
* not relying on JMS 1.1 methods like MessageListenerAdapter itself.
*
* <p>This class can be used for JMS 1.0.2 providers, offering the same facility
* as MessageListenerAdapter does for JMS 1.1 providers.
*
* @author Juergen Hoeller
* @author Rick Evans
* @since 2.0
* @deprecated as of Spring 3.0, in favor of the JMS 1.1 based {@link MessageListenerAdapter}
*/
@Deprecated
public class MessageListenerAdapter102 extends MessageListenerAdapter {
/**
* Create a new instance of the MessageListenerAdapter102 class
* with the default settings.
*/
public MessageListenerAdapter102() {
}
/**
* Create a new instance of the MessageListenerAdapter102 class
* for the given delegate.
* @param delegate the target object to delegate message listening to
*/
public MessageListenerAdapter102(Object delegate) {
super(delegate);
}
/**
* Initialize the default implementations for the adapter's strategies:
* SimpleMessageConverter102.
* @see #setMessageConverter
* @see org.springframework.jms.support.converter.SimpleMessageConverter102
*/
protected void initDefaultStrategies() {
setMessageConverter(new SimpleMessageConverter102());
}
/**
* Overrides the superclass method to use the JMS 1.0.2 API to send a response.
* <p>Uses the JMS pub-sub API if the given destination is a topic,
* else uses the JMS queue API.
*/
protected void sendResponse(Session session, Destination destination, Message response) throws JMSException {
MessageProducer producer = null;
try {
if (destination instanceof Topic) {
producer = ((TopicSession) session).createPublisher((Topic) destination);
postProcessProducer(producer, response);
((TopicPublisher) producer).publish(response);
}
else {
producer = ((QueueSession) session).createSender((Queue) destination);
postProcessProducer(producer, response);
((QueueSender) producer).send(response);
}
}
finally {
JmsUtils.closeMessageProducer(producer);
}
}
}

View File

@@ -1,10 +0,0 @@
/**
*
* Message listener adapter mechanism that delegates to target listener
* methods, converting messages to appropriate message content types
* (such as String or byte array) that get passed into listener methods.
*
*/
package org.springframework.jms.listener.adapter;

View File

@@ -1,181 +0,0 @@
/*
* Copyright 2002-2008 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.springframework.jms.listener.endpoint;
import javax.jms.Session;
import javax.resource.spi.ResourceAdapter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanWrapper;
/**
* Default implementation of the {@link JmsActivationSpecFactory} interface.
* Supports the standard JMS properties as defined by the JMS 1.5 specification,
* as well as Spring's extended "maxConcurrency" and "prefetchSize" settings
* through autodetection of well-known vendor-specific provider properties.
*
* <p>An ActivationSpec factory is effectively dependent on the concrete
* JMS provider, e.g. on ActiveMQ. This default implementation simply
* guesses the ActivationSpec class name from the provider's class name
* ("ActiveMQResourceAdapter" -> "ActiveMQActivationSpec" in the same package,
* or "ActivationSpecImpl" in the same package as the ResourceAdapter class),
* and populates the ActivationSpec properties as suggested by the
* JCA 1.5 specification (Appendix B). Specify the 'activationSpecClass'
* property explicitly if these default naming rules do not apply.
*
* <p>Note: ActiveMQ, JORAM and WebSphere are supported in terms of extended
* settings (through the detection of their bean property naming conventions).
* The default ActivationSpec class detection rules may apply to other
* JMS providers as well.
*
* <p>Thanks to Agim Emruli and Laurie Chan for pointing out WebSphere MQ
* settings and contributing corresponding tests!
*
* @author Juergen Hoeller
* @since 2.5
* @see #setActivationSpecClass
*/
public class DefaultJmsActivationSpecFactory extends StandardJmsActivationSpecFactory {
private static final String RESOURCE_ADAPTER_SUFFIX = "ResourceAdapter";
private static final String RESOURCE_ADAPTER_IMPL_SUFFIX = "ResourceAdapterImpl";
private static final String ACTIVATION_SPEC_SUFFIX = "ActivationSpec";
private static final String ACTIVATION_SPEC_IMPL_SUFFIX = "ActivationSpecImpl";
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
/**
* This implementation guesses the ActivationSpec class name from the
* provider's class name: e.g. "ActiveMQResourceAdapter" ->
* "ActiveMQActivationSpec" in the same package, or a class named
* "ActivationSpecImpl" in the same package as the ResourceAdapter class.
*/
protected Class determineActivationSpecClass(ResourceAdapter adapter) {
String adapterClassName = adapter.getClass().getName();
if (adapterClassName.endsWith(RESOURCE_ADAPTER_SUFFIX)) {
// e.g. ActiveMQ
String providerName =
adapterClassName.substring(0, adapterClassName.length() - RESOURCE_ADAPTER_SUFFIX.length());
String specClassName = providerName + ACTIVATION_SPEC_SUFFIX;
try {
return adapter.getClass().getClassLoader().loadClass(specClassName);
}
catch (ClassNotFoundException ex) {
logger.debug("No default <Provider>ActivationSpec class found: " + specClassName);
}
}
else if (adapterClassName.endsWith(RESOURCE_ADAPTER_IMPL_SUFFIX)){
//e.g. WebSphere
String providerName =
adapterClassName.substring(0, adapterClassName.length() - RESOURCE_ADAPTER_IMPL_SUFFIX.length());
String specClassName = providerName + ACTIVATION_SPEC_IMPL_SUFFIX;
try {
return adapter.getClass().getClassLoader().loadClass(specClassName);
}
catch (ClassNotFoundException ex) {
logger.debug("No default <Provider>ActivationSpecImpl class found: " + specClassName);
}
}
// e.g. JORAM
String providerPackage = adapterClassName.substring(0, adapterClassName.lastIndexOf('.') + 1);
String specClassName = providerPackage + ACTIVATION_SPEC_IMPL_SUFFIX;
try {
return adapter.getClass().getClassLoader().loadClass(specClassName);
}
catch (ClassNotFoundException ex) {
logger.debug("No default ActivationSpecImpl class found in provider package: " + specClassName);
}
// ActivationSpecImpl class in "inbound" subpackage (WebSphere MQ 6.0.2.1)
specClassName = providerPackage + "inbound." + ACTIVATION_SPEC_IMPL_SUFFIX;
try {
return adapter.getClass().getClassLoader().loadClass(specClassName);
}
catch (ClassNotFoundException ex) {
logger.debug("No default ActivationSpecImpl class found in inbound subpackage: " + specClassName);
}
throw new IllegalStateException("No ActivationSpec class defined - " +
"specify the 'activationSpecClass' property or override the 'determineActivationSpecClass' method");
}
/**
* This implementation supports Spring's extended "maxConcurrency"
* and "prefetchSize" settings through detecting corresponding
* ActivationSpec properties: "maxSessions"/"maxNumberOfWorks" and
* "maxMessagesPerSessions"/"maxMessages", respectively
* (following ActiveMQ's and JORAM's naming conventions).
*/
protected void populateActivationSpecProperties(BeanWrapper bw, JmsActivationSpecConfig config) {
super.populateActivationSpecProperties(bw, config);
if (config.getMaxConcurrency() > 0) {
if (bw.isWritableProperty("maxSessions")) {
// ActiveMQ
bw.setPropertyValue("maxSessions", Integer.toString(config.getMaxConcurrency()));
}
else if (bw.isWritableProperty("maxNumberOfWorks")) {
// JORAM
bw.setPropertyValue("maxNumberOfWorks", Integer.toString(config.getMaxConcurrency()));
}
else if (bw.isWritableProperty("maxConcurrency")){
// WebSphere
bw.setPropertyValue("maxConcurrency", Integer.toString(config.getMaxConcurrency()));
}
}
if (config.getPrefetchSize() > 0) {
if (bw.isWritableProperty("maxMessagesPerSessions")) {
// ActiveMQ
bw.setPropertyValue("maxMessagesPerSessions", Integer.toString(config.getPrefetchSize()));
}
else if (bw.isWritableProperty("maxMessages")) {
// JORAM
bw.setPropertyValue("maxMessages", Integer.toString(config.getPrefetchSize()));
}
else if(bw.isWritableProperty("maxBatchSize")){
// WebSphere
bw.setPropertyValue("maxBatchSize", Integer.toString(config.getPrefetchSize()));
}
}
}
/**
* This implementation maps <code>SESSION_TRANSACTED</code> onto an
* ActivationSpec property named "useRAManagedTransaction", if available
* (following ActiveMQ's naming conventions).
*/
protected void applyAcknowledgeMode(BeanWrapper bw, int ackMode) {
if (ackMode == Session.SESSION_TRANSACTED && bw.isWritableProperty("useRAManagedTransaction")) {
// ActiveMQ
bw.setPropertyValue("useRAManagedTransaction", "true");
}
else {
super.applyAcknowledgeMode(bw, ackMode);
}
}
}

View File

@@ -1,204 +0,0 @@
/*
* Copyright 2002-2010 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.springframework.jms.listener.endpoint;
import javax.jms.Session;
import org.springframework.core.Constants;
/**
* Common configuration object for activating a JMS message endpoint.
* Gets converted into a provider-specific JCA 1.5 ActivationSpec
* object for activating the endpoint.
*
* <p>Typically used in combination with {@link JmsMessageEndpointManager},
* but not tied to it.
*
* @author Juergen Hoeller
* @since 2.5
* @see JmsActivationSpecFactory
* @see JmsMessageEndpointManager#setActivationSpecConfig
* @see javax.resource.spi.ResourceAdapter#endpointActivation
*/
public class JmsActivationSpecConfig {
/** Constants instance for javax.jms.Session */
private static final Constants sessionConstants = new Constants(Session.class);
private String destinationName;
private boolean pubSubDomain = false;
private boolean subscriptionDurable = false;
private String durableSubscriptionName;
private String clientId;
private String messageSelector;
private int acknowledgeMode = Session.AUTO_ACKNOWLEDGE;
private int maxConcurrency = -1;
private int prefetchSize = -1;
public void setDestinationName(String destinationName) {
this.destinationName = destinationName;
}
public String getDestinationName() {
return this.destinationName;
}
public void setPubSubDomain(boolean pubSubDomain) {
this.pubSubDomain = pubSubDomain;
}
public boolean isPubSubDomain() {
return this.pubSubDomain;
}
public void setSubscriptionDurable(boolean subscriptionDurable) {
this.subscriptionDurable = subscriptionDurable;
}
public boolean isSubscriptionDurable() {
return this.subscriptionDurable;
}
public void setDurableSubscriptionName(String durableSubscriptionName) {
this.durableSubscriptionName = durableSubscriptionName;
}
public String getDurableSubscriptionName() {
return this.durableSubscriptionName;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getClientId() {
return this.clientId;
}
public void setMessageSelector(String messageSelector) {
this.messageSelector = messageSelector;
}
public String getMessageSelector() {
return this.messageSelector;
}
/**
* Set the JMS acknowledgement mode by the name of the corresponding constant
* in the JMS {@link Session} interface, e.g. "CLIENT_ACKNOWLEDGE".
* <p>Note that JCA resource adapters generally only support auto and dups-ok
* (see Spring's {@link StandardJmsActivationSpecFactory}). ActiveMQ also
* supports "SESSION_TRANSACTED" in the form of RA-managed transactions
* (automatically translated by Spring's {@link DefaultJmsActivationSpecFactory}.
* @param constantName the name of the {@link Session} acknowledge mode constant
* @see javax.jms.Session#AUTO_ACKNOWLEDGE
* @see javax.jms.Session#CLIENT_ACKNOWLEDGE
* @see javax.jms.Session#DUPS_OK_ACKNOWLEDGE
* @see javax.jms.Session#SESSION_TRANSACTED
* @see StandardJmsActivationSpecFactory
* @see DefaultJmsActivationSpecFactory
*/
public void setAcknowledgeModeName(String constantName) {
setAcknowledgeMode(sessionConstants.asNumber(constantName).intValue());
}
/**
* Set the JMS acknowledgement mode to use.
* @see javax.jms.Session#AUTO_ACKNOWLEDGE
* @see javax.jms.Session#CLIENT_ACKNOWLEDGE
* @see javax.jms.Session#DUPS_OK_ACKNOWLEDGE
* @see javax.jms.Session#SESSION_TRANSACTED
*/
public void setAcknowledgeMode(int acknowledgeMode) {
this.acknowledgeMode = acknowledgeMode;
}
/**
* Return the JMS acknowledgement mode to use.
*/
public int getAcknowledgeMode() {
return this.acknowledgeMode;
}
/**
* Specify concurrency limits via a "lower-upper" String, e.g. "5-10", or a simple
* upper limit String, e.g. "10".
* <p>JCA listener containers will always scale from zero to the given upper limit.
* A specified lower limit will effectively be ignored.
* <p>This property is primarily supported for configuration compatibility with
* {@link org.springframework.jms.listener.DefaultMessageListenerContainer}.
* For this activation config, generally use {@link #setMaxConcurrency} instead.
*/
public void setConcurrency(String concurrency) {
try {
int separatorIndex = concurrency.indexOf('-');
if (separatorIndex != -1) {
setMaxConcurrency(Integer.parseInt(concurrency.substring(separatorIndex + 1, concurrency.length())));
}
else {
setMaxConcurrency(Integer.parseInt(concurrency));
}
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException("Invalid concurrency value [" + concurrency + "]: only " +
"single maximum integer (e.g. \"5\") and minimum-maximum combo (e.g. \"3-5\") supported. " +
"Note that JmsActivationSpecConfig will effectively ignore the minimum value and " +
"scale from zero up to the number of consumers according to the maximum value.");
}
}
/**
* Specify the maximum number of consumers/sessions to use, effectively
* controlling the number of concurrent invocations on the target listener.
*/
public void setMaxConcurrency(int maxConcurrency) {
this.maxConcurrency = maxConcurrency;
}
/**
* Return the maximum number of consumers/sessions to use.
*/
public int getMaxConcurrency() {
return this.maxConcurrency;
}
/**
* Specify the maximum number of messages to load into a session
* (a kind of batch size).
*/
public void setPrefetchSize(int prefetchSize) {
this.prefetchSize = prefetchSize;
}
/**
* Return the maximum number of messages to load into a session.
*/
public int getPrefetchSize() {
return this.prefetchSize;
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms.listener.endpoint;
import javax.resource.spi.ActivationSpec;
import javax.resource.spi.ResourceAdapter;
/**
* Strategy interface for creating JCA 1.5 ActivationSpec objects
* based on a configured {@link JmsActivationSpecConfig} object.
*
* <p>JCA 1.5 ActivationSpec objects are typically JavaBeans, but
* unfortunately provider-specific. This strategy interface allows
* for plugging in any JCA-based JMS provider, creating corresponding
* ActivationSpec objects based on common JMS configuration settings.
*
* @author Juergen Hoeller
* @since 2.5
* @see JmsActivationSpecConfig
* @see JmsMessageEndpointManager#setActivationSpecFactory
* @see javax.resource.spi.ResourceAdapter#endpointActivation
*/
public interface JmsActivationSpecFactory {
/**
* Create a JCA 1.5 ActivationSpec object based on the given
* {@link JmsActivationSpecConfig} object.
* @param adapter the ResourceAdapter to create an ActivationSpec object for
* @param config the configured object holding common JMS settings
* @return the provider-specific JCA ActivationSpec object,
* representing the same settings
*/
ActivationSpec createActivationSpec(ResourceAdapter adapter, JmsActivationSpecConfig config);
}

View File

@@ -1,126 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms.listener.endpoint;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.resource.ResourceException;
import javax.resource.spi.UnavailableException;
import org.springframework.jca.endpoint.AbstractMessageEndpointFactory;
/**
* JMS-specific implementation of the JCA 1.5
* {@link javax.resource.spi.endpoint.MessageEndpointFactory} interface,
* providing transaction management capabilities for a JMS listener object
* (e.g. a {@link javax.jms.MessageListener} object).
*
* <p>Uses a static endpoint implementation, simply wrapping the
* specified message listener object and exposing all of its implemented
* interfaces on the endpoint instance.
*
* <p>Typically used with Spring's {@link JmsMessageEndpointManager},
* but not tied to it. As a consequence, this endpoint factory could
* also be used with programmatic endpoint management on a native
* {@link javax.resource.spi.ResourceAdapter} instance.
*
* @author Juergen Hoeller
* @since 2.5
* @see #setMessageListener
* @see #setTransactionManager
* @see JmsMessageEndpointManager
*/
public class JmsMessageEndpointFactory extends AbstractMessageEndpointFactory {
private MessageListener messageListener;
/**
* Set the JMS MessageListener for this endpoint.
*/
public void setMessageListener(MessageListener messageListener) {
this.messageListener = messageListener;
}
/**
* Creates a concrete JMS message endpoint, internal to this factory.
*/
protected AbstractMessageEndpoint createEndpointInternal() throws UnavailableException {
return new JmsMessageEndpoint();
}
/**
* Private inner class that implements the concrete JMS message endpoint.
*/
private class JmsMessageEndpoint extends AbstractMessageEndpoint implements MessageListener {
public void onMessage(Message message) {
boolean applyDeliveryCalls = !hasBeforeDeliveryBeenCalled();
if (applyDeliveryCalls) {
try {
beforeDelivery(null);
}
catch (ResourceException ex) {
throw new JmsResourceException(ex);
}
}
try {
messageListener.onMessage(message);
}
catch (RuntimeException ex) {
onEndpointException(ex);
throw ex;
}
catch (Error err) {
onEndpointException(err);
throw err;
}
finally {
if (applyDeliveryCalls) {
try {
afterDelivery();
}
catch (ResourceException ex) {
throw new JmsResourceException(ex);
}
}
}
}
protected ClassLoader getEndpointClassLoader() {
return messageListener.getClass().getClassLoader();
}
}
/**
* Internal exception thrown when a ResourceExeption has been encountered
* during the endpoint invocation.
* <p>Will only be used if the ResourceAdapter does not invoke the
* endpoint's <code>beforeDelivery</code> and <code>afterDelivery</code>
* directly, leavng it up to the concrete endpoint to apply those -
* and to handle any ResourceExceptions thrown from them.
*/
public static class JmsResourceException extends RuntimeException {
public JmsResourceException(ResourceException cause) {
super(cause);
}
}
}

View File

@@ -1,142 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms.listener.endpoint;
import javax.jms.MessageListener;
import javax.resource.ResourceException;
import org.springframework.jca.endpoint.GenericMessageEndpointManager;
import org.springframework.jms.support.destination.DestinationResolver;
/**
* Extension of the generic JCA 1.5
* {@link org.springframework.jca.endpoint.GenericMessageEndpointManager},
* adding JMS-specific support for ActivationSpec configuration.
*
* <p>Allows for defining a common {@link JmsActivationSpecConfig} object
* that gets converted into a provider-specific JCA 1.5 ActivationSpec
* object for activating the endpoint.
*
* <p><b>NOTE:</b> This JCA-based endpoint manager supports standard JMS
* {@link javax.jms.MessageListener} endpoints only. It does <i>not</i> support
* Spring's {@link org.springframework.jms.listener.SessionAwareMessageListener}
* variant, simply because the JCA endpoint management contract does not allow
* for obtaining the current JMS {@link javax.jms.Session}.
*
* @author Juergen Hoeller
* @since 2.5
* @see javax.jms.MessageListener
* @see #setActivationSpecConfig
* @see JmsActivationSpecConfig
* @see JmsActivationSpecFactory
* @see JmsMessageEndpointFactory
*/
public class JmsMessageEndpointManager extends GenericMessageEndpointManager {
private final JmsMessageEndpointFactory endpointFactory = new JmsMessageEndpointFactory();
private boolean messageListenerSet = false;
private JmsActivationSpecFactory activationSpecFactory = new DefaultJmsActivationSpecFactory();
private JmsActivationSpecConfig activationSpecConfig;
/**
* Set the JMS MessageListener for this endpoint.
* <p>This is a shortcut for configuring a dedicated JmsMessageEndpointFactory.
* @see JmsMessageEndpointFactory#setMessageListener
*/
public void setMessageListener(MessageListener messageListener) {
this.endpointFactory.setMessageListener(messageListener);
this.messageListenerSet = true;
}
/**
* Set the XA transaction manager to use for wrapping endpoint
* invocations, enlisting the endpoint resource in each such transaction.
* <p>The passed-in object may be a transaction manager which implements
* Spring's {@link org.springframework.transaction.jta.TransactionFactory}
* interface, or a plain {@link javax.transaction.TransactionManager}.
* <p>If no transaction manager is specified, the endpoint invocation
* will simply not be wrapped in an XA transaction. Consult your
* resource provider's ActivationSpec documentation for the local
* transaction options of your particular provider.
* <p>This is a shortcut for configuring a dedicated JmsMessageEndpointFactory.
* @see JmsMessageEndpointFactory#setTransactionManager
*/
public void setTransactionManager(Object transactionManager) {
this.endpointFactory.setTransactionManager(transactionManager);
}
/**
* Set the factory for concrete JCA 1.5 ActivationSpec objects,
* creating JCA ActivationSpecs based on
* {@link #setActivationSpecConfig JmsActivationSpecConfig} objects.
* <p>This factory is dependent on the concrete JMS provider, e.g. on ActiveMQ.
* The default implementation simply guesses the ActivationSpec class name
* from the provider's class name (e.g. "ActiveMQResourceAdapter" ->
* "ActiveMQActivationSpec" in the same package), and populates the
* ActivationSpec properties as suggested by the JCA 1.5 specification
* (plus a couple of autodetected vendor-specific properties).
* @see DefaultJmsActivationSpecFactory
*/
public void setActivationSpecFactory(JmsActivationSpecFactory activationSpecFactory) {
this.activationSpecFactory =
(activationSpecFactory != null ? activationSpecFactory : new DefaultJmsActivationSpecFactory());
}
/**
* Set the DestinationResolver to use for resolving destination names
* into the JCA 1.5 ActivationSpec "destination" property.
* <p>If not specified, destination names will simply be passed in as Strings.
* If specified, destination names will be resolved into Destination objects first.
* <p>Note that a DestinationResolver is usually specified on the JmsActivationSpecFactory
* (see {@link StandardJmsActivationSpecFactory#setDestinationResolver}). This is simply
* a shortcut for parameterizing the default JmsActivationSpecFactory; it will replace
* any custom JmsActivationSpecFactory that might have been set before.
* @see StandardJmsActivationSpecFactory#setDestinationResolver
*/
public void setDestinationResolver(DestinationResolver destinationResolver) {
DefaultJmsActivationSpecFactory factory = new DefaultJmsActivationSpecFactory();
factory.setDestinationResolver(destinationResolver);
this.activationSpecFactory = factory;
}
/**
* Specify the {@link JmsActivationSpecConfig} object that this endpoint manager
* should use for activating its listener.
* <p>This config object will be turned into a concrete JCA 1.5 ActivationSpec
* object through a {@link #setActivationSpecFactory JmsActivationSpecFactory}.
*/
public void setActivationSpecConfig(JmsActivationSpecConfig activationSpecConfig) {
this.activationSpecConfig = activationSpecConfig;
}
public void afterPropertiesSet() throws ResourceException {
if (this.messageListenerSet) {
setMessageEndpointFactory(this.endpointFactory);
}
if (this.activationSpecConfig != null) {
setActivationSpec(
this.activationSpecFactory.createActivationSpec(getResourceAdapter(), this.activationSpecConfig));
}
super.afterPropertiesSet();
}
}

View File

@@ -1,203 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.listener.endpoint;
import java.util.Map;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.Topic;
import javax.resource.spi.ActivationSpec;
import javax.resource.spi.ResourceAdapter;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.jms.support.destination.DestinationResolutionException;
import org.springframework.jms.support.destination.DestinationResolver;
/**
* Standard implementation of the {@link JmsActivationSpecFactory} interface.
* Supports the standard JMS properties as defined by the JMS 1.5 specification
* (Appendix B); ignores Spring's "maxConcurrency" and "prefetchSize" settings.
*
* <p>The 'activationSpecClass' property is required, explicitly defining
* the fully-qualified class name of the provider's ActivationSpec class
* (e.g. "org.apache.activemq.ra.ActiveMQActivationSpec").
*
* <p>Check out {@link DefaultJmsActivationSpecFactory} for an extended variant
* of this class, supporting some further default conventions beyond the plain
* JMS 1.5 specification.
*
* @author Juergen Hoeller
* @since 2.5
* @see #setActivationSpecClass
* @see DefaultJmsActivationSpecFactory
*/
public class StandardJmsActivationSpecFactory implements JmsActivationSpecFactory {
private Class activationSpecClass;
private Map<String, String> defaultProperties;
private DestinationResolver destinationResolver;
/**
* Specify the fully-qualified ActivationSpec class name for the target
* provider (e.g. "org.apache.activemq.ra.ActiveMQActivationSpec").
*/
public void setActivationSpecClass(Class activationSpecClass) {
this.activationSpecClass = activationSpecClass;
}
/**
* Specify custom default properties, with String keys and String values.
* <p>Applied to each ActivationSpec object before it gets populated with
* listener-specific settings. Allows for configuring vendor-specific properties
* beyond the Spring-defined settings in {@link JmsActivationSpecConfig}.
*/
public void setDefaultProperties(Map<String, String> defaultProperties) {
this.defaultProperties = defaultProperties;
}
/**
* Set the DestinationResolver to use for resolving destination names
* into the JCA 1.5 ActivationSpec "destination" property.
* <p>If not specified, destination names will simply be passed in as Strings.
* If specified, destination names will be resolved into Destination objects first.
* <p>Note that a DestinationResolver for use with this factory must be
* able to work <i>without</i> an active JMS Session: e.g.
* {@link org.springframework.jms.support.destination.JndiDestinationResolver}
* or {@link org.springframework.jms.support.destination.BeanFactoryDestinationResolver}
* but not {@link org.springframework.jms.support.destination.DynamicDestinationResolver}.
*/
public void setDestinationResolver(DestinationResolver destinationResolver) {
this.destinationResolver = destinationResolver;
}
public ActivationSpec createActivationSpec(ResourceAdapter adapter, JmsActivationSpecConfig config) {
Class activationSpecClassToUse = this.activationSpecClass;
if (activationSpecClassToUse == null) {
activationSpecClassToUse = determineActivationSpecClass(adapter);
if (activationSpecClassToUse == null) {
throw new IllegalStateException("Property 'activationSpecClass' is required");
}
}
ActivationSpec spec = (ActivationSpec) BeanUtils.instantiateClass(activationSpecClassToUse);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(spec);
if (this.defaultProperties != null) {
bw.setPropertyValues(this.defaultProperties);
}
populateActivationSpecProperties(bw, config);
return spec;
}
/**
* Determine the ActivationSpec class for the given ResourceAdapter,
* if possible. Called if no 'activationSpecClass' has been set explicitly
* @param adapter the ResourceAdapter to check
* @return the corresponding ActivationSpec class, or <code>null</code>
* if not determinable
* @see #setActivationSpecClass
*/
protected Class determineActivationSpecClass(ResourceAdapter adapter) {
return null;
}
/**
* Populate the given ApplicationSpec object with the settings
* defined in the given configuration object.
* <p>This implementation applies all standard JMS settings, but ignores
* "maxConcurrency" and "prefetchSize" - not supported in standard JCA 1.5.
* @param bw the BeanWrapper wrapping the ActivationSpec object
* @param config the configured object holding common JMS settings
*/
protected void populateActivationSpecProperties(BeanWrapper bw, JmsActivationSpecConfig config) {
String destinationName = config.getDestinationName();
boolean pubSubDomain = config.isPubSubDomain();
Object destination = destinationName;
if (this.destinationResolver != null) {
try {
destination = this.destinationResolver.resolveDestinationName(null, destinationName, pubSubDomain);
}
catch (JMSException ex) {
throw new DestinationResolutionException("Cannot resolve destination name [" + destinationName + "]", ex);
}
}
bw.setPropertyValue("destination", destination);
bw.setPropertyValue("destinationType", pubSubDomain ? Topic.class.getName() : Queue.class.getName());
if (bw.isWritableProperty("subscriptionDurability")) {
bw.setPropertyValue("subscriptionDurability", config.isSubscriptionDurable() ? "Durable" : "NonDurable");
}
else if (config.isSubscriptionDurable()) {
// Standard JCA 1.5 "subscriptionDurability" apparently not supported...
throw new IllegalArgumentException(
"Durable subscriptions not supported by underlying provider: " + this.activationSpecClass.getName());
}
if (config.getDurableSubscriptionName() != null) {
bw.setPropertyValue("subscriptionName", config.getDurableSubscriptionName());
}
if (config.getClientId() != null) {
bw.setPropertyValue("clientId", config.getClientId());
}
if (config.getMessageSelector() != null) {
bw.setPropertyValue("messageSelector", config.getMessageSelector());
}
applyAcknowledgeMode(bw, config.getAcknowledgeMode());
}
/**
* Apply the specified acknowledge mode to the ActivationSpec object.
* <p>This implementation applies the standard JCA 1.5 acknowledge modes
* "Auto-acknowledge" and "Dups-ok-acknowledge". It throws an exception in
* case of <code>CLIENT_ACKNOWLEDGE</code> or <code>SESSION_TRANSACTED</code>
* having been requested.
* @param bw the BeanWrapper wrapping the ActivationSpec object
* @param ackMode the configured acknowledge mode
* (according to the constants in {@link javax.jms.Session}
* @see javax.jms.Session#AUTO_ACKNOWLEDGE
* @see javax.jms.Session#DUPS_OK_ACKNOWLEDGE
* @see javax.jms.Session#CLIENT_ACKNOWLEDGE
* @see javax.jms.Session#SESSION_TRANSACTED
*/
protected void applyAcknowledgeMode(BeanWrapper bw, int ackMode) {
if (ackMode == Session.SESSION_TRANSACTED) {
throw new IllegalArgumentException("No support for SESSION_TRANSACTED: Only \"Auto-acknowledge\" " +
"and \"Dups-ok-acknowledge\" supported in standard JCA 1.5");
}
else if (ackMode == Session.CLIENT_ACKNOWLEDGE) {
throw new IllegalArgumentException("No support for CLIENT_ACKNOWLEDGE: Only \"Auto-acknowledge\" " +
"and \"Dups-ok-acknowledge\" supported in standard JCA 1.5");
}
else if (bw.isWritableProperty("acknowledgeMode")) {
bw.setPropertyValue("acknowledgeMode",
ackMode == Session.DUPS_OK_ACKNOWLEDGE ? "Dups-ok-acknowledge" : "Auto-acknowledge");
}
else if (ackMode == Session.DUPS_OK_ACKNOWLEDGE) {
// Standard JCA 1.5 "acknowledgeMode" apparently not supported (e.g. WebSphere MQ 6.0.2.1)
throw new IllegalArgumentException(
"Dups-ok-acknowledge not supported by underlying provider: " + this.activationSpecClass.getName());
}
}
}

View File

@@ -1,8 +0,0 @@
/**
*
* This package provides JCA-based endpoint management for JMS message listeners.
*
*/
package org.springframework.jms.listener.endpoint;

View File

@@ -1,10 +0,0 @@
/**
*
* This package contains the base message listener container facility.
* It also offers the DefaultMessageListenerContainer and SimpleMessageListenerContainer
* implementations, based on the plain JMS client API.
*
*/
package org.springframework.jms.listener;

View File

@@ -1,9 +0,0 @@
/**
*
* This package contains integration classes for JMS,
* allowing for Spring-style JMS access.
*
*/
package org.springframework.jms;

View File

@@ -1,434 +0,0 @@
/*
* Copyright 2002-2011 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.springframework.jms.remoting;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageFormatException;
import javax.jms.MessageProducer;
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.TemporaryQueue;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jms.connection.ConnectionFactoryUtils;
import org.springframework.jms.support.JmsUtils;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.SimpleMessageConverter;
import org.springframework.jms.support.destination.DestinationResolver;
import org.springframework.jms.support.destination.DynamicDestinationResolver;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.remoting.RemoteInvocationFailureException;
import org.springframework.remoting.support.DefaultRemoteInvocationFactory;
import org.springframework.remoting.support.RemoteInvocation;
import org.springframework.remoting.support.RemoteInvocationFactory;
import org.springframework.remoting.support.RemoteInvocationResult;
import org.springframework.util.ClassUtils;
/**
* {@link org.aopalliance.intercept.MethodInterceptor} for accessing a
* JMS-based remote service.
*
* <p>Serializes remote invocation objects and deserializes remote invocation
* result objects. Uses Java serialization just like RMI, but with the JMS
* provider as communication infrastructure.
*
* <p>To be configured with a {@link javax.jms.QueueConnectionFactory} and a
* target queue (either as {@link javax.jms.Queue} reference or as queue name).
*
* <p>Thanks to James Strachan for the original prototype that this
* JMS invoker mechanism was inspired by!
*
* @author Juergen Hoeller
* @author James Strachan
* @since 2.0
* @see #setConnectionFactory
* @see #setQueue
* @see #setQueueName
* @see org.springframework.jms.remoting.JmsInvokerServiceExporter
* @see org.springframework.jms.remoting.JmsInvokerProxyFactoryBean
*/
public class JmsInvokerClientInterceptor implements MethodInterceptor, InitializingBean {
private static final boolean jms11Available = ClassUtils.hasMethod(ConnectionFactory.class, "createConnection");
private ConnectionFactory connectionFactory;
private Object queue;
private DestinationResolver destinationResolver = new DynamicDestinationResolver();
private RemoteInvocationFactory remoteInvocationFactory = new DefaultRemoteInvocationFactory();
private MessageConverter messageConverter = new SimpleMessageConverter();
private long receiveTimeout = 0;
/**
* Set the QueueConnectionFactory to use for obtaining JMS QueueConnections.
*/
public void setConnectionFactory(ConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
/**
* Return the QueueConnectionFactory to use for obtaining JMS QueueConnections.
*/
protected ConnectionFactory getConnectionFactory() {
return this.connectionFactory;
}
/**
* Set the target Queue to send invoker requests to.
*/
public void setQueue(Queue queue) {
this.queue = queue;
}
/**
* Set the name of target queue to send invoker requests to.
* The specified name will be dynamically resolved via the
* {@link #setDestinationResolver DestinationResolver}.
*/
public void setQueueName(String queueName) {
this.queue = queueName;
}
/**
* Set the DestinationResolver that is to be used to resolve Queue
* references for this accessor.
* <p>The default resolver is a DynamicDestinationResolver. Specify a
* JndiDestinationResolver for resolving destination names as JNDI locations.
* @see org.springframework.jms.support.destination.DynamicDestinationResolver
* @see org.springframework.jms.support.destination.JndiDestinationResolver
*/
public void setDestinationResolver(DestinationResolver destinationResolver) {
this.destinationResolver =
(destinationResolver != null ? destinationResolver : new DynamicDestinationResolver());
}
/**
* Set the RemoteInvocationFactory to use for this accessor.
* Default is a {@link org.springframework.remoting.support.DefaultRemoteInvocationFactory}.
* <p>A custom invocation factory can add further context information
* to the invocation, for example user credentials.
*/
public void setRemoteInvocationFactory(RemoteInvocationFactory remoteInvocationFactory) {
this.remoteInvocationFactory =
(remoteInvocationFactory != null ? remoteInvocationFactory : new DefaultRemoteInvocationFactory());
}
/**
* Specify the MessageConverter to use for turning
* {@link org.springframework.remoting.support.RemoteInvocation}
* objects into request messages, as well as response messages into
* {@link org.springframework.remoting.support.RemoteInvocationResult} objects.
* <p>Default is a {@link org.springframework.jms.support.converter.SimpleMessageConverter},
* using a standard JMS {@link javax.jms.ObjectMessage} for each invocation /
* invocation result object.
* <p>Custom implementations may generally adapt Serializables into
* special kinds of messages, or might be specifically tailored for
* translating RemoteInvocation(Result)s into specific kinds of messages.
*/
public void setMessageConverter(MessageConverter messageConverter) {
this.messageConverter = (messageConverter != null ? messageConverter : new SimpleMessageConverter());
}
/**
* Set the timeout to use for receiving the response message for a request
* (in milliseconds).
* <p>The default is 0, which indicates a blocking receive without timeout.
* @see javax.jms.MessageConsumer#receive(long)
* @see javax.jms.MessageConsumer#receive()
*/
public void setReceiveTimeout(long receiveTimeout) {
this.receiveTimeout = receiveTimeout;
}
/**
* Return the timeout to use for receiving the response message for a request
* (in milliseconds).
*/
protected long getReceiveTimeout() {
return this.receiveTimeout;
}
public void afterPropertiesSet() {
if (getConnectionFactory() == null) {
throw new IllegalArgumentException("Property 'connectionFactory' is required");
}
if (this.queue == null) {
throw new IllegalArgumentException("'queue' or 'queueName' is required");
}
}
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
return "JMS invoker proxy for queue [" + this.queue + "]";
}
RemoteInvocation invocation = createRemoteInvocation(methodInvocation);
RemoteInvocationResult result;
try {
result = executeRequest(invocation);
}
catch (JMSException ex) {
throw convertJmsInvokerAccessException(ex);
}
try {
return recreateRemoteInvocationResult(result);
}
catch (Throwable ex) {
if (result.hasInvocationTargetException()) {
throw ex;
}
else {
throw new RemoteInvocationFailureException("Invocation of method [" + methodInvocation.getMethod() +
"] failed in JMS invoker remote service at queue [" + this.queue + "]", ex);
}
}
}
/**
* Create a new RemoteInvocation object for the given AOP method invocation.
* The default implementation delegates to the RemoteInvocationFactory.
* <p>Can be overridden in subclasses to provide custom RemoteInvocation
* subclasses, containing additional invocation parameters like user credentials.
* Note that it is preferable to use a custom RemoteInvocationFactory which
* is a reusable strategy.
* @param methodInvocation the current AOP method invocation
* @return the RemoteInvocation object
* @see RemoteInvocationFactory#createRemoteInvocation
*/
protected RemoteInvocation createRemoteInvocation(MethodInvocation methodInvocation) {
return this.remoteInvocationFactory.createRemoteInvocation(methodInvocation);
}
/**
* Execute the given remote invocation, sending an invoker request message
* to this accessor's target queue and waiting for a corresponding response.
* @param invocation the RemoteInvocation to execute
* @return the RemoteInvocationResult object
* @throws JMSException in case of JMS failure
* @see #doExecuteRequest
*/
protected RemoteInvocationResult executeRequest(RemoteInvocation invocation) throws JMSException {
Connection con = createConnection();
Session session = null;
try {
session = createSession(con);
Queue queueToUse = resolveQueue(session);
Message requestMessage = createRequestMessage(session, invocation);
con.start();
Message responseMessage = doExecuteRequest(session, queueToUse, requestMessage);
return extractInvocationResult(responseMessage);
}
finally {
JmsUtils.closeSession(session);
ConnectionFactoryUtils.releaseConnection(con, getConnectionFactory(), true);
}
}
/**
* Create a new JMS Connection for this JMS invoker.
*/
protected Connection createConnection() throws JMSException {
ConnectionFactory cf = getConnectionFactory();
if (jms11Available) {
return cf.createConnection();
}
else {
return ((QueueConnectionFactory) cf).createQueueConnection();
}
}
/**
* Create a new JMS Session for this JMS invoker.
*/
protected Session createSession(Connection con) throws JMSException {
if (jms11Available) {
return con.createSession(false, Session.AUTO_ACKNOWLEDGE);
}
else {
return ((QueueConnection) con).createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
}
}
/**
* Resolve this accessor's target queue.
* @param session the current JMS Session
* @return the resolved target Queue
* @throws JMSException if resolution failed
*/
protected Queue resolveQueue(Session session) throws JMSException {
if (this.queue instanceof Queue) {
return (Queue) this.queue;
}
else if (this.queue instanceof String) {
return resolveQueueName(session, (String) this.queue);
}
else {
throw new javax.jms.IllegalStateException(
"Queue object [" + this.queue + "] is neither a [javax.jms.Queue] nor a queue name String");
}
}
/**
* Resolve the given queue name into a JMS {@link javax.jms.Queue},
* via this accessor's {@link DestinationResolver}.
* @param session the current JMS Session
* @param queueName the name of the queue
* @return the located Queue
* @throws JMSException if resolution failed
* @see #setDestinationResolver
*/
protected Queue resolveQueueName(Session session, String queueName) throws JMSException {
return (Queue) this.destinationResolver.resolveDestinationName(session, queueName, false);
}
/**
* Create the invoker request message.
* <p>The default implementation creates a JMS ObjectMessage
* for the given RemoteInvocation object.
* @param session the current JMS Session
* @param invocation the remote invocation to send
* @return the JMS Message to send
* @throws JMSException if the message could not be created
*/
protected Message createRequestMessage(Session session, RemoteInvocation invocation) throws JMSException {
return this.messageConverter.toMessage(invocation, session);
}
/**
* Actually execute the given request, sending the invoker request message
* to the specified target queue and waiting for a corresponding response.
* <p>The default implementation is based on standard JMS send/receive,
* using a {@link javax.jms.TemporaryQueue} for receiving the response.
* @param session the JMS Session to use
* @param queue the resolved target Queue to send to
* @param requestMessage the JMS Message to send
* @return the RemoteInvocationResult object
* @throws JMSException in case of JMS failure
*/
protected Message doExecuteRequest(Session session, Queue queue, Message requestMessage) throws JMSException {
TemporaryQueue responseQueue = null;
MessageProducer producer = null;
MessageConsumer consumer = null;
try {
if (jms11Available) {
// Standard JMS 1.1 API usage...
responseQueue = session.createTemporaryQueue();
producer = session.createProducer(queue);
consumer = session.createConsumer(responseQueue);
requestMessage.setJMSReplyTo(responseQueue);
producer.send(requestMessage);
}
else {
// Perform all calls on QueueSession reference for JMS 1.0.2 compatibility...
// DEPRECATED but kept around with the deprecated JmsTemplate102 etc classes for the time being.
QueueSession queueSession = (QueueSession) session;
responseQueue = queueSession.createTemporaryQueue();
QueueSender sender = queueSession.createSender(queue);
producer = sender;
consumer = queueSession.createReceiver(responseQueue);
requestMessage.setJMSReplyTo(responseQueue);
sender.send(requestMessage);
}
long timeout = getReceiveTimeout();
return (timeout > 0 ? consumer.receive(timeout) : consumer.receive());
}
finally {
JmsUtils.closeMessageConsumer(consumer);
JmsUtils.closeMessageProducer(producer);
if (responseQueue != null) {
responseQueue.delete();
}
}
}
/**
* Extract the invocation result from the response message.
* <p>The default implementation expects a JMS ObjectMessage carrying
* a RemoteInvocationResult object. If an invalid response message is
* encountered, the <code>onInvalidResponse</code> callback gets invoked.
* @param responseMessage the response message
* @return the invocation result
* @throws JMSException is thrown if a JMS exception occurs
* @see #onInvalidResponse
*/
protected RemoteInvocationResult extractInvocationResult(Message responseMessage) throws JMSException {
Object content = this.messageConverter.fromMessage(responseMessage);
if (content instanceof RemoteInvocationResult) {
return (RemoteInvocationResult) content;
}
return onInvalidResponse(responseMessage);
}
/**
* Callback that is invoked by <code>extractInvocationResult</code>
* when it encounters an invalid response message.
* <p>The default implementation throws a MessageFormatException.
* @param responseMessage the invalid response message
* @return an alternative invocation result that should be
* returned to the caller (if desired)
* @throws JMSException if the invalid response should lead
* to an infrastructure exception propagated to the caller
* @see #extractInvocationResult
*/
protected RemoteInvocationResult onInvalidResponse(Message responseMessage) throws JMSException {
throw new MessageFormatException("Invalid response message: " + responseMessage);
}
/**
* Recreate the invocation result contained in the given RemoteInvocationResult
* object. The default implementation calls the default recreate method.
* <p>Can be overridden in subclass to provide custom recreation, potentially
* processing the returned result object.
* @param result the RemoteInvocationResult to recreate
* @return a return value if the invocation result is a successful return
* @throws Throwable if the invocation result is an exception
* @see org.springframework.remoting.support.RemoteInvocationResult#recreate()
*/
protected Object recreateRemoteInvocationResult(RemoteInvocationResult result) throws Throwable {
return result.recreate();
}
/**
* Convert the given JMS invoker access exception to an appropriate
* Spring RemoteAccessException.
* @param ex the exception to convert
* @return the RemoteAccessException to throw
*/
protected RemoteAccessException convertJmsInvokerAccessException(JMSException ex) {
throw new RemoteAccessException("Could not access JMS invoker queue [" + this.queue + "]", ex);
}
}

View File

@@ -1,92 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.remoting;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.util.ClassUtils;
/**
* FactoryBean for JMS invoker proxies. Exposes the proxied service for use
* as a bean reference, using the specified service interface.
*
* <p>Serializes remote invocation objects and deserializes remote invocation
* result objects. Uses Java serialization just like RMI, but with the JMS
* provider as communication infrastructure.
*
* <p>To be configured with a {@link javax.jms.QueueConnectionFactory} and a
* target queue (either as {@link javax.jms.Queue} reference or as queue name).
*
* @author Juergen Hoeller
* @since 2.0
* @see #setConnectionFactory
* @see #setQueueName
* @see #setServiceInterface
* @see org.springframework.jms.remoting.JmsInvokerClientInterceptor
* @see org.springframework.jms.remoting.JmsInvokerServiceExporter
*/
public class JmsInvokerProxyFactoryBean extends JmsInvokerClientInterceptor
implements FactoryBean<Object>, BeanClassLoaderAware {
private Class serviceInterface;
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private Object serviceProxy;
/**
* Set the interface that the proxy must implement.
* @param serviceInterface the interface that the proxy must implement
* @throws IllegalArgumentException if the supplied <code>serviceInterface</code>
* is <code>null</code>, or if the supplied <code>serviceInterface</code>
* is not an interface type
*/
public void setServiceInterface(Class serviceInterface) {
if (serviceInterface == null || !serviceInterface.isInterface()) {
throw new IllegalArgumentException("'serviceInterface' must be an interface");
}
this.serviceInterface = serviceInterface;
}
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
public void afterPropertiesSet() {
super.afterPropertiesSet();
if (this.serviceInterface == null) {
throw new IllegalArgumentException("Property 'serviceInterface' is required");
}
this.serviceProxy = new ProxyFactory(this.serviceInterface, this).getProxy(this.beanClassLoader);
}
public Object getObject() {
return this.serviceProxy;
}
public Class<?> getObjectType() {
return this.serviceInterface;
}
public boolean isSingleton() {
return true;
}
}

View File

@@ -1,191 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.remoting;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageFormatException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jms.listener.SessionAwareMessageListener;
import org.springframework.jms.support.JmsUtils;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.SimpleMessageConverter;
import org.springframework.remoting.support.RemoteInvocation;
import org.springframework.remoting.support.RemoteInvocationBasedExporter;
import org.springframework.remoting.support.RemoteInvocationResult;
/**
* JMS message listener that exports the specified service bean as a
* JMS service endpoint, accessible via a JMS invoker proxy.
*
* <p>Note that this class implements Spring's
* {@link org.springframework.jms.listener.SessionAwareMessageListener}
* interface, since it requires access to the active JMS Session.
* Hence, this class can only be used with message listener containers
* which support the SessionAwareMessageListener interface (e.g. Spring's
* {@link org.springframework.jms.listener.DefaultMessageListenerContainer}).
*
* <p>Thanks to James Strachan for the original prototype that this
* JMS invoker mechanism was inspired by!
*
* @author Juergen Hoeller
* @author James Strachan
* @since 2.0
* @see JmsInvokerClientInterceptor
* @see JmsInvokerProxyFactoryBean
*/
public class JmsInvokerServiceExporter extends RemoteInvocationBasedExporter
implements SessionAwareMessageListener<Message>, InitializingBean {
private MessageConverter messageConverter = new SimpleMessageConverter();
private boolean ignoreInvalidRequests = true;
private Object proxy;
/**
* Specify the MessageConverter to use for turning request messages into
* {@link org.springframework.remoting.support.RemoteInvocation} objects,
* as well as {@link org.springframework.remoting.support.RemoteInvocationResult}
* objects into response messages.
* <p>Default is a {@link org.springframework.jms.support.converter.SimpleMessageConverter},
* using a standard JMS {@link javax.jms.ObjectMessage} for each invocation /
* invocation result object.
* <p>Custom implementations may generally adapt Serializables into
* special kinds of messages, or might be specifically tailored for
* translating RemoteInvocation(Result)s into specific kinds of messages.
*/
public void setMessageConverter(MessageConverter messageConverter) {
this.messageConverter = (messageConverter != null ? messageConverter : new SimpleMessageConverter());
}
/**
* Set whether invalidly formatted messages should be discarded.
* Default is "true".
* <p>Switch this flag to "false" to throw an exception back to the
* listener container. This will typically lead to redelivery of
* the message, which is usually undesirable - since the message
* content will be the same (that is, still invalid).
*/
public void setIgnoreInvalidRequests(boolean ignoreInvalidRequests) {
this.ignoreInvalidRequests = ignoreInvalidRequests;
}
public void afterPropertiesSet() {
this.proxy = getProxyForService();
}
public void onMessage(Message requestMessage, Session session) throws JMSException {
RemoteInvocation invocation = readRemoteInvocation(requestMessage);
if (invocation != null) {
RemoteInvocationResult result = invokeAndCreateResult(invocation, this.proxy);
writeRemoteInvocationResult(requestMessage, session, result);
}
}
/**
* Read a RemoteInvocation from the given JMS message.
* @param requestMessage current request message
* @return the RemoteInvocation object (or <code>null</code>
* in case of an invalid message that will simply be ignored)
* @throws javax.jms.JMSException in case of message access failure
*/
protected RemoteInvocation readRemoteInvocation(Message requestMessage) throws JMSException {
Object content = this.messageConverter.fromMessage(requestMessage);
if (content instanceof RemoteInvocation) {
return (RemoteInvocation) content;
}
return onInvalidRequest(requestMessage);
}
/**
* Send the given RemoteInvocationResult as a JMS message to the originator.
* @param requestMessage current request message
* @param session the JMS Session to use
* @param result the RemoteInvocationResult object
* @throws javax.jms.JMSException if thrown by trying to send the message
*/
protected void writeRemoteInvocationResult(
Message requestMessage, Session session, RemoteInvocationResult result) throws JMSException {
Message response = createResponseMessage(requestMessage, session, result);
MessageProducer producer = session.createProducer(requestMessage.getJMSReplyTo());
try {
producer.send(response);
}
finally {
JmsUtils.closeMessageProducer(producer);
}
}
/**
* Create the invocation result response message.
* <p>The default implementation creates a JMS ObjectMessage for the given
* RemoteInvocationResult object. It sets the response's correlation id
* to the request message's correlation id, if any; otherwise to the
* request message id.
* @param request the original request message
* @param session the JMS session to use
* @param result the invocation result
* @return the message response to send
* @throws javax.jms.JMSException if creating the messsage failed
*/
protected Message createResponseMessage(Message request, Session session, RemoteInvocationResult result)
throws JMSException {
Message response = this.messageConverter.toMessage(result, session);
String correlation = request.getJMSCorrelationID();
if (correlation == null) {
correlation = request.getJMSMessageID();
}
response.setJMSCorrelationID(correlation);
return response;
}
/**
* Callback that is invoked by {@link #readRemoteInvocation}
* when it encounters an invalid request message.
* <p>The default implementation either discards the invalid message or
* throws a MessageFormatException - according to the "ignoreInvalidRequests"
* flag, which is set to "true" (that is, discard invalid messages) by default.
* @param requestMessage the invalid request message
* @return the RemoteInvocation to expose for the invalid request (typically
* <code>null</code> in case of an invalid message that will simply be ignored)
* @throws javax.jms.JMSException in case of the invalid request supposed
* to lead to an exception (instead of ignoring it)
* @see #readRemoteInvocation
* @see #setIgnoreInvalidRequests
*/
protected RemoteInvocation onInvalidRequest(Message requestMessage) throws JMSException {
if (this.ignoreInvalidRequests) {
if (logger.isWarnEnabled()) {
logger.warn("Invalid request message will be discarded: " + requestMessage);
}
return null;
}
else {
throw new MessageFormatException("Invalid request message: " + requestMessage);
}
}
}

View File

@@ -1,12 +0,0 @@
/**
*
* Remoting classes for transparent Java-to-Java remoting via a JMS provider.
*
* <p>Allows the target service to be load-balanced across a number of queue
* receivers, and provides a level of indirection between the client and the
* service: They only need to agree on a queue name and a service interface.
*
*/
package org.springframework.jms.remoting;

View File

@@ -1,212 +0,0 @@
/*
* Copyright 2002-2008 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.springframework.jms.support;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Session;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Constants;
import org.springframework.jms.JmsException;
/**
* Base class for {@link org.springframework.jms.core.JmsTemplate} and other
* JMS-accessing gateway helpers, defining common properties such as the
* JMS {@link ConnectionFactory} to operate on. The subclass
* {@link org.springframework.jms.support.destination.JmsDestinationAccessor}
* adds further, destination-related properties.
*
* <p>Not intended to be used directly.
* See {@link org.springframework.jms.core.JmsTemplate}.
*
* @author Juergen Hoeller
* @since 1.2
* @see org.springframework.jms.support.destination.JmsDestinationAccessor
* @see org.springframework.jms.core.JmsTemplate
*/
public abstract class JmsAccessor implements InitializingBean {
/** Constants instance for javax.jms.Session */
private static final Constants sessionConstants = new Constants(Session.class);
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private ConnectionFactory connectionFactory;
private boolean sessionTransacted = false;
private int sessionAcknowledgeMode = Session.AUTO_ACKNOWLEDGE;
/**
* Set the ConnectionFactory to use for obtaining JMS {@link Connection Connections}.
*/
public void setConnectionFactory(ConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
/**
* Return the ConnectionFactory that this accessor uses for obtaining
* JMS {@link Connection Connections}.
*/
public ConnectionFactory getConnectionFactory() {
return this.connectionFactory;
}
/**
* Set the transaction mode that is used when creating a JMS {@link Session}.
* Default is "false".
* <p>Note that within a JTA transaction, the parameters passed to
* <code>create(Queue/Topic)Session(boolean transacted, int acknowledgeMode)</code>
* method are not taken into account. Depending on the J2EE transaction context,
* the container makes its own decisions on these values. Analogously, these
* parameters are not taken into account within a locally managed transaction
* either, since the accessor operates on an existing JMS Session in this case.
* <p>Setting this flag to "true" will use a short local JMS transaction
* when running outside of a managed transaction, and a synchronized local
* JMS transaction in case of a managed transaction (other than an XA
* transaction) being present. The latter has the effect of a local JMS
* transaction being managed alongside the main transaction (which might
* be a native JDBC transaction), with the JMS transaction committing
* right after the main transaction.
* @see javax.jms.Connection#createSession(boolean, int)
*/
public void setSessionTransacted(boolean sessionTransacted) {
this.sessionTransacted = sessionTransacted;
}
/**
* Return whether the JMS {@link Session sessions} used by this
* accessor are supposed to be transacted.
* @see #setSessionTransacted(boolean)
*/
public boolean isSessionTransacted() {
return this.sessionTransacted;
}
/**
* Set the JMS acknowledgement mode by the name of the corresponding constant
* in the JMS {@link Session} interface, e.g. "CLIENT_ACKNOWLEDGE".
* <p>If you want to use vendor-specific extensions to the acknowledgment mode,
* use {@link #setSessionAcknowledgeModeName(String)} instead.
* @param constantName the name of the {@link Session} acknowledge mode constant
* @see javax.jms.Session#AUTO_ACKNOWLEDGE
* @see javax.jms.Session#CLIENT_ACKNOWLEDGE
* @see javax.jms.Session#DUPS_OK_ACKNOWLEDGE
* @see javax.jms.Connection#createSession(boolean, int)
*/
public void setSessionAcknowledgeModeName(String constantName) {
setSessionAcknowledgeMode(sessionConstants.asNumber(constantName).intValue());
}
/**
* Set the JMS acknowledgement mode that is used when creating a JMS
* {@link Session} to send a message.
* <p>Default is {@link Session#AUTO_ACKNOWLEDGE}.
* <p>Vendor-specific extensions to the acknowledgment mode can be set here as well.
* <p>Note that that inside an EJB the parameters to
* create(Queue/Topic)Session(boolean transacted, int acknowledgeMode) method
* are not taken into account. Depending on the transaction context in the EJB,
* the container makes its own decisions on these values. See section 17.3.5
* of the EJB spec.
* @param sessionAcknowledgeMode the acknowledgement mode constant
* @see javax.jms.Session#AUTO_ACKNOWLEDGE
* @see javax.jms.Session#CLIENT_ACKNOWLEDGE
* @see javax.jms.Session#DUPS_OK_ACKNOWLEDGE
* @see javax.jms.Connection#createSession(boolean, int)
*/
public void setSessionAcknowledgeMode(int sessionAcknowledgeMode) {
this.sessionAcknowledgeMode = sessionAcknowledgeMode;
}
/**
* Return the acknowledgement mode for JMS {@link Session sessions}.
*/
public int getSessionAcknowledgeMode() {
return this.sessionAcknowledgeMode;
}
public void afterPropertiesSet() {
if (getConnectionFactory() == null) {
throw new IllegalArgumentException("Property 'connectionFactory' is required");
}
}
/**
* Convert the specified checked {@link javax.jms.JMSException JMSException} to
* a Spring runtime {@link org.springframework.jms.JmsException JmsException}
* equivalent.
* <p>The default implementation delegates to the
* {@link org.springframework.jms.support.JmsUtils#convertJmsAccessException} method.
* @param ex the original checked {@link JMSException} to convert
* @return the Spring runtime {@link JmsException} wrapping <code>ex</code>
* @see org.springframework.jms.support.JmsUtils#convertJmsAccessException
*/
protected JmsException convertJmsAccessException(JMSException ex) {
return JmsUtils.convertJmsAccessException(ex);
}
//-------------------------------------------------------------------------
// JMS 1.1 factory methods, potentially overridden for JMS 1.0.2
//-------------------------------------------------------------------------
/**
* Create a JMS Connection via this template's ConnectionFactory.
* <p>This implementation uses JMS 1.1 API.
* @return the new JMS Connection
* @throws JMSException if thrown by JMS API methods
* @see javax.jms.ConnectionFactory#createConnection()
*/
protected Connection createConnection() throws JMSException {
return getConnectionFactory().createConnection();
}
/**
* Create a JMS Session for the given Connection.
* <p>This implementation uses JMS 1.1 API.
* @param con the JMS Connection to create a Session for
* @return the new JMS Session
* @throws JMSException if thrown by JMS API methods
* @see javax.jms.Connection#createSession(boolean, int)
*/
protected Session createSession(Connection con) throws JMSException {
return con.createSession(isSessionTransacted(), getSessionAcknowledgeMode());
}
/**
* Determine whether the given Session is in client acknowledge mode.
* <p>This implementation uses JMS 1.1 API.
* @param session the JMS Session to check
* @return whether the given Session is in client acknowledge mode
* @throws javax.jms.JMSException if thrown by JMS API methods
* @see javax.jms.Session#getAcknowledgeMode()
* @see javax.jms.Session#CLIENT_ACKNOWLEDGE
*/
protected boolean isClientAcknowledge(Session session) throws JMSException {
return (session.getAcknowledgeMode() == Session.CLIENT_ACKNOWLEDGE);
}
}

View File

@@ -1,319 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.support;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.QueueBrowser;
import javax.jms.QueueRequestor;
import javax.jms.Session;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jms.InvalidClientIDException;
import org.springframework.jms.InvalidDestinationException;
import org.springframework.jms.InvalidSelectorException;
import org.springframework.jms.JmsException;
import org.springframework.jms.JmsSecurityException;
import org.springframework.jms.MessageEOFException;
import org.springframework.jms.MessageFormatException;
import org.springframework.jms.MessageNotReadableException;
import org.springframework.jms.MessageNotWriteableException;
import org.springframework.jms.ResourceAllocationException;
import org.springframework.jms.TransactionInProgressException;
import org.springframework.jms.TransactionRolledBackException;
import org.springframework.jms.UncategorizedJmsException;
import org.springframework.util.Assert;
/**
* Generic utility methods for working with JMS. Mainly for internal use
* within the framework, but also useful for custom JMS access code.
*
* @author Juergen Hoeller
* @since 1.1
*/
public abstract class JmsUtils {
private static final Log logger = LogFactory.getLog(JmsUtils.class);
/**
* Close the given JMS Connection and ignore any thrown exception.
* This is useful for typical <code>finally</code> blocks in manual JMS code.
* @param con the JMS Connection to close (may be <code>null</code>)
*/
public static void closeConnection(Connection con) {
closeConnection(con, false);
}
/**
* Close the given JMS Connection and ignore any thrown exception.
* This is useful for typical <code>finally</code> blocks in manual JMS code.
* @param con the JMS Connection to close (may be <code>null</code>)
* @param stop whether to call <code>stop()</code> before closing
*/
public static void closeConnection(Connection con, boolean stop) {
if (con != null) {
try {
if (stop) {
try {
con.stop();
}
finally {
con.close();
}
}
else {
con.close();
}
}
catch (javax.jms.IllegalStateException ex) {
logger.debug("Ignoring Connection state exception - assuming already closed: " + ex);
}
catch (JMSException ex) {
logger.debug("Could not close JMS Connection", ex);
}
catch (Throwable ex) {
// We don't trust the JMS provider: It might throw RuntimeException or Error.
logger.debug("Unexpected exception on closing JMS Connection", ex);
}
}
}
/**
* Close the given JMS Session and ignore any thrown exception.
* This is useful for typical <code>finally</code> blocks in manual JMS code.
* @param session the JMS Session to close (may be <code>null</code>)
*/
public static void closeSession(Session session) {
if (session != null) {
try {
session.close();
}
catch (JMSException ex) {
logger.trace("Could not close JMS Session", ex);
}
catch (Throwable ex) {
// We don't trust the JMS provider: It might throw RuntimeException or Error.
logger.trace("Unexpected exception on closing JMS Session", ex);
}
}
}
/**
* Close the given JMS MessageProducer and ignore any thrown exception.
* This is useful for typical <code>finally</code> blocks in manual JMS code.
* @param producer the JMS MessageProducer to close (may be <code>null</code>)
*/
public static void closeMessageProducer(MessageProducer producer) {
if (producer != null) {
try {
producer.close();
}
catch (JMSException ex) {
logger.trace("Could not close JMS MessageProducer", ex);
}
catch (Throwable ex) {
// We don't trust the JMS provider: It might throw RuntimeException or Error.
logger.trace("Unexpected exception on closing JMS MessageProducer", ex);
}
}
}
/**
* Close the given JMS MessageConsumer and ignore any thrown exception.
* This is useful for typical <code>finally</code> blocks in manual JMS code.
* @param consumer the JMS MessageConsumer to close (may be <code>null</code>)
*/
public static void closeMessageConsumer(MessageConsumer consumer) {
if (consumer != null) {
// Clear interruptions to ensure that the consumer closes successfully...
// (working around misbehaving JMS providers such as ActiveMQ)
boolean wasInterrupted = Thread.interrupted();
try {
consumer.close();
}
catch (JMSException ex) {
logger.trace("Could not close JMS MessageConsumer", ex);
}
catch (Throwable ex) {
// We don't trust the JMS provider: It might throw RuntimeException or Error.
logger.trace("Unexpected exception on closing JMS MessageConsumer", ex);
}
finally {
if (wasInterrupted) {
// Reset the interrupted flag as it was before.
Thread.currentThread().interrupt();
}
}
}
}
/**
* Close the given JMS QueueBrowser and ignore any thrown exception.
* This is useful for typical <code>finally</code> blocks in manual JMS code.
* @param browser the JMS QueueBrowser to close (may be <code>null</code>)
*/
public static void closeQueueBrowser(QueueBrowser browser) {
if (browser != null) {
try {
browser.close();
}
catch (JMSException ex) {
logger.trace("Could not close JMS QueueBrowser", ex);
}
catch (Throwable ex) {
// We don't trust the JMS provider: It might throw RuntimeException or Error.
logger.trace("Unexpected exception on closing JMS QueueBrowser", ex);
}
}
}
/**
* Close the given JMS QueueRequestor and ignore any thrown exception.
* This is useful for typical <code>finally</code> blocks in manual JMS code.
* @param requestor the JMS QueueRequestor to close (may be <code>null</code>)
*/
public static void closeQueueRequestor(QueueRequestor requestor) {
if (requestor != null) {
try {
requestor.close();
}
catch (JMSException ex) {
logger.trace("Could not close JMS QueueRequestor", ex);
}
catch (Throwable ex) {
// We don't trust the JMS provider: It might throw RuntimeException or Error.
logger.trace("Unexpected exception on closing JMS QueueRequestor", ex);
}
}
}
/**
* Commit the Session if not within a JTA transaction.
* @param session the JMS Session to commit
* @throws JMSException if committing failed
*/
public static void commitIfNecessary(Session session) throws JMSException {
Assert.notNull(session, "Session must not be null");
try {
session.commit();
}
catch (javax.jms.TransactionInProgressException ex) {
// Ignore -> can only happen in case of a JTA transaction.
}
catch (javax.jms.IllegalStateException ex) {
// Ignore -> can only happen in case of a JTA transaction.
}
}
/**
* Rollback the Session if not within a JTA transaction.
* @param session the JMS Session to rollback
* @throws JMSException if committing failed
*/
public static void rollbackIfNecessary(Session session) throws JMSException {
Assert.notNull(session, "Session must not be null");
try {
session.rollback();
}
catch (javax.jms.TransactionInProgressException ex) {
// Ignore -> can only happen in case of a JTA transaction.
}
catch (javax.jms.IllegalStateException ex) {
// Ignore -> can only happen in case of a JTA transaction.
}
}
/**
* Build a descriptive exception message for the given JMSException,
* incorporating a linked exception's message if appropriate.
* @param ex the JMSException to build a message for
* @return the descriptive message String
* @see javax.jms.JMSException#getLinkedException()
*/
public static String buildExceptionMessage(JMSException ex) {
String message = ex.getMessage();
Exception linkedEx = ex.getLinkedException();
if (linkedEx != null) {
if (message == null) {
message = linkedEx.toString();
}
else {
String linkedMessage = linkedEx.getMessage();
if (linkedMessage != null && !message.contains(linkedMessage)) {
message = message + "; nested exception is " + linkedEx;
}
}
}
return message;
}
/**
* Convert the specified checked {@link javax.jms.JMSException JMSException} to a
* Spring runtime {@link org.springframework.jms.JmsException JmsException} equivalent.
* @param ex the original checked JMSException to convert
* @return the Spring runtime JmsException wrapping the given exception
*/
public static JmsException convertJmsAccessException(JMSException ex) {
Assert.notNull(ex, "JMSException must not be null");
if (ex instanceof javax.jms.IllegalStateException) {
return new org.springframework.jms.IllegalStateException((javax.jms.IllegalStateException) ex);
}
if (ex instanceof javax.jms.InvalidClientIDException) {
return new InvalidClientIDException((javax.jms.InvalidClientIDException) ex);
}
if (ex instanceof javax.jms.InvalidDestinationException) {
return new InvalidDestinationException((javax.jms.InvalidDestinationException) ex);
}
if (ex instanceof javax.jms.InvalidSelectorException) {
return new InvalidSelectorException((javax.jms.InvalidSelectorException) ex);
}
if (ex instanceof javax.jms.JMSSecurityException) {
return new JmsSecurityException((javax.jms.JMSSecurityException) ex);
}
if (ex instanceof javax.jms.MessageEOFException) {
return new MessageEOFException((javax.jms.MessageEOFException) ex);
}
if (ex instanceof javax.jms.MessageFormatException) {
return new MessageFormatException((javax.jms.MessageFormatException) ex);
}
if (ex instanceof javax.jms.MessageNotReadableException) {
return new MessageNotReadableException((javax.jms.MessageNotReadableException) ex);
}
if (ex instanceof javax.jms.MessageNotWriteableException) {
return new MessageNotWriteableException((javax.jms.MessageNotWriteableException) ex);
}
if (ex instanceof javax.jms.ResourceAllocationException) {
return new ResourceAllocationException((javax.jms.ResourceAllocationException) ex);
}
if (ex instanceof javax.jms.TransactionInProgressException) {
return new TransactionInProgressException((javax.jms.TransactionInProgressException) ex);
}
if (ex instanceof javax.jms.TransactionRolledBackException) {
return new TransactionRolledBackException((javax.jms.TransactionRolledBackException) ex);
}
// fallback
return new UncategorizedJmsException(ex);
}
}

View File

@@ -1,373 +0,0 @@
/*
* Copyright 2002-2011 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.springframework.jms.support.converter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.xml.transform.Result;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.type.TypeFactory;
import org.codehaus.jackson.type.JavaType;
import org.springframework.oxm.Marshaller;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Message converter that uses the Jackson library to convert messages to and from JSON.
* Maps an object to a {@link BytesMessage}, or to a {@link TextMessage} if the
* {@link #setTargetType targetType} is set to {@link MessageType#TEXT}.
* Converts from a {@link TextMessage} or {@link BytesMessage} to an object.
*
* @author Mark Pollack
* @author Dave Syer
* @author Juergen Hoeller
* @since 3.1
*/
public class MappingJacksonMessageConverter implements MessageConverter {
/**
* The default encoding used for writing to text messages: UTF-8.
*/
public static final String DEFAULT_ENCODING = "UTF-8";
private ObjectMapper objectMapper = new ObjectMapper();
private MessageType targetType = MessageType.BYTES;
private String encoding = DEFAULT_ENCODING;
private String encodingPropertyName;
private String typeIdPropertyName;
private Map<String, Class<?>> idClassMappings = new HashMap<String, Class<?>>();
private Map<Class<?>, String> classIdMappings = new HashMap<Class<?>, String>();
/**
* Specify the {@link ObjectMapper} to use instead of using the default.
*/
public void setObjectMapper(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "ObjectMapper must not be null");
this.objectMapper = objectMapper;
}
/**
* Specify whether {@link #toMessage(Object, Session)} should marshal to a
* {@link BytesMessage} or a {@link TextMessage}.
* <p>The default is {@link MessageType#BYTES}, i.e. this converter marshals to
* a {@link BytesMessage}. Note that the default version of this converter
* supports {@link MessageType#BYTES} and {@link MessageType#TEXT} only.
* @see MessageType#BYTES
* @see MessageType#TEXT
*/
public void setTargetType(MessageType targetType) {
Assert.notNull(targetType, "MessageType must not be null");
this.targetType = targetType;
}
/**
* Specify the encoding to use when converting to and from text-based
* message body content. The default encoding will be "UTF-8".
* <p>When reading from a a text-based message, an encoding may have been
* suggested through a special JMS property which will then be preferred
* over the encoding set on this MessageConverter instance.
* @see #setEncodingPropertyName
*/
public void setEncoding(String encoding) {
this.encoding = encoding;
}
/**
* Specify the name of the JMS message property that carries the encoding from
* bytes to String and back is BytesMessage is used during the conversion process.
* <p>Default is none. Setting this property is optional; if not set, UTF-8 will
* be used for decoding any incoming bytes message.
* @see #setEncoding
*/
public void setEncodingPropertyName(String encodingPropertyName) {
this.encodingPropertyName = encodingPropertyName;
}
/**
* Specify the name of the JMS message property that carries the type id for the
* contained object: either a mapped id value or a raw Java class name.
* <p>Default is none. <b>NOTE: This property needs to be set in order to allow
* for converting from an incoming message to a Java object.</b>
* @see #setTypeIdMappings
*/
public void setTypeIdPropertyName(String typeIdPropertyName) {
this.typeIdPropertyName = typeIdPropertyName;
}
/**
* Specify mappings from type ids to Java classes, if desired.
* This allows for synthetic ids in the type id message property,
* instead of transferring Java class names.
* <p>Default is no custom mappings, i.e. transferring raw Java class names.
* @param typeIdMappings a Map with type id values as keys and Java classes as values
*/
public void setTypeIdMappings(Map<String, Class<?>> typeIdMappings) {
this.idClassMappings = new HashMap<String, Class<?>>();
for (Map.Entry<String, Class<?>> entry : typeIdMappings.entrySet()) {
String id = entry.getKey();
Class<?> clazz = entry.getValue();
this.idClassMappings.put(id, clazz);
this.classIdMappings.put(clazz, id);
}
}
public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
Message message;
try {
switch (this.targetType) {
case TEXT:
message = mapToTextMessage(object, session, this.objectMapper);
break;
case BYTES:
message = mapToBytesMessage(object, session, this.objectMapper);
break;
default:
message = mapToMessage(object, session, this.objectMapper, this.targetType);
}
}
catch (IOException ex) {
throw new MessageConversionException("Could not map JSON object [" + object + "]", ex);
}
setTypeIdOnMessage(object, message);
return message;
}
public Object fromMessage(Message message) throws JMSException, MessageConversionException {
try {
JavaType targetJavaType = getJavaTypeForMessage(message);
return convertToObject(message, targetJavaType);
}
catch (IOException ex) {
throw new MessageConversionException("Failed to convert JSON message content", ex);
}
}
/**
* Map the given object to a {@link TextMessage}.
* @param object the object to be mapped
* @param session current JMS session
* @param objectMapper the mapper to use
* @return the resulting message
* @throws JMSException if thrown by JMS methods
* @throws IOException in case of I/O errors
* @see Session#createBytesMessage
* @see Marshaller#marshal(Object, Result)
*/
protected TextMessage mapToTextMessage(Object object, Session session, ObjectMapper objectMapper)
throws JMSException, IOException {
StringWriter writer = new StringWriter();
objectMapper.writeValue(writer, object);
return session.createTextMessage(writer.toString());
}
/**
* Map the given object to a {@link BytesMessage}.
* @param object the object to be mapped
* @param session current JMS session
* @param objectMapper the mapper to use
* @return the resulting message
* @throws JMSException if thrown by JMS methods
* @throws IOException in case of I/O errors
* @see Session#createBytesMessage
* @see Marshaller#marshal(Object, Result)
*/
protected BytesMessage mapToBytesMessage(Object object, Session session, ObjectMapper objectMapper)
throws JMSException, IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(bos, this.encoding);
objectMapper.writeValue(writer, object);
BytesMessage message = session.createBytesMessage();
message.writeBytes(bos.toByteArray());
if (this.encodingPropertyName != null) {
message.setStringProperty(this.encodingPropertyName, this.encoding);
}
return message;
}
/**
* Template method that allows for custom message mapping.
* Invoked when {@link #setTargetType} is not {@link MessageType#TEXT} or
* {@link MessageType#BYTES}.
* <p>The default implementation throws an {@link IllegalArgumentException}.
* @param object the object to marshal
* @param session the JMS Session
* @param objectMapper the mapper to use
* @param targetType the target message type (other than TEXT or BYTES)
* @return the resulting message
* @throws JMSException if thrown by JMS methods
* @throws IOException in case of I/O errors
*/
protected Message mapToMessage(Object object, Session session, ObjectMapper objectMapper, MessageType targetType)
throws JMSException, IOException {
throw new IllegalArgumentException("Unsupported message type [" + targetType +
"]. MappingJacksonMessageConverter by default only supports TextMessages and BytesMessages.");
}
/**
* Set a type id for the given payload object on the given JMS Message.
* <p>The default implementation consults the configured type id mapping and
* sets the resulting value (either a mapped id or the raw Java class name)
* into the configured type id message property.
* @param object the payload object to set a type id for
* @param message the JMS Message to set the type id on
* @throws JMSException if thrown by JMS methods
* @see #getJavaTypeForMessage(javax.jms.Message)
* @see #setTypeIdPropertyName(String)
* @see #setTypeIdMappings(java.util.Map)
*/
protected void setTypeIdOnMessage(Object object, Message message) throws JMSException {
if (this.typeIdPropertyName != null) {
String typeId = this.classIdMappings.get(object.getClass());
if (typeId == null) {
typeId = object.getClass().getName();
}
message.setStringProperty(this.typeIdPropertyName, typeId);
}
}
/**
* Convenience method to dispatch to converters for individual message types.
*/
private Object convertToObject(Message message, JavaType targetJavaType) throws JMSException, IOException {
if (message instanceof TextMessage) {
return convertFromTextMessage((TextMessage) message, targetJavaType);
}
else if (message instanceof BytesMessage) {
return convertFromBytesMessage((BytesMessage) message, targetJavaType);
}
else {
return convertFromMessage(message, targetJavaType);
}
}
/**
* Convert a TextMessage to a Java Object with the specified type.
* @param message the input message
* @param targetJavaType the target type
* @return the message converted to an object
* @throws JMSException if thrown by JMS
* @throws IOException in case of I/O errors
*/
protected Object convertFromTextMessage(TextMessage message, JavaType targetJavaType)
throws JMSException, IOException {
String body = message.getText();
return this.objectMapper.readValue(body, targetJavaType);
}
/**
* Convert a BytesMessage to a Java Object with the specified type.
* @param message the input message
* @param targetJavaType the target type
* @return the message converted to an object
* @throws JMSException if thrown by JMS
* @throws IOException in case of I/O errors
*/
protected Object convertFromBytesMessage(BytesMessage message, JavaType targetJavaType)
throws JMSException, IOException {
String encoding = this.encoding;
if (this.encodingPropertyName != null && message.propertyExists(this.encodingPropertyName)) {
encoding = message.getStringProperty(this.encodingPropertyName);
}
byte[] bytes = new byte[(int) message.getBodyLength()];
message.readBytes(bytes);
try {
String body = new String(bytes, encoding);
return this.objectMapper.readValue(body, targetJavaType);
}
catch (UnsupportedEncodingException ex) {
throw new MessageConversionException("Cannot convert bytes to String", ex);
}
}
/**
* Template method that allows for custom message mapping.
* Invoked when {@link #setTargetType} is not {@link MessageType#TEXT} or
* {@link MessageType#BYTES}.
* <p>The default implementation throws an {@link IllegalArgumentException}.
* @param message the input message
* @param targetJavaType the target type
* @return the message converted to an object
* @throws JMSException if thrown by JMS
* @throws IOException in case of I/O errors
*/
protected Object convertFromMessage(Message message, JavaType targetJavaType)
throws JMSException, IOException {
throw new IllegalArgumentException("Unsupported message type [" + message.getClass() +
"]. MappingJacksonMessageConverter by default only supports TextMessages and BytesMessages.");
}
/**
* Determine a Jackson JavaType for the given JMS Message,
* typically parsing a type id message property.
* <p>The default implementation parses the configured type id property name
* and consults the configured type id mapping. This can be overridden with
* a different strategy, e.g. doing some heuristics based on message origin.
* @param object the payload object to set a type id for
* @param message the JMS Message to set the type id on
* @throws JMSException if thrown by JMS methods
* @see #setTypeIdOnMessage(Object, javax.jms.Message)
* @see #setTypeIdPropertyName(String)
* @see #setTypeIdMappings(java.util.Map)
*/
protected JavaType getJavaTypeForMessage(Message message) throws JMSException {
String typeId = message.getStringProperty(this.typeIdPropertyName);
if (typeId == null) {
throw new MessageConversionException("Could not find type id property [" + this.typeIdPropertyName + "]");
}
Class mappedClass = this.idClassMappings.get(typeId);
if (mappedClass != null) {
return TypeFactory.type(mappedClass);
}
try {
return TypeFactory.type(ClassUtils.forName(typeId, getClass().getClassLoader()));
}
catch (Throwable ex) {
throw new MessageConversionException("Failed to resolve type id [" + typeId + "]", ex);
}
}
}

View File

@@ -1,317 +0,0 @@
/*
* Copyright 2002-2011 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.springframework.jms.support.converter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.Unmarshaller;
import org.springframework.oxm.XmlMappingException;
import org.springframework.util.Assert;
/**
* Spring JMS {@link MessageConverter} that uses a {@link Marshaller} and {@link Unmarshaller}.
* Marshals an object to a {@link BytesMessage}, or to a {@link TextMessage} if the
* {@link #setTargetType targetType} is set to {@link MessageType#TEXT}.
* Unmarshals from a {@link TextMessage} or {@link BytesMessage} to an object.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 3.0
* @see org.springframework.jms.core.JmsTemplate#convertAndSend
* @see org.springframework.jms.core.JmsTemplate#receiveAndConvert
*/
public class MarshallingMessageConverter implements MessageConverter, InitializingBean {
private Marshaller marshaller;
private Unmarshaller unmarshaller;
private MessageType targetType = MessageType.BYTES;
/**
* Construct a new <code>MarshallingMessageConverter</code> with no {@link Marshaller}
* or {@link Unmarshaller} set. The marshaller must be set after construction by invoking
* {@link #setMarshaller(Marshaller)} and {@link #setUnmarshaller(Unmarshaller)} .
*/
public MarshallingMessageConverter() {
}
/**
* Construct a new <code>MarshallingMessageConverter</code> with the given {@link Marshaller} set.
* <p>If the given {@link Marshaller} also implements the {@link Unmarshaller} interface,
* it is used for both marshalling and unmarshalling. Otherwise, an exception is thrown.
* <p>Note that all {@link Marshaller} implementations in Spring also implement the
* {@link Unmarshaller} interface, so that you can safely use this constructor.
* @param marshaller object used as marshaller and unmarshaller
* @throws IllegalArgumentException when <code>marshaller</code> does not implement the
* {@link Unmarshaller} interface as well
*/
public MarshallingMessageConverter(Marshaller marshaller) {
Assert.notNull(marshaller, "Marshaller must not be null");
if (!(marshaller instanceof Unmarshaller)) {
throw new IllegalArgumentException(
"Marshaller [" + marshaller + "] does not implement the Unmarshaller " +
"interface. Please set an Unmarshaller explicitly by using the " +
"MarshallingMessageConverter(Marshaller, Unmarshaller) constructor.");
}
else {
this.marshaller = marshaller;
this.unmarshaller = (Unmarshaller) marshaller;
}
}
/**
* Construct a new <code>MarshallingMessageConverter</code> with the
* given Marshaller and Unmarshaller.
* @param marshaller the Marshaller to use
* @param unmarshaller the Unmarshaller to use
*/
public MarshallingMessageConverter(Marshaller marshaller, Unmarshaller unmarshaller) {
Assert.notNull(marshaller, "Marshaller must not be null");
Assert.notNull(unmarshaller, "Unmarshaller must not be null");
this.marshaller = marshaller;
this.unmarshaller = unmarshaller;
}
/**
* Set the {@link Marshaller} to be used by this message converter.
*/
public void setMarshaller(Marshaller marshaller) {
this.marshaller = marshaller;
}
/**
* Set the {@link Unmarshaller} to be used by this message converter.
*/
public void setUnmarshaller(Unmarshaller unmarshaller) {
this.unmarshaller = unmarshaller;
}
/**
* Specify whether {@link #toMessage(Object, Session)} should marshal to
* a {@link BytesMessage} or a {@link TextMessage}.
* <p>The default is {@link MessageType#BYTES}, i.e. this converter marshals
* to a {@link BytesMessage}. Note that the default version of this converter
* supports {@link MessageType#BYTES} and {@link MessageType#TEXT} only.
* @see MessageType#BYTES
* @see MessageType#TEXT
*/
public void setTargetType(MessageType targetType) {
Assert.notNull(targetType, "MessageType must not be null");
this.targetType = targetType;
}
public void afterPropertiesSet() {
Assert.notNull(this.marshaller, "Property 'marshaller' is required");
Assert.notNull(this.unmarshaller, "Property 'unmarshaller' is required");
}
/**
* This implementation marshals the given object to a {@link javax.jms.TextMessage} or
* {@link javax.jms.BytesMessage}. The desired message type can be defined by setting
* the {@link #setTargetType "marshalTo"} property.
* @see #marshalToTextMessage
* @see #marshalToBytesMessage
*/
public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
try {
switch (this.targetType) {
case TEXT:
return marshalToTextMessage(object, session, this.marshaller);
case BYTES:
return marshalToBytesMessage(object, session, this.marshaller);
default:
return marshalToMessage(object, session, this.marshaller, this.targetType);
}
}
catch (XmlMappingException ex) {
throw new MessageConversionException("Could not marshal [" + object + "]", ex);
}
catch (IOException ex) {
throw new MessageConversionException("Could not marshal [" + object + "]", ex);
}
}
/**
* This implementation unmarshals the given {@link Message} into an object.
* @see #unmarshalFromTextMessage
* @see #unmarshalFromBytesMessage
*/
public Object fromMessage(Message message) throws JMSException, MessageConversionException {
try {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
return unmarshalFromTextMessage(textMessage, this.unmarshaller);
}
else if (message instanceof BytesMessage) {
BytesMessage bytesMessage = (BytesMessage) message;
return unmarshalFromBytesMessage(bytesMessage, this.unmarshaller);
}
else {
return unmarshalFromMessage(message, this.unmarshaller);
}
}
catch (IOException ex) {
throw new MessageConversionException("Could not access message content: " + message, ex);
}
catch (XmlMappingException ex) {
throw new MessageConversionException("Could not unmarshal message: " + message, ex);
}
}
/**
* Marshal the given object to a {@link TextMessage}.
* @param object the object to be marshalled
* @param session current JMS session
* @param marshaller the marshaller to use
* @return the resulting message
* @throws JMSException if thrown by JMS methods
* @throws IOException in case of I/O errors
* @throws XmlMappingException in case of OXM mapping errors
* @see Session#createTextMessage
* @see Marshaller#marshal(Object, Result)
*/
protected TextMessage marshalToTextMessage(Object object, Session session, Marshaller marshaller)
throws JMSException, IOException, XmlMappingException {
StringWriter writer = new StringWriter();
Result result = new StreamResult(writer);
marshaller.marshal(object, result);
return session.createTextMessage(writer.toString());
}
/**
* Marshal the given object to a {@link BytesMessage}.
* @param object the object to be marshalled
* @param session current JMS session
* @param marshaller the marshaller to use
* @return the resulting message
* @throws JMSException if thrown by JMS methods
* @throws IOException in case of I/O errors
* @throws XmlMappingException in case of OXM mapping errors
* @see Session#createBytesMessage
* @see Marshaller#marshal(Object, Result)
*/
protected BytesMessage marshalToBytesMessage(Object object, Session session, Marshaller marshaller)
throws JMSException, IOException, XmlMappingException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
StreamResult streamResult = new StreamResult(bos);
marshaller.marshal(object, streamResult);
BytesMessage message = session.createBytesMessage();
message.writeBytes(bos.toByteArray());
return message;
}
/**
* Template method that allows for custom message marshalling.
* Invoked when {@link #setTargetType} is not {@link MessageType#TEXT} or
* {@link MessageType#BYTES}.
* <p>The default implementation throws an {@link IllegalArgumentException}.
* @param object the object to marshal
* @param session the JMS session
* @param marshaller the marshaller to use
* @param targetType the target message type (other than TEXT or BYTES)
* @return the resulting message
* @throws JMSException if thrown by JMS methods
* @throws IOException in case of I/O errors
* @throws XmlMappingException in case of OXM mapping errors
*/
protected Message marshalToMessage(Object object, Session session, Marshaller marshaller, MessageType targetType)
throws JMSException, IOException, XmlMappingException {
throw new IllegalArgumentException("Unsupported message type [" + targetType +
"]. MarshallingMessageConverter by default only supports TextMessages and BytesMessages.");
}
/**
* Unmarshal the given {@link TextMessage} into an object.
* @param message the message
* @param unmarshaller the unmarshaller to use
* @return the unmarshalled object
* @throws JMSException if thrown by JMS methods
* @throws IOException in case of I/O errors
* @throws XmlMappingException in case of OXM mapping errors
* @see Unmarshaller#unmarshal(Source)
*/
protected Object unmarshalFromTextMessage(TextMessage message, Unmarshaller unmarshaller)
throws JMSException, IOException, XmlMappingException {
Source source = new StreamSource(new StringReader(message.getText()));
return unmarshaller.unmarshal(source);
}
/**
* Unmarshal the given {@link BytesMessage} into an object.
* @param message the message
* @param unmarshaller the unmarshaller to use
* @return the unmarshalled object
* @throws JMSException if thrown by JMS methods
* @throws IOException in case of I/O errors
* @throws XmlMappingException in case of OXM mapping errors
* @see Unmarshaller#unmarshal(Source)
*/
protected Object unmarshalFromBytesMessage(BytesMessage message, Unmarshaller unmarshaller)
throws JMSException, IOException, XmlMappingException {
byte[] bytes = new byte[(int) message.getBodyLength()];
message.readBytes(bytes);
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
StreamSource source = new StreamSource(bis);
return unmarshaller.unmarshal(source);
}
/**
* Template method that allows for custom message unmarshalling.
* Invoked when {@link #fromMessage(Message)} is invoked with a message
* that is not a {@link TextMessage} or {@link BytesMessage}.
* <p>The default implementation throws an {@link IllegalArgumentException}.
* @param message the message
* @param unmarshaller the unmarshaller to use
* @return the unmarshalled object
* @throws JMSException if thrown by JMS methods
* @throws IOException in case of I/O errors
* @throws XmlMappingException in case of OXM mapping errors
*/
protected Object unmarshalFromMessage(Message message, Unmarshaller unmarshaller)
throws JMSException, IOException, XmlMappingException {
throw new IllegalArgumentException("Unsupported message type [" + message.getClass() +
"]. MarshallingMessageConverter by default only supports TextMessages and BytesMessages.");
}
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright 2002-2006 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.springframework.jms.support.converter;
import org.springframework.jms.JmsException;
/**
* Thrown by {@link MessageConverter} implementations when the conversion
* of an object to/from a {@link javax.jms.Message} fails.
*
* @author Mark Pollack
* @since 1.1
* @see MessageConverter
*/
public class MessageConversionException extends JmsException {
/**
* Create a new MessageConversionException.
* @param msg the detail message
*/
public MessageConversionException(String msg) {
super(msg);
}
/**
* Create a new MessageConversionException.
* @param msg the detail message
* @param cause the root cause (if any)
*/
public MessageConversionException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,59 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms.support.converter;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
/**
* Strategy interface that specifies a converter between Java objects and JMS messages.
*
* <p>Check out {@link SimpleMessageConverter} for a default implementation,
* converting between the 'standard' message payloads and JMS Message types.
*
* @author Mark Pollack
* @author Juergen Hoeller
* @since 1.1
* @see org.springframework.jms.core.JmsTemplate#setMessageConverter
* @see org.springframework.jms.listener.adapter.MessageListenerAdapter#setMessageConverter
* @see org.springframework.jms.remoting.JmsInvokerClientInterceptor#setMessageConverter
* @see org.springframework.jms.remoting.JmsInvokerServiceExporter#setMessageConverter
*/
public interface MessageConverter {
/**
* Convert a Java object to a JMS Message using the supplied session
* to create the message object.
* @param object the object to convert
* @param session the Session to use for creating a JMS Message
* @return the JMS Message
* @throws javax.jms.JMSException if thrown by JMS API methods
* @throws MessageConversionException in case of conversion failure
*/
Message toMessage(Object object, Session session) throws JMSException, MessageConversionException;
/**
* Convert from a JMS Message to a Java object.
* @param message the message to convert
* @return the converted Java object
* @throws javax.jms.JMSException if thrown by JMS API methods
* @throws MessageConversionException in case of conversion failure
*/
Object fromMessage(Message message) throws JMSException, MessageConversionException;
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.support.converter;
/**
* Constants that indicate a target message type to convert to: a
* {@link javax.jms.TextMessage}, a {@link javax.jms.BytesMessage},
* a {@link javax.jms.MapMessage} or an {@link ObjectMessage}.
*
* @author Juergen Hoeller
* @since 3.0
* @see MarshallingMessageConverter#setTargetType
*/
public enum MessageType {
TEXT, BYTES, MAP, OBJECT
}

View File

@@ -1,218 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.support.converter;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.springframework.util.ObjectUtils;
/**
* A simple message converter which is able to handle TextMessages, BytesMessages,
* MapMessages, and ObjectMessages. Used as default conversion strategy
* by {@link org.springframework.jms.core.JmsTemplate}, for
* <code>convertAndSend</code> and <code>receiveAndConvert</code> operations.
*
* <p>Converts a String to a {@link javax.jms.TextMessage}, a byte array to a
* {@link javax.jms.BytesMessage}, a Map to a {@link javax.jms.MapMessage}, and
* a Serializable object to a {@link javax.jms.ObjectMessage} (or vice versa).
*
* @author Juergen Hoeller
* @since 1.1
* @see org.springframework.jms.core.JmsTemplate#convertAndSend
* @see org.springframework.jms.core.JmsTemplate#receiveAndConvert
*/
public class SimpleMessageConverter implements MessageConverter {
/**
* This implementation creates a TextMessage for a String, a
* BytesMessage for a byte array, a MapMessage for a Map,
* and an ObjectMessage for a Serializable object.
* @see #createMessageForString
* @see #createMessageForByteArray
* @see #createMessageForMap
* @see #createMessageForSerializable
*/
public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException {
if (object instanceof Message) {
return (Message) object;
}
else if (object instanceof String) {
return createMessageForString((String) object, session);
}
else if (object instanceof byte[]) {
return createMessageForByteArray((byte[]) object, session);
}
else if (object instanceof Map) {
return createMessageForMap((Map) object, session);
}
else if (object instanceof Serializable) {
return createMessageForSerializable(((Serializable) object), session);
}
else {
throw new MessageConversionException("Cannot convert object of type [" +
ObjectUtils.nullSafeClassName(object) + "] to JMS message. Supported message " +
"payloads are: String, byte array, Map<String,?>, Serializable object.");
}
}
/**
* This implementation converts a TextMessage back to a String, a
* ByteMessage back to a byte array, a MapMessage back to a Map,
* and an ObjectMessage back to a Serializable object. Returns
* the plain Message object in case of an unknown message type.
* @see #extractStringFromMessage
* @see #extractByteArrayFromMessage
* @see #extractMapFromMessage
* @see #extractSerializableFromMessage
*/
public Object fromMessage(Message message) throws JMSException, MessageConversionException {
if (message instanceof TextMessage) {
return extractStringFromMessage((TextMessage) message);
}
else if (message instanceof BytesMessage) {
return extractByteArrayFromMessage((BytesMessage) message);
}
else if (message instanceof MapMessage) {
return extractMapFromMessage((MapMessage) message);
}
else if (message instanceof ObjectMessage) {
return extractSerializableFromMessage((ObjectMessage) message);
}
else {
return message;
}
}
/**
* Create a JMS TextMessage for the given String.
* @param text the String to convert
* @param session current JMS session
* @return the resulting message
* @throws JMSException if thrown by JMS methods
* @see javax.jms.Session#createTextMessage
*/
protected TextMessage createMessageForString(String text, Session session) throws JMSException {
return session.createTextMessage(text);
}
/**
* Create a JMS BytesMessage for the given byte array.
* @param bytes the byyte array to convert
* @param session current JMS session
* @return the resulting message
* @throws JMSException if thrown by JMS methods
* @see javax.jms.Session#createBytesMessage
*/
protected BytesMessage createMessageForByteArray(byte[] bytes, Session session) throws JMSException {
BytesMessage message = session.createBytesMessage();
message.writeBytes(bytes);
return message;
}
/**
* Create a JMS MapMessage for the given Map.
* @param map the Map to convert
* @param session current JMS session
* @return the resulting message
* @throws JMSException if thrown by JMS methods
* @see javax.jms.Session#createMapMessage
*/
protected MapMessage createMessageForMap(Map<?, ?> map, Session session) throws JMSException {
MapMessage message = session.createMapMessage();
for (Map.Entry entry : map.entrySet()) {
if (!(entry.getKey() instanceof String)) {
throw new MessageConversionException("Cannot convert non-String key of type [" +
ObjectUtils.nullSafeClassName(entry.getKey()) + "] to JMS MapMessage entry");
}
message.setObject((String) entry.getKey(), entry.getValue());
}
return message;
}
/**
* Create a JMS ObjectMessage for the given Serializable object.
* @param object the Serializable object to convert
* @param session current JMS session
* @return the resulting message
* @throws JMSException if thrown by JMS methods
* @see javax.jms.Session#createObjectMessage
*/
protected ObjectMessage createMessageForSerializable(Serializable object, Session session) throws JMSException {
return session.createObjectMessage(object);
}
/**
* Extract a String from the given TextMessage.
* @param message the message to convert
* @return the resulting String
* @throws JMSException if thrown by JMS methods
*/
protected String extractStringFromMessage(TextMessage message) throws JMSException {
return message.getText();
}
/**
* Extract a byte array from the given {@link BytesMessage}.
* @param message the message to convert
* @return the resulting byte array
* @throws JMSException if thrown by JMS methods
*/
protected byte[] extractByteArrayFromMessage(BytesMessage message) throws JMSException {
byte[] bytes = new byte[(int) message.getBodyLength()];
message.readBytes(bytes);
return bytes;
}
/**
* Extract a Map from the given {@link MapMessage}.
* @param message the message to convert
* @return the resulting Map
* @throws JMSException if thrown by JMS methods
*/
protected Map extractMapFromMessage(MapMessage message) throws JMSException {
Map<String, Object> map = new HashMap<String, Object>();
Enumeration en = message.getMapNames();
while (en.hasMoreElements()) {
String key = (String) en.nextElement();
map.put(key, message.getObject(key));
}
return map;
}
/**
* Extract a Serializable object from the given {@link ObjectMessage}.
* @param message the message to convert
* @return the resulting Serializable object
* @throws JMSException if thrown by JMS methods
*/
protected Serializable extractSerializableFromMessage(ObjectMessage message) throws JMSException {
return message.getObject();
}
}

View File

@@ -1,66 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.support.converter;
import java.io.ByteArrayOutputStream;
import javax.jms.BytesMessage;
import javax.jms.JMSException;
/**
* A subclass of {@link SimpleMessageConverter} for the JMS 1.0.2 specification,
* not relying on JMS 1.1 methods like SimpleMessageConverter itself.
* This class can be used for JMS 1.0.2 providers, offering the same functionality
* as SimpleMessageConverter does for JMS 1.1 providers.
*
* <p>The only difference to the default SimpleMessageConverter is that BytesMessage
* is handled differently: namely, without using the <code>getBodyLength()</code>
* method which has been introduced in JMS 1.1 and is therefore not available on a
* JMS 1.0.2 provider.
*
* @author Juergen Hoeller
* @since 1.1.1
* @see javax.jms.BytesMessage#getBodyLength()
* @deprecated as of Spring 3.0, in favor of the JMS 1.1 based {@link SimpleMessageConverter}
*/
@Deprecated
public class SimpleMessageConverter102 extends SimpleMessageConverter {
public static final int BUFFER_SIZE = 4096;
/**
* Overrides superclass method to copy bytes from the message into a
* ByteArrayOutputStream, using a buffer, to avoid using the
* <code>getBodyLength()</code> method which has been introduced in
* JMS 1.1 and is therefore not available on a JMS 1.0.2 provider.
* @see javax.jms.BytesMessage#getBodyLength()
*/
protected byte[] extractByteArrayFromMessage(BytesMessage message) throws JMSException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(BUFFER_SIZE);
byte[] buffer = new byte[BUFFER_SIZE];
int bufferCount = -1;
while ((bufferCount = message.readBytes(buffer)) >= 0) {
baos.write(buffer, 0, bufferCount);
if (bufferCount < BUFFER_SIZE) {
break;
}
}
return baos.toByteArray();
}
}

View File

@@ -1,9 +0,0 @@
/**
*
* Provides a MessageConverter abstraction to convert
* between Java objects and JMS messages.
*
*/
package org.springframework.jms.support.converter;

View File

@@ -1,84 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms.support.destination;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Session;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.util.Assert;
/**
* {@link DestinationResolver} implementation based on a Spring {@link BeanFactory}.
*
* <p>Will lookup Spring managed beans identified by bean name,
* expecting them to be of type <code>javax.jms.Destination</code>.
*
* @author Juergen Hoeller
* @since 2.5
* @see org.springframework.beans.factory.BeanFactory
*/
public class BeanFactoryDestinationResolver implements DestinationResolver, BeanFactoryAware {
private BeanFactory beanFactory;
/**
* Create a new instance of the {@link BeanFactoryDestinationResolver} class.
* <p>The BeanFactory to access must be set via <code>setBeanFactory</code>.
* @see #setBeanFactory
*/
public BeanFactoryDestinationResolver() {
}
/**
* Create a new instance of the {@link BeanFactoryDestinationResolver} class.
* <p>Use of this constructor is redundant if this object is being created
* by a Spring IoC container, as the supplied {@link BeanFactory} will be
* replaced by the {@link BeanFactory} that creates it (c.f. the
* {@link BeanFactoryAware} contract). So only use this constructor if you
* are using this class outside the context of a Spring IoC container.
* @param beanFactory the bean factory to be used to lookup {@link javax.jms.Destination Destinatiosn}
*/
public BeanFactoryDestinationResolver(BeanFactory beanFactory) {
Assert.notNull(beanFactory, "BeanFactory is required");
this.beanFactory = beanFactory;
}
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain)
throws JMSException {
Assert.state(this.beanFactory != null, "BeanFactory is required");
try {
return (Destination) this.beanFactory.getBean(destinationName, Destination.class);
}
catch (BeansException ex) {
throw new DestinationResolutionException(
"Failed to look up Destinaton bean with name '" + destinationName + "'", ex);
}
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2002-2006 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.springframework.jms.support.destination;
/**
* Extension of the DestinationResolver interface,
* exposing methods for clearing the cache.
*
* @author Juergen Hoeller
* @since 2.0
*/
public interface CachingDestinationResolver extends DestinationResolver {
/**
* Remove the destination with the given name from the cache
* (if cached by this resolver in the first place).
* <p>To be called if access to the specified destination failed,
* assuming that the JMS Destination object might have become invalid.
* @param destinationName the name of the destination
*/
void removeFromCache(String destinationName);
/**
* Clear the entire destination cache.
* <p>To be called in case of general JMS provider failure.
*/
void clearCache();
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright 2002-2006 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.springframework.jms.support.destination;
import org.springframework.jms.JmsException;
/**
* Thrown by a DestinationResolver when it cannot resolve a destination name.
*
* @author Juergen Hoeller
* @since 1.1
* @see DestinationResolver
*/
public class DestinationResolutionException extends JmsException {
/**
* Create a new DestinationResolutionException.
* @param msg the detail message
*/
public DestinationResolutionException(String msg) {
super(msg);
}
/**
* Create a new DestinationResolutionException.
* @param msg the detail message
* @param cause the root cause (if any)
*/
public DestinationResolutionException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms.support.destination;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Session;
/**
* Strategy interface for resolving JMS destinations.
*
* <p>Used by {@link org.springframework.jms.core.JmsTemplate} for resolving
* destination names from simple {@link String Strings} to actual
* {@link Destination} implementation instances.
*
* <p>The default {@link DestinationResolver} implementation used by
* {@link org.springframework.jms.core.JmsTemplate} instances is the
* {@link DynamicDestinationResolver} class. Consider using the
* {@link JndiDestinationResolver} for more advanced scenarios.
*
* @author Juergen Hoeller
* @since 1.1
* @see org.springframework.jms.core.JmsTemplate#setDestinationResolver
* @see org.springframework.jms.support.destination.DynamicDestinationResolver
* @see org.springframework.jms.support.destination.JndiDestinationResolver
*/
public interface DestinationResolver {
/**
* Resolve the given destination name, either as located resource
* or as dynamic destination.
* @param session the current JMS Session
* (may be <code>null</code> if the resolver implementation is able to work without it)
* @param destinationName the name of the destination
* @param pubSubDomain <code>true</code> if the domain is pub-sub, <code>false</code> if P2P
* @return the JMS destination (either a topic or a queue)
* @throws javax.jms.JMSException if the JMS Session failed to resolve the destination
* @throws DestinationResolutionException in case of general destination resolution failure
*/
Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain)
throws JMSException;
}

View File

@@ -1,109 +0,0 @@
/*
* Copyright 2002-2006 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.springframework.jms.support.destination;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.Topic;
import javax.jms.TopicSession;
import org.springframework.util.Assert;
/**
* Simple {@link DestinationResolver} implementation resolving destination names
* as dynamic destinations.
*
* <p>This implementation will work on both JMS 1.1 and JMS 1.0.2,
* because it uses the {@link javax.jms.QueueSession} or {@link javax.jms.TopicSession}
* methods if possible, falling back to JMS 1.1's generic {@link javax.jms.Session}
* methods.
*
* @author Juergen Hoeller
* @since 1.1
* @see javax.jms.QueueSession#createQueue
* @see javax.jms.TopicSession#createTopic
* @see javax.jms.Session#createQueue
* @see javax.jms.Session#createTopic
*/
public class DynamicDestinationResolver implements DestinationResolver {
/**
* Resolve the specified destination name as a dynamic destination.
* @param session the current JMS Session
* @param destinationName the name of the destination
* @param pubSubDomain <code>true</code> if the domain is pub-sub, <code>false</code> if P2P
* @return the JMS destination (either a topic or a queue)
* @throws javax.jms.JMSException if resolution failed
* @see #resolveTopic(javax.jms.Session, String)
* @see #resolveQueue(javax.jms.Session, String)
*/
public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain)
throws JMSException {
Assert.notNull(session, "Session must not be null");
Assert.notNull(destinationName, "Destination name must not be null");
if (pubSubDomain) {
return resolveTopic(session, destinationName);
}
else {
return resolveQueue(session, destinationName);
}
}
/**
* Resolve the given destination name to a {@link Topic}.
* @param session the current JMS Session
* @param topicName the name of the desired {@link Topic}
* @return the JMS {@link Topic}
* @throws javax.jms.JMSException if resolution failed
* @see Session#createTopic(String)
*/
protected Topic resolveTopic(Session session, String topicName) throws JMSException {
if (session instanceof TopicSession) {
// Cast to TopicSession: will work on both JMS 1.1 and 1.0.2
return ((TopicSession) session).createTopic(topicName);
}
else {
// Fall back to generic JMS Session: will only work on JMS 1.1
return session.createTopic(topicName);
}
}
/**
* Resolve the given destination name to a {@link Queue}.
* @param session the current JMS Session
* @param queueName the name of the desired {@link Queue}
* @return the JMS {@link Queue}
* @throws javax.jms.JMSException if resolution failed
* @see Session#createQueue(String)
*/
protected Queue resolveQueue(Session session, String queueName) throws JMSException {
if (session instanceof QueueSession) {
// Cast to QueueSession: will work on both JMS 1.1 and 1.0.2
return ((QueueSession) session).createQueue(queueName);
}
else {
// Fall back to generic JMS Session: will only work on JMS 1.1
return session.createQueue(queueName);
}
}
}

View File

@@ -1,103 +0,0 @@
/*
* Copyright 2002-2007 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.springframework.jms.support.destination;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Session;
import org.springframework.jms.support.JmsAccessor;
import org.springframework.util.Assert;
/**
* Base class for {@link org.springframework.jms.core.JmsTemplate} and other
* JMS-accessing gateway helpers, adding destination-related properties to
* {@link JmsAccessor JmsAccessor's} common properties.
*
* <p>Not intended to be used directly.
* See {@link org.springframework.jms.core.JmsTemplate}.
*
* @author Juergen Hoeller
* @since 1.2.5
* @see org.springframework.jms.support.JmsAccessor
* @see org.springframework.jms.core.JmsTemplate
*/
public abstract class JmsDestinationAccessor extends JmsAccessor {
private DestinationResolver destinationResolver = new DynamicDestinationResolver();
private boolean pubSubDomain = false;
/**
* Set the {@link DestinationResolver} that is to be used to resolve
* {@link javax.jms.Destination} references for this accessor.
* <p>The default resolver is a DynamicDestinationResolver. Specify a
* JndiDestinationResolver for resolving destination names as JNDI locations.
* @see org.springframework.jms.support.destination.DynamicDestinationResolver
* @see org.springframework.jms.support.destination.JndiDestinationResolver
*/
public void setDestinationResolver(DestinationResolver destinationResolver) {
Assert.notNull(destinationResolver, "'destinationResolver' must not be null");
this.destinationResolver = destinationResolver;
}
/**
* Return the DestinationResolver for this accessor (never <code>null</code>).
*/
public DestinationResolver getDestinationResolver() {
return this.destinationResolver;
}
/**
* Configure the destination accessor with knowledge of the JMS domain used.
* Default is Point-to-Point (Queues).
* <p>For JMS 1.0.2 based accessors, this tells the JMS provider which class hierarchy
* to use in the implementation of its operations. For JMS 1.1 based accessors, this
* setting does usually not affect operations. However, for both JMS versions, this
* setting tells what type of destination to resolve if dynamic destinations are enabled.
* @param pubSubDomain "true" for the Publish/Subscribe domain ({@link javax.jms.Topic Topics}),
* "false" for the Point-to-Point domain ({@link javax.jms.Queue Queues})
* @see #setDestinationResolver
*/
public void setPubSubDomain(boolean pubSubDomain) {
this.pubSubDomain = pubSubDomain;
}
/**
* Return whether the Publish/Subscribe domain ({@link javax.jms.Topic Topics}) is used.
* Otherwise, the Point-to-Point domain ({@link javax.jms.Queue Queues}) is used.
*/
public boolean isPubSubDomain() {
return this.pubSubDomain;
}
/**
* Resolve the given destination name into a JMS {@link Destination},
* via this accessor's {@link DestinationResolver}.
* @param session the current JMS {@link Session}
* @param destinationName the name of the destination
* @return the located {@link Destination}
* @throws javax.jms.JMSException if resolution failed
* @see #setDestinationResolver
*/
protected Destination resolveDestinationName(Session session, String destinationName) throws JMSException {
return getDestinationResolver().resolveDestinationName(session, destinationName, isPubSubDomain());
}
}

View File

@@ -1,161 +0,0 @@
/*
* Copyright 2002-2009 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.springframework.jms.support.destination;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.Topic;
import javax.naming.NamingException;
import org.springframework.jndi.JndiLocatorSupport;
import org.springframework.util.Assert;
/**
* {@link DestinationResolver} implementation which interprets destination names
* as JNDI locations (with a configurable fallback strategy).
*
* <p>Allows for customizing the JNDI environment if necessary, for example
* specifying appropriate JNDI environment properties.
*
* <p>Dynamic queues and topics get cached by destination name. As a consequence,
* you need to use unique destination names across both queues and topics.
* Caching can be turned off through the {@link #setCache "cache"} flag.
*
* <p>Note that the fallback to resolution of dynamic destinations
* is turned <i>off</i> by default. Switch the
* {@link #setFallbackToDynamicDestination "fallbackToDynamicDestination"}
* flag on to enable this functionality.
*
* @author Mark Pollack
* @author Juergen Hoeller
* @since 1.1
* @see #setJndiTemplate
* @see #setJndiEnvironment
* @see #setCache
* @see #setFallbackToDynamicDestination
*/
public class JndiDestinationResolver extends JndiLocatorSupport implements CachingDestinationResolver {
private boolean cache = true;
private boolean fallbackToDynamicDestination = false;
private DestinationResolver dynamicDestinationResolver = new DynamicDestinationResolver();
private final Map<String, Destination> destinationCache = new ConcurrentHashMap<String, Destination>();
/**
* Set whether to cache resolved destinations. Default is "true".
* <p>This flag can be turned off to re-lookup a destination for each operation,
* which allows for hot restarting of destinations. This is mainly useful
* during development.
* <p>Note that dynamic queues and topics get cached by destination name.
* As a consequence, you need to use unique destination names across both
* queues and topics.
*/
public void setCache(boolean cache) {
this.cache = cache;
}
/**
* Set whether this resolver is supposed to create dynamic destinations
* if the destination name is not found in JNDI. Default is "false".
* <p>Turn this flag on to enable transparent fallback to dynamic destinations.
* @see #setDynamicDestinationResolver
*/
public void setFallbackToDynamicDestination(boolean fallbackToDynamicDestination) {
this.fallbackToDynamicDestination = fallbackToDynamicDestination;
}
/**
* Set the {@link DestinationResolver} to use when falling back to dynamic
* destinations.
* <p>The default is Spring's standard {@link DynamicDestinationResolver}.
* @see #setFallbackToDynamicDestination
* @see DynamicDestinationResolver
*/
public void setDynamicDestinationResolver(DestinationResolver dynamicDestinationResolver) {
this.dynamicDestinationResolver = dynamicDestinationResolver;
}
public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain)
throws JMSException {
Assert.notNull(destinationName, "Destination name must not be null");
Destination dest = this.destinationCache.get(destinationName);
if (dest != null) {
validateDestination(dest, destinationName, pubSubDomain);
}
else {
try {
dest = lookup(destinationName, Destination.class);
validateDestination(dest, destinationName, pubSubDomain);
}
catch (NamingException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Destination [" + destinationName + "] not found in JNDI", ex);
}
if (this.fallbackToDynamicDestination) {
dest = this.dynamicDestinationResolver.resolveDestinationName(session, destinationName, pubSubDomain);
}
else {
throw new DestinationResolutionException(
"Destination [" + destinationName + "] not found in JNDI", ex);
}
}
if (this.cache) {
this.destinationCache.put(destinationName, dest);
}
}
return dest;
}
/**
* Validate the given Destination object, checking whether it matches
* the expected type.
* @param destination the Destination object to validate
* @param destinationName the name of the destination
* @param pubSubDomain <code>true</code> if a Topic is expected,
* <code>false</code> in case of a Queue
*/
protected void validateDestination(Destination destination, String destinationName, boolean pubSubDomain) {
Class targetClass = Queue.class;
if (pubSubDomain) {
targetClass = Topic.class;
}
if (!targetClass.isInstance(destination)) {
throw new DestinationResolutionException(
"Destination [" + destinationName + "] is not of expected type [" + targetClass.getName() + "]");
}
}
public void removeFromCache(String destinationName) {
this.destinationCache.remove(destinationName);
}
public void clearCache() {
this.destinationCache.clear();
}
}

View File

@@ -1,8 +0,0 @@
/**
*
* Support classes for Spring's JMS framework.
*
*/
package org.springframework.jms.support.destination;

View File

@@ -1,9 +0,0 @@
/**
*
* This package provides generic JMS support classes,
* to be used by higher-level classes like JmsTemplate.
*
*/
package org.springframework.jms.support;

View File

@@ -1,7 +0,0 @@
<html>
<body>
<p>
Spring's JMS support package, covering synchronous JMS access as well as message listener containers.
</p>
</body>
</html>

View File

@@ -1 +0,0 @@
http\://www.springframework.org/schema/jms=org.springframework.jms.config.JmsNamespaceHandler

View File

@@ -1,4 +0,0 @@
http\://www.springframework.org/schema/jms/spring-jms-2.5.xsd=org/springframework/jms/config/spring-jms-2.5.xsd
http\://www.springframework.org/schema/jms/spring-jms-3.0.xsd=org/springframework/jms/config/spring-jms-3.0.xsd
http\://www.springframework.org/schema/jms/spring-jms-3.1.xsd=org/springframework/jms/config/spring-jms-3.1.xsd
http\://www.springframework.org/schema/jms/spring-jms.xsd=org/springframework/jms/config/spring-jms-3.1.xsd

View File

@@ -1,4 +0,0 @@
# Tooling related information for the jms namespace
http\://www.springframework.org/schema/jms@name=jms Namespace
http\://www.springframework.org/schema/jms@prefix=jms
http\://www.springframework.org/schema/jms@icon=org/springframework/jms/config/spring-jms.gif

View File

@@ -1,439 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/jms"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tool="http://www.springframework.org/schema/tool"
targetNamespace="http://www.springframework.org/schema/jms"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool-2.5.xsd"/>
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the configuration elements for the Spring Framework's JMS support.
Allows for configuring JMS listener containers in XML 'shortcut' style.
]]></xsd:documentation>
</xsd:annotation>
<xsd:element name="listener-container">
<xsd:annotation>
<xsd:documentation><![CDATA[
Each listener child element will be hosted by a container whose configuration
is determined by this parent element. This variant builds standard JMS
listener containers, operating against a specified JMS ConnectionFactory.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.springframework.jms.listener.AbstractMessageListenerContainer"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="listener" type="listenerType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="container-type" default="default">
<xsd:annotation>
<xsd:documentation><![CDATA[
The type of this listener container: "default" or "simple", choosing
between DefaultMessageListenerContainer and SimpleMessageListenerContainer.
The "102" suffix adapts to a JMS provider that implements JMS 1.0.2 only.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="default"/>
<xsd:enumeration value="default102"/>
<xsd:enumeration value="simple"/>
<xsd:enumeration value="simple102"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="container-class" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A custom listener container implementation class as fully qualified class name.
Default is Spring's standard DefaultMessageListenerContainer or
SimpleMessageListenerContainer, according to the "container-type" attribute.
Note that a custom container class will typically be a subclass of either of
those two Spring-provided standard container classes: Nake sure that the
"container-type" attribute matches the actual base type that the custom class
derives from ("default" will usually be fine anyway, since most custom classes
will derive from DefaultMessageListenerContainer).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:expected-type type="java.lang.Class"/>
<tool:assignable-to type="org.springframework.jms.listener.AbstractMessageListenerContainer"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="connection-factory" type="xsd:string" default="connectionFactory">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the JMS ConnectionFactory bean.
Default is "connectionFactory".
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="javax.jms.ConnectionFactory"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="task-executor" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the Spring TaskExecutor for the JMS listener invokers.
Default is a SimpleAsyncTaskExecutor, using internally managed threads.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.core.task.TaskExecutor"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-resolver" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the DestinationResolver strategy for resolving destination names.
Default is a DynamicDestinationResolver, using the JMS provider's queue/topic
name resolution. Alternatively, specify a reference to a JndiDestinationResolver
(typically in a J2EE environment).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.support.destination.DestinationResolver"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="message-converter" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the MessageConverter strategy for converting JMS Messages to
listener method arguments. Default is a SimpleMessageConverter.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.support.converter.MessageConverter"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-type" default="queue">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS destination type for this listener: "queue", "topic" or "durableTopic".
The default is "queue".
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="queue"/>
<xsd:enumeration value="topic"/>
<xsd:enumeration value="durableTopic"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="client-id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS client id for this listener container.
Needs to be specified when using durable subscriptions.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cache" default="auto">
<xsd:annotation>
<xsd:documentation><![CDATA[
The cache level for JMS resources: "none", "connection", "session", "consumer"
or "auto". By default ("auto"), the cache level will effectively be "consumer",
unless an external transaction manager has been specified - in which case the
effective default will be "none" (assuming J2EE-style transaction management
where the given ConnectionFactory is an XA-aware pool).
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="none"/>
<xsd:enumeration value="connection"/>
<xsd:enumeration value="session"/>
<xsd:enumeration value="consumer"/>
<xsd:enumeration value="auto"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="acknowledge" default="auto">
<xsd:annotation>
<xsd:documentation><![CDATA[
The native JMS acknowledge mode: "auto", "client", "dups-ok" or "transacted".
A value of "transacted" effectively activates a locally transacted Session;
as alternative, specify an external "transaction-manager" via the corresponding
attribute. Default is "auto".
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="auto"/>
<xsd:enumeration value="client"/>
<xsd:enumeration value="dups-ok"/>
<xsd:enumeration value="transacted"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="transaction-manager" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to an external PlatformTransactionManager (typically an
XA-based transaction coordinator, e.g. Spring's JtaTransactionManager).
If not specified, native acknowledging will be used (see "acknowledge" attribute).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.transaction.PlatformTransactionManager"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="concurrency" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The number of concurrent sessions/consumers to start for each listener.
Can either be a simple number indicating the maximum number (e.g. "5")
or a range indicating the lower as well as the upper limit (e.g. "3-5").
Note that a specified minimum is just a hint and might be ignored at runtime.
Default is 1; keep concurrency limited to 1 in case of a topic listener
or if message ordering is important; consider raising it for general queues.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="prefetch" type="xsd:int">
<xsd:annotation>
<xsd:documentation><![CDATA[
The maximum number of messages to load into a single session.
Note that raising this number might lead to starvation of concurrent consumers!
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="jca-listener-container">
<xsd:annotation>
<xsd:documentation><![CDATA[
Each listener child element will be hosted by a container whose configuration
is determined by this parent element. This variant builds standard JCA-based
listener containers, operating against a specified JCA ResourceAdapter
(which needs to be provided by the JMS message broker, e.g. ActiveMQ).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="listener" type="listenerType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="resource-adapter" type="xsd:string" default="resourceAdapter">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the JCA ResourceAdapter bean for the JMS provider.
Default is "resourceAdapter".
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="javax.resource.spi.ResourceAdapter"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="activation-spec-factory" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the JmsActivationSpecFactory.
Default is to autodetect the JMS provider and its ActivationSpec class
(see DefaultJmsActivationSpecFactory).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.listener.endpoint.JmsActivationSpecFactory"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-resolver" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the DestinationResolver strategy for resolving destination names.
Default is to pass in the destination name Strings into the JCA ActivationSpec as-is.
Alternatively, specify a reference to a JndiDestinationResolver (typically in a J2EE
environment, in particular if the server insists on receiving Destination objects).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.support.destination.DestinationResolver"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="message-converter" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the MessageConverter strategy for converting JMS Messages to
listener method arguments. Default is a SimpleMessageConverter.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.support.converter.MessageConverter"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-type" default="queue">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS destination type for this listener: "queue", "topic" or "durableTopic".
Default is "queue".
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="queue"/>
<xsd:enumeration value="topic"/>
<xsd:enumeration value="durableTopic"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="client-id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS client id for this listener container.
Needs to be specified when using durable subscriptions.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="acknowledge" default="auto">
<xsd:annotation>
<xsd:documentation><![CDATA[
The native JMS acknowledge mode: "auto", "client", "dups-ok" or "transacted".
A value of "transacted" effectively activates a locally transacted Session;
as alternative, specify an external "transaction-manager" via the corresponding
attribute. Default is "auto".
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="auto"/>
<xsd:enumeration value="client"/>
<xsd:enumeration value="dups-ok"/>
<xsd:enumeration value="transacted"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="transaction-manager" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the Spring JtaTransactionManager or [javax.transaction.TransactionManager],
for kicking off an XA transaction for each incoming message.
If not specified, native acknowledging will be used (see "acknowledge" attribute).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="concurrency" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The number of concurrent sessions/consumers to start for each listener.
Can either be a simple number indicating the maximum number (e.g. "5")
or a range indicating the lower as well as the upper limit (e.g. "3-5").
Note that a specified minimum is just a hint and will typically be ignored
at runtime when using a JCA listener container. Default is 1.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="prefetch" type="xsd:int">
<xsd:annotation>
<xsd:documentation><![CDATA[
The maximum number of messages to load into a single session.
Note that raising this number might lead to starvation of concurrent consumers!
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="listenerType">
<xsd:attribute name="id" type="xsd:ID">
<xsd:annotation>
<xsd:documentation><![CDATA[
The unique identifier for a listener.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The destination name for this listener, resolved through the
container-wide DestinationResolver strategy (if any). Required.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="subscription" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name for the durable subscription, if any.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="selector" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS message selector for this listener.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ref" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The bean name of the listener object, implementing
the MessageListener/SessionAwareMessageListener interface
or defining the specified listener method. Required.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="method" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the listener method to invoke. If not specified,
the target bean is supposed to implement the MessageListener
or SessionAwareMessageListener interface.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="response-destination" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the default response destination to send response messages to.
This will be applied in case of a request message that does not carry
a "JMSReplyTo" field. The type of this destination will be determined
by the listener-container's "destination-type" attribute.
Note: This only applies to a listener method with a return value,
for which each result object will be converted into a response message.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:schema>

View File

@@ -1,472 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/jms"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tool="http://www.springframework.org/schema/tool"
targetNamespace="http://www.springframework.org/schema/jms"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool-3.0.xsd"/>
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the configuration elements for the Spring Framework's JMS support.
Allows for configuring JMS listener containers in XML 'shortcut' style.
]]></xsd:documentation>
</xsd:annotation>
<xsd:element name="listener-container">
<xsd:annotation>
<xsd:documentation><![CDATA[
Each listener child element will be hosted by a container whose configuration
is determined by this parent element. This variant builds standard JMS
listener containers, operating against a specified JMS ConnectionFactory.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.springframework.jms.listener.AbstractMessageListenerContainer"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="listener" type="listenerType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="container-type" default="default">
<xsd:annotation>
<xsd:documentation><![CDATA[
The type of this listener container: "default" or "simple", choosing
between DefaultMessageListenerContainer and SimpleMessageListenerContainer.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="default"/>
<xsd:enumeration value="simple"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="container-class" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A custom listener container implementation class as fully qualified class name.
Default is Spring's standard DefaultMessageListenerContainer or
SimpleMessageListenerContainer, according to the "container-type" attribute.
Note that a custom container class will typically be a subclass of either of
those two Spring-provided standard container classes: Nake sure that the
"container-type" attribute matches the actual base type that the custom class
derives from ("default" will usually be fine anyway, since most custom classes
will derive from DefaultMessageListenerContainer).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:expected-type type="java.lang.Class"/>
<tool:assignable-to type="org.springframework.jms.listener.AbstractMessageListenerContainer"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="connection-factory" type="xsd:string" default="connectionFactory">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the JMS ConnectionFactory bean.
Default is "connectionFactory".
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="javax.jms.ConnectionFactory"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="task-executor" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to a Spring TaskExecutor (or standard JDK 1.5 Executor) for executing
JMS listener invokers. Default is a SimpleAsyncTaskExecutor in case of a
DefaultMessageListenerContainer, using internally managed threads. For a
SimpleMessageListenerContainer, listeners will always get invoked within the
JMS provider's receive thread by default.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="java.util.concurrent.Executor"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-resolver" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the DestinationResolver strategy for resolving destination names.
Default is a DynamicDestinationResolver, using the JMS provider's queue/topic
name resolution. Alternatively, specify a reference to a JndiDestinationResolver
(typically in a J2EE environment).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.support.destination.DestinationResolver"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="message-converter" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the MessageConverter strategy for converting JMS Messages to
listener method arguments. Default is a SimpleMessageConverter.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.support.converter.MessageConverter"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="error-handler" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to an ErrorHandler strategy for handling any uncaught Exceptions
that may occur during the execution of the MessageListener.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.util.ErrorHandler"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-type" default="queue">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS destination type for this listener: "queue", "topic" or "durableTopic".
The default is "queue".
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="queue"/>
<xsd:enumeration value="topic"/>
<xsd:enumeration value="durableTopic"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="client-id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS client id for this listener container.
Needs to be specified when using durable subscriptions.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cache" default="auto">
<xsd:annotation>
<xsd:documentation><![CDATA[
The cache level for JMS resources: "none", "connection", "session", "consumer"
or "auto". By default ("auto"), the cache level will effectively be "consumer",
unless an external transaction manager has been specified - in which case the
effective default will be "none" (assuming J2EE-style transaction management
where the given ConnectionFactory is an XA-aware pool).
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="none"/>
<xsd:enumeration value="connection"/>
<xsd:enumeration value="session"/>
<xsd:enumeration value="consumer"/>
<xsd:enumeration value="auto"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="acknowledge" default="auto">
<xsd:annotation>
<xsd:documentation><![CDATA[
The native JMS acknowledge mode: "auto", "client", "dups-ok" or "transacted".
A value of "transacted" effectively activates a locally transacted Session;
as alternative, specify an external "transaction-manager" via the corresponding
attribute. Default is "auto".
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="auto"/>
<xsd:enumeration value="client"/>
<xsd:enumeration value="dups-ok"/>
<xsd:enumeration value="transacted"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="transaction-manager" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to an external PlatformTransactionManager (typically an
XA-based transaction coordinator, e.g. Spring's JtaTransactionManager).
If not specified, native acknowledging will be used (see "acknowledge" attribute).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.transaction.PlatformTransactionManager"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="concurrency" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The number of concurrent sessions/consumers to start for each listener.
Can either be a simple number indicating the maximum number (e.g. "5")
or a range indicating the lower as well as the upper limit (e.g. "3-5").
Note that a specified minimum is just a hint and might be ignored at runtime.
Default is 1; keep concurrency limited to 1 in case of a topic listener
or if message ordering is important; consider raising it for general queues.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="prefetch" type="xsd:int">
<xsd:annotation>
<xsd:documentation><![CDATA[
The maximum number of messages to load into a single session.
Note that raising this number might lead to starvation of concurrent consumers!
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="phase" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The lifecycle phase within which this container should start and stop. The lower
the value the earlier this container will start and the later it will stop. The
default is Integer.MAX_VALUE meaning the container will start as late as possible
and stop as soon as possible.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="jca-listener-container">
<xsd:annotation>
<xsd:documentation><![CDATA[
Each listener child element will be hosted by a container whose configuration
is determined by this parent element. This variant builds standard JCA-based
listener containers, operating against a specified JCA ResourceAdapter
(which needs to be provided by the JMS message broker, e.g. ActiveMQ).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="listener" type="listenerType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="resource-adapter" type="xsd:string" default="resourceAdapter">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the JCA ResourceAdapter bean for the JMS provider.
Default is "resourceAdapter".
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="javax.resource.spi.ResourceAdapter"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="activation-spec-factory" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the JmsActivationSpecFactory.
Default is to autodetect the JMS provider and its ActivationSpec class
(see DefaultJmsActivationSpecFactory).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.listener.endpoint.JmsActivationSpecFactory"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-resolver" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the DestinationResolver strategy for resolving destination names.
Default is to pass in the destination name Strings into the JCA ActivationSpec as-is.
Alternatively, specify a reference to a JndiDestinationResolver (typically in a J2EE
environment, in particular if the server insists on receiving Destination objects).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.support.destination.DestinationResolver"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="message-converter" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the MessageConverter strategy for converting JMS Messages to
listener method arguments. Default is a SimpleMessageConverter.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.support.converter.MessageConverter"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-type" default="queue">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS destination type for this listener: "queue", "topic" or "durableTopic".
Default is "queue".
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="queue"/>
<xsd:enumeration value="topic"/>
<xsd:enumeration value="durableTopic"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="client-id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS client id for this listener container.
Needs to be specified when using durable subscriptions.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="acknowledge" default="auto">
<xsd:annotation>
<xsd:documentation><![CDATA[
The native JMS acknowledge mode: "auto", "client", "dups-ok" or "transacted".
A value of "transacted" effectively activates a locally transacted Session;
as alternative, specify an external "transaction-manager" via the corresponding
attribute. Default is "auto".
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="auto"/>
<xsd:enumeration value="client"/>
<xsd:enumeration value="dups-ok"/>
<xsd:enumeration value="transacted"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="transaction-manager" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the Spring JtaTransactionManager or [javax.transaction.TransactionManager],
for kicking off an XA transaction for each incoming message.
If not specified, native acknowledging will be used (see "acknowledge" attribute).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="concurrency" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The number of concurrent sessions/consumers to start for each listener.
Can either be a simple number indicating the maximum number (e.g. "5")
or a range indicating the lower as well as the upper limit (e.g. "3-5").
Note that a specified minimum is just a hint and will typically be ignored
at runtime when using a JCA listener container. Default is 1.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="prefetch" type="xsd:int">
<xsd:annotation>
<xsd:documentation><![CDATA[
The maximum number of messages to load into a single session.
Note that raising this number might lead to starvation of concurrent consumers!
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="phase" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The lifecycle phase within which this container should start and stop. The lower
the value the earlier this container will start and the later it will stop. The
default is Integer.MAX_VALUE meaning the container will start as late as possible
and stop as soon as possible.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="listenerType">
<xsd:attribute name="id" type="xsd:ID">
<xsd:annotation>
<xsd:documentation><![CDATA[
The unique identifier for a listener.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The destination name for this listener, resolved through the
container-wide DestinationResolver strategy (if any). Required.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="subscription" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name for the durable subscription, if any.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="selector" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS message selector for this listener.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ref" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The bean name of the listener object, implementing
the MessageListener/SessionAwareMessageListener interface
or defining the specified listener method. Required.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="method" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the listener method to invoke. If not specified,
the target bean is supposed to implement the MessageListener
or SessionAwareMessageListener interface.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="response-destination" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the default response destination to send response messages to.
This will be applied in case of a request message that does not carry
a "JMSReplyTo" field. The type of this destination will be determined
by the listener-container's "destination-type" attribute.
Note: This only applies to a listener method with a return value,
for which each result object will be converted into a response message.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:schema>

View File

@@ -1,472 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/jms"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tool="http://www.springframework.org/schema/tool"
targetNamespace="http://www.springframework.org/schema/jms"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool-3.1.xsd"/>
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the configuration elements for the Spring Framework's JMS support.
Allows for configuring JMS listener containers in XML 'shortcut' style.
]]></xsd:documentation>
</xsd:annotation>
<xsd:element name="listener-container">
<xsd:annotation>
<xsd:documentation><![CDATA[
Each listener child element will be hosted by a container whose configuration
is determined by this parent element. This variant builds standard JMS
listener containers, operating against a specified JMS ConnectionFactory.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.springframework.jms.listener.AbstractMessageListenerContainer"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="listener" type="listenerType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="container-type" default="default">
<xsd:annotation>
<xsd:documentation><![CDATA[
The type of this listener container: "default" or "simple", choosing
between DefaultMessageListenerContainer and SimpleMessageListenerContainer.
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="default"/>
<xsd:enumeration value="simple"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="container-class" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A custom listener container implementation class as fully qualified class name.
Default is Spring's standard DefaultMessageListenerContainer or
SimpleMessageListenerContainer, according to the "container-type" attribute.
Note that a custom container class will typically be a subclass of either of
those two Spring-provided standard container classes: Nake sure that the
"container-type" attribute matches the actual base type that the custom class
derives from ("default" will usually be fine anyway, since most custom classes
will derive from DefaultMessageListenerContainer).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:expected-type type="java.lang.Class"/>
<tool:assignable-to type="org.springframework.jms.listener.AbstractMessageListenerContainer"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="connection-factory" type="xsd:string" default="connectionFactory">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the JMS ConnectionFactory bean.
Default is "connectionFactory".
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="javax.jms.ConnectionFactory"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="task-executor" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to a Spring TaskExecutor (or standard JDK 1.5 Executor) for executing
JMS listener invokers. Default is a SimpleAsyncTaskExecutor in case of a
DefaultMessageListenerContainer, using internally managed threads. For a
SimpleMessageListenerContainer, listeners will always get invoked within the
JMS provider's receive thread by default.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="java.util.concurrent.Executor"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-resolver" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the DestinationResolver strategy for resolving destination names.
Default is a DynamicDestinationResolver, using the JMS provider's queue/topic
name resolution. Alternatively, specify a reference to a JndiDestinationResolver
(typically in a J2EE environment).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.support.destination.DestinationResolver"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="message-converter" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the MessageConverter strategy for converting JMS Messages to
listener method arguments. Default is a SimpleMessageConverter.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.support.converter.MessageConverter"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="error-handler" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to an ErrorHandler strategy for handling any uncaught Exceptions
that may occur during the execution of the MessageListener.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.util.ErrorHandler"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-type" default="queue">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS destination type for this listener: "queue", "topic" or "durableTopic".
The default is "queue".
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="queue"/>
<xsd:enumeration value="topic"/>
<xsd:enumeration value="durableTopic"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="client-id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS client id for this listener container.
Needs to be specified when using durable subscriptions.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cache" default="auto">
<xsd:annotation>
<xsd:documentation><![CDATA[
The cache level for JMS resources: "none", "connection", "session", "consumer"
or "auto". By default ("auto"), the cache level will effectively be "consumer",
unless an external transaction manager has been specified - in which case the
effective default will be "none" (assuming J2EE-style transaction management
where the given ConnectionFactory is an XA-aware pool).
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="none"/>
<xsd:enumeration value="connection"/>
<xsd:enumeration value="session"/>
<xsd:enumeration value="consumer"/>
<xsd:enumeration value="auto"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="acknowledge" default="auto">
<xsd:annotation>
<xsd:documentation><![CDATA[
The native JMS acknowledge mode: "auto", "client", "dups-ok" or "transacted".
A value of "transacted" effectively activates a locally transacted Session;
as alternative, specify an external "transaction-manager" via the corresponding
attribute. Default is "auto".
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="auto"/>
<xsd:enumeration value="client"/>
<xsd:enumeration value="dups-ok"/>
<xsd:enumeration value="transacted"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="transaction-manager" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to an external PlatformTransactionManager (typically an
XA-based transaction coordinator, e.g. Spring's JtaTransactionManager).
If not specified, native acknowledging will be used (see "acknowledge" attribute).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.transaction.PlatformTransactionManager"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="concurrency" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The number of concurrent sessions/consumers to start for each listener.
Can either be a simple number indicating the maximum number (e.g. "5")
or a range indicating the lower as well as the upper limit (e.g. "3-5").
Note that a specified minimum is just a hint and might be ignored at runtime.
Default is 1; keep concurrency limited to 1 in case of a topic listener
or if message ordering is important; consider raising it for general queues.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="prefetch" type="xsd:int">
<xsd:annotation>
<xsd:documentation><![CDATA[
The maximum number of messages to load into a single session.
Note that raising this number might lead to starvation of concurrent consumers!
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="phase" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The lifecycle phase within which this container should start and stop. The lower
the value the earlier this container will start and the later it will stop. The
default is Integer.MAX_VALUE meaning the container will start as late as possible
and stop as soon as possible.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:element name="jca-listener-container">
<xsd:annotation>
<xsd:documentation><![CDATA[
Each listener child element will be hosted by a container whose configuration
is determined by this parent element. This variant builds standard JCA-based
listener containers, operating against a specified JCA ResourceAdapter
(which needs to be provided by the JMS message broker, e.g. ActiveMQ).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation>
<tool:exports type="org.springframework.jms.listener.endpoint.JmsMessageEndpointManager"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="listener" type="listenerType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="resource-adapter" type="xsd:string" default="resourceAdapter">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the JCA ResourceAdapter bean for the JMS provider.
Default is "resourceAdapter".
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="javax.resource.spi.ResourceAdapter"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="activation-spec-factory" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the JmsActivationSpecFactory.
Default is to autodetect the JMS provider and its ActivationSpec class
(see DefaultJmsActivationSpecFactory).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.listener.endpoint.JmsActivationSpecFactory"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-resolver" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the DestinationResolver strategy for resolving destination names.
Default is to pass in the destination name Strings into the JCA ActivationSpec as-is.
Alternatively, specify a reference to a JndiDestinationResolver (typically in a J2EE
environment, in particular if the server insists on receiving Destination objects).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.support.destination.DestinationResolver"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="message-converter" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the MessageConverter strategy for converting JMS Messages to
listener method arguments. Default is a SimpleMessageConverter.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.jms.support.converter.MessageConverter"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-type" default="queue">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS destination type for this listener: "queue", "topic" or "durableTopic".
Default is "queue".
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="queue"/>
<xsd:enumeration value="topic"/>
<xsd:enumeration value="durableTopic"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="client-id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS client id for this listener container.
Needs to be specified when using durable subscriptions.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="acknowledge" default="auto">
<xsd:annotation>
<xsd:documentation><![CDATA[
The native JMS acknowledge mode: "auto", "client", "dups-ok" or "transacted".
A value of "transacted" effectively activates a locally transacted Session;
as alternative, specify an external "transaction-manager" via the corresponding
attribute. Default is "auto".
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="auto"/>
<xsd:enumeration value="client"/>
<xsd:enumeration value="dups-ok"/>
<xsd:enumeration value="transacted"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="transaction-manager" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the Spring JtaTransactionManager or [javax.transaction.TransactionManager],
for kicking off an XA transaction for each incoming message.
If not specified, native acknowledging will be used (see "acknowledge" attribute).
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="concurrency" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The number of concurrent sessions/consumers to start for each listener.
Can either be a simple number indicating the maximum number (e.g. "5")
or a range indicating the lower as well as the upper limit (e.g. "3-5").
Note that a specified minimum is just a hint and will typically be ignored
at runtime when using a JCA listener container. Default is 1.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="prefetch" type="xsd:int">
<xsd:annotation>
<xsd:documentation><![CDATA[
The maximum number of messages to load into a single session.
Note that raising this number might lead to starvation of concurrent consumers!
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="phase" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The lifecycle phase within which this container should start and stop. The lower
the value the earlier this container will start and the later it will stop. The
default is Integer.MAX_VALUE meaning the container will start as late as possible
and stop as soon as possible.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="listenerType">
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The unique identifier for a listener.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The destination name for this listener, resolved through the
container-wide DestinationResolver strategy (if any). Required.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="subscription" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name for the durable subscription, if any.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="selector" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The JMS message selector for this listener.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ref" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The bean name of the listener object, implementing
the MessageListener/SessionAwareMessageListener interface
or defining the specified listener method. Required.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="method" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the listener method to invoke. If not specified,
the target bean is supposed to implement the MessageListener
or SessionAwareMessageListener interface.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="response-destination" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the default response destination to send response messages to.
This will be applied in case of a request message that does not carry
a "JMSReplyTo" field. The type of this destination will be determined
by the listener-container's "destination-type" attribute.
Note: This only applies to a listener method with a return value,
for which each result object will be converted into a response message.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:schema>

Some files were not shown because too many files have changed in this diff Show More