001/* 002 * Copyright 2002-2013 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.jca.context; 018 019import javax.resource.NotSupportedException; 020import javax.resource.ResourceException; 021import javax.resource.spi.ActivationSpec; 022import javax.resource.spi.BootstrapContext; 023import javax.resource.spi.ResourceAdapter; 024import javax.resource.spi.ResourceAdapterInternalException; 025import javax.resource.spi.endpoint.MessageEndpointFactory; 026import javax.transaction.xa.XAResource; 027 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030 031import org.springframework.beans.factory.support.BeanDefinitionRegistry; 032import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; 033import org.springframework.context.ConfigurableApplicationContext; 034import org.springframework.core.env.ConfigurableEnvironment; 035import org.springframework.core.env.StandardEnvironment; 036import org.springframework.util.ObjectUtils; 037import org.springframework.util.StringUtils; 038 039/** 040 * JCA 1.5 {@link javax.resource.spi.ResourceAdapter} implementation 041 * that loads a Spring {@link org.springframework.context.ApplicationContext}, 042 * starting and stopping Spring-managed beans as part of the ResourceAdapter's 043 * lifecycle. 044 * 045 * <p>Ideal for application contexts that do not need any HTTP entry points 046 * but rather just consist of message endpoints and scheduled jobs etc. 047 * Beans in such a context may use application server resources such as the 048 * JTA transaction manager and JNDI-bound JDBC DataSources and JMS 049 * ConnectionFactory instances, and may also register with the platform's 050 * JMX server - all through Spring's standard transaction management and 051 * JNDI and JMX support facilities. 052 * 053 * <p>If the need for scheduling asynchronous work arises, consider using 054 * Spring's {@link org.springframework.jca.work.WorkManagerTaskExecutor} 055 * as a standard bean definition, to be injected into application beans 056 * through dependency injection. This WorkManagerTaskExecutor will automatically 057 * use the JCA WorkManager from the BootstrapContext that has been provided 058 * to this ResourceAdapter. 059 * 060 * <p>The JCA {@link javax.resource.spi.BootstrapContext} may also be 061 * accessed directly, through application components that implement the 062 * {@link BootstrapContextAware} interface. When deployed using this 063 * ResourceAdapter, the BootstrapContext is guaranteed to be passed on 064 * to such components. 065 * 066 * <p>This ResourceAdapter is to be defined in a "META-INF/ra.xml" file 067 * within a J2EE ".rar" deployment unit like as follows: 068 * 069 * <pre class="code"> 070 * <?xml version="1.0" encoding="UTF-8"?> 071 * <connector xmlns="http://java.sun.com/xml/ns/j2ee" 072 * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 073 * xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee https://java.sun.com/xml/ns/j2ee/connector_1_5.xsd" 074 * version="1.5"> 075 * <vendor-name>Spring Framework</vendor-name> 076 * <eis-type>Spring Connector</eis-type> 077 * <resourceadapter-version>1.0</resourceadapter-version> 078 * <resourceadapter> 079 * <resourceadapter-class>org.springframework.jca.context.SpringContextResourceAdapter</resourceadapter-class> 080 * <config-property> 081 * <config-property-name>ContextConfigLocation</config-property-name> 082 * <config-property-type>java.lang.String</config-property-type> 083 * <config-property-value>META-INF/applicationContext.xml</config-property-value> 084 * </config-property> 085 * </resourceadapter> 086 * </connector></pre> 087 * 088 * Note that "META-INF/applicationContext.xml" is the default context config 089 * location, so it doesn't have to specified unless you intend to specify 090 * different/additional config files. So in the default case, you may remove 091 * the entire {@code config-property} section above. 092 * 093 * <p><b>For simple deployment needs, all you need to do is the following:</b> 094 * Package all application classes into a RAR file (which is just a standard 095 * JAR file with a different file extension), add all required library jars 096 * into the root of the RAR archive, add a "META-INF/ra.xml" deployment 097 * descriptor as shown above as well as the corresponding Spring XML bean 098 * definition file(s) (typically "META-INF/applicationContext.xml"), 099 * and drop the resulting RAR file into your application server's 100 * deployment directory! 101 * 102 * @author Juergen Hoeller 103 * @since 2.5 104 * @see #setContextConfigLocation 105 * @see #loadBeanDefinitions 106 * @see ResourceAdapterApplicationContext 107 */ 108public class SpringContextResourceAdapter implements ResourceAdapter { 109 110 /** 111 * Any number of these characters are considered delimiters between 112 * multiple context config paths in a single String value. 113 * @see #setContextConfigLocation 114 */ 115 public static final String CONFIG_LOCATION_DELIMITERS = ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS; 116 117 public static final String DEFAULT_CONTEXT_CONFIG_LOCATION = "META-INF/applicationContext.xml"; 118 119 120 protected final Log logger = LogFactory.getLog(getClass()); 121 122 private String contextConfigLocation = DEFAULT_CONTEXT_CONFIG_LOCATION; 123 124 private ConfigurableApplicationContext applicationContext; 125 126 127 /** 128 * Set the location of the context configuration files, within the 129 * resource adapter's deployment unit. This can be a delimited 130 * String that consists of multiple resource location, separated 131 * by commas, semicolons, whitespace, or line breaks. 132 * <p>This can be specified as "ContextConfigLocation" config 133 * property in the {@code ra.xml} deployment descriptor. 134 * <p>The default is "classpath:META-INF/applicationContext.xml". 135 */ 136 public void setContextConfigLocation(String contextConfigLocation) { 137 this.contextConfigLocation = contextConfigLocation; 138 } 139 140 /** 141 * Return the specified context configuration files. 142 */ 143 protected String getContextConfigLocation() { 144 return this.contextConfigLocation; 145 } 146 147 /** 148 * Return a new {@link StandardEnvironment}. 149 * <p>Subclasses may override this method in order to supply 150 * a custom {@link ConfigurableEnvironment} implementation. 151 */ 152 protected ConfigurableEnvironment createEnvironment() { 153 return new StandardEnvironment(); 154 } 155 156 /** 157 * This implementation loads a Spring ApplicationContext through the 158 * {@link #createApplicationContext} template method. 159 */ 160 @Override 161 public void start(BootstrapContext bootstrapContext) throws ResourceAdapterInternalException { 162 if (logger.isInfoEnabled()) { 163 logger.info("Starting SpringContextResourceAdapter with BootstrapContext: " + bootstrapContext); 164 } 165 this.applicationContext = createApplicationContext(bootstrapContext); 166 } 167 168 /** 169 * Build a Spring ApplicationContext for the given JCA BootstrapContext. 170 * <p>The default implementation builds a {@link ResourceAdapterApplicationContext} 171 * and delegates to {@link #loadBeanDefinitions} for actually parsing the 172 * specified configuration files. 173 * @param bootstrapContext this ResourceAdapter's BootstrapContext 174 * @return the Spring ApplicationContext instance 175 */ 176 protected ConfigurableApplicationContext createApplicationContext(BootstrapContext bootstrapContext) { 177 ResourceAdapterApplicationContext applicationContext = 178 new ResourceAdapterApplicationContext(bootstrapContext); 179 // Set ResourceAdapter's ClassLoader as bean class loader. 180 applicationContext.setClassLoader(getClass().getClassLoader()); 181 // Extract individual config locations. 182 String[] configLocations = 183 StringUtils.tokenizeToStringArray(getContextConfigLocation(), CONFIG_LOCATION_DELIMITERS); 184 if (configLocations != null) { 185 loadBeanDefinitions(applicationContext, configLocations); 186 } 187 applicationContext.refresh(); 188 return applicationContext; 189 } 190 191 /** 192 * Load the bean definitions into the given registry, 193 * based on the specified configuration files. 194 * @param registry the registry to load into 195 * @param configLocations the parsed config locations 196 * @see #setContextConfigLocation 197 */ 198 protected void loadBeanDefinitions(BeanDefinitionRegistry registry, String[] configLocations) { 199 new XmlBeanDefinitionReader(registry).loadBeanDefinitions(configLocations); 200 } 201 202 /** 203 * This implementation closes the Spring ApplicationContext. 204 */ 205 @Override 206 public void stop() { 207 logger.info("Stopping SpringContextResourceAdapter"); 208 this.applicationContext.close(); 209 } 210 211 212 /** 213 * This implementation always throws a NotSupportedException. 214 */ 215 @Override 216 public void endpointActivation(MessageEndpointFactory messageEndpointFactory, ActivationSpec activationSpec) 217 throws ResourceException { 218 219 throw new NotSupportedException("SpringContextResourceAdapter does not support message endpoints"); 220 } 221 222 /** 223 * This implementation does nothing. 224 */ 225 @Override 226 public void endpointDeactivation(MessageEndpointFactory messageEndpointFactory, ActivationSpec activationSpec) { 227 } 228 229 /** 230 * This implementation always returns {@code null}. 231 */ 232 @Override 233 public XAResource[] getXAResources(ActivationSpec[] activationSpecs) throws ResourceException { 234 return null; 235 } 236 237 238 @Override 239 public boolean equals(Object obj) { 240 return (obj instanceof SpringContextResourceAdapter && 241 ObjectUtils.nullSafeEquals(getContextConfigLocation(), 242 ((SpringContextResourceAdapter) obj).getContextConfigLocation())); 243 } 244 245 @Override 246 public int hashCode() { 247 return ObjectUtils.nullSafeHashCode(getContextConfigLocation()); 248 } 249 250}