001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.jms.listener.endpoint; 018 019import javax.jms.Message; 020import javax.jms.MessageListener; 021import javax.resource.ResourceException; 022import javax.resource.spi.UnavailableException; 023 024import org.springframework.jca.endpoint.AbstractMessageEndpointFactory; 025import org.springframework.lang.Nullable; 026import org.springframework.util.Assert; 027 028/** 029 * JMS-specific implementation of the JCA 1.7 030 * {@link javax.resource.spi.endpoint.MessageEndpointFactory} interface, 031 * providing transaction management capabilities for a JMS listener object 032 * (e.g. a {@link javax.jms.MessageListener} object). 033 * 034 * <p>Uses a static endpoint implementation, simply wrapping the 035 * specified message listener object and exposing all of its implemented 036 * interfaces on the endpoint instance. 037 * 038 * <p>Typically used with Spring's {@link JmsMessageEndpointManager}, 039 * but not tied to it. As a consequence, this endpoint factory could 040 * also be used with programmatic endpoint management on a native 041 * {@link javax.resource.spi.ResourceAdapter} instance. 042 * 043 * @author Juergen Hoeller 044 * @author Stephane Nicoll 045 * @since 2.5 046 * @see #setMessageListener 047 * @see #setTransactionManager 048 * @see JmsMessageEndpointManager 049 */ 050public class JmsMessageEndpointFactory extends AbstractMessageEndpointFactory { 051 052 @Nullable 053 private MessageListener messageListener; 054 055 056 /** 057 * Set the JMS MessageListener for this endpoint. 058 */ 059 public void setMessageListener(MessageListener messageListener) { 060 this.messageListener = messageListener; 061 } 062 063 /** 064 * Return the JMS MessageListener for this endpoint. 065 */ 066 protected MessageListener getMessageListener() { 067 Assert.state(this.messageListener != null, "No MessageListener set"); 068 return this.messageListener; 069 } 070 071 /** 072 * Creates a concrete JMS message endpoint, internal to this factory. 073 */ 074 @Override 075 protected AbstractMessageEndpoint createEndpointInternal() throws UnavailableException { 076 return new JmsMessageEndpoint(); 077 } 078 079 080 /** 081 * Private inner class that implements the concrete JMS message endpoint. 082 */ 083 private class JmsMessageEndpoint extends AbstractMessageEndpoint implements MessageListener { 084 085 @Override 086 public void onMessage(Message message) { 087 Throwable endpointEx = null; 088 boolean applyDeliveryCalls = !hasBeforeDeliveryBeenCalled(); 089 if (applyDeliveryCalls) { 090 try { 091 beforeDelivery(null); 092 } 093 catch (ResourceException ex) { 094 throw new JmsResourceException(ex); 095 } 096 } 097 try { 098 getMessageListener().onMessage(message); 099 } 100 catch (RuntimeException | Error ex) { 101 endpointEx = ex; 102 onEndpointException(ex); 103 throw ex; 104 } 105 finally { 106 if (applyDeliveryCalls) { 107 try { 108 afterDelivery(); 109 } 110 catch (ResourceException ex) { 111 if (endpointEx == null) { 112 throw new JmsResourceException(ex); 113 } 114 } 115 } 116 } 117 } 118 119 @Override 120 protected ClassLoader getEndpointClassLoader() { 121 return getMessageListener().getClass().getClassLoader(); 122 } 123 } 124 125 126 /** 127 * Internal exception thrown when a ResourceException has been encountered 128 * during the endpoint invocation. 129 * <p>Will only be used if the ResourceAdapter does not invoke the 130 * endpoint's {@code beforeDelivery} and {@code afterDelivery} 131 * directly, leaving it up to the concrete endpoint to apply those - 132 * and to handle any ResourceExceptions thrown from them. 133 */ 134 @SuppressWarnings("serial") 135 public static class JmsResourceException extends RuntimeException { 136 137 public JmsResourceException(ResourceException cause) { 138 super(cause); 139 } 140 } 141 142}