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