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