001/* 002 * Copyright 2002-2019 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.Session; 020import javax.resource.spi.ResourceAdapter; 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024 025import org.springframework.beans.BeanWrapper; 026 027/** 028 * Default implementation of the {@link JmsActivationSpecFactory} interface. 029 * Supports the standard JMS properties as defined by the JCA 1.5 specification, 030 * as well as Spring's extended "maxConcurrency" and "prefetchSize" settings 031 * through autodetection of well-known vendor-specific provider properties. 032 * 033 * <p>An ActivationSpec factory is effectively dependent on the concrete 034 * JMS provider, e.g. on ActiveMQ. This default implementation simply 035 * guesses the ActivationSpec class name from the provider's class name 036 * ("ActiveMQResourceAdapter" -> "ActiveMQActivationSpec" in the same package, 037 * or "ActivationSpecImpl" in the same package as the ResourceAdapter class), 038 * and populates the ActivationSpec properties as suggested by the 039 * JCA 1.5 specification (Appendix B). Specify the 'activationSpecClass' 040 * property explicitly if these default naming rules do not apply. 041 * 042 * <p>Note: ActiveMQ, JORAM and WebSphere are supported in terms of extended 043 * settings (through the detection of their bean property naming conventions). 044 * The default ActivationSpec class detection rules may apply to other 045 * JMS providers as well. 046 * 047 * <p>Thanks to Agim Emruli and Laurie Chan for pointing out WebSphere MQ 048 * settings and contributing corresponding tests! 049 * 050 * @author Juergen Hoeller 051 * @since 2.5 052 * @see #setActivationSpecClass 053 */ 054public class DefaultJmsActivationSpecFactory extends StandardJmsActivationSpecFactory { 055 056 private static final String RESOURCE_ADAPTER_SUFFIX = "ResourceAdapter"; 057 058 private static final String RESOURCE_ADAPTER_IMPL_SUFFIX = "ResourceAdapterImpl"; 059 060 private static final String ACTIVATION_SPEC_SUFFIX = "ActivationSpec"; 061 062 private static final String ACTIVATION_SPEC_IMPL_SUFFIX = "ActivationSpecImpl"; 063 064 065 /** Logger available to subclasses. */ 066 protected final Log logger = LogFactory.getLog(getClass()); 067 068 069 /** 070 * This implementation guesses the ActivationSpec class name from the 071 * provider's class name: e.g. "ActiveMQResourceAdapter" -> 072 * "ActiveMQActivationSpec" in the same package, or a class named 073 * "ActivationSpecImpl" in the same package as the ResourceAdapter class. 074 */ 075 @Override 076 protected Class<?> determineActivationSpecClass(ResourceAdapter adapter) { 077 String adapterClassName = adapter.getClass().getName(); 078 079 if (adapterClassName.endsWith(RESOURCE_ADAPTER_SUFFIX)) { 080 // e.g. ActiveMQ 081 String providerName = 082 adapterClassName.substring(0, adapterClassName.length() - RESOURCE_ADAPTER_SUFFIX.length()); 083 String specClassName = providerName + ACTIVATION_SPEC_SUFFIX; 084 try { 085 return adapter.getClass().getClassLoader().loadClass(specClassName); 086 } 087 catch (ClassNotFoundException ex) { 088 if (logger.isDebugEnabled()) { 089 logger.debug("No default <Provider>ActivationSpec class found: " + specClassName); 090 } 091 } 092 } 093 094 else if (adapterClassName.endsWith(RESOURCE_ADAPTER_IMPL_SUFFIX)){ 095 //e.g. WebSphere 096 String providerName = 097 adapterClassName.substring(0, adapterClassName.length() - RESOURCE_ADAPTER_IMPL_SUFFIX.length()); 098 String specClassName = providerName + ACTIVATION_SPEC_IMPL_SUFFIX; 099 try { 100 return adapter.getClass().getClassLoader().loadClass(specClassName); 101 } 102 catch (ClassNotFoundException ex) { 103 if (logger.isDebugEnabled()) { 104 logger.debug("No default <Provider>ActivationSpecImpl class found: " + specClassName); 105 } 106 } 107 } 108 109 // e.g. JORAM 110 String providerPackage = adapterClassName.substring(0, adapterClassName.lastIndexOf('.') + 1); 111 String specClassName = providerPackage + ACTIVATION_SPEC_IMPL_SUFFIX; 112 try { 113 return adapter.getClass().getClassLoader().loadClass(specClassName); 114 } 115 catch (ClassNotFoundException ex) { 116 if (logger.isDebugEnabled()) { 117 logger.debug("No default ActivationSpecImpl class found in provider package: " + specClassName); 118 } 119 } 120 121 // ActivationSpecImpl class in "inbound" subpackage (WebSphere MQ 6.0.2.1) 122 specClassName = providerPackage + "inbound." + ACTIVATION_SPEC_IMPL_SUFFIX; 123 try { 124 return adapter.getClass().getClassLoader().loadClass(specClassName); 125 } 126 catch (ClassNotFoundException ex) { 127 if (logger.isDebugEnabled()) { 128 logger.debug("No default ActivationSpecImpl class found in inbound subpackage: " + specClassName); 129 } 130 } 131 132 throw new IllegalStateException("No ActivationSpec class defined - " + 133 "specify the 'activationSpecClass' property or override the 'determineActivationSpecClass' method"); 134 } 135 136 /** 137 * This implementation supports Spring's extended "maxConcurrency" 138 * and "prefetchSize" settings through detecting corresponding 139 * ActivationSpec properties: "maxSessions"/"maxNumberOfWorks" and 140 * "maxMessagesPerSessions"/"maxMessages", respectively 141 * (following ActiveMQ's and JORAM's naming conventions). 142 */ 143 @Override 144 protected void populateActivationSpecProperties(BeanWrapper bw, JmsActivationSpecConfig config) { 145 super.populateActivationSpecProperties(bw, config); 146 if (config.getMaxConcurrency() > 0) { 147 if (bw.isWritableProperty("maxSessions")) { 148 // ActiveMQ 149 bw.setPropertyValue("maxSessions", Integer.toString(config.getMaxConcurrency())); 150 } 151 else if (bw.isWritableProperty("maxNumberOfWorks")) { 152 // JORAM 153 bw.setPropertyValue("maxNumberOfWorks", Integer.toString(config.getMaxConcurrency())); 154 } 155 else if (bw.isWritableProperty("maxConcurrency")){ 156 // WebSphere 157 bw.setPropertyValue("maxConcurrency", Integer.toString(config.getMaxConcurrency())); 158 } 159 } 160 if (config.getPrefetchSize() > 0) { 161 if (bw.isWritableProperty("maxMessagesPerSessions")) { 162 // ActiveMQ 163 bw.setPropertyValue("maxMessagesPerSessions", Integer.toString(config.getPrefetchSize())); 164 } 165 else if (bw.isWritableProperty("maxMessages")) { 166 // JORAM 167 bw.setPropertyValue("maxMessages", Integer.toString(config.getPrefetchSize())); 168 } 169 else if (bw.isWritableProperty("maxBatchSize")){ 170 // WebSphere 171 bw.setPropertyValue("maxBatchSize", Integer.toString(config.getPrefetchSize())); 172 } 173 } 174 } 175 176 /** 177 * This implementation maps {@code SESSION_TRANSACTED} onto an 178 * ActivationSpec property named "useRAManagedTransaction", if available 179 * (following ActiveMQ's naming conventions). 180 */ 181 @Override 182 protected void applyAcknowledgeMode(BeanWrapper bw, int ackMode) { 183 if (ackMode == Session.SESSION_TRANSACTED && bw.isWritableProperty("useRAManagedTransaction")) { 184 // ActiveMQ 185 bw.setPropertyValue("useRAManagedTransaction", "true"); 186 } 187 else { 188 super.applyAcknowledgeMode(bw, ackMode); 189 } 190 } 191 192}