Initial import of context support module
This commit is contained in:
parent
da208c282a
commit
4df7d71c1e
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="org.springframework.context.support">
|
||||
<property file="${basedir}/../build.properties"/>
|
||||
<import file="${basedir}/../build-spring-framework/package-bundle.xml"/>
|
||||
<import file="${basedir}/../spring-build/standard/default.xml"/>
|
||||
</project>
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet type="text/xsl" href="http://ivyrep.jayasoft.org/ivy-doc.xsl"?>
|
||||
<ivy-module
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://incubator.apache.org/ivy/schemas/ivy.xsd"
|
||||
version="1.3">
|
||||
|
||||
<info organisation="org.springframework" module="${ant.project.name}">
|
||||
<license name="Apache 2.0" url="http://www.apache.org/licenses/LICENSE-2.0"/>
|
||||
</info>
|
||||
|
||||
<configurations>
|
||||
<include file="${spring.build.dir}/common/default-ivy-configurations.xml"/>
|
||||
</configurations>
|
||||
|
||||
<publications>
|
||||
<artifact name="${ant.project.name}"/>
|
||||
<artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
|
||||
</publications>
|
||||
|
||||
<dependencies>
|
||||
<!-- compile dependencies -->
|
||||
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.logging" rev="1.1.1" conf="compile->runtime" />
|
||||
<dependency org="org.springframework" name="org.springframework.core" rev="latest.integration" conf="compile->compile" />
|
||||
<dependency org="org.springframework" name="org.springframework.context" rev="latest.integration" conf="compile->compile" />
|
||||
<!-- optional dependencies -->
|
||||
<dependency org="org.springframework" name="org.springframework.jdbc" rev="latest.integration" conf="optional->compile" />
|
||||
<dependency org="org.springframework" name="org.springframework.transaction" rev="latest.integration" conf="optional->compile" />
|
||||
<dependency org="javax.mail" name="com.springsource.javax.mail" rev="1.4.1" conf="optional->compile" />
|
||||
<dependency org="org.apache.velocity" name="com.springsource.org.apache.velocity" rev="1.5.0" conf="optional->compile" />
|
||||
<dependency org="org.freemarker" name="com.springsource.freemarker" rev="2.3.12" conf="optional->compile" />
|
||||
<dependency org="net.sourceforge.jasperreports" name="com.springsource.net.sf.jasperreports" rev="2.0.5" conf="optional->compile" />
|
||||
<dependency org="com.opensymphony.quartz" name="com.springsource.org.quartz" rev="1.6.0" conf="optional->compile" />
|
||||
<dependency org="com.bea.commonj" name="com.springsource.commonj" rev="1.1.0" conf="optional->compile" />
|
||||
<dependency org="net.sourceforge.ehcache" name="com.springsource.net.sf.ehcache" rev="1.4.1" conf="optional->compile" />
|
||||
<!-- test dependencies -->
|
||||
<dependency org="org.apache.log4j" name="com.springsource.org.apache.log4j" rev="1.2.15" conf="test->runtime"/>
|
||||
<dependency org="org.junit" name="com.springsource.org.junit" rev="4.4.0" conf="test->runtime" />
|
||||
|
||||
</dependencies>
|
||||
|
||||
</ivy-module>
|
|
@ -0,0 +1,141 @@
|
|||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.parent</artifactId>
|
||||
<version>3.0-M1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>org.springframework.context.support</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Framework: Context Support</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.jdbc</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.transaction</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.mail</groupId>
|
||||
<artifactId>com.springsource.javax.mail</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>com.springsource.org.apache.velocity</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
<artifactId>com.springsource.freemarker</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.jasperreports</groupId>
|
||||
<artifactId>com.springsource.net.sf.jasperreports</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.bea.commonj</groupId>
|
||||
<artifactId>com.springsource.commonj</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.opensymphony.quartz</groupId>
|
||||
<artifactId>com.springsource.org.quartz</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.ehcache</groupId>
|
||||
<artifactId>com.springsource.net.sf.ehcache</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.beans</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.agent</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.ejb</groupId>
|
||||
<artifactId>com.springsource.javax.ejb</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.jms</groupId>
|
||||
<artifactId>com.springsource.javax.jms</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.persistence</groupId>
|
||||
<artifactId>com.springsource.javax.persistence</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.cglib</groupId>
|
||||
<artifactId>com.springsource.net.sf.cglib</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aopalliance</groupId>
|
||||
<artifactId>com.springsource.org.aopalliance</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jruby</groupId>
|
||||
<artifactId>com.springsource.org.jruby</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>com.springsource.org.codehaus.groovy</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.beanshell</groupId>
|
||||
<artifactId>com.springsource.bsh</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>edu.emory.mathcs.backport</groupId>
|
||||
<artifactId>com.springsource.edu.emory.mathcs.backport</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.oracle.oc4j</groupId>
|
||||
<artifactId>com.springsource.oracle.classloader</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.enterprise</groupId>
|
||||
<artifactId>com.springsource.com.sun.enterprise.loader</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
-->
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.ehcache;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheException;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
import net.sf.ehcache.constructs.blocking.BlockingCache;
|
||||
import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
|
||||
import net.sf.ehcache.constructs.blocking.SelfPopulatingCache;
|
||||
import net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory;
|
||||
import net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache;
|
||||
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* FactoryBean that creates a named EHCache {@link net.sf.ehcache.Cache} instance
|
||||
* (or a decorator that implements the {@link net.sf.ehcache.Ehcache} interface),
|
||||
* representing a cache region within an EHCache {@link net.sf.ehcache.CacheManager}.
|
||||
*
|
||||
* <p>If the specified named cache is not configured in the cache configuration descriptor,
|
||||
* this FactoryBean will construct an instance of a Cache with the provided name and the
|
||||
* specified cache properties and add it to the CacheManager for later retrieval. If some
|
||||
* or all properties are not set at configuration time, this FactoryBean will use defaults.
|
||||
*
|
||||
* <p>Note: If the named Cache instance is found, the properties will be ignored and the
|
||||
* Cache instance will be retrieved from the CacheManager.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.1
|
||||
* @see #setCacheManager
|
||||
* @see EhCacheManagerFactoryBean
|
||||
* @see net.sf.ehcache.Cache
|
||||
*/
|
||||
public class EhCacheFactoryBean implements FactoryBean, BeanNameAware, InitializingBean {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private CacheManager cacheManager;
|
||||
|
||||
private String cacheName;
|
||||
|
||||
private int maxElementsInMemory = 10000;
|
||||
|
||||
private int maxElementsOnDisk = 10000000;
|
||||
|
||||
private MemoryStoreEvictionPolicy memoryStoreEvictionPolicy = MemoryStoreEvictionPolicy.LRU;
|
||||
|
||||
private boolean overflowToDisk = true;
|
||||
|
||||
private String diskStorePath;
|
||||
|
||||
private boolean eternal = false;
|
||||
|
||||
private int timeToLive = 120;
|
||||
|
||||
private int timeToIdle = 120;
|
||||
|
||||
private boolean diskPersistent = false;
|
||||
|
||||
private int diskExpiryThreadIntervalSeconds = 120;
|
||||
|
||||
private boolean blocking = false;
|
||||
|
||||
private CacheEntryFactory cacheEntryFactory;
|
||||
|
||||
private String beanName;
|
||||
|
||||
private Ehcache cache;
|
||||
|
||||
|
||||
/**
|
||||
* Set a CacheManager from which to retrieve a named Cache instance.
|
||||
* By default, <code>CacheManager.getInstance()</code> will be called.
|
||||
* <p>Note that in particular for persistent caches, it is advisable to
|
||||
* properly handle the shutdown of the CacheManager: Set up a separate
|
||||
* EhCacheManagerFactoryBean and pass a reference to this bean property.
|
||||
* <p>A separate EhCacheManagerFactoryBean is also necessary for loading
|
||||
* EHCache configuration from a non-default config location.
|
||||
* @see EhCacheManagerFactoryBean
|
||||
* @see net.sf.ehcache.CacheManager#getInstance
|
||||
*/
|
||||
public void setCacheManager(CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a name for which to retrieve or create a cache instance.
|
||||
* Default is the bean name of this EhCacheFactoryBean.
|
||||
*/
|
||||
public void setCacheName(String cacheName) {
|
||||
this.cacheName = cacheName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the maximum number of cached objects in memory.
|
||||
* Default is 10000 elements.
|
||||
*/
|
||||
public void setMaxElementsInMemory(int maxElementsInMemory) {
|
||||
this.maxElementsInMemory = maxElementsInMemory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the maximum number of cached objects on disk.
|
||||
* Default is 10000000 elements.
|
||||
*/
|
||||
public void setMaxElementsOnDisk(int maxElementsOnDisk) {
|
||||
this.maxElementsOnDisk = maxElementsOnDisk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the memory style eviction policy for this cache.
|
||||
* Supported values are "LRU", "LFU" and "FIFO", according to the
|
||||
* constants defined in EHCache's MemoryStoreEvictionPolicy class.
|
||||
* Default is "LRU".
|
||||
*/
|
||||
public void setMemoryStoreEvictionPolicy(MemoryStoreEvictionPolicy memoryStoreEvictionPolicy) {
|
||||
Assert.notNull(memoryStoreEvictionPolicy, "memoryStoreEvictionPolicy must not be null");
|
||||
this.memoryStoreEvictionPolicy = memoryStoreEvictionPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether elements can overflow to disk when the in-memory cache
|
||||
* has reached the maximum size limit. Default is "true".
|
||||
*/
|
||||
public void setOverflowToDisk(boolean overflowToDisk) {
|
||||
this.overflowToDisk = overflowToDisk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether elements are considered as eternal. If "true", timeouts
|
||||
* are ignored and the element is never expired. Default is "false".
|
||||
*/
|
||||
public void setEternal(boolean eternal) {
|
||||
this.eternal = eternal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set t he time in seconds to live for an element before it expires,
|
||||
* i.e. the maximum time between creation time and when an element expires.
|
||||
* It is only used if the element is not eternal. Default is 120 seconds.
|
||||
*/
|
||||
public void setTimeToLive(int timeToLive) {
|
||||
this.timeToLive = timeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time in seconds to idle for an element before it expires, that is,
|
||||
* the maximum amount of time between accesses before an element expires.
|
||||
* This is only used if the element is not eternal. Default is 120 seconds.
|
||||
*/
|
||||
public void setTimeToIdle(int timeToIdle) {
|
||||
this.timeToIdle = timeToIdle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the disk store persists between restarts of the Virtual Machine.
|
||||
* The default is "false".
|
||||
*/
|
||||
public void setDiskPersistent(boolean diskPersistent) {
|
||||
this.diskPersistent = diskPersistent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of seconds between runs of the disk expiry thread.
|
||||
* The default is 120 seconds.
|
||||
*/
|
||||
public void setDiskExpiryThreadIntervalSeconds(int diskExpiryThreadIntervalSeconds) {
|
||||
this.diskExpiryThreadIntervalSeconds = diskExpiryThreadIntervalSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use a blocking cache that lets read attempts block
|
||||
* until the requested element is created.
|
||||
* <p>If you intend to build a self-populating blocking cache,
|
||||
* consider specifying a {@link #setCacheEntryFactory CacheEntryFactory}.
|
||||
* @see net.sf.ehcache.constructs.blocking.BlockingCache
|
||||
* @see #setCacheEntryFactory
|
||||
*/
|
||||
public void setBlocking(boolean blocking) {
|
||||
this.blocking = blocking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an EHCache {@link net.sf.ehcache.constructs.blocking.CacheEntryFactory}
|
||||
* to use for a self-populating cache. If such a factory is specified,
|
||||
* the cache will be decorated with EHCache's
|
||||
* {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}.
|
||||
* <p>The specified factory can be of type
|
||||
* {@link net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory},
|
||||
* which will lead to the use of an
|
||||
* {@link net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache}.
|
||||
* <p>Note: Any such self-populating cache is automatically a blocking cache.
|
||||
* @see net.sf.ehcache.constructs.blocking.SelfPopulatingCache
|
||||
* @see net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache
|
||||
* @see net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory
|
||||
*/
|
||||
public void setCacheEntryFactory(CacheEntryFactory cacheEntryFactory) {
|
||||
this.cacheEntryFactory = cacheEntryFactory;
|
||||
}
|
||||
|
||||
public void setBeanName(String name) {
|
||||
this.beanName = name;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws CacheException, IOException {
|
||||
// If no CacheManager given, fetch the default.
|
||||
if (this.cacheManager == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Using default EHCache CacheManager for cache region '" + this.cacheName + "'");
|
||||
}
|
||||
this.cacheManager = CacheManager.getInstance();
|
||||
}
|
||||
|
||||
// If no cache name given, use bean name as cache name.
|
||||
if (this.cacheName == null) {
|
||||
this.cacheName = this.beanName;
|
||||
}
|
||||
|
||||
// Fetch cache region: If none with the given name exists,
|
||||
// create one on the fly.
|
||||
Ehcache rawCache = null;
|
||||
if (this.cacheManager.cacheExists(this.cacheName)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Using existing EHCache cache region '" + this.cacheName + "'");
|
||||
}
|
||||
rawCache = this.cacheManager.getEhcache(this.cacheName);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Creating new EHCache cache region '" + this.cacheName + "'");
|
||||
}
|
||||
rawCache = createCache();
|
||||
this.cacheManager.addCache(rawCache);
|
||||
}
|
||||
|
||||
// Decorate cache if necessary.
|
||||
Ehcache decoratedCache = decorateCache(rawCache);
|
||||
if (decoratedCache != rawCache) {
|
||||
this.cacheManager.replaceCacheWithDecoratedCache(rawCache, decoratedCache);
|
||||
}
|
||||
this.cache = decoratedCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a raw Cache object based on the configuration of this FactoryBean.
|
||||
*/
|
||||
private Cache createCache() {
|
||||
return new Cache(
|
||||
this.cacheName, this.maxElementsInMemory, this.memoryStoreEvictionPolicy,
|
||||
this.overflowToDisk, null, this.eternal, this.timeToLive, this.timeToIdle,
|
||||
this.diskPersistent, this.diskExpiryThreadIntervalSeconds, null, null, this.maxElementsOnDisk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorate the given Cache, if necessary.
|
||||
* @param cache the raw Cache object, based on the configuration of this FactoryBean
|
||||
* @return the (potentially decorated) cache object to be registered with the CacheManager
|
||||
*/
|
||||
protected Ehcache decorateCache(Ehcache cache) {
|
||||
if (this.cacheEntryFactory != null) {
|
||||
if (this.cacheEntryFactory instanceof UpdatingCacheEntryFactory) {
|
||||
return new UpdatingSelfPopulatingCache(cache, (UpdatingCacheEntryFactory) this.cacheEntryFactory);
|
||||
}
|
||||
else {
|
||||
return new SelfPopulatingCache(cache, this.cacheEntryFactory);
|
||||
}
|
||||
}
|
||||
if (this.blocking) {
|
||||
return new BlockingCache(cache);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
|
||||
public Object getObject() {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return (this.cache != null ? this.cache.getClass() : Ehcache.class);
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.ehcache;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.sf.ehcache.CacheException;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* FactoryBean that exposes an EHCache {@link net.sf.ehcache.CacheManager} instance
|
||||
* (independent or shared), configured from a specified config location.
|
||||
*
|
||||
* <p>If no config location is specified, a CacheManager will be configured from
|
||||
* "ehcache.xml" in the root of the class path (that is, default EHCache initialization
|
||||
* - as defined in the EHCache docs - will apply).
|
||||
*
|
||||
* <p>Setting up a separate EhCacheManagerFactoryBean is also advisable when using
|
||||
* EhCacheFactoryBean, as it provides a (by default) independent CacheManager instance
|
||||
* and cares for proper shutdown of the CacheManager. EhCacheManagerFactoryBean is
|
||||
* also necessary for loading EHCache configuration from a non-default config location.
|
||||
*
|
||||
* <p>Note: As of Spring 2.0, this FactoryBean will by default create an independent
|
||||
* CacheManager instance, which requires EHCache 1.2 or higher.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.1
|
||||
* @see #setConfigLocation
|
||||
* @see #setShared
|
||||
* @see EhCacheFactoryBean
|
||||
* @see net.sf.ehcache.CacheManager
|
||||
*/
|
||||
public class EhCacheManagerFactoryBean implements FactoryBean, InitializingBean, DisposableBean {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Resource configLocation;
|
||||
|
||||
private boolean shared = false;
|
||||
|
||||
private String cacheManagerName;
|
||||
|
||||
private CacheManager cacheManager;
|
||||
|
||||
|
||||
/**
|
||||
* Set the location of the EHCache config file. A typical value is "/WEB-INF/ehcache.xml".
|
||||
* <p>Default is "ehcache.xml" in the root of the class path, or if not found,
|
||||
* "ehcache-failsafe.xml" in the EHCache jar (default EHCache initialization).
|
||||
* @see net.sf.ehcache.CacheManager#create(java.io.InputStream)
|
||||
* @see net.sf.ehcache.CacheManager#CacheManager(java.io.InputStream)
|
||||
*/
|
||||
public void setConfigLocation(Resource configLocation) {
|
||||
this.configLocation = configLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the EHCache CacheManager should be shared (as a singleton at the VM level)
|
||||
* or independent (typically local within the application). Default is "false", creating
|
||||
* an independent instance.
|
||||
* @see net.sf.ehcache.CacheManager#create()
|
||||
* @see net.sf.ehcache.CacheManager#CacheManager()
|
||||
*/
|
||||
public void setShared(boolean shared) {
|
||||
this.shared = shared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the EHCache CacheManager (if a specific name is desired).
|
||||
* @see net.sf.ehcache.CacheManager#setName(String)
|
||||
*/
|
||||
public void setCacheManagerName(String cacheManagerName) {
|
||||
this.cacheManagerName = cacheManagerName;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws IOException, CacheException {
|
||||
logger.info("Initializing EHCache CacheManager");
|
||||
if (this.shared) {
|
||||
// Shared CacheManager singleton at the VM level.
|
||||
if (this.configLocation != null) {
|
||||
this.cacheManager = CacheManager.create(this.configLocation.getInputStream());
|
||||
}
|
||||
else {
|
||||
this.cacheManager = CacheManager.create();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Independent CacheManager instance (the default).
|
||||
if (this.configLocation != null) {
|
||||
this.cacheManager = new CacheManager(this.configLocation.getInputStream());
|
||||
}
|
||||
else {
|
||||
this.cacheManager = new CacheManager();
|
||||
}
|
||||
}
|
||||
if (this.cacheManagerName != null) {
|
||||
this.cacheManager.setName(this.cacheManagerName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Object getObject() {
|
||||
return this.cacheManager;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return (this.cacheManager != null ? this.cacheManager.getClass() : CacheManager.class);
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void destroy() {
|
||||
logger.info("Shutting down EHCache CacheManager");
|
||||
this.cacheManager.shutdown();
|
||||
}
|
||||
|
||||
}
|
10
org.springframework.context.support/src/main/java/org/springframework/cache/ehcache/package.html
vendored
Normal file
10
org.springframework.context.support/src/main/java/org/springframework/cache/ehcache/package.html
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Support classes for the open source cache
|
||||
<a href="http://ehcache.sourceforge.net">EHCache</a>,
|
||||
allowing to set up an EHCache CacheManager and Caches
|
||||
as beans in a Spring context.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail;
|
||||
|
||||
/**
|
||||
* Exception thrown on failed authentication.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class MailAuthenticationException extends MailException {
|
||||
|
||||
/**
|
||||
* Constructor for MailAuthenticationException.
|
||||
* @param msg message
|
||||
*/
|
||||
public MailAuthenticationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MailAuthenticationException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the mail API in use
|
||||
*/
|
||||
public MailAuthenticationException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MailAuthenticationException.
|
||||
* @param cause the root cause from the mail API in use
|
||||
*/
|
||||
public MailAuthenticationException(Throwable cause) {
|
||||
super("Authentication failed", cause);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail;
|
||||
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
|
||||
/**
|
||||
* Base class for all mail exceptions.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
*/
|
||||
public abstract class MailException extends NestedRuntimeException {
|
||||
|
||||
/**
|
||||
* Constructor for MailException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public MailException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MailException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the mail API in use
|
||||
*/
|
||||
public MailException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* This is a common interface for mail messages, allowing a user to set key
|
||||
* values required in assembling a mail message, without needing to know if
|
||||
* the underlying message is a simple text message or a more sophisticated
|
||||
* MIME message.
|
||||
*
|
||||
* <p>Implemented by both SimpleMailMessage and MimeMessageHelper,
|
||||
* to let message population code interact with a simple message or a
|
||||
* MIME message through a common interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.5
|
||||
* @see SimpleMailMessage
|
||||
* @see org.springframework.mail.javamail.MimeMessageHelper
|
||||
*/
|
||||
public interface MailMessage {
|
||||
|
||||
public void setFrom(String from) throws MailParseException;
|
||||
|
||||
public void setReplyTo(String replyTo) throws MailParseException;
|
||||
|
||||
public void setTo(String to) throws MailParseException;
|
||||
|
||||
public void setTo(String[] to) throws MailParseException;
|
||||
|
||||
public void setCc(String cc) throws MailParseException;
|
||||
|
||||
public void setCc(String[] cc) throws MailParseException;
|
||||
|
||||
public void setBcc(String bcc) throws MailParseException;
|
||||
|
||||
public void setBcc(String[] bcc) throws MailParseException;
|
||||
|
||||
public void setSentDate(Date sentDate) throws MailParseException;
|
||||
|
||||
public void setSubject(String subject) throws MailParseException;
|
||||
|
||||
public void setText(String text) throws MailParseException;
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail;
|
||||
|
||||
/**
|
||||
* Exception thrown if illegal message properties are encountered.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class MailParseException extends MailException {
|
||||
|
||||
/**
|
||||
* Constructor for MailParseException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public MailParseException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MailParseException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the mail API in use
|
||||
*/
|
||||
public MailParseException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MailParseException.
|
||||
* @param cause the root cause from the mail API in use
|
||||
*/
|
||||
public MailParseException(Throwable cause) {
|
||||
super("Could not parse mail", cause);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail;
|
||||
|
||||
/**
|
||||
* Exception to be thrown by user code if a mail cannot be prepared properly,
|
||||
* for example when a Velocity template cannot be rendered for the mail text.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see org.springframework.ui.velocity.VelocityEngineUtils#mergeTemplateIntoString
|
||||
* @see org.springframework.ui.freemarker.FreeMarkerTemplateUtils#processTemplateIntoString
|
||||
*/
|
||||
public class MailPreparationException extends MailException {
|
||||
|
||||
/**
|
||||
* Constructor for MailPreparationException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public MailPreparationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MailPreparationException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the mail API in use
|
||||
*/
|
||||
public MailPreparationException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
public MailPreparationException(Throwable cause) {
|
||||
super("Could not prepare mail", cause);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Exception thrown when a mail sending error is encountered.
|
||||
* Can register failed messages with their exceptions.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class MailSendException extends MailException {
|
||||
|
||||
private transient Map failedMessages;
|
||||
|
||||
private Exception[] messageExceptions;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for MailSendException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public MailSendException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MailSendException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the mail API in use
|
||||
*/
|
||||
public MailSendException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for registration of failed messages, with the
|
||||
* messages that failed as keys, and the thrown exceptions as values.
|
||||
* <p>The messages should be the same that were originally passed
|
||||
* to the invoked send method.
|
||||
* @param failedMessages Map of failed messages as keys and thrown
|
||||
* exceptions as values
|
||||
*/
|
||||
public MailSendException(Map failedMessages) {
|
||||
super(null);
|
||||
this.failedMessages = new LinkedHashMap(failedMessages);
|
||||
this.messageExceptions = (Exception[]) failedMessages.values().toArray(new Exception[failedMessages.size()]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a Map with the failed messages as keys, and the thrown exceptions
|
||||
* as values.
|
||||
* <p>Note that a general mail server connection failure will not result
|
||||
* in failed messages being returned here: A message will only be
|
||||
* contained here if actually sending it was attempted but failed.
|
||||
* <p>The messages will be the same that were originally passed to the
|
||||
* invoked send method, that is, SimpleMailMessages in case of using
|
||||
* the generic MailSender interface.
|
||||
* <p>In case of sending MimeMessage instances via JavaMailSender,
|
||||
* the messages will be of type MimeMessage.
|
||||
* <p><b>NOTE:</b> This Map will not be available after serialization.
|
||||
* Use {@link #getMessageExceptions()} in such a scenario, which will
|
||||
* be available after serialization as well.
|
||||
* @return the Map of failed messages as keys and thrown exceptions as
|
||||
* values, or an empty Map if no failed messages
|
||||
* @see SimpleMailMessage
|
||||
* @see javax.mail.internet.MimeMessage
|
||||
*/
|
||||
public final Map getFailedMessages() {
|
||||
return (this.failedMessages != null ? this.failedMessages : Collections.EMPTY_MAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array with thrown message exceptions.
|
||||
* <p>Note that a general mail server connection failure will not result
|
||||
* in failed messages being returned here: A message will only be
|
||||
* contained here if actually sending it was attempted but failed.
|
||||
* @return the array of thrown message exceptions,
|
||||
* or an empty array if no failed messages
|
||||
*/
|
||||
public final Exception[] getMessageExceptions() {
|
||||
return (this.messageExceptions != null ? this.messageExceptions : new Exception[0]);
|
||||
}
|
||||
|
||||
|
||||
public String getMessage() {
|
||||
if (ObjectUtils.isEmpty(this.messageExceptions)) {
|
||||
return super.getMessage();
|
||||
}
|
||||
else {
|
||||
StringBuffer sb = new StringBuffer("Failed messages: ");
|
||||
for (int i = 0; i < this.messageExceptions.length; i++) {
|
||||
Exception subEx = this.messageExceptions[i];
|
||||
sb.append(subEx.toString());
|
||||
if (i < this.messageExceptions.length - 1) {
|
||||
sb.append("; ");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (ObjectUtils.isEmpty(this.messageExceptions)) {
|
||||
return super.toString();
|
||||
}
|
||||
else {
|
||||
StringBuffer sb = new StringBuffer(getClass().getName());
|
||||
sb.append("; nested exceptions (").append(this.messageExceptions.length).append(") are:");
|
||||
for (int i = 0; i < this.messageExceptions.length; i++) {
|
||||
Exception subEx = this.messageExceptions[i];
|
||||
sb.append('\n').append("Failed message ").append(i + 1).append(": ");
|
||||
sb.append(subEx);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public void printStackTrace(PrintStream ps) {
|
||||
if (ObjectUtils.isEmpty(this.messageExceptions)) {
|
||||
super.printStackTrace(ps);
|
||||
}
|
||||
else {
|
||||
ps.println(getClass().getName() + "; nested exception details (" +
|
||||
this.messageExceptions.length + ") are:");
|
||||
for (int i = 0; i < this.messageExceptions.length; i++) {
|
||||
Exception subEx = this.messageExceptions[i];
|
||||
ps.println("Failed message " + (i + 1) + ":");
|
||||
subEx.printStackTrace(ps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void printStackTrace(PrintWriter pw) {
|
||||
if (ObjectUtils.isEmpty(this.messageExceptions)) {
|
||||
super.printStackTrace(pw);
|
||||
}
|
||||
else {
|
||||
pw.println(getClass().getName() + "; nested exception details (" +
|
||||
this.messageExceptions.length + ") are:");
|
||||
for (int i = 0; i < this.messageExceptions.length; i++) {
|
||||
Exception subEx = this.messageExceptions[i];
|
||||
pw.println("Failed message " + (i + 1) + ":");
|
||||
subEx.printStackTrace(pw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail;
|
||||
|
||||
/**
|
||||
* This interface defines a strategy for sending simple mails. Can be
|
||||
* implemented for a variety of mailing systems due to the simple requirements.
|
||||
* For richer functionality like MIME messages, consider JavaMailSender.
|
||||
*
|
||||
* <p>Allows for easy testing of clients, as it does not depend on JavaMail's
|
||||
* infrastructure classes: no mocking of JavaMail Session or Transport necessary.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
* @since 10.09.2003
|
||||
* @see org.springframework.mail.javamail.JavaMailSender
|
||||
*/
|
||||
public interface MailSender {
|
||||
|
||||
/**
|
||||
* Send the given simple mail message.
|
||||
* @param simpleMessage the message to send
|
||||
* @throws org.springframework.mail.MailParseException
|
||||
* in case of failure when parsing the message
|
||||
* @throws org.springframework.mail.MailAuthenticationException
|
||||
* in case of authentication failure
|
||||
* @throws org.springframework.mail.MailSendException
|
||||
* in case of failure when sending the message
|
||||
*/
|
||||
void send(SimpleMailMessage simpleMessage) throws MailException;
|
||||
|
||||
/**
|
||||
* Send the given array of simple mail messages in batch.
|
||||
* @param simpleMessages the messages to send
|
||||
* @throws org.springframework.mail.MailParseException
|
||||
* in case of failure when parsing a message
|
||||
* @throws org.springframework.mail.MailAuthenticationException
|
||||
* in case of authentication failure
|
||||
* @throws org.springframework.mail.MailSendException
|
||||
* in case of failure when sending a message
|
||||
*/
|
||||
void send(SimpleMailMessage[] simpleMessages) throws MailException;
|
||||
|
||||
}
|
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Models a simple mail message, including data such as the from, to, cc, subject, and text fields.
|
||||
*
|
||||
* <p>Consider <code>JavaMailSender</code> and JavaMail <code>MimeMessages</code> for creating
|
||||
* more sophisticated messages, for example messages with attachments, special
|
||||
* character encodings, or personal names that accompany mail addresses.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
* @since 10.09.2003
|
||||
* @see MailSender
|
||||
* @see org.springframework.mail.javamail.JavaMailSender
|
||||
* @see org.springframework.mail.javamail.MimeMessagePreparator
|
||||
* @see org.springframework.mail.javamail.MimeMessageHelper
|
||||
* @see org.springframework.mail.javamail.MimeMailMessage
|
||||
*/
|
||||
public class SimpleMailMessage implements MailMessage, Serializable {
|
||||
|
||||
private String from;
|
||||
|
||||
private String replyTo;
|
||||
|
||||
private String[] to;
|
||||
|
||||
private String[] cc;
|
||||
|
||||
private String[] bcc;
|
||||
|
||||
private Date sentDate;
|
||||
|
||||
private String subject;
|
||||
|
||||
private String text;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new <code>SimpleMailMessage</code>.
|
||||
*/
|
||||
public SimpleMailMessage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor for creating a new <code>SimpleMailMessage</code> from the state
|
||||
* of an existing <code>SimpleMailMessage</code> instance.
|
||||
* @throws IllegalArgumentException if the supplied message is <code>null</code>
|
||||
*/
|
||||
public SimpleMailMessage(SimpleMailMessage original) {
|
||||
Assert.notNull(original, "The 'original' message argument cannot be null");
|
||||
this.from = original.getFrom();
|
||||
this.replyTo = original.getReplyTo();
|
||||
if (original.getTo() != null) {
|
||||
this.to = copy(original.getTo());
|
||||
}
|
||||
if (original.getCc() != null) {
|
||||
this.cc = copy(original.getCc());
|
||||
}
|
||||
if (original.getBcc() != null) {
|
||||
this.bcc = copy(original.getBcc());
|
||||
}
|
||||
this.sentDate = original.getSentDate();
|
||||
this.subject = original.getSubject();
|
||||
this.text = original.getText();
|
||||
}
|
||||
|
||||
|
||||
public void setFrom(String from) {
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
public String getFrom() {
|
||||
return this.from;
|
||||
}
|
||||
|
||||
public void setReplyTo(String replyTo) {
|
||||
this.replyTo = replyTo;
|
||||
}
|
||||
|
||||
public String getReplyTo() {
|
||||
return replyTo;
|
||||
}
|
||||
|
||||
public void setTo(String to) {
|
||||
this.to = new String[] {to};
|
||||
}
|
||||
|
||||
public void setTo(String[] to) {
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
public String[] getTo() {
|
||||
return this.to;
|
||||
}
|
||||
|
||||
public void setCc(String cc) {
|
||||
this.cc = new String[] {cc};
|
||||
}
|
||||
|
||||
public void setCc(String[] cc) {
|
||||
this.cc = cc;
|
||||
}
|
||||
|
||||
public String[] getCc() {
|
||||
return cc;
|
||||
}
|
||||
|
||||
public void setBcc(String bcc) {
|
||||
this.bcc = new String[] {bcc};
|
||||
}
|
||||
|
||||
public void setBcc(String[] bcc) {
|
||||
this.bcc = bcc;
|
||||
}
|
||||
|
||||
public String[] getBcc() {
|
||||
return bcc;
|
||||
}
|
||||
|
||||
public void setSentDate(Date sentDate) {
|
||||
this.sentDate = sentDate;
|
||||
}
|
||||
|
||||
public Date getSentDate() {
|
||||
return sentDate;
|
||||
}
|
||||
|
||||
public void setSubject(String subject) {
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
return this.subject;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy the contents of this message to the given target message.
|
||||
* @param target the <code>MailMessage</code> to copy to
|
||||
* @throws IllegalArgumentException if the supplied <code>target</code> is <code>null</code>
|
||||
*/
|
||||
public void copyTo(MailMessage target) {
|
||||
Assert.notNull(target, "The 'target' message argument cannot be null");
|
||||
if (getFrom() != null) {
|
||||
target.setFrom(getFrom());
|
||||
}
|
||||
if (getReplyTo() != null) {
|
||||
target.setReplyTo(getReplyTo());
|
||||
}
|
||||
if (getTo() != null) {
|
||||
target.setTo(getTo());
|
||||
}
|
||||
if (getCc() != null) {
|
||||
target.setCc(getCc());
|
||||
}
|
||||
if (getBcc() != null) {
|
||||
target.setBcc(getBcc());
|
||||
}
|
||||
if (getSentDate() != null) {
|
||||
target.setSentDate(getSentDate());
|
||||
}
|
||||
if (getSubject() != null) {
|
||||
target.setSubject(getSubject());
|
||||
}
|
||||
if (getText() != null) {
|
||||
target.setText(getText());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer("SimpleMailMessage: ");
|
||||
sb.append("from=").append(this.from).append("; ");
|
||||
sb.append("replyTo=").append(this.replyTo).append("; ");
|
||||
sb.append("to=").append(StringUtils.arrayToCommaDelimitedString(this.to)).append("; ");
|
||||
sb.append("cc=").append(StringUtils.arrayToCommaDelimitedString(this.cc)).append("; ");
|
||||
sb.append("bcc=").append(StringUtils.arrayToCommaDelimitedString(this.bcc)).append("; ");
|
||||
sb.append("sentDate=").append(this.sentDate).append("; ");
|
||||
sb.append("subject=").append(this.subject).append("; ");
|
||||
sb.append("text=").append(this.text);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof SimpleMailMessage)) {
|
||||
return false;
|
||||
}
|
||||
SimpleMailMessage otherMessage = (SimpleMailMessage) other;
|
||||
return (ObjectUtils.nullSafeEquals(this.from, otherMessage.from) &&
|
||||
ObjectUtils.nullSafeEquals(this.replyTo, otherMessage.replyTo) &&
|
||||
java.util.Arrays.equals(this.to, otherMessage.to) &&
|
||||
java.util.Arrays.equals(this.cc, otherMessage.cc) &&
|
||||
java.util.Arrays.equals(this.bcc, otherMessage.bcc) &&
|
||||
ObjectUtils.nullSafeEquals(this.sentDate, otherMessage.sentDate) &&
|
||||
ObjectUtils.nullSafeEquals(this.subject, otherMessage.subject) &&
|
||||
ObjectUtils.nullSafeEquals(this.text, otherMessage.text));
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int hashCode = (this.from == null ? 0 : this.from.hashCode());
|
||||
hashCode = 29 * hashCode + (this.replyTo == null ? 0 : this.replyTo.hashCode());
|
||||
for (int i = 0; this.to != null && i < this.to.length; i++) {
|
||||
hashCode = 29 * hashCode + (this.to == null ? 0 : this.to[i].hashCode());
|
||||
}
|
||||
for (int i = 0; this.cc != null && i < this.cc.length; i++) {
|
||||
hashCode = 29 * hashCode + (this.cc == null ? 0 : this.cc[i].hashCode());
|
||||
}
|
||||
for (int i = 0; this.bcc != null && i < this.bcc.length; i++) {
|
||||
hashCode = 29 * hashCode + (this.bcc == null ? 0 : this.bcc[i].hashCode());
|
||||
}
|
||||
hashCode = 29 * hashCode + (this.sentDate == null ? 0 : this.sentDate.hashCode());
|
||||
hashCode = 29 * hashCode + (this.subject == null ? 0 : this.subject.hashCode());
|
||||
hashCode = 29 * hashCode + (this.text == null ? 0 : this.text.hashCode());
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
|
||||
private static String[] copy(String[] state) {
|
||||
String[] copy = new String[state.length];
|
||||
System.arraycopy(state, 0, copy, 0, state.length);
|
||||
return copy;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail.javamail;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.activation.MimetypesFileTypeMap;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Spring-configurable <code>FileTypeMap</code> implementation that will read
|
||||
* MIME type to file extension mappings from a standard JavaMail MIME type
|
||||
* mapping file, using a standard <code>MimetypesFileTypeMap</code> underneath.
|
||||
*
|
||||
* <p>The mapping file should be in the following format, as specified by the
|
||||
* Java Activation Framework:
|
||||
*
|
||||
* <pre>
|
||||
* # map text/html to .htm and .html files
|
||||
* text/html html htm HTML HTM</pre>
|
||||
*
|
||||
* Lines starting with <code>#</code> are treated as comments and are ignored. All
|
||||
* other lines are treated as mappings. Each mapping line should contain the MIME
|
||||
* type as the first entry and then each file extension to map to that MIME type
|
||||
* as subsequent entries. Each entry is separated by spaces or tabs.
|
||||
*
|
||||
* <p>By default, the mappings in the <code>mime.types</code> file located in the
|
||||
* same package as this class are used, which cover many common file extensions
|
||||
* (in contrast to the out-of-the-box mappings in <code>activation.jar</code>).
|
||||
* This can be overridden using the <code>mappingLocation</code> property.
|
||||
*
|
||||
* <p>Additional mappings can be added via the <code>mappings</code> bean property,
|
||||
* as lines that follow the <code>mime.types<code> file format.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2
|
||||
* @see #setMappingLocation
|
||||
* @see #setMappings
|
||||
* @see javax.activation.MimetypesFileTypeMap
|
||||
*/
|
||||
public class ConfigurableMimeFileTypeMap extends FileTypeMap implements InitializingBean {
|
||||
|
||||
/**
|
||||
* The <code>Resource</code> to load the mapping file from.
|
||||
*/
|
||||
private Resource mappingLocation = new ClassPathResource("mime.types", getClass());
|
||||
|
||||
/**
|
||||
* Used to configure additional mappings.
|
||||
*/
|
||||
private String[] mappings;
|
||||
|
||||
/**
|
||||
* The delegate FileTypeMap, compiled from the mappings in the mapping file
|
||||
* and the entries in the <code>mappings</code> property.
|
||||
*/
|
||||
private FileTypeMap fileTypeMap;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the <code>Resource</code> from which mappings are loaded.
|
||||
* <p>Needs to follow the <code>mime.types<code> file format, as specified
|
||||
* by the Java Activation Framework, containing lines such as:<br>
|
||||
* <code>text/html html htm HTML HTM</code>
|
||||
*/
|
||||
public void setMappingLocation(Resource mappingLocation) {
|
||||
this.mappingLocation = mappingLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify additional MIME type mappings as lines that follow the
|
||||
* <code>mime.types<code> file format, as specified by the
|
||||
* Java Activation Framework, for example:<br>
|
||||
* <code>text/html html htm HTML HTM</code>
|
||||
*/
|
||||
public void setMappings(String[] mappings) {
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the final merged mapping set.
|
||||
*/
|
||||
public void afterPropertiesSet() {
|
||||
getFileTypeMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the delegate FileTypeMap, compiled from the mappings in the mapping file
|
||||
* and the entries in the <code>mappings</code> property.
|
||||
* @see #setMappingLocation
|
||||
* @see #setMappings
|
||||
* @see #createFileTypeMap
|
||||
*/
|
||||
protected final FileTypeMap getFileTypeMap() {
|
||||
if (this.fileTypeMap == null) {
|
||||
try {
|
||||
this.fileTypeMap = createFileTypeMap(this.mappingLocation, this.mappings);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
IllegalStateException ise = new IllegalStateException(
|
||||
"Could not load specified MIME type mapping file: " + this.mappingLocation);
|
||||
ise.initCause(ex);
|
||||
throw ise;
|
||||
}
|
||||
}
|
||||
return this.fileTypeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a {@link FileTypeMap} from the mappings in the given mapping file
|
||||
* and the given mapping entries.
|
||||
* <p>The default implementation creates an Activation Framework {@link MimetypesFileTypeMap},
|
||||
* passing in an InputStream from the mapping resource (if any) and registering
|
||||
* the mapping lines programmatically.
|
||||
* @param mappingLocation a <code>mime.types</code> mapping resource (can be <code>null</code>)
|
||||
* @param mappings MIME type mapping lines (can be <code>null</code>)
|
||||
* @return the compiled FileTypeMap
|
||||
* @throws IOException if resource access failed
|
||||
* @see javax.activation.MimetypesFileTypeMap#MimetypesFileTypeMap(java.io.InputStream)
|
||||
* @see javax.activation.MimetypesFileTypeMap#addMimeTypes(String)
|
||||
*/
|
||||
protected FileTypeMap createFileTypeMap(Resource mappingLocation, String[] mappings) throws IOException {
|
||||
MimetypesFileTypeMap fileTypeMap = null;
|
||||
if (mappingLocation != null) {
|
||||
InputStream is = mappingLocation.getInputStream();
|
||||
try {
|
||||
fileTypeMap = new MimetypesFileTypeMap(is);
|
||||
}
|
||||
finally {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
else {
|
||||
fileTypeMap = new MimetypesFileTypeMap();
|
||||
}
|
||||
if (mappings != null) {
|
||||
for (int i = 0; i < mappings.length; i++) {
|
||||
fileTypeMap.addMimeTypes(mappings[i]);
|
||||
}
|
||||
}
|
||||
return fileTypeMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegates to the underlying FileTypeMap.
|
||||
* @see #getFileTypeMap()
|
||||
*/
|
||||
public String getContentType(File file) {
|
||||
return getFileTypeMap().getContentType(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to the underlying FileTypeMap.
|
||||
* @see #getFileTypeMap()
|
||||
*/
|
||||
public String getContentType(String fileName) {
|
||||
return getFileTypeMap().getContentType(fileName);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail.javamail;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Editor for <code>java.mail.internet.InternetAddress</code>,
|
||||
* to directly populate an InternetAddress property.
|
||||
*
|
||||
* <p>Expects the same syntax as InternetAddress's constructor with
|
||||
* a String argument. Converts empty Strings into null values.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2.3
|
||||
* @see javax.mail.internet.InternetAddress
|
||||
*/
|
||||
public class InternetAddressEditor extends PropertyEditorSupport {
|
||||
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
if (StringUtils.hasText(text)) {
|
||||
try {
|
||||
setValue(new InternetAddress(text));
|
||||
}
|
||||
catch (AddressException ex) {
|
||||
throw new IllegalArgumentException("Could not parse mail address: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
else {
|
||||
setValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
public String getAsText() {
|
||||
InternetAddress value = (InternetAddress) getValue();
|
||||
return (value != null ? value.toUnicodeString() : "");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail.javamail;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.mail.MailException;
|
||||
import org.springframework.mail.MailSender;
|
||||
|
||||
/**
|
||||
* Extended {@link org.springframework.mail.MailSender} interface for JavaMail,
|
||||
* supporting MIME messages both as direct arguments and through preparation
|
||||
* callbacks. Typically used in conjunction with the {@link MimeMessageHelper}
|
||||
* class for convenient creation of JavaMail {@link MimeMessage MimeMessages},
|
||||
* including attachments etc.
|
||||
*
|
||||
* <p>Clients should talk to the mail sender through this interface if they need
|
||||
* mail functionality beyond {@link org.springframework.mail.SimpleMailMessage}.
|
||||
* The production implementation is {@link JavaMailSenderImpl}; for testing,
|
||||
* mocks can be created based on this interface. Clients will typically receive
|
||||
* the JavaMailSender reference through dependency injection.
|
||||
*
|
||||
* <p>The recommended way of using this interface is the {@link MimeMessagePreparator}
|
||||
* mechanism, possibly using a {@link MimeMessageHelper} for populating the message.
|
||||
* See {@link MimeMessageHelper MimeMessageHelper's javadoc} for an example.
|
||||
*
|
||||
* <p>The entire JavaMail {@link javax.mail.Session} management is abstracted
|
||||
* by the JavaMailSender. Client code should not deal with a Session in any way,
|
||||
* rather leave the entire JavaMail configuration and resource handling to the
|
||||
* JavaMailSender implementation. This also increases testability.
|
||||
*
|
||||
* <p>A JavaMailSender client is not as easy to test as a plain
|
||||
* {@link org.springframework.mail.MailSender} client, but still straightforward
|
||||
* compared to traditional JavaMail code: Just let {@link #createMimeMessage()}
|
||||
* return a plain {@link MimeMessage} created with a
|
||||
* <code>Session.getInstance(new Properties())</code> call, and check the passed-in
|
||||
* messages in your mock implementations of the various <code>send</code> methods.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 07.10.2003
|
||||
* @see javax.mail.internet.MimeMessage
|
||||
* @see javax.mail.Session
|
||||
* @see JavaMailSenderImpl
|
||||
* @see MimeMessagePreparator
|
||||
* @see MimeMessageHelper
|
||||
*/
|
||||
public interface JavaMailSender extends MailSender {
|
||||
|
||||
/**
|
||||
* Create a new JavaMail MimeMessage for the underlying JavaMail Session
|
||||
* of this sender. Needs to be called to create MimeMessage instances
|
||||
* that can be prepared by the client and passed to send(MimeMessage).
|
||||
* @return the new MimeMessage instance
|
||||
* @see #send(MimeMessage)
|
||||
* @see #send(MimeMessage[])
|
||||
*/
|
||||
MimeMessage createMimeMessage();
|
||||
|
||||
/**
|
||||
* Create a new JavaMail MimeMessage for the underlying JavaMail Session
|
||||
* of this sender, using the given input stream as the message source.
|
||||
* @param contentStream the raw MIME input stream for the message
|
||||
* @return the new MimeMessage instance
|
||||
* @throws org.springframework.mail.MailParseException
|
||||
* in case of message creation failure
|
||||
*/
|
||||
MimeMessage createMimeMessage(InputStream contentStream) throws MailException;
|
||||
|
||||
/**
|
||||
* Send the given JavaMail MIME message.
|
||||
* The message needs to have been created with {@link #createMimeMessage()}.
|
||||
* @param mimeMessage message to send
|
||||
* @throws org.springframework.mail.MailAuthenticationException
|
||||
* in case of authentication failure
|
||||
* @throws org.springframework.mail.MailSendException
|
||||
* in case of failure when sending the message
|
||||
* @see #createMimeMessage
|
||||
*/
|
||||
void send(MimeMessage mimeMessage) throws MailException;
|
||||
|
||||
/**
|
||||
* Send the given array of JavaMail MIME messages in batch.
|
||||
* The messages need to have been created with {@link #createMimeMessage()}.
|
||||
* @param mimeMessages messages to send
|
||||
* @throws org.springframework.mail.MailAuthenticationException
|
||||
* in case of authentication failure
|
||||
* @throws org.springframework.mail.MailSendException
|
||||
* in case of failure when sending a message
|
||||
* @see #createMimeMessage
|
||||
*/
|
||||
void send(MimeMessage[] mimeMessages) throws MailException;
|
||||
|
||||
/**
|
||||
* Send the JavaMail MIME message prepared by the given MimeMessagePreparator.
|
||||
* <p>Alternative way to prepare MimeMessage instances, instead of
|
||||
* {@link #createMimeMessage()} and {@link #send(MimeMessage)} calls.
|
||||
* Takes care of proper exception conversion.
|
||||
* @param mimeMessagePreparator the preparator to use
|
||||
* @throws org.springframework.mail.MailPreparationException
|
||||
* in case of failure when preparing the message
|
||||
* @throws org.springframework.mail.MailParseException
|
||||
* in case of failure when parsing the message
|
||||
* @throws org.springframework.mail.MailAuthenticationException
|
||||
* in case of authentication failure
|
||||
* @throws org.springframework.mail.MailSendException
|
||||
* in case of failure when sending the message
|
||||
*/
|
||||
void send(MimeMessagePreparator mimeMessagePreparator) throws MailException;
|
||||
|
||||
/**
|
||||
* Send the JavaMail MIME messages prepared by the given MimeMessagePreparators.
|
||||
* <p>Alternative way to prepare MimeMessage instances, instead of
|
||||
* {@link #createMimeMessage()} and {@link #send(MimeMessage[])} calls.
|
||||
* Takes care of proper exception conversion.
|
||||
* @param mimeMessagePreparators the preparator to use
|
||||
* @throws org.springframework.mail.MailPreparationException
|
||||
* in case of failure when preparing a message
|
||||
* @throws org.springframework.mail.MailParseException
|
||||
* in case of failure when parsing a message
|
||||
* @throws org.springframework.mail.MailAuthenticationException
|
||||
* in case of authentication failure
|
||||
* @throws org.springframework.mail.MailSendException
|
||||
* in case of failure when sending a message
|
||||
*/
|
||||
void send(MimeMessagePreparator[] mimeMessagePreparators) throws MailException;
|
||||
|
||||
}
|
|
@ -0,0 +1,437 @@
|
|||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail.javamail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.mail.AuthenticationFailedException;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.NoSuchProviderException;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.Transport;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.mail.MailAuthenticationException;
|
||||
import org.springframework.mail.MailException;
|
||||
import org.springframework.mail.MailParseException;
|
||||
import org.springframework.mail.MailPreparationException;
|
||||
import org.springframework.mail.MailSendException;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Production implementation of the {@link JavaMailSender} interface,
|
||||
* supporting both JavaMail {@link MimeMessage MimeMessages} and Spring
|
||||
* {@link SimpleMailMessage SimpleMailMessages}. Can also be used as a
|
||||
* plain {@link org.springframework.mail.MailSender} implementation.
|
||||
*
|
||||
* <p>Allows for defining all settings locally as bean properties.
|
||||
* Alternatively, a pre-configured JavaMail {@link javax.mail.Session} can be
|
||||
* specified, possibly pulled from an application server's JNDI environment.
|
||||
*
|
||||
* <p>Non-default properties in this object will always override the settings
|
||||
* in the JavaMail <code>Session</code>. Note that if overriding all values locally,
|
||||
* there is no added value in setting a pre-configured <code>Session</code>.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
* @since 10.09.2003
|
||||
* @see javax.mail.internet.MimeMessage
|
||||
* @see javax.mail.Session
|
||||
* @see #setSession
|
||||
* @see #setJavaMailProperties
|
||||
* @see #setHost
|
||||
* @see #setPort
|
||||
* @see #setUsername
|
||||
* @see #setPassword
|
||||
*/
|
||||
public class JavaMailSenderImpl implements JavaMailSender {
|
||||
|
||||
/** The default protocol: 'smtp' */
|
||||
public static final String DEFAULT_PROTOCOL = "smtp";
|
||||
|
||||
/** The default port: -1 */
|
||||
public static final int DEFAULT_PORT = -1;
|
||||
|
||||
private static final String HEADER_MESSAGE_ID = "Message-ID";
|
||||
|
||||
|
||||
private Properties javaMailProperties = new Properties();
|
||||
|
||||
private Session session;
|
||||
|
||||
private String protocol = DEFAULT_PROTOCOL;
|
||||
|
||||
private String host;
|
||||
|
||||
private int port = DEFAULT_PORT;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private String defaultEncoding;
|
||||
|
||||
private FileTypeMap defaultFileTypeMap;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance of the <code>JavaMailSenderImpl</code> class.
|
||||
* <p>Initializes the {@link #setDefaultFileTypeMap "defaultFileTypeMap"}
|
||||
* property with a default {@link ConfigurableMimeFileTypeMap}.
|
||||
*/
|
||||
public JavaMailSenderImpl() {
|
||||
ConfigurableMimeFileTypeMap fileTypeMap = new ConfigurableMimeFileTypeMap();
|
||||
fileTypeMap.afterPropertiesSet();
|
||||
this.defaultFileTypeMap = fileTypeMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set JavaMail properties for the <code>Session</code>.
|
||||
* <p>A new <code>Session</code> will be created with those properties.
|
||||
* Use either this method or {@link #setSession}, but not both.
|
||||
* <p>Non-default properties in this instance will override given
|
||||
* JavaMail properties.
|
||||
*/
|
||||
public void setJavaMailProperties(Properties javaMailProperties) {
|
||||
this.javaMailProperties = javaMailProperties;
|
||||
synchronized (this) {
|
||||
this.session = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow Map access to the JavaMail properties of this sender,
|
||||
* with the option to add or override specific entries.
|
||||
* <p>Useful for specifying entries directly, for example via
|
||||
* "javaMailProperties[mail.smtp.auth]".
|
||||
*/
|
||||
public Properties getJavaMailProperties() {
|
||||
return this.javaMailProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JavaMail <code>Session</code>, possibly pulled from JNDI.
|
||||
* <p>Default is a new <code>Session</code> without defaults, that is
|
||||
* completely configured via this instance's properties.
|
||||
* <p>If using a pre-configured <code>Session</code>, non-default properties
|
||||
* in this instance will override the settings in the <code>Session</code>.
|
||||
* @see #setJavaMailProperties
|
||||
*/
|
||||
public synchronized void setSession(Session session) {
|
||||
Assert.notNull(session, "Session must not be null");
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JavaMail <code>Session</code>,
|
||||
* lazily initializing it if hasn't been specified explicitly.
|
||||
*/
|
||||
public synchronized Session getSession() {
|
||||
if (this.session == null) {
|
||||
this.session = Session.getInstance(this.javaMailProperties);
|
||||
}
|
||||
return this.session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mail protocol. Default is "smtp".
|
||||
*/
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mail protocol.
|
||||
*/
|
||||
public String getProtocol() {
|
||||
return this.protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mail server host, typically an SMTP host.
|
||||
* <p>Default is the default host of the underlying JavaMail Session.
|
||||
*/
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mail server host.
|
||||
*/
|
||||
public String getHost() {
|
||||
return this.host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mail server port.
|
||||
* <p>Default is {@link #DEFAULT_PORT}, letting JavaMail use the default
|
||||
* SMTP port (25).
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mail server port.
|
||||
*/
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the username for the account at the mail host, if any.
|
||||
* <p>Note that the underlying JavaMail <code>Session</code> has to be
|
||||
* configured with the property <code>"mail.smtp.auth"</code> set to
|
||||
* <code>true</code>, else the specified username will not be sent to the
|
||||
* mail server by the JavaMail runtime. If you are not explicitly passing
|
||||
* in a <code>Session</code> to use, simply specify this setting via
|
||||
* {@link #setJavaMailProperties}.
|
||||
* @see #setSession
|
||||
* @see #setPassword
|
||||
*/
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the username for the account at the mail host.
|
||||
*/
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the password for the account at the mail host, if any.
|
||||
* <p>Note that the underlying JavaMail <code>Session</code> has to be
|
||||
* configured with the property <code>"mail.smtp.auth"</code> set to
|
||||
* <code>true</code>, else the specified password will not be sent to the
|
||||
* mail server by the JavaMail runtime. If you are not explicitly passing
|
||||
* in a <code>Session</code> to use, simply specify this setting via
|
||||
* {@link #setJavaMailProperties}.
|
||||
* @see #setSession
|
||||
* @see #setUsername
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the password for the account at the mail host.
|
||||
*/
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default encoding to use for {@link MimeMessage MimeMessages}
|
||||
* created by this instance.
|
||||
* <p>Such an encoding will be auto-detected by {@link MimeMessageHelper}.
|
||||
*/
|
||||
public void setDefaultEncoding(String defaultEncoding) {
|
||||
this.defaultEncoding = defaultEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default encoding for {@link MimeMessage MimeMessages},
|
||||
* or <code>null</code> if none.
|
||||
*/
|
||||
public String getDefaultEncoding() {
|
||||
return this.defaultEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default Java Activation {@link FileTypeMap} to use for
|
||||
* {@link MimeMessage MimeMessages} created by this instance.
|
||||
* <p>A <code>FileTypeMap</code> specified here will be autodetected by
|
||||
* {@link MimeMessageHelper}, avoiding the need to specify the
|
||||
* <code>FileTypeMap</code> for each <code>MimeMessageHelper</code> instance.
|
||||
* <p>For example, you can specify a custom instance of Spring's
|
||||
* {@link ConfigurableMimeFileTypeMap} here. If not explicitly specified,
|
||||
* a default <code>ConfigurableMimeFileTypeMap</code> will be used, containing
|
||||
* an extended set of MIME type mappings (as defined by the
|
||||
* <code>mime.types</code> file contained in the Spring jar).
|
||||
* @see MimeMessageHelper#setFileTypeMap
|
||||
*/
|
||||
public void setDefaultFileTypeMap(FileTypeMap defaultFileTypeMap) {
|
||||
this.defaultFileTypeMap = defaultFileTypeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default Java Activation {@link FileTypeMap} for
|
||||
* {@link MimeMessage MimeMessages}, or <code>null</code> if none.
|
||||
*/
|
||||
public FileTypeMap getDefaultFileTypeMap() {
|
||||
return this.defaultFileTypeMap;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of MailSender
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void send(SimpleMailMessage simpleMessage) throws MailException {
|
||||
send(new SimpleMailMessage[] { simpleMessage });
|
||||
}
|
||||
|
||||
public void send(SimpleMailMessage[] simpleMessages) throws MailException {
|
||||
List mimeMessages = new ArrayList(simpleMessages.length);
|
||||
for (int i = 0; i < simpleMessages.length; i++) {
|
||||
SimpleMailMessage simpleMessage = simpleMessages[i];
|
||||
MimeMailMessage message = new MimeMailMessage(createMimeMessage());
|
||||
simpleMessage.copyTo(message);
|
||||
mimeMessages.add(message.getMimeMessage());
|
||||
}
|
||||
doSend((MimeMessage[]) mimeMessages.toArray(new MimeMessage[mimeMessages.size()]), simpleMessages);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of JavaMailSender
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* This implementation creates a SmartMimeMessage, holding the specified
|
||||
* default encoding and default FileTypeMap. This special defaults-carrying
|
||||
* message will be autodetected by {@link MimeMessageHelper}, which will use
|
||||
* the carried encoding and FileTypeMap unless explicitly overridden.
|
||||
* @see #setDefaultEncoding
|
||||
* @see #setDefaultFileTypeMap
|
||||
*/
|
||||
public MimeMessage createMimeMessage() {
|
||||
return new SmartMimeMessage(getSession(), getDefaultEncoding(), getDefaultFileTypeMap());
|
||||
}
|
||||
|
||||
public MimeMessage createMimeMessage(InputStream contentStream) throws MailException {
|
||||
try {
|
||||
return new MimeMessage(getSession(), contentStream);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException("Could not parse raw MIME content", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void send(MimeMessage mimeMessage) throws MailException {
|
||||
send(new MimeMessage[] { mimeMessage });
|
||||
}
|
||||
|
||||
public void send(MimeMessage[] mimeMessages) throws MailException {
|
||||
doSend(mimeMessages, null);
|
||||
}
|
||||
|
||||
public void send(MimeMessagePreparator mimeMessagePreparator) throws MailException {
|
||||
send(new MimeMessagePreparator[] { mimeMessagePreparator });
|
||||
}
|
||||
|
||||
public void send(MimeMessagePreparator[] mimeMessagePreparators) throws MailException {
|
||||
try {
|
||||
List mimeMessages = new ArrayList(mimeMessagePreparators.length);
|
||||
for (int i = 0; i < mimeMessagePreparators.length; i++) {
|
||||
MimeMessage mimeMessage = createMimeMessage();
|
||||
mimeMessagePreparators[i].prepare(mimeMessage);
|
||||
mimeMessages.add(mimeMessage);
|
||||
}
|
||||
send((MimeMessage[]) mimeMessages.toArray(new MimeMessage[mimeMessages.size()]));
|
||||
}
|
||||
catch (MailException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new MailPreparationException(ex);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new MailPreparationException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Actually send the given array of MimeMessages via JavaMail.
|
||||
* @param mimeMessages MimeMessage objects to send
|
||||
* @param originalMessages corresponding original message objects
|
||||
* that the MimeMessages have been created from (with same array
|
||||
* length and indices as the "mimeMessages" array), if any
|
||||
* @throws org.springframework.mail.MailAuthenticationException
|
||||
* in case of authentication failure
|
||||
* @throws org.springframework.mail.MailSendException
|
||||
* in case of failure when sending a message
|
||||
*/
|
||||
protected void doSend(MimeMessage[] mimeMessages, Object[] originalMessages) throws MailException {
|
||||
Map failedMessages = new LinkedHashMap();
|
||||
try {
|
||||
Transport transport = getTransport(getSession());
|
||||
transport.connect(getHost(), getPort(), getUsername(), getPassword());
|
||||
try {
|
||||
for (int i = 0; i < mimeMessages.length; i++) {
|
||||
MimeMessage mimeMessage = mimeMessages[i];
|
||||
try {
|
||||
if (mimeMessage.getSentDate() == null) {
|
||||
mimeMessage.setSentDate(new Date());
|
||||
}
|
||||
String messageId = mimeMessage.getMessageID();
|
||||
mimeMessage.saveChanges();
|
||||
if (messageId != null) {
|
||||
// Preserve explicitly specified message id...
|
||||
mimeMessage.setHeader(HEADER_MESSAGE_ID, messageId);
|
||||
}
|
||||
transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
Object original = (originalMessages != null ? originalMessages[i] : mimeMessage);
|
||||
failedMessages.put(original, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
transport.close();
|
||||
}
|
||||
}
|
||||
catch (AuthenticationFailedException ex) {
|
||||
throw new MailAuthenticationException(ex);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailSendException("Mail server connection failed", ex);
|
||||
}
|
||||
if (!failedMessages.isEmpty()) {
|
||||
throw new MailSendException(failedMessages);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a Transport object from the given JavaMail Session,
|
||||
* using the configured protocol.
|
||||
* <p>Can be overridden in subclasses, e.g. to return a mock Transport object.
|
||||
* @see javax.mail.Session#getTransport(String)
|
||||
* @see #getProtocol()
|
||||
*/
|
||||
protected Transport getTransport(Session session) throws NoSuchProviderException {
|
||||
return session.getTransport(getProtocol());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail.javamail;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.mail.MailMessage;
|
||||
import org.springframework.mail.MailParseException;
|
||||
|
||||
/**
|
||||
* Implementation of the MailMessage interface for a JavaMail MIME message,
|
||||
* to let message population code interact with a simple message or a MIME
|
||||
* message through a common interface.
|
||||
*
|
||||
* <p>Uses a MimeMessageHelper underneath. Can either be created with a
|
||||
* MimeMessageHelper instance or with a JavaMail MimeMessage instance.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.5
|
||||
* @see MimeMessageHelper
|
||||
* @see javax.mail.internet.MimeMessage
|
||||
*/
|
||||
public class MimeMailMessage implements MailMessage {
|
||||
|
||||
private final MimeMessageHelper helper;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MimeMailMessage based on the given MimeMessageHelper.
|
||||
* @param mimeMessageHelper the MimeMessageHelper
|
||||
*/
|
||||
public MimeMailMessage(MimeMessageHelper mimeMessageHelper) {
|
||||
this.helper = mimeMessageHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MimeMailMessage based on the given JavaMail MimeMessage.
|
||||
* @param mimeMessage the JavaMail MimeMessage
|
||||
*/
|
||||
public MimeMailMessage(MimeMessage mimeMessage) {
|
||||
this.helper = new MimeMessageHelper(mimeMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the MimeMessageHelper that this MimeMailMessage is based on.
|
||||
*/
|
||||
public final MimeMessageHelper getMimeMessageHelper() {
|
||||
return this.helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JavaMail MimeMessage that this MimeMailMessage is based on.
|
||||
*/
|
||||
public final MimeMessage getMimeMessage() {
|
||||
return this.helper.getMimeMessage();
|
||||
}
|
||||
|
||||
|
||||
public void setFrom(String from) throws MailParseException {
|
||||
try {
|
||||
this.helper.setFrom(from);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setReplyTo(String replyTo) throws MailParseException {
|
||||
try {
|
||||
this.helper.setReplyTo(replyTo);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTo(String to) throws MailParseException {
|
||||
try {
|
||||
this.helper.setTo(to);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTo(String[] to) throws MailParseException {
|
||||
try {
|
||||
this.helper.setTo(to);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCc(String cc) throws MailParseException {
|
||||
try {
|
||||
this.helper.setCc(cc);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCc(String[] cc) throws MailParseException {
|
||||
try {
|
||||
this.helper.setCc(cc);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBcc(String bcc) throws MailParseException {
|
||||
try {
|
||||
this.helper.setBcc(bcc);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBcc(String[] bcc) throws MailParseException {
|
||||
try {
|
||||
this.helper.setBcc(bcc);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSentDate(Date sentDate) throws MailParseException {
|
||||
try {
|
||||
this.helper.setSentDate(sentDate);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSubject(String subject) throws MailParseException {
|
||||
try {
|
||||
this.helper.setSubject(subject);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setText(String text) throws MailParseException {
|
||||
try {
|
||||
this.helper.setText(text);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail.javamail;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
/**
|
||||
* Callback interface for the preparation of JavaMail MIME messages.
|
||||
*
|
||||
* <p>The corresponding <code>send</code> methods of {@link JavaMailSender}
|
||||
* will take care of the actual creation of a {@link MimeMessage} instance,
|
||||
* and of proper exception conversion.
|
||||
*
|
||||
* <p>It is often convenient to use a {@link MimeMessageHelper} for populating
|
||||
* the passed-in MimeMessage, in particular when working with attachments or
|
||||
* special character encodings.
|
||||
* See {@link MimeMessageHelper MimeMessageHelper's javadoc} for an example.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 07.10.2003
|
||||
* @see JavaMailSender#send(MimeMessagePreparator)
|
||||
* @see JavaMailSender#send(MimeMessagePreparator[])
|
||||
* @see MimeMessageHelper
|
||||
*/
|
||||
public interface MimeMessagePreparator {
|
||||
|
||||
/**
|
||||
* Prepare the given new MimeMessage instance.
|
||||
* @param mimeMessage the message to prepare
|
||||
* @throws javax.mail.MessagingException passing any exceptions thrown by MimeMessage
|
||||
* methods through for automatic conversion to the MailException hierarchy
|
||||
* @throws java.io.IOException passing any exceptions thrown by MimeMessage methods
|
||||
* through for automatic conversion to the MailException hierarchy
|
||||
* @throws Exception if mail preparation failed, for example when a
|
||||
* Velocity template cannot be rendered for the mail text
|
||||
*/
|
||||
void prepare(MimeMessage mimeMessage) throws Exception;
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.mail.javamail;
|
||||
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
/**
|
||||
* Special subclass of the standard JavaMail {@link MimeMessage}, carrying a
|
||||
* default encoding to be used when populating the message and a default Java
|
||||
* Activation {@link FileTypeMap} to be used for resolving attachment types.
|
||||
*
|
||||
* <p>Created by {@link JavaMailSenderImpl} in case of a specified default encoding
|
||||
* and/or default FileTypeMap. Autodetected by {@link MimeMessageHelper}, which
|
||||
* will use the carried encoding and FileTypeMap unless explicitly overridden.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2
|
||||
* @see JavaMailSenderImpl#createMimeMessage()
|
||||
* @see MimeMessageHelper#getDefaultEncoding(javax.mail.internet.MimeMessage)
|
||||
* @see MimeMessageHelper#getDefaultFileTypeMap(javax.mail.internet.MimeMessage)
|
||||
*/
|
||||
class SmartMimeMessage extends MimeMessage {
|
||||
|
||||
private final String defaultEncoding;
|
||||
|
||||
private final FileTypeMap defaultFileTypeMap;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SmartMimeMessage.
|
||||
* @param session the JavaMail Session to create the message for
|
||||
* @param defaultEncoding the default encoding, or <code>null</code> if none
|
||||
* @param defaultFileTypeMap the default FileTypeMap, or <code>null</code> if none
|
||||
*/
|
||||
public SmartMimeMessage(Session session, String defaultEncoding, FileTypeMap defaultFileTypeMap) {
|
||||
super(session);
|
||||
this.defaultEncoding = defaultEncoding;
|
||||
this.defaultFileTypeMap = defaultFileTypeMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the default encoding of this message, or <code>null</code> if none.
|
||||
*/
|
||||
public final String getDefaultEncoding() {
|
||||
return this.defaultEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default FileTypeMap of this message, or <code>null</code> if none.
|
||||
*/
|
||||
public final FileTypeMap getDefaultFileTypeMap() {
|
||||
return this.defaultFileTypeMap;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
################################################################################
|
||||
#
|
||||
# Defaults for the Java Activation Framework
|
||||
# Additional extensions registered in this file:
|
||||
# text/plain java c c++ pl cc h
|
||||
#
|
||||
################################################################################
|
||||
|
||||
text/html html htm HTML HTM
|
||||
text/plain txt text TXT TEXT java c c++ pl cc h
|
||||
image/gif gif GIF
|
||||
image/ief ief
|
||||
image/jpeg jpeg jpg jpe JPG
|
||||
image/tiff tiff tif
|
||||
image/x-xwindowdump xwd
|
||||
application/postscript ai eps ps
|
||||
application/rtf rtf
|
||||
application/x-tex tex
|
||||
application/x-texinfo texinfo texi
|
||||
application/x-troff t tr roff
|
||||
audio/basic au
|
||||
audio/midi midi mid
|
||||
audio/x-aifc aifc
|
||||
audio/x-aiff aif aiff
|
||||
audio/x-mpeg mpeg mpg
|
||||
audio/x-wav wav
|
||||
video/mpeg mpeg mpg mpe
|
||||
video/quicktime qt mov
|
||||
video/x-msvideo avi
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# Additional file types adapted from
|
||||
# http://www.utoronto.ca/webdocs/HTMLdocs/Book/Book-3ed/appb/mimetype.html
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# TEXT TYPES
|
||||
|
||||
text/x-speech talk
|
||||
text/css css
|
||||
text/csv csv
|
||||
|
||||
# IMAGE TYPES
|
||||
|
||||
# X-Windows bitmap (b/w)
|
||||
image/x-xbitmap xbm
|
||||
# X-Windows pixelmap (8-bit color)
|
||||
image/x-xpixmap xpm
|
||||
# Portable Network Graphics
|
||||
image/x-png png
|
||||
# Image Exchange Format (RFC 1314)
|
||||
image/ief ief
|
||||
# JPEG
|
||||
image/jpeg jpeg jpg jpe
|
||||
# RGB
|
||||
image/rgb rgb
|
||||
# Group III Fax (RFC 1494)
|
||||
image/g3fax g3f
|
||||
# X Windowdump format
|
||||
image/x-xwindowdump xwd
|
||||
# Macintosh PICT format
|
||||
image/x-pict pict
|
||||
# PPM (UNIX PPM package)
|
||||
image/x-portable-pixmap ppm
|
||||
# PGM (UNIX PPM package)
|
||||
image/x-portable-graymap pgm
|
||||
# PBM (UNIX PPM package)
|
||||
image/x-portable-bitmap pbm
|
||||
# PNM (UNIX PPM package)
|
||||
image/x-portable-anymap pnm
|
||||
# Microsoft Windows bitmap
|
||||
image/x-ms-bmp bmp
|
||||
# CMU raster
|
||||
image/x-cmu-raster ras
|
||||
# Kodak Photo-CD
|
||||
image/x-photo-cd pcd
|
||||
# Computer Graphics Metafile
|
||||
image/cgm cgm
|
||||
# CALS Type 1 or 2
|
||||
image/x-cals mil cal
|
||||
# Fractal Image Format (Iterated Systems)
|
||||
image/fif fif
|
||||
# QuickSilver active image (Micrografx)
|
||||
image/x-mgx-dsf dsf
|
||||
# CMX vector image (Corel)
|
||||
image/x-cmx cmx
|
||||
# Wavelet-compressed (Summus)
|
||||
image/wavelet wi
|
||||
# AutoCad Drawing (SoftSource)
|
||||
image/vnd.dwg dwg
|
||||
# AutoCad DXF file (SoftSource)
|
||||
image/vnd.dxf dxf
|
||||
# Simple Vector Format (SoftSource)
|
||||
image/vnd.svf svf
|
||||
|
||||
# AUDIO/VOICE/MUSIC RELATED TYPES
|
||||
|
||||
# """basic""audio - 8-bit u-law PCM"
|
||||
audio/basic au snd
|
||||
# Macintosh audio format (AIpple)
|
||||
audio/x-aiff aif aiff aifc
|
||||
# Microsoft audio
|
||||
audio/x-wav wav
|
||||
# MPEG audio
|
||||
audio/x-mpeg mpa abs mpega
|
||||
# MPEG-2 audio
|
||||
audio/x-mpeg-2 mp2a mpa2
|
||||
# compressed speech (Echo Speech Corp.)
|
||||
audio/echospeech es
|
||||
# Toolvox speech audio (Voxware)
|
||||
audio/voxware vox
|
||||
# RapidTransit compressed audio (Fast Man)
|
||||
application/fastman lcc
|
||||
# Realaudio (Progressive Networks)
|
||||
application/x-pn-realaudio ra ram
|
||||
# MIDI music data
|
||||
x-music/x-midi mmid
|
||||
# Koan music data (SSeyo)
|
||||
application/vnd.koan skp
|
||||
# Speech synthesis data (MVP Solutions)
|
||||
text/x-speech talk
|
||||
|
||||
# VIDEO TYPES
|
||||
|
||||
# MPEG video
|
||||
video/mpeg mpeg mpg mpe
|
||||
# MPEG-2 video
|
||||
video/mpeg-2 mpv2 mp2v
|
||||
# Macintosh Quicktime
|
||||
video/quicktime qt mov
|
||||
# Microsoft video
|
||||
video/x-msvideo avi
|
||||
# SGI Movie format
|
||||
video/x-sgi-movie movie
|
||||
# VDOlive streaming video (VDOnet)
|
||||
video/vdo vdo
|
||||
# Vivo streaming video (Vivo software)
|
||||
video/vnd.vivo viv
|
||||
|
||||
# SPECIAL HTTP/WEB APPLICATION TYPES
|
||||
|
||||
# Proxy autoconfiguration (Netscape browsers)
|
||||
application/x-ns-proxy-autoconfig pac
|
||||
# Netscape Cooltalk chat data (Netscape)
|
||||
x-conference/x-cooltalk ice
|
||||
|
||||
# TEXT-RELATED
|
||||
|
||||
# PostScript
|
||||
application/postscript ai eps ps
|
||||
# Microsoft Rich Text Format
|
||||
application/rtf rtf
|
||||
# Adobe Acrobat PDF
|
||||
application/pdf pdf
|
||||
# Maker Interchange Format (FrameMaker)
|
||||
application/vnd.mif mif
|
||||
# Troff document
|
||||
application/x-troff t tr roff
|
||||
# Troff document with MAN macros
|
||||
application/x-troff-man man
|
||||
# Troff document with ME macros
|
||||
application/x-troff-me me
|
||||
# Troff document with MS macros
|
||||
application/x-troff-ms ms
|
||||
# LaTeX document
|
||||
application/x-latex latex
|
||||
# Tex/LateX document
|
||||
application/x-tex tex
|
||||
# GNU TexInfo document
|
||||
application/x-texinfo texinfo texi
|
||||
# TeX dvi format
|
||||
application/x-dvi dvi
|
||||
# MS word document
|
||||
application/msword doc DOC
|
||||
# Office Document Architecture
|
||||
application/oda oda
|
||||
# Envoy Document
|
||||
application/envoy evy
|
||||
|
||||
# ARCHIVE/COMPRESSED ARCHIVES
|
||||
|
||||
# Gnu tar format
|
||||
application/x-gtar gtar
|
||||
# 4.3BSD tar format
|
||||
application/x-tar tar
|
||||
# POSIX tar format
|
||||
application/x-ustar ustar
|
||||
# Old CPIO format
|
||||
application/x-bcpio bcpio
|
||||
# POSIX CPIO format
|
||||
application/x-cpio cpio
|
||||
# UNIX sh shell archive
|
||||
application/x-shar shar
|
||||
# DOS/PC - Pkzipped archive
|
||||
application/zip zip
|
||||
# Macintosh Binhexed archive
|
||||
application/mac-binhex40 hqx
|
||||
# Macintosh Stuffit Archive
|
||||
application/x-stuffit sit sea
|
||||
# Fractal Image Format
|
||||
application/fractals fif
|
||||
# "Binary UUencoded"
|
||||
application/octet-stream bin uu
|
||||
# PC executable
|
||||
application/octet-stream exe
|
||||
# "WAIS ""sources"""
|
||||
application/x-wais-source src wsrc
|
||||
# NCSA HDF data format
|
||||
application/hdf hdf
|
||||
|
||||
# DOWNLOADABLE PROGRAM/SCRIPTS
|
||||
|
||||
# Javascript program
|
||||
text/javascript js ls mocha
|
||||
# UNIX bourne shell program
|
||||
application/x-sh sh
|
||||
# UNIX c-shell program
|
||||
application/x-csh csh
|
||||
# Perl program
|
||||
application/x-perl pl
|
||||
# Tcl (Tool Control Language) program
|
||||
application/x-tcl tcl
|
||||
|
||||
# ANIMATION/MULTIMEDIA
|
||||
|
||||
# FutureSplash vector animation (FutureWave)
|
||||
application/futuresplash spl
|
||||
# mBED multimedia data (mBED)
|
||||
application/mbedlet mbd
|
||||
# PowerMedia multimedia (RadMedia)
|
||||
application/x-rad-powermedia rad
|
||||
|
||||
# PRESENTATION
|
||||
|
||||
# PowerPoint presentation (Microsoft)
|
||||
application/mspowerpoint ppz
|
||||
# ASAP WordPower (Software Publishing Corp.)
|
||||
application/x-asap asp
|
||||
# Astound Web Player multimedia data (GoldDisk)
|
||||
application/astound asn
|
||||
|
||||
# SPECIAL EMBEDDED OBJECT
|
||||
|
||||
# OLE script e.g. Visual Basic (Ncompass)
|
||||
application/x-olescript axs
|
||||
# OLE Object (Microsoft/NCompass)
|
||||
application/x-oleobject ods
|
||||
# OpenScape OLE/OCX objects (Business@Web)
|
||||
x-form/x-openscape opp
|
||||
# Visual Basic objects (Amara)
|
||||
application/x-webbasic wba
|
||||
# Specialized data entry forms (Alpha Software)
|
||||
application/x-alpha-form frm
|
||||
# client-server objects (Wayfarer Communications)
|
||||
x-script/x-wfxclient wfx
|
||||
|
||||
# GENERAL APPLICATIONS
|
||||
|
||||
# Undefined binary data (often executable progs)
|
||||
application/octet-stream exe com
|
||||
# Pointcast news data (Pointcast)
|
||||
application/x-pcn pcn
|
||||
# Excel spreadsheet (Microsoft)
|
||||
application/vnd.ms-excel xls
|
||||
# PowerPoint (Microsoft)
|
||||
application/vnd.ms-powerpoint ppt
|
||||
# Microsoft Project (Microsoft)
|
||||
application/vnd.ms-project mpp
|
||||
# SourceView document (Dataware Electronics)
|
||||
application/vnd.svd svd
|
||||
# Net Install - software install (20/20 Software)
|
||||
application/x-net-install ins
|
||||
# Carbon Copy - remote control/access (Microcom)
|
||||
application/ccv ccv
|
||||
# Spreadsheets (Visual Components)
|
||||
workbook/formulaone vts
|
||||
|
||||
# 2D/3D DATA/VIRTUAL REALITY TYPES
|
||||
|
||||
# VRML data file
|
||||
x-world/x-vrml wrl vrml
|
||||
# WIRL - VRML data (VREAM)
|
||||
x-world/x-vream vrw
|
||||
# Play3D 3d scene data (Play3D)
|
||||
application/x-p3d p3d
|
||||
# Viscape Interactive 3d world data (Superscape)
|
||||
x-world/x-svr svr
|
||||
# WebActive 3d data (Plastic Thought)
|
||||
x-world/x-wvr wvr
|
||||
# QuickDraw3D scene data (Apple)
|
||||
x-world/x-3dmf 3dmf
|
||||
|
||||
# SCIENTIFIC/MATH/CAD TYPES
|
||||
|
||||
# Mathematica notebook
|
||||
application/mathematica ma
|
||||
# Computational meshes for numerical simulations
|
||||
x-model/x-mesh msh
|
||||
# Vis5D 5-dimensional data
|
||||
application/vis5d v5d
|
||||
# IGES models -- CAD/CAM (CGM) data
|
||||
application/iges igs
|
||||
# Autocad WHIP vector drawings
|
||||
drawing/x-dwf dwf
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
JavaMail support for Spring's mail infrastructure.
|
||||
Provides an extended JavaMailSender interface and a MimeMessageHelper
|
||||
class for convenient population of a JavaMail MimeMessage.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Spring's generic mail infrastructure.
|
||||
Concrete implementations are provided in the subpackages.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.commonj;
|
||||
|
||||
import commonj.timers.Timer;
|
||||
import commonj.timers.TimerListener;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple TimerListener adapter that delegates to a given Runnable.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see commonj.timers.TimerListener
|
||||
* @see java.lang.Runnable
|
||||
*/
|
||||
public class DelegatingTimerListener implements TimerListener {
|
||||
|
||||
private final Runnable runnable;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new DelegatingTimerListener.
|
||||
* @param runnable the Runnable implementation to delegate to
|
||||
*/
|
||||
public DelegatingTimerListener(Runnable runnable) {
|
||||
Assert.notNull(runnable, "Runnable is required");
|
||||
this.runnable = runnable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegates execution to the underlying Runnable.
|
||||
*/
|
||||
public void timerExpired(Timer timer) {
|
||||
this.runnable.run();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.commonj;
|
||||
|
||||
import commonj.work.Work;
|
||||
|
||||
import org.springframework.scheduling.SchedulingAwareRunnable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple Work adapter that delegates to a given Runnable.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see commonj.work.Work
|
||||
* @see java.lang.Runnable
|
||||
*/
|
||||
public class DelegatingWork implements Work {
|
||||
|
||||
private final Runnable delegate;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new DelegatingWork.
|
||||
* @param delegate the Runnable implementation to delegate to
|
||||
* (may be a SchedulingAwareRunnable for extended support)
|
||||
* @see org.springframework.scheduling.SchedulingAwareRunnable
|
||||
* @see #isDaemon()
|
||||
*/
|
||||
public DelegatingWork(Runnable delegate) {
|
||||
Assert.notNull(delegate, "Delegate must not be null");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the wrapped Runnable implementation.
|
||||
*/
|
||||
public final Runnable getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegates execution to the underlying Runnable.
|
||||
*/
|
||||
public void run() {
|
||||
this.delegate.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation delegates to
|
||||
* {@link org.springframework.scheduling.SchedulingAwareRunnable#isLongLived()},
|
||||
* if available.
|
||||
*/
|
||||
public boolean isDaemon() {
|
||||
return (this.delegate instanceof SchedulingAwareRunnable &&
|
||||
((SchedulingAwareRunnable) this.delegate).isLongLived());
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation is empty, since we expect the Runnable
|
||||
* to terminate based on some specific shutdown signal.
|
||||
*/
|
||||
public void release() {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.commonj;
|
||||
|
||||
import commonj.timers.TimerListener;
|
||||
|
||||
/**
|
||||
* JavaBean that describes a scheduled TimerListener, consisting of
|
||||
* the TimerListener itself (or a Runnable to create a TimerListener for)
|
||||
* and a delay plus period. Period needs to be specified;
|
||||
* there is no point in a default for it.
|
||||
*
|
||||
* <p>The CommonJ TimerManager does not offer more sophisticated scheduling
|
||||
* options such as cron expressions. Consider using Quartz for such
|
||||
* advanced needs.
|
||||
*
|
||||
* <p>Note that the TimerManager uses a TimerListener instance that is
|
||||
* shared between repeated executions, in contrast to Quartz which
|
||||
* instantiates a new Job for each execution.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see commonj.timers.TimerListener
|
||||
* @see commonj.timers.TimerManager#schedule(commonj.timers.TimerListener, long, long)
|
||||
* @see commonj.timers.TimerManager#scheduleAtFixedRate(commonj.timers.TimerListener, long, long)
|
||||
*/
|
||||
public class ScheduledTimerListener {
|
||||
|
||||
private TimerListener timerListener;
|
||||
|
||||
private long delay = 0;
|
||||
|
||||
private long period = -1;
|
||||
|
||||
private boolean fixedRate = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ScheduledTimerListener,
|
||||
* to be populated via bean properties.
|
||||
* @see #setTimerListener
|
||||
* @see #setDelay
|
||||
* @see #setPeriod
|
||||
* @see #setFixedRate
|
||||
*/
|
||||
public ScheduledTimerListener() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ScheduledTimerListener, with default
|
||||
* one-time execution without delay.
|
||||
* @param timerListener the TimerListener to schedule
|
||||
*/
|
||||
public ScheduledTimerListener(TimerListener timerListener) {
|
||||
this.timerListener = timerListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ScheduledTimerListener, with default
|
||||
* one-time execution with the given delay.
|
||||
* @param timerListener the TimerListener to schedule
|
||||
* @param delay the delay before starting the task for the first time (ms)
|
||||
*/
|
||||
public ScheduledTimerListener(TimerListener timerListener, long delay) {
|
||||
this.timerListener = timerListener;
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ScheduledTimerListener.
|
||||
* @param timerListener the TimerListener to schedule
|
||||
* @param delay the delay before starting the task for the first time (ms)
|
||||
* @param period the period between repeated task executions (ms)
|
||||
* @param fixedRate whether to schedule as fixed-rate execution
|
||||
*/
|
||||
public ScheduledTimerListener(TimerListener timerListener, long delay, long period, boolean fixedRate) {
|
||||
this.timerListener = timerListener;
|
||||
this.delay = delay;
|
||||
this.period = period;
|
||||
this.fixedRate = fixedRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ScheduledTimerListener, with default
|
||||
* one-time execution without delay.
|
||||
* @param timerTask the Runnable to schedule as TimerListener
|
||||
*/
|
||||
public ScheduledTimerListener(Runnable timerTask) {
|
||||
setRunnable(timerTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ScheduledTimerListener, with default
|
||||
* one-time execution with the given delay.
|
||||
* @param timerTask the Runnable to schedule as TimerListener
|
||||
* @param delay the delay before starting the task for the first time (ms)
|
||||
*/
|
||||
public ScheduledTimerListener(Runnable timerTask, long delay) {
|
||||
setRunnable(timerTask);
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ScheduledTimerListener.
|
||||
* @param timerTask the Runnable to schedule as TimerListener
|
||||
* @param delay the delay before starting the task for the first time (ms)
|
||||
* @param period the period between repeated task executions (ms)
|
||||
* @param fixedRate whether to schedule as fixed-rate execution
|
||||
*/
|
||||
public ScheduledTimerListener(Runnable timerTask, long delay, long period, boolean fixedRate) {
|
||||
setRunnable(timerTask);
|
||||
this.delay = delay;
|
||||
this.period = period;
|
||||
this.fixedRate = fixedRate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the Runnable to schedule as TimerListener.
|
||||
* @see DelegatingTimerListener
|
||||
*/
|
||||
public void setRunnable(Runnable timerTask) {
|
||||
this.timerListener = new DelegatingTimerListener(timerTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the TimerListener to schedule.
|
||||
*/
|
||||
public void setTimerListener(TimerListener timerListener) {
|
||||
this.timerListener = timerListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the TimerListener to schedule.
|
||||
*/
|
||||
public TimerListener getTimerListener() {
|
||||
return this.timerListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the delay before starting the task for the first time,
|
||||
* in milliseconds. Default is 0, immediately starting the
|
||||
* task after successful scheduling.
|
||||
* <p>If the "firstTime" property is specified, this property will be ignored.
|
||||
* Specify one or the other, not both.
|
||||
*/
|
||||
public void setDelay(long delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the delay before starting the job for the first time.
|
||||
*/
|
||||
public long getDelay() {
|
||||
return this.delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the period between repeated task executions, in milliseconds.
|
||||
* <p>Default is -1, leading to one-time execution. In case of zero or a
|
||||
* positive value, the task will be executed repeatedly, with the given
|
||||
* interval inbetween executions.
|
||||
* <p>Note that the semantics of the period value vary between fixed-rate
|
||||
* and fixed-delay execution.
|
||||
* <p><b>Note:</b> A period of 0 (for example as fixed delay) <i>is</i>
|
||||
* supported, because the CommonJ specification defines this as a legal value.
|
||||
* Hence a value of 0 will result in immediate re-execution after a job has
|
||||
* finished (not in one-time execution like with <code>java.util.Timer</code>).
|
||||
* @see #setFixedRate
|
||||
* @see #isOneTimeTask()
|
||||
* @see commonj.timers.TimerManager#schedule(commonj.timers.TimerListener, long, long)
|
||||
*/
|
||||
public void setPeriod(long period) {
|
||||
this.period = period;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the period between repeated task executions.
|
||||
*/
|
||||
public long getPeriod() {
|
||||
return this.period;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this task only ever going to execute once?
|
||||
* @return <code>true</code> if this task is only ever going to execute once
|
||||
* @see #getPeriod()
|
||||
*/
|
||||
public boolean isOneTimeTask() {
|
||||
return (this.period < 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to schedule as fixed-rate execution, rather than
|
||||
* fixed-delay execution. Default is "false", i.e. fixed delay.
|
||||
* <p>See TimerManager javadoc for details on those execution modes.
|
||||
* @see commonj.timers.TimerManager#schedule(commonj.timers.TimerListener, long, long)
|
||||
* @see commonj.timers.TimerManager#scheduleAtFixedRate(commonj.timers.TimerListener, long, long)
|
||||
*/
|
||||
public void setFixedRate(boolean fixedRate) {
|
||||
this.fixedRate = fixedRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to schedule as fixed-rate execution.
|
||||
*/
|
||||
public boolean isFixedRate() {
|
||||
return this.fixedRate;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.commonj;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import commonj.timers.Timer;
|
||||
import commonj.timers.TimerManager;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.jndi.JndiLocatorSupport;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.FactoryBean} that retrieves a
|
||||
* CommonJ {@link commonj.timers.TimerManager} and exposes it for bean references.
|
||||
*
|
||||
* <p><b>This is the central convenience class for setting up a
|
||||
* CommonJ TimerManager in a Spring context.</b>
|
||||
*
|
||||
* <p>Allows for registration of ScheduledTimerListeners. This is the main
|
||||
* purpose of this class; the TimerManager itself could also be fetched
|
||||
* from JNDI via {@link org.springframework.jndi.JndiObjectFactoryBean}.
|
||||
* In scenarios that just require static registration of tasks at startup,
|
||||
* there is no need to access the TimerManager itself in application code.
|
||||
*
|
||||
* <p>Note that the TimerManager uses a TimerListener instance that is
|
||||
* shared between repeated executions, in contrast to Quartz which
|
||||
* instantiates a new Job for each execution.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see ScheduledTimerListener
|
||||
* @see commonj.timers.TimerManager
|
||||
* @see commonj.timers.TimerListener
|
||||
*/
|
||||
public class TimerManagerFactoryBean extends JndiLocatorSupport
|
||||
implements FactoryBean, InitializingBean, DisposableBean, Lifecycle {
|
||||
|
||||
private TimerManager timerManager;
|
||||
|
||||
private String timerManagerName;
|
||||
|
||||
private boolean shared = false;
|
||||
|
||||
private ScheduledTimerListener[] scheduledTimerListeners;
|
||||
|
||||
private final List timers = new LinkedList();
|
||||
|
||||
|
||||
/**
|
||||
* Specify the CommonJ TimerManager to delegate to.
|
||||
* <p>Note that the given TimerManager's lifecycle will be managed
|
||||
* by this FactoryBean.
|
||||
* <p>Alternatively (and typically), you can specify the JNDI name
|
||||
* of the target TimerManager.
|
||||
* @see #setTimerManagerName
|
||||
*/
|
||||
public void setTimerManager(TimerManager timerManager) {
|
||||
this.timerManager = timerManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JNDI name of the CommonJ TimerManager.
|
||||
* <p>This can either be a fully qualified JNDI name, or the JNDI name relative
|
||||
* to the current environment naming context if "resourceRef" is set to "true".
|
||||
* @see #setTimerManager
|
||||
* @see #setResourceRef
|
||||
*/
|
||||
public void setTimerManagerName(String timerManagerName) {
|
||||
this.timerManagerName = timerManagerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether the TimerManager obtained by this FactoryBean
|
||||
* is a shared instance ("true") or an independent instance ("false").
|
||||
* The lifecycle of the former is supposed to be managed by the application
|
||||
* server, while the lifecycle of the latter is up to the application.
|
||||
* <p>Default is "false", i.e. managing an independent TimerManager instance.
|
||||
* This is what the CommonJ specification suggests that application servers
|
||||
* are supposed to offer via JNDI lookups, typically declared as a
|
||||
* <code>resource-ref</code> of type <code>commonj.timers.TimerManager</code>
|
||||
* in <code>web.xml<code>, with <code>res-sharing-scope</code> set to 'Unshareable'.
|
||||
* <p>Switch this flag to "true" if you are obtaining a shared TimerManager,
|
||||
* typically through specifying the JNDI location of a TimerManager that
|
||||
* has been explicitly declared as 'Shareable'. Note that WebLogic's
|
||||
* cluster-aware Job Scheduler is a shared TimerManager too.
|
||||
* <p>The sole difference between this FactoryBean being in shared or
|
||||
* non-shared mode is that it will only attempt to suspend / resume / stop
|
||||
* the underlying TimerManager in case of an independent (non-shared) instance.
|
||||
* This only affects the {@link org.springframework.context.Lifecycle} support
|
||||
* as well as application context shutdown.
|
||||
* @see #stop()
|
||||
* @see #start()
|
||||
* @see #destroy()
|
||||
* @see commonj.timers.TimerManager
|
||||
*/
|
||||
public void setShared(boolean shared) {
|
||||
this.shared = shared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a list of ScheduledTimerListener objects with the TimerManager
|
||||
* that this FactoryBean creates. Depending on each ScheduledTimerListener's settings,
|
||||
* it will be registered via one of TimerManager's schedule methods.
|
||||
* @see commonj.timers.TimerManager#schedule(commonj.timers.TimerListener, long)
|
||||
* @see commonj.timers.TimerManager#schedule(commonj.timers.TimerListener, long, long)
|
||||
* @see commonj.timers.TimerManager#scheduleAtFixedRate(commonj.timers.TimerListener, long, long)
|
||||
*/
|
||||
public void setScheduledTimerListeners(ScheduledTimerListener[] scheduledTimerListeners) {
|
||||
this.scheduledTimerListeners = scheduledTimerListeners;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of InitializingBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void afterPropertiesSet() throws NamingException {
|
||||
if (this.timerManager == null) {
|
||||
if (this.timerManagerName == null) {
|
||||
throw new IllegalArgumentException("Either 'timerManager' or 'timerManagerName' must be specified");
|
||||
}
|
||||
this.timerManager = (TimerManager) lookup(this.timerManagerName, TimerManager.class);
|
||||
}
|
||||
|
||||
if (this.scheduledTimerListeners != null) {
|
||||
for (int i = 0; i < this.scheduledTimerListeners.length; i++) {
|
||||
ScheduledTimerListener scheduledTask = this.scheduledTimerListeners[i];
|
||||
Timer timer = null;
|
||||
if (scheduledTask.isOneTimeTask()) {
|
||||
timer = this.timerManager.schedule(scheduledTask.getTimerListener(), scheduledTask.getDelay());
|
||||
}
|
||||
else {
|
||||
if (scheduledTask.isFixedRate()) {
|
||||
timer = this.timerManager.scheduleAtFixedRate(
|
||||
scheduledTask.getTimerListener(), scheduledTask.getDelay(), scheduledTask.getPeriod());
|
||||
}
|
||||
else {
|
||||
timer = this.timerManager.schedule(
|
||||
scheduledTask.getTimerListener(), scheduledTask.getDelay(), scheduledTask.getPeriod());
|
||||
}
|
||||
}
|
||||
this.timers.add(timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of FactoryBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public Object getObject() {
|
||||
return this.timerManager;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return (this.timerManager != null ? this.timerManager.getClass() : TimerManager.class);
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of Lifecycle interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Resumes the underlying TimerManager (if not shared).
|
||||
* @see commonj.timers.TimerManager#resume()
|
||||
*/
|
||||
public void start() {
|
||||
if (!this.shared) {
|
||||
this.timerManager.resume();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspends the underlying TimerManager (if not shared).
|
||||
* @see commonj.timers.TimerManager#suspend()
|
||||
*/
|
||||
public void stop() {
|
||||
if (!this.shared) {
|
||||
this.timerManager.suspend();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Considers the underlying TimerManager as running if it is
|
||||
* neither suspending nor stopping.
|
||||
* @see commonj.timers.TimerManager#isSuspending()
|
||||
* @see commonj.timers.TimerManager#isStopping()
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return (!this.timerManager.isSuspending() && !this.timerManager.isStopping());
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of DisposableBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Cancels all statically registered Timers on shutdown,
|
||||
* and stops the underlying TimerManager (if not shared).
|
||||
* @see commonj.timers.Timer#cancel()
|
||||
* @see commonj.timers.TimerManager#stop()
|
||||
*/
|
||||
public void destroy() {
|
||||
// Cancel all registered timers.
|
||||
for (Iterator it = this.timers.iterator(); it.hasNext();) {
|
||||
Timer timer = (Timer) it.next();
|
||||
try {
|
||||
timer.cancel();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.warn("Could not cancel CommonJ Timer", ex);
|
||||
}
|
||||
}
|
||||
this.timers.clear();
|
||||
|
||||
// Stop the entire TimerManager, if necessary.
|
||||
if (!this.shared) {
|
||||
// May return early, but at least we already cancelled all known Timers.
|
||||
this.timerManager.stop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.commonj;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import commonj.work.Work;
|
||||
import commonj.work.WorkException;
|
||||
import commonj.work.WorkItem;
|
||||
import commonj.work.WorkListener;
|
||||
import commonj.work.WorkManager;
|
||||
import commonj.work.WorkRejectedException;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.task.TaskRejectedException;
|
||||
import org.springframework.jndi.JndiLocatorSupport;
|
||||
import org.springframework.scheduling.SchedulingException;
|
||||
import org.springframework.scheduling.SchedulingTaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* TaskExecutor implementation that delegates to a CommonJ WorkManager,
|
||||
* implementing the {@link commonj.work.WorkManager} interface,
|
||||
* which either needs to be specified as reference or through the JNDI name.
|
||||
*
|
||||
* <p><b>This is the central convenience class for setting up a
|
||||
* CommonJ WorkManager in a Spring context.</b>
|
||||
*
|
||||
* <p>Also implements the CommonJ WorkManager interface itself, delegating all
|
||||
* calls to the target WorkManager. Hence, a caller can choose whether it wants
|
||||
* to talk to this executor through the Spring TaskExecutor interface or the
|
||||
* CommonJ WorkManager interface.
|
||||
*
|
||||
* <p>The CommonJ WorkManager will usually be retrieved from the application
|
||||
* server's JNDI environment, as defined in the server's management console.
|
||||
*
|
||||
* <p><b>Note: At the time of this writing, the CommonJ WorkManager facility
|
||||
* is only supported on IBM WebSphere 6.0+ and BEA WebLogic 9.0+,
|
||||
* despite being such a crucial API for an application server.</b>
|
||||
* (There is a similar facility available on WebSphere 5.1 Enterprise,
|
||||
* though, which we will discuss below.)
|
||||
*
|
||||
* <p><b>On JBoss and GlassFish, a similar facility is available through
|
||||
* the JCA WorkManager.</b> See the
|
||||
* {@link org.springframework.jca.work.jboss.JBossWorkManagerTaskExecutor}
|
||||
* {@link org.springframework.jca.work.glassfish.GlassFishWorkManagerTaskExecutor}
|
||||
* classes which are the direct equivalent of this CommonJ adapter class.
|
||||
*
|
||||
* <p>A similar facility is available on WebSphere 5.1, under the name
|
||||
* "Asynch Beans". Its central interface is called WorkManager too and is
|
||||
* also obtained from JNDI, just like a standard CommonJ WorkManager.
|
||||
* However, this WorkManager variant is notably different: The central
|
||||
* execution method is called "startWork" instead of "schedule",
|
||||
* and takes a slightly different Work interface as parameter.
|
||||
*
|
||||
* <p>Support for this WebSphere 5.1 variant can be built with this class
|
||||
* and its helper DelegatingWork as template: Call the WorkManager's
|
||||
* <code>startWork(Work)</code> instead of <code>schedule(Work)</code>
|
||||
* in the <code>execute(Runnable)</code> implementation. Furthermore,
|
||||
* for simplicity's sake, drop the entire "Implementation of the CommonJ
|
||||
* WorkManager interface" section (and the corresponding
|
||||
* <code>implements WorkManager</code> clause at the class level).
|
||||
* Of course, you also need to change all <code>commonj.work</code> imports in
|
||||
* your WorkManagerTaskExecutor and DelegatingWork variants to the corresponding
|
||||
* WebSphere API imports (<code>com.ibm.websphere.asynchbeans.WorkManager</code>
|
||||
* and <code>com.ibm.websphere.asynchbeans.Work</code>, respectively).
|
||||
* This should be sufficient to get a TaskExecutor adapter for WebSphere 5.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class WorkManagerTaskExecutor extends JndiLocatorSupport
|
||||
implements SchedulingTaskExecutor, WorkManager, InitializingBean {
|
||||
|
||||
private WorkManager workManager;
|
||||
|
||||
private String workManagerName;
|
||||
|
||||
private WorkListener workListener;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the CommonJ WorkManager to delegate to.
|
||||
* <p>Alternatively, you can also specify the JNDI name
|
||||
* of the target WorkManager.
|
||||
* @see #setWorkManagerName
|
||||
*/
|
||||
public void setWorkManager(WorkManager workManager) {
|
||||
this.workManager = workManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JNDI name of the CommonJ WorkManager.
|
||||
* <p>This can either be a fully qualified JNDI name,
|
||||
* or the JNDI name relative to the current environment
|
||||
* naming context if "resourceRef" is set to "true".
|
||||
* @see #setWorkManager
|
||||
* @see #setResourceRef
|
||||
*/
|
||||
public void setWorkManagerName(String workManagerName) {
|
||||
this.workManagerName = workManagerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a CommonJ WorkListener to apply, if any.
|
||||
* <p>This shared WorkListener instance will be passed on to the
|
||||
* WorkManager by all {@link #execute} calls on this TaskExecutor.
|
||||
*/
|
||||
public void setWorkListener(WorkListener workListener) {
|
||||
this.workListener = workListener;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws NamingException {
|
||||
if (this.workManager == null) {
|
||||
if (this.workManagerName == null) {
|
||||
throw new IllegalArgumentException("Either 'workManager' or 'workManagerName' must be specified");
|
||||
}
|
||||
this.workManager = (WorkManager) lookup(this.workManagerName, WorkManager.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Implementation of the Spring SchedulingTaskExecutor interface
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
public void execute(Runnable task) {
|
||||
Assert.state(this.workManager != null, "No WorkManager specified");
|
||||
Work work = new DelegatingWork(task);
|
||||
try {
|
||||
if (this.workListener != null) {
|
||||
this.workManager.schedule(work, this.workListener);
|
||||
}
|
||||
else {
|
||||
this.workManager.schedule(work);
|
||||
}
|
||||
}
|
||||
catch (WorkRejectedException ex) {
|
||||
throw new TaskRejectedException("CommonJ WorkManager did not accept task: " + task, ex);
|
||||
}
|
||||
catch (WorkException ex) {
|
||||
throw new SchedulingException("Could not schedule task on CommonJ WorkManager", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This task executor prefers short-lived work units.
|
||||
*/
|
||||
public boolean prefersShortLivedTasks() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Implementation of the CommonJ WorkManager interface
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
public WorkItem schedule(Work work)
|
||||
throws WorkException, IllegalArgumentException {
|
||||
|
||||
return this.workManager.schedule(work);
|
||||
}
|
||||
|
||||
public WorkItem schedule(Work work, WorkListener workListener)
|
||||
throws WorkException, IllegalArgumentException {
|
||||
|
||||
return this.workManager.schedule(work, workListener);
|
||||
}
|
||||
|
||||
public boolean waitForAll(Collection workItems, long timeout)
|
||||
throws InterruptedException, IllegalArgumentException {
|
||||
|
||||
return this.workManager.waitForAll(workItems, timeout);
|
||||
}
|
||||
|
||||
public Collection waitForAny(Collection workItems, long timeout)
|
||||
throws InterruptedException, IllegalArgumentException {
|
||||
|
||||
return this.workManager.waitForAny(workItems, timeout);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Convenience classes for scheduling based on the CommonJ WorkManager/TimerManager
|
||||
facility, as supported by IBM WebSphere 6.0+ and BEA WebLogic 9.0+.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import org.quartz.Job;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.quartz.spi.JobFactory;
|
||||
import org.quartz.spi.TriggerFiredBundle;
|
||||
|
||||
/**
|
||||
* JobFactory implementation that supports {@link java.lang.Runnable}
|
||||
* objects as well as standard Quartz {@link org.quartz.Job} instances.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see DelegatingJob
|
||||
* @see #adaptJob(Object)
|
||||
*/
|
||||
public class AdaptableJobFactory implements JobFactory {
|
||||
|
||||
public Job newJob(TriggerFiredBundle bundle) throws SchedulerException {
|
||||
try {
|
||||
Object jobObject = createJobInstance(bundle);
|
||||
return adaptJob(jobObject);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new SchedulerException("Job instantiation failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the specified job class.
|
||||
* <p>Can be overridden to post-process the job instance.
|
||||
* @param bundle the TriggerFiredBundle from which the JobDetail
|
||||
* and other info relating to the trigger firing can be obtained
|
||||
* @return the job instance
|
||||
* @throws Exception if job instantiation failed
|
||||
*/
|
||||
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
|
||||
return bundle.getJobDetail().getJobClass().newInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapt the given job object to the Quartz Job interface.
|
||||
* <p>The default implementation supports straight Quartz Jobs
|
||||
* as well as Runnables, which get wrapped in a DelegatingJob.
|
||||
* @param jobObject the original instance of the specified job class
|
||||
* @return the adapted Quartz Job instance
|
||||
* @throws Exception if the given job could not be adapted
|
||||
* @see DelegatingJob
|
||||
*/
|
||||
protected Job adaptJob(Object jobObject) throws Exception {
|
||||
if (jobObject instanceof Job) {
|
||||
return (Job) jobObject;
|
||||
}
|
||||
else if (jobObject instanceof Runnable) {
|
||||
return new DelegatingJob((Runnable) jobObject);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unable to execute job class [" + jobObject.getClass().getName() +
|
||||
"]: only [org.quartz.Job] and [java.lang.Runnable] supported.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.quartz.CronTrigger;
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.Scheduler;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Constants;
|
||||
|
||||
/**
|
||||
* Convenience subclass of Quartz's {@link org.quartz.CronTrigger}
|
||||
* class, making bean-style usage easier.
|
||||
*
|
||||
* <p>CronTrigger itself is already a JavaBean but lacks sensible defaults.
|
||||
* This class uses the Spring bean name as job name, the Quartz default group
|
||||
* ("DEFAULT") as job group, the current time as start time, and indefinite
|
||||
* repetition, if not specified.
|
||||
*
|
||||
* <p>This class will also register the trigger with the job name and group of
|
||||
* a given {@link org.quartz.JobDetail}. This allows {@link SchedulerFactoryBean}
|
||||
* to automatically register a trigger for the corresponding JobDetail,
|
||||
* instead of registering the JobDetail separately.
|
||||
*
|
||||
* <p><b>NOTE:</b> This convenience subclass does not work with trigger
|
||||
* persistence in Quartz 1.6, due to a change in Quartz's trigger handling.
|
||||
* Use Quartz 1.5 if you rely on trigger persistence based on this class,
|
||||
* or the standard Quartz {@link org.quartz.CronTrigger} class instead.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 18.02.2004
|
||||
* @see #setName
|
||||
* @see #setGroup
|
||||
* @see #setStartTime
|
||||
* @see #setJobName
|
||||
* @see #setJobGroup
|
||||
* @see #setJobDetail
|
||||
* @see SchedulerFactoryBean#setTriggers
|
||||
* @see SchedulerFactoryBean#setJobDetails
|
||||
* @see SimpleTriggerBean
|
||||
*/
|
||||
public class CronTriggerBean extends CronTrigger
|
||||
implements JobDetailAwareTrigger, BeanNameAware, InitializingBean {
|
||||
|
||||
/** Constants for the CronTrigger class */
|
||||
private static final Constants constants = new Constants(CronTrigger.class);
|
||||
|
||||
|
||||
private JobDetail jobDetail;
|
||||
|
||||
private String beanName;
|
||||
|
||||
|
||||
/**
|
||||
* Register objects in the JobDataMap via a given Map.
|
||||
* <p>These objects will be available to this Trigger only,
|
||||
* in contrast to objects in the JobDetail's data map.
|
||||
* @param jobDataAsMap Map with String keys and any objects as values
|
||||
* (for example Spring-managed beans)
|
||||
* @see JobDetailBean#setJobDataAsMap
|
||||
*/
|
||||
public void setJobDataAsMap(Map jobDataAsMap) {
|
||||
getJobDataMap().putAll(jobDataAsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the misfire instruction via the name of the corresponding
|
||||
* constant in the {@link org.quartz.CronTrigger} class.
|
||||
* Default is <code>MISFIRE_INSTRUCTION_SMART_POLICY</code>.
|
||||
* @see org.quartz.CronTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
|
||||
* @see org.quartz.CronTrigger#MISFIRE_INSTRUCTION_DO_NOTHING
|
||||
* @see org.quartz.Trigger#MISFIRE_INSTRUCTION_SMART_POLICY
|
||||
*/
|
||||
public void setMisfireInstructionName(String constantName) {
|
||||
setMisfireInstruction(constants.asNumber(constantName).intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a list of TriggerListener names for this job, referring to
|
||||
* non-global TriggerListeners registered with the Scheduler.
|
||||
* <p>A TriggerListener name always refers to the name returned
|
||||
* by the TriggerListener implementation.
|
||||
* @see SchedulerFactoryBean#setTriggerListeners
|
||||
* @see org.quartz.TriggerListener#getName
|
||||
*/
|
||||
public void setTriggerListenerNames(String[] names) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
addTriggerListener(names[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JobDetail that this trigger should be associated with.
|
||||
* <p>This is typically used with a bean reference if the JobDetail
|
||||
* is a Spring-managed bean. Alternatively, the trigger can also
|
||||
* be associated with a job by name and group.
|
||||
* @see #setJobName
|
||||
* @see #setJobGroup
|
||||
*/
|
||||
public void setJobDetail(JobDetail jobDetail) {
|
||||
this.jobDetail = jobDetail;
|
||||
}
|
||||
|
||||
public JobDetail getJobDetail() {
|
||||
return this.jobDetail;
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws ParseException {
|
||||
if (getName() == null) {
|
||||
setName(this.beanName);
|
||||
}
|
||||
if (getGroup() == null) {
|
||||
setGroup(Scheduler.DEFAULT_GROUP);
|
||||
}
|
||||
if (getStartTime() == null) {
|
||||
setStartTime(new Date());
|
||||
}
|
||||
if (getTimeZone() == null) {
|
||||
setTimeZone(TimeZone.getDefault());
|
||||
}
|
||||
if (this.jobDetail != null) {
|
||||
setJobName(this.jobDetail.getName());
|
||||
setJobGroup(this.jobDetail.getGroup());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple Quartz {@link org.quartz.Job} adapter that delegates to a
|
||||
* given {@link java.lang.Runnable} instance.
|
||||
*
|
||||
* <p>Typically used in combination with property injection on the
|
||||
* Runnable instance, receiving parameters from the Quartz JobDataMap
|
||||
* that way instead of via the JobExecutionContext.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see SpringBeanJobFactory
|
||||
* @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
|
||||
*/
|
||||
public class DelegatingJob implements Job {
|
||||
|
||||
private final Runnable delegate;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new DelegatingJob.
|
||||
* @param delegate the Runnable implementation to delegate to
|
||||
*/
|
||||
public DelegatingJob(Runnable delegate) {
|
||||
Assert.notNull(delegate, "Delegate must not be null");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the wrapped Runnable implementation.
|
||||
*/
|
||||
public final Runnable getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegates execution to the underlying Runnable.
|
||||
*/
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
this.delegate.run();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import org.quartz.JobDetail;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by Quartz Triggers that are aware
|
||||
* of the JobDetail object that they are associated with.
|
||||
*
|
||||
* <p>SchedulerFactoryBean will auto-detect Triggers that implement this
|
||||
* interface and register them for the respective JobDetail accordingly.
|
||||
*
|
||||
* <p>The alternative is to configure a Trigger for a Job name and group:
|
||||
* This involves the need to register the JobDetail object separately
|
||||
* with SchedulerFactoryBean.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 18.02.2004
|
||||
* @see SchedulerFactoryBean#setTriggers
|
||||
* @see SchedulerFactoryBean#setJobDetails
|
||||
* @see org.quartz.Trigger#setJobName
|
||||
* @see org.quartz.Trigger#setJobGroup
|
||||
*/
|
||||
public interface JobDetailAwareTrigger {
|
||||
|
||||
/**
|
||||
* Return the JobDetail that this Trigger is associated with.
|
||||
* @return the associated JobDetail, or <code>null</code> if none
|
||||
*/
|
||||
JobDetail getJobDetail();
|
||||
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.Scheduler;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
/**
|
||||
* Convenience subclass of Quartz' JobDetail class that eases bean-style
|
||||
* usage.
|
||||
*
|
||||
* <p><code>JobDetail</code> itself is already a JavaBean but lacks
|
||||
* sensible defaults. This class uses the Spring bean name as job name,
|
||||
* and the Quartz default group ("DEFAULT") as job group if not specified.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 18.02.2004
|
||||
* @see #setName
|
||||
* @see #setGroup
|
||||
* @see org.springframework.beans.factory.BeanNameAware
|
||||
* @see org.quartz.Scheduler#DEFAULT_GROUP
|
||||
*/
|
||||
public class JobDetailBean extends JobDetail
|
||||
implements BeanNameAware, ApplicationContextAware, InitializingBean {
|
||||
|
||||
private Class actualJobClass;
|
||||
|
||||
private String beanName;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private String applicationContextJobDataKey;
|
||||
|
||||
|
||||
/**
|
||||
* Overridden to support any job class, to allow a custom JobFactory
|
||||
* to adapt the given job class to the Quartz Job interface.
|
||||
* @see SchedulerFactoryBean#setJobFactory
|
||||
*/
|
||||
public void setJobClass(Class jobClass) {
|
||||
if (jobClass != null && !Job.class.isAssignableFrom(jobClass)) {
|
||||
super.setJobClass(DelegatingJob.class);
|
||||
this.actualJobClass = jobClass;
|
||||
}
|
||||
else {
|
||||
super.setJobClass(jobClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to support any job class, to allow a custom JobFactory
|
||||
* to adapt the given job class to the Quartz Job interface.
|
||||
*/
|
||||
public Class getJobClass() {
|
||||
return (this.actualJobClass != null ? this.actualJobClass : super.getJobClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Register objects in the JobDataMap via a given Map.
|
||||
* <p>These objects will be available to this Job only,
|
||||
* in contrast to objects in the SchedulerContext.
|
||||
* <p>Note: When using persistent Jobs whose JobDetail will be kept in the
|
||||
* database, do not put Spring-managed beans or an ApplicationContext
|
||||
* reference into the JobDataMap but rather into the SchedulerContext.
|
||||
* @param jobDataAsMap Map with String keys and any objects as values
|
||||
* (for example Spring-managed beans)
|
||||
* @see SchedulerFactoryBean#setSchedulerContextAsMap
|
||||
*/
|
||||
public void setJobDataAsMap(Map jobDataAsMap) {
|
||||
getJobDataMap().putAll(jobDataAsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a list of JobListener names for this job, referring to
|
||||
* non-global JobListeners registered with the Scheduler.
|
||||
* <p>A JobListener name always refers to the name returned
|
||||
* by the JobListener implementation.
|
||||
* @see SchedulerFactoryBean#setJobListeners
|
||||
* @see org.quartz.JobListener#getName
|
||||
*/
|
||||
public void setJobListenerNames(String[] names) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
addJobListener(names[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the key of an ApplicationContext reference to expose in the JobDataMap,
|
||||
* for example "applicationContext". Default is none.
|
||||
* Only applicable when running in a Spring ApplicationContext.
|
||||
* <p>In case of a QuartzJobBean, the reference will be applied to the Job
|
||||
* instance as bean property. An "applicationContext" attribute will correspond
|
||||
* to a "setApplicationContext" method in that scenario.
|
||||
* <p>Note that BeanFactory callback interfaces like ApplicationContextAware
|
||||
* are not automatically applied to Quartz Job instances, because Quartz
|
||||
* itself is responsible for the lifecycle of its Jobs.
|
||||
* <p><b>Note: When using persistent job stores where JobDetail contents will
|
||||
* be kept in the database, do not put an ApplicationContext reference into
|
||||
* the JobDataMap but rather into the SchedulerContext.</b>
|
||||
* @see SchedulerFactoryBean#setApplicationContextSchedulerContextKey
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
*/
|
||||
public void setApplicationContextJobDataKey(String applicationContextJobDataKey) {
|
||||
this.applicationContextJobDataKey = applicationContextJobDataKey;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
if (getName() == null) {
|
||||
setName(this.beanName);
|
||||
}
|
||||
if (getGroup() == null) {
|
||||
setGroup(Scheduler.DEFAULT_GROUP);
|
||||
}
|
||||
if (this.applicationContextJobDataKey != null) {
|
||||
if (this.applicationContext == null) {
|
||||
throw new IllegalStateException(
|
||||
"JobDetailBean needs to be set up in an ApplicationContext " +
|
||||
"to be able to handle an 'applicationContextJobDataKey'");
|
||||
}
|
||||
getJobDataMap().put(this.applicationContextJobDataKey, this.applicationContext);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
import org.springframework.util.MethodInvoker;
|
||||
|
||||
/**
|
||||
* Unchecked exception that wraps an exception thrown from a target method.
|
||||
* Propagated to the Quartz scheduler from a Job that reflectively invokes
|
||||
* an arbitrary target method.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.3
|
||||
* @see MethodInvokingJobDetailFactoryBean
|
||||
*/
|
||||
public class JobMethodInvocationFailedException extends NestedRuntimeException {
|
||||
|
||||
/**
|
||||
* Constructor for JobMethodInvocationFailedException.
|
||||
* @param methodInvoker the MethodInvoker used for reflective invocation
|
||||
* @param cause the root cause (as thrown from the target method)
|
||||
*/
|
||||
public JobMethodInvocationFailedException(MethodInvoker methodInvoker, Throwable cause) {
|
||||
super("Invocation of method '" + methodInvoker.getTargetMethod() +
|
||||
"' on target class [" + methodInvoker.getTargetClass() + "] failed", cause);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.quartz.SchedulerConfigException;
|
||||
import org.quartz.impl.jdbcjobstore.JobStoreCMT;
|
||||
import org.quartz.spi.ClassLoadHelper;
|
||||
import org.quartz.spi.SchedulerSignaler;
|
||||
import org.quartz.utils.ConnectionProvider;
|
||||
import org.quartz.utils.DBConnectionManager;
|
||||
|
||||
import org.springframework.jdbc.datasource.DataSourceUtils;
|
||||
|
||||
/**
|
||||
* Subclass of Quartz's JobStoreCMT class that delegates to a Spring-managed
|
||||
* DataSource instead of using a Quartz-managed connection pool. This JobStore
|
||||
* will be used if SchedulerFactoryBean's "dataSource" property is set.
|
||||
*
|
||||
* <p>Supports both transactional and non-transactional DataSource access.
|
||||
* With a non-XA DataSource and local Spring transactions, a single DataSource
|
||||
* argument is sufficient. In case of an XA DataSource and global JTA transactions,
|
||||
* SchedulerFactoryBean's "nonTransactionalDataSource" property should be set,
|
||||
* passing in a non-XA DataSource that will not participate in global transactions.
|
||||
*
|
||||
* <p>Operations performed by this JobStore will properly participate in any
|
||||
* kind of Spring-managed transaction, as it uses Spring's DataSourceUtils
|
||||
* connection handling methods that are aware of a current transaction.
|
||||
*
|
||||
* <p>Note that all Quartz Scheduler operations that affect the persistent
|
||||
* job store should usually be performed within active transactions,
|
||||
* as they assume to get proper locks etc.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see SchedulerFactoryBean#setDataSource
|
||||
* @see SchedulerFactoryBean#setNonTransactionalDataSource
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
|
||||
*/
|
||||
public class LocalDataSourceJobStore extends JobStoreCMT {
|
||||
|
||||
/**
|
||||
* Name used for the transactional ConnectionProvider for Quartz.
|
||||
* This provider will delegate to the local Spring-managed DataSource.
|
||||
* @see org.quartz.utils.DBConnectionManager#addConnectionProvider
|
||||
* @see SchedulerFactoryBean#setDataSource
|
||||
*/
|
||||
public static final String TX_DATA_SOURCE_PREFIX = "springTxDataSource.";
|
||||
|
||||
/**
|
||||
* Name used for the non-transactional ConnectionProvider for Quartz.
|
||||
* This provider will delegate to the local Spring-managed DataSource.
|
||||
* @see org.quartz.utils.DBConnectionManager#addConnectionProvider
|
||||
* @see SchedulerFactoryBean#setDataSource
|
||||
*/
|
||||
public static final String NON_TX_DATA_SOURCE_PREFIX = "springNonTxDataSource.";
|
||||
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
|
||||
public void initialize(ClassLoadHelper loadHelper, SchedulerSignaler signaler)
|
||||
throws SchedulerConfigException {
|
||||
|
||||
// Absolutely needs thread-bound DataSource to initialize.
|
||||
this.dataSource = SchedulerFactoryBean.getConfigTimeDataSource();
|
||||
if (this.dataSource == null) {
|
||||
throw new SchedulerConfigException(
|
||||
"No local DataSource found for configuration - " +
|
||||
"'dataSource' property must be set on SchedulerFactoryBean");
|
||||
}
|
||||
|
||||
// Configure transactional connection settings for Quartz.
|
||||
setDataSource(TX_DATA_SOURCE_PREFIX + getInstanceName());
|
||||
setDontSetAutoCommitFalse(true);
|
||||
|
||||
// Register transactional ConnectionProvider for Quartz.
|
||||
DBConnectionManager.getInstance().addConnectionProvider(
|
||||
TX_DATA_SOURCE_PREFIX + getInstanceName(),
|
||||
new ConnectionProvider() {
|
||||
public Connection getConnection() throws SQLException {
|
||||
// Return a transactional Connection, if any.
|
||||
return DataSourceUtils.doGetConnection(dataSource);
|
||||
}
|
||||
public void shutdown() {
|
||||
// Do nothing - a Spring-managed DataSource has its own lifecycle.
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Non-transactional DataSource is optional: fall back to default
|
||||
// DataSource if not explicitly specified.
|
||||
DataSource nonTxDataSource = SchedulerFactoryBean.getConfigTimeNonTransactionalDataSource();
|
||||
final DataSource nonTxDataSourceToUse =
|
||||
(nonTxDataSource != null ? nonTxDataSource : this.dataSource);
|
||||
|
||||
// Configure non-transactional connection settings for Quartz.
|
||||
setNonManagedTXDataSource(NON_TX_DATA_SOURCE_PREFIX + getInstanceName());
|
||||
|
||||
// Register non-transactional ConnectionProvider for Quartz.
|
||||
DBConnectionManager.getInstance().addConnectionProvider(
|
||||
NON_TX_DATA_SOURCE_PREFIX + getInstanceName(),
|
||||
new ConnectionProvider() {
|
||||
public Connection getConnection() throws SQLException {
|
||||
// Always return a non-transactional Connection.
|
||||
return nonTxDataSourceToUse.getConnection();
|
||||
}
|
||||
public void shutdown() {
|
||||
// Do nothing - a Spring-managed DataSource has its own lifecycle.
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
super.initialize(loadHelper, signaler);
|
||||
}
|
||||
|
||||
protected void closeConnection(Connection con) {
|
||||
// Will work for transactional and non-transactional connections.
|
||||
DataSourceUtils.releaseConnection(con, this.dataSource);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.quartz.SchedulerConfigException;
|
||||
import org.quartz.spi.ThreadPool;
|
||||
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.core.task.TaskRejectedException;
|
||||
|
||||
/**
|
||||
* Quartz ThreadPool adapter that delegates to a Spring-managed
|
||||
* TaskExecutor instance, specified on SchedulerFactoryBean.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see SchedulerFactoryBean#setTaskExecutor
|
||||
*/
|
||||
public class LocalTaskExecutorThreadPool implements ThreadPool {
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private TaskExecutor taskExecutor;
|
||||
|
||||
|
||||
public void initialize() throws SchedulerConfigException {
|
||||
// Absolutely needs thread-bound TaskExecutor to initialize.
|
||||
this.taskExecutor = SchedulerFactoryBean.getConfigTimeTaskExecutor();
|
||||
if (this.taskExecutor == null) {
|
||||
throw new SchedulerConfigException(
|
||||
"No local TaskExecutor found for configuration - " +
|
||||
"'taskExecutor' property must be set on SchedulerFactoryBean");
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown(boolean waitForJobsToComplete) {
|
||||
}
|
||||
|
||||
public int getPoolSize() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
public boolean runInThread(Runnable runnable) {
|
||||
if (runnable == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
this.taskExecutor.execute(runnable);
|
||||
return true;
|
||||
}
|
||||
catch (TaskRejectedException ex) {
|
||||
logger.error("Task has been rejected by TaskExecutor", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int blockForAvailableThreads() {
|
||||
// The present implementation always returns 1, making Quartz (1.6)
|
||||
// always schedule any tasks that it feels like scheduling.
|
||||
// This could be made smarter for specific TaskExecutors,
|
||||
// for example calling <code>getMaximumPoolSize() - getActiveCount()</code>
|
||||
// on a <code>java.util.concurrent.ThreadPoolExecutor</code>.
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.StatefulJob;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.support.ArgumentConvertingMethodInvoker;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MethodInvoker;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.FactoryBean} that exposes a
|
||||
* {@link org.quartz.JobDetail} object which delegates job execution to a
|
||||
* specified (static or non-static) method. Avoids the need for implementing
|
||||
* a one-line Quartz Job that just invokes an existing service method on a
|
||||
* Spring-managed target bean.
|
||||
*
|
||||
* <p>Inherits common configuration properties from the {@link MethodInvoker}
|
||||
* base class, such as {@link #setTargetObject "targetObject"} and
|
||||
* {@link #setTargetMethod "targetMethod"}, adding support for lookup of the target
|
||||
* bean by name through the {@link #setTargetBeanName "targetBeanName"} property
|
||||
* (as alternative to specifying a "targetObject" directly, allowing for
|
||||
* non-singleton target objects).
|
||||
*
|
||||
* <p>Supports both concurrently running jobs and non-currently running
|
||||
* jobs through the "concurrent" property. Jobs created by this
|
||||
* MethodInvokingJobDetailFactoryBean are by default volatile and durable
|
||||
* (according to Quartz terminology).
|
||||
*
|
||||
* <p><b>NOTE: JobDetails created via this FactoryBean are <i>not</i>
|
||||
* serializable and thus not suitable for persistent job stores.</b>
|
||||
* You need to implement your own Quartz Job as a thin wrapper for each case
|
||||
* where you want a persistent job to delegate to a specific service method.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Alef Arendsen
|
||||
* @since 18.02.2004
|
||||
* @see #setTargetBeanName
|
||||
* @see #setTargetObject
|
||||
* @see #setTargetMethod
|
||||
* @see #setConcurrent
|
||||
*/
|
||||
public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethodInvoker
|
||||
implements FactoryBean, BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean {
|
||||
|
||||
private String name;
|
||||
|
||||
private String group = Scheduler.DEFAULT_GROUP;
|
||||
|
||||
private boolean concurrent = true;
|
||||
|
||||
private String targetBeanName;
|
||||
|
||||
private String[] jobListenerNames;
|
||||
|
||||
private String beanName;
|
||||
|
||||
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private JobDetail jobDetail;
|
||||
|
||||
|
||||
/**
|
||||
* Set the name of the job.
|
||||
* <p>Default is the bean name of this FactoryBean.
|
||||
* @see org.quartz.JobDetail#setName
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the group of the job.
|
||||
* <p>Default is the default group of the Scheduler.
|
||||
* @see org.quartz.JobDetail#setGroup
|
||||
* @see org.quartz.Scheduler#DEFAULT_GROUP
|
||||
*/
|
||||
public void setGroup(String group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether or not multiple jobs should be run in a concurrent
|
||||
* fashion. The behavior when one does not want concurrent jobs to be
|
||||
* executed is realized through adding the {@link StatefulJob} interface.
|
||||
* More information on stateful versus stateless jobs can be found
|
||||
* <a href="http://www.opensymphony.com/quartz/tutorial.html#jobsMore">here</a>.
|
||||
* <p>The default setting is to run jobs concurrently.
|
||||
*/
|
||||
public void setConcurrent(boolean concurrent) {
|
||||
this.concurrent = concurrent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the target bean in the Spring BeanFactory.
|
||||
* <p>This is an alternative to specifying {@link #setTargetObject "targetObject"},
|
||||
* allowing for non-singleton beans to be invoked. Note that specified
|
||||
* "targetObject" and {@link #setTargetClass "targetClass"} values will
|
||||
* override the corresponding effect of this "targetBeanName" setting
|
||||
* (i.e. statically pre-define the bean type or even the bean object).
|
||||
*/
|
||||
public void setTargetBeanName(String targetBeanName) {
|
||||
this.targetBeanName = targetBeanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a list of JobListener names for this job, referring to
|
||||
* non-global JobListeners registered with the Scheduler.
|
||||
* <p>A JobListener name always refers to the name returned
|
||||
* by the JobListener implementation.
|
||||
* @see SchedulerFactoryBean#setJobListeners
|
||||
* @see org.quartz.JobListener#getName
|
||||
*/
|
||||
public void setJobListenerNames(String[] names) {
|
||||
this.jobListenerNames = names;
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
protected Class resolveClassName(String className) throws ClassNotFoundException {
|
||||
return ClassUtils.forName(className, this.beanClassLoader);
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws ClassNotFoundException, NoSuchMethodException {
|
||||
prepare();
|
||||
|
||||
// Use specific name if given, else fall back to bean name.
|
||||
String name = (this.name != null ? this.name : this.beanName);
|
||||
|
||||
// Consider the concurrent flag to choose between stateful and stateless job.
|
||||
Class jobClass = (this.concurrent ? (Class) MethodInvokingJob.class : StatefulMethodInvokingJob.class);
|
||||
|
||||
// Build JobDetail instance.
|
||||
this.jobDetail = new JobDetail(name, this.group, jobClass);
|
||||
this.jobDetail.getJobDataMap().put("methodInvoker", this);
|
||||
this.jobDetail.setVolatility(true);
|
||||
this.jobDetail.setDurability(true);
|
||||
|
||||
// Register job listener names.
|
||||
if (this.jobListenerNames != null) {
|
||||
for (int i = 0; i < this.jobListenerNames.length; i++) {
|
||||
this.jobDetail.addJobListener(this.jobListenerNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
postProcessJobDetail(this.jobDetail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for post-processing the JobDetail to be exposed by this FactoryBean.
|
||||
* <p>The default implementation is empty. Can be overridden in subclasses.
|
||||
* @param jobDetail the JobDetail prepared by this FactoryBean
|
||||
*/
|
||||
protected void postProcessJobDetail(JobDetail jobDetail) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Overridden to support the {@link #setTargetBeanName "targetBeanName"} feature.
|
||||
*/
|
||||
public Class getTargetClass() {
|
||||
Class targetClass = super.getTargetClass();
|
||||
if (targetClass == null && this.targetBeanName != null) {
|
||||
Assert.state(this.beanFactory != null, "BeanFactory must be set when using 'targetBeanName'");
|
||||
targetClass = this.beanFactory.getType(this.targetBeanName);
|
||||
}
|
||||
return targetClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to support the {@link #setTargetBeanName "targetBeanName"} feature.
|
||||
*/
|
||||
public Object getTargetObject() {
|
||||
Object targetObject = super.getTargetObject();
|
||||
if (targetObject == null && this.targetBeanName != null) {
|
||||
Assert.state(this.beanFactory != null, "BeanFactory must be set when using 'targetBeanName'");
|
||||
targetObject = this.beanFactory.getBean(this.targetBeanName);
|
||||
}
|
||||
return targetObject;
|
||||
}
|
||||
|
||||
|
||||
public Object getObject() {
|
||||
return this.jobDetail;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return JobDetail.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Quartz Job implementation that invokes a specified method.
|
||||
* Automatically applied by MethodInvokingJobDetailFactoryBean.
|
||||
*/
|
||||
public static class MethodInvokingJob extends QuartzJobBean {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(MethodInvokingJob.class);
|
||||
|
||||
private MethodInvoker methodInvoker;
|
||||
|
||||
/**
|
||||
* Set the MethodInvoker to use.
|
||||
*/
|
||||
public void setMethodInvoker(MethodInvoker methodInvoker) {
|
||||
this.methodInvoker = methodInvoker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the method via the MethodInvoker.
|
||||
*/
|
||||
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
|
||||
try {
|
||||
this.methodInvoker.invoke();
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
if (ex.getTargetException() instanceof JobExecutionException) {
|
||||
// -> JobExecutionException, to be logged at info level by Quartz
|
||||
throw (JobExecutionException) ex.getTargetException();
|
||||
}
|
||||
else {
|
||||
// -> "unhandled exception", to be logged at error level by Quartz
|
||||
throw new JobMethodInvocationFailedException(this.methodInvoker, ex.getTargetException());
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// -> "unhandled exception", to be logged at error level by Quartz
|
||||
throw new JobMethodInvocationFailedException(this.methodInvoker, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extension of the MethodInvokingJob, implementing the StatefulJob interface.
|
||||
* Quartz checks whether or not jobs are stateful and if so,
|
||||
* won't let jobs interfere with each other.
|
||||
*/
|
||||
public static class StatefulMethodInvokingJob extends MethodInvokingJob implements StatefulJob {
|
||||
|
||||
// No implementation, just an addition of the tag interface StatefulJob
|
||||
// in order to allow stateful method invoking jobs.
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
import org.quartz.SchedulerException;
|
||||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyAccessorFactory;
|
||||
|
||||
/**
|
||||
* Simple implementation of the Quartz Job interface, applying the
|
||||
* passed-in JobDataMap and also the SchedulerContext as bean property
|
||||
* values. This is appropriate because a new Job instance will be created
|
||||
* for each execution. JobDataMap entries will override SchedulerContext
|
||||
* entries with the same keys.
|
||||
*
|
||||
* <p>For example, let's assume that the JobDataMap contains a key
|
||||
* "myParam" with value "5": The Job implementation can then expose
|
||||
* a bean property "myParam" of type int to receive such a value,
|
||||
* i.e. a method "setMyParam(int)". This will also work for complex
|
||||
* types like business objects etc.
|
||||
*
|
||||
* <p>Note: The QuartzJobBean class itself only implements the standard
|
||||
* Quartz {@link org.quartz.Job} interface. Let your subclass explicitly
|
||||
* implement the Quartz {@link org.quartz.StatefulJob} interface to
|
||||
* mark your concrete job bean as stateful.
|
||||
*
|
||||
* <p>This version of QuartzJobBean requires Quartz 1.5 or higher,
|
||||
* due to the support for trigger-specific job data.
|
||||
*
|
||||
* <p><b>Note that as of Spring 2.0 and Quartz 1.5, the preferred way
|
||||
* to apply dependency injection to Job instances is via a JobFactory:</b>
|
||||
* that is, to specify {@link SpringBeanJobFactory} as Quartz JobFactory
|
||||
* (typically via
|
||||
* {@link SchedulerFactoryBean#setJobFactory} SchedulerFactoryBean's "jobFactory" property}).
|
||||
* This allows to implement dependency-injected Quartz Jobs without
|
||||
* a dependency on Spring base classes.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 18.02.2004
|
||||
* @see org.quartz.JobExecutionContext#getMergedJobDataMap()
|
||||
* @see org.quartz.Scheduler#getContext()
|
||||
* @see JobDetailBean#setJobDataAsMap
|
||||
* @see SimpleTriggerBean#setJobDataAsMap
|
||||
* @see CronTriggerBean#setJobDataAsMap
|
||||
* @see SchedulerFactoryBean#setSchedulerContextAsMap
|
||||
* @see SpringBeanJobFactory
|
||||
* @see SchedulerFactoryBean#setJobFactory
|
||||
*/
|
||||
public abstract class QuartzJobBean implements Job {
|
||||
|
||||
/**
|
||||
* This implementation applies the passed-in job data map as bean property
|
||||
* values, and delegates to <code>executeInternal</code> afterwards.
|
||||
* @see #executeInternal
|
||||
*/
|
||||
public final void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
try {
|
||||
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
pvs.addPropertyValues(context.getScheduler().getContext());
|
||||
pvs.addPropertyValues(context.getMergedJobDataMap());
|
||||
bw.setPropertyValues(pvs, true);
|
||||
}
|
||||
catch (SchedulerException ex) {
|
||||
throw new JobExecutionException(ex);
|
||||
}
|
||||
executeInternal(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the actual job. The job data map will already have been
|
||||
* applied as bean property values by execute. The contract is
|
||||
* exactly the same as for the standard Quartz execute method.
|
||||
* @see #execute
|
||||
*/
|
||||
protected abstract void executeInternal(JobExecutionContext context) throws JobExecutionException;
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.quartz.spi.ClassLoadHelper;
|
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
/**
|
||||
* Wrapper that adapts from the Quartz {@link ClassLoadHelper} interface
|
||||
* onto Spring's {@link ResourceLoader} interface. Used by default when
|
||||
* the SchedulerFactoryBean runs in a Spring ApplicationContext.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.5
|
||||
* @see SchedulerFactoryBean#setApplicationContext
|
||||
*/
|
||||
public class ResourceLoaderClassLoadHelper implements ClassLoadHelper {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(ResourceLoaderClassLoadHelper.class);
|
||||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ResourceLoaderClassLoadHelper for the default
|
||||
* ResourceLoader.
|
||||
* @see SchedulerFactoryBean#getConfigTimeResourceLoader()
|
||||
*/
|
||||
public ResourceLoaderClassLoadHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ResourceLoaderClassLoadHelper for the given ResourceLoader.
|
||||
* @param resourceLoader the ResourceLoader to delegate to
|
||||
*/
|
||||
public ResourceLoaderClassLoadHelper(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
|
||||
public void initialize() {
|
||||
if (this.resourceLoader == null) {
|
||||
this.resourceLoader = SchedulerFactoryBean.getConfigTimeResourceLoader();
|
||||
if (this.resourceLoader == null) {
|
||||
this.resourceLoader = new DefaultResourceLoader();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Class loadClass(String name) throws ClassNotFoundException {
|
||||
return this.resourceLoader.getClassLoader().loadClass(name);
|
||||
}
|
||||
|
||||
public URL getResource(String name) {
|
||||
Resource resource = this.resourceLoader.getResource(name);
|
||||
try {
|
||||
return resource.getURL();
|
||||
}
|
||||
catch (FileNotFoundException ex) {
|
||||
return null;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.warn("Could not load " + resource);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream getResourceAsStream(String name) {
|
||||
Resource resource = this.resourceLoader.getResource(name);
|
||||
try {
|
||||
return resource.getInputStream();
|
||||
}
|
||||
catch (FileNotFoundException ex) {
|
||||
return null;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.warn("Could not load " + resource);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ClassLoader getClassLoader() {
|
||||
return this.resourceLoader.getClassLoader();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,407 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.quartz.Calendar;
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.JobListener;
|
||||
import org.quartz.ObjectAlreadyExistsException;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.quartz.SchedulerListener;
|
||||
import org.quartz.Trigger;
|
||||
import org.quartz.TriggerListener;
|
||||
import org.quartz.spi.ClassLoadHelper;
|
||||
import org.quartz.xml.JobSchedulingDataProcessor;
|
||||
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||
|
||||
/**
|
||||
* Common base class for accessing a Quartz Scheduler, i.e. for registering jobs,
|
||||
* triggers and listeners on a {@link org.quartz.Scheduler} instance.
|
||||
*
|
||||
* <p>For concrete usage, check out the {@link SchedulerFactoryBean} and
|
||||
* {@link SchedulerAccessorBean} classes.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.6
|
||||
*/
|
||||
public abstract class SchedulerAccessor implements ResourceLoaderAware {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
|
||||
private boolean overwriteExistingJobs = false;
|
||||
|
||||
private String[] jobSchedulingDataLocations;
|
||||
|
||||
private List jobDetails;
|
||||
|
||||
private Map calendars;
|
||||
|
||||
private List triggers;
|
||||
|
||||
|
||||
private SchedulerListener[] schedulerListeners;
|
||||
|
||||
private JobListener[] globalJobListeners;
|
||||
|
||||
private JobListener[] jobListeners;
|
||||
|
||||
private TriggerListener[] globalTriggerListeners;
|
||||
|
||||
private TriggerListener[] triggerListeners;
|
||||
|
||||
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
protected ResourceLoader resourceLoader;
|
||||
|
||||
|
||||
/**
|
||||
* Set whether any jobs defined on this SchedulerFactoryBean should overwrite
|
||||
* existing job definitions. Default is "false", to not overwrite already
|
||||
* registered jobs that have been read in from a persistent job store.
|
||||
*/
|
||||
public void setOverwriteExistingJobs(boolean overwriteExistingJobs) {
|
||||
this.overwriteExistingJobs = overwriteExistingJobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the location of a Quartz job definition XML file that follows the
|
||||
* "job_scheduling_data_1_5" XSD. Can be specified to automatically
|
||||
* register jobs that are defined in such a file, possibly in addition
|
||||
* to jobs defined directly on this SchedulerFactoryBean.
|
||||
* @see org.quartz.xml.JobSchedulingDataProcessor
|
||||
*/
|
||||
public void setJobSchedulingDataLocation(String jobSchedulingDataLocation) {
|
||||
this.jobSchedulingDataLocations = new String[] {jobSchedulingDataLocation};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locations of Quartz job definition XML files that follow the
|
||||
* "job_scheduling_data_1_5" XSD. Can be specified to automatically
|
||||
* register jobs that are defined in such files, possibly in addition
|
||||
* to jobs defined directly on this SchedulerFactoryBean.
|
||||
* @see org.quartz.xml.JobSchedulingDataProcessor
|
||||
*/
|
||||
public void setJobSchedulingDataLocations(String[] jobSchedulingDataLocations) {
|
||||
this.jobSchedulingDataLocations = jobSchedulingDataLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a list of JobDetail objects with the Scheduler that
|
||||
* this FactoryBean creates, to be referenced by Triggers.
|
||||
* <p>This is not necessary when a Trigger determines the JobDetail
|
||||
* itself: In this case, the JobDetail will be implicitly registered
|
||||
* in combination with the Trigger.
|
||||
* @see #setTriggers
|
||||
* @see org.quartz.JobDetail
|
||||
* @see JobDetailBean
|
||||
* @see JobDetailAwareTrigger
|
||||
* @see org.quartz.Trigger#setJobName
|
||||
*/
|
||||
public void setJobDetails(JobDetail[] jobDetails) {
|
||||
// Use modifiable ArrayList here, to allow for further adding of
|
||||
// JobDetail objects during autodetection of JobDetailAwareTriggers.
|
||||
this.jobDetails = new ArrayList(Arrays.asList(jobDetails));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a list of Quartz Calendar objects with the Scheduler
|
||||
* that this FactoryBean creates, to be referenced by Triggers.
|
||||
* @param calendars Map with calendar names as keys as Calendar
|
||||
* objects as values
|
||||
* @see org.quartz.Calendar
|
||||
* @see org.quartz.Trigger#setCalendarName
|
||||
*/
|
||||
public void setCalendars(Map calendars) {
|
||||
this.calendars = calendars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a list of Trigger objects with the Scheduler that
|
||||
* this FactoryBean creates.
|
||||
* <p>If the Trigger determines the corresponding JobDetail itself,
|
||||
* the job will be automatically registered with the Scheduler.
|
||||
* Else, the respective JobDetail needs to be registered via the
|
||||
* "jobDetails" property of this FactoryBean.
|
||||
* @see #setJobDetails
|
||||
* @see org.quartz.JobDetail
|
||||
* @see JobDetailAwareTrigger
|
||||
* @see CronTriggerBean
|
||||
* @see SimpleTriggerBean
|
||||
*/
|
||||
public void setTriggers(Trigger[] triggers) {
|
||||
this.triggers = Arrays.asList(triggers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify Quartz SchedulerListeners to be registered with the Scheduler.
|
||||
*/
|
||||
public void setSchedulerListeners(SchedulerListener[] schedulerListeners) {
|
||||
this.schedulerListeners = schedulerListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify global Quartz JobListeners to be registered with the Scheduler.
|
||||
* Such JobListeners will apply to all Jobs in the Scheduler.
|
||||
*/
|
||||
public void setGlobalJobListeners(JobListener[] globalJobListeners) {
|
||||
this.globalJobListeners = globalJobListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify named Quartz JobListeners to be registered with the Scheduler.
|
||||
* Such JobListeners will only apply to Jobs that explicitly activate
|
||||
* them via their name.
|
||||
* @see org.quartz.JobListener#getName
|
||||
* @see org.quartz.JobDetail#addJobListener
|
||||
* @see JobDetailBean#setJobListenerNames
|
||||
*/
|
||||
public void setJobListeners(JobListener[] jobListeners) {
|
||||
this.jobListeners = jobListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify global Quartz TriggerListeners to be registered with the Scheduler.
|
||||
* Such TriggerListeners will apply to all Triggers in the Scheduler.
|
||||
*/
|
||||
public void setGlobalTriggerListeners(TriggerListener[] globalTriggerListeners) {
|
||||
this.globalTriggerListeners = globalTriggerListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify named Quartz TriggerListeners to be registered with the Scheduler.
|
||||
* Such TriggerListeners will only apply to Triggers that explicitly activate
|
||||
* them via their name.
|
||||
* @see org.quartz.TriggerListener#getName
|
||||
* @see org.quartz.Trigger#addTriggerListener
|
||||
* @see CronTriggerBean#setTriggerListenerNames
|
||||
* @see SimpleTriggerBean#setTriggerListenerNames
|
||||
*/
|
||||
public void setTriggerListeners(TriggerListener[] triggerListeners) {
|
||||
this.triggerListeners = triggerListeners;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the transaction manager to be used for registering jobs and triggers
|
||||
* that are defined by this SchedulerFactoryBean. Default is none; setting
|
||||
* this only makes sense when specifying a DataSource for the Scheduler.
|
||||
*/
|
||||
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register jobs and triggers (within a transaction, if possible).
|
||||
*/
|
||||
protected void registerJobsAndTriggers() throws SchedulerException {
|
||||
TransactionStatus transactionStatus = null;
|
||||
if (this.transactionManager != null) {
|
||||
transactionStatus = this.transactionManager.getTransaction(new DefaultTransactionDefinition());
|
||||
}
|
||||
try {
|
||||
|
||||
if (this.jobSchedulingDataLocations != null) {
|
||||
ClassLoadHelper clh = new ResourceLoaderClassLoadHelper(this.resourceLoader);
|
||||
clh.initialize();
|
||||
JobSchedulingDataProcessor dataProcessor = new JobSchedulingDataProcessor(clh, true, true);
|
||||
for (int i = 0; i < this.jobSchedulingDataLocations.length; i++) {
|
||||
dataProcessor.processFileAndScheduleJobs(
|
||||
this.jobSchedulingDataLocations[i], getScheduler(), this.overwriteExistingJobs);
|
||||
}
|
||||
}
|
||||
|
||||
// Register JobDetails.
|
||||
if (this.jobDetails != null) {
|
||||
for (Iterator it = this.jobDetails.iterator(); it.hasNext();) {
|
||||
JobDetail jobDetail = (JobDetail) it.next();
|
||||
addJobToScheduler(jobDetail);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Create empty list for easier checks when registering triggers.
|
||||
this.jobDetails = new LinkedList();
|
||||
}
|
||||
|
||||
// Register Calendars.
|
||||
if (this.calendars != null) {
|
||||
for (Iterator it = this.calendars.keySet().iterator(); it.hasNext();) {
|
||||
String calendarName = (String) it.next();
|
||||
Calendar calendar = (Calendar) this.calendars.get(calendarName);
|
||||
getScheduler().addCalendar(calendarName, calendar, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Register Triggers.
|
||||
if (this.triggers != null) {
|
||||
for (Iterator it = this.triggers.iterator(); it.hasNext();) {
|
||||
Trigger trigger = (Trigger) it.next();
|
||||
addTriggerToScheduler(trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (Throwable ex) {
|
||||
if (transactionStatus != null) {
|
||||
try {
|
||||
this.transactionManager.rollback(transactionStatus);
|
||||
}
|
||||
catch (TransactionException tex) {
|
||||
logger.error("Job registration exception overridden by rollback exception", ex);
|
||||
throw tex;
|
||||
}
|
||||
}
|
||||
if (ex instanceof SchedulerException) {
|
||||
throw (SchedulerException) ex;
|
||||
}
|
||||
if (ex instanceof Exception) {
|
||||
throw new SchedulerException(
|
||||
"Registration of jobs and triggers failed: " + ex.getMessage(), (Exception) ex);
|
||||
}
|
||||
throw new SchedulerException("Registration of jobs and triggers failed: " + ex.getMessage());
|
||||
}
|
||||
|
||||
if (transactionStatus != null) {
|
||||
this.transactionManager.commit(transactionStatus);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given job to the Scheduler, if it doesn't already exist.
|
||||
* Overwrites the job in any case if "overwriteExistingJobs" is set.
|
||||
* @param jobDetail the job to add
|
||||
* @return <code>true</code> if the job was actually added,
|
||||
* <code>false</code> if it already existed before
|
||||
* @see #setOverwriteExistingJobs
|
||||
*/
|
||||
private boolean addJobToScheduler(JobDetail jobDetail) throws SchedulerException {
|
||||
if (this.overwriteExistingJobs ||
|
||||
getScheduler().getJobDetail(jobDetail.getName(), jobDetail.getGroup()) == null) {
|
||||
getScheduler().addJob(jobDetail, true);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given trigger to the Scheduler, if it doesn't already exist.
|
||||
* Overwrites the trigger in any case if "overwriteExistingJobs" is set.
|
||||
* @param trigger the trigger to add
|
||||
* @return <code>true</code> if the trigger was actually added,
|
||||
* <code>false</code> if it already existed before
|
||||
* @see #setOverwriteExistingJobs
|
||||
*/
|
||||
private boolean addTriggerToScheduler(Trigger trigger) throws SchedulerException {
|
||||
boolean triggerExists = (getScheduler().getTrigger(trigger.getName(), trigger.getGroup()) != null);
|
||||
if (!triggerExists || this.overwriteExistingJobs) {
|
||||
// Check if the Trigger is aware of an associated JobDetail.
|
||||
if (trigger instanceof JobDetailAwareTrigger) {
|
||||
JobDetail jobDetail = ((JobDetailAwareTrigger) trigger).getJobDetail();
|
||||
// Automatically register the JobDetail too.
|
||||
if (!this.jobDetails.contains(jobDetail) && addJobToScheduler(jobDetail)) {
|
||||
this.jobDetails.add(jobDetail);
|
||||
}
|
||||
}
|
||||
if (!triggerExists) {
|
||||
try {
|
||||
getScheduler().scheduleJob(trigger);
|
||||
}
|
||||
catch (ObjectAlreadyExistsException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Unexpectedly found existing trigger, assumably due to cluster race condition: " +
|
||||
ex.getMessage() + " - can safely be ignored");
|
||||
}
|
||||
if (this.overwriteExistingJobs) {
|
||||
getScheduler().rescheduleJob(trigger.getName(), trigger.getGroup(), trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
getScheduler().rescheduleJob(trigger.getName(), trigger.getGroup(), trigger);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register all specified listeners with the Scheduler.
|
||||
*/
|
||||
protected void registerListeners() throws SchedulerException {
|
||||
if (this.schedulerListeners != null) {
|
||||
for (int i = 0; i < this.schedulerListeners.length; i++) {
|
||||
getScheduler().addSchedulerListener(this.schedulerListeners[i]);
|
||||
}
|
||||
}
|
||||
if (this.globalJobListeners != null) {
|
||||
for (int i = 0; i < this.globalJobListeners.length; i++) {
|
||||
getScheduler().addGlobalJobListener(this.globalJobListeners[i]);
|
||||
}
|
||||
}
|
||||
if (this.jobListeners != null) {
|
||||
for (int i = 0; i < this.jobListeners.length; i++) {
|
||||
getScheduler().addJobListener(this.jobListeners[i]);
|
||||
}
|
||||
}
|
||||
if (this.globalTriggerListeners != null) {
|
||||
for (int i = 0; i < this.globalTriggerListeners.length; i++) {
|
||||
getScheduler().addGlobalTriggerListener(this.globalTriggerListeners[i]);
|
||||
}
|
||||
}
|
||||
if (this.triggerListeners != null) {
|
||||
for (int i = 0; i < this.triggerListeners.length; i++) {
|
||||
getScheduler().addTriggerListener(this.triggerListeners[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Template method that determines the Scheduler to operate on.
|
||||
* To be implemented by subclasses.
|
||||
*/
|
||||
protected abstract Scheduler getScheduler();
|
||||
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.quartz.impl.SchedulerRepository;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
|
||||
/**
|
||||
* Spring bean-style class for accessing a Quartz Scheduler, i.e. for registering jobs,
|
||||
* triggers and listeners on a given {@link org.quartz.Scheduler} instance.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.6
|
||||
* @see #setScheduler
|
||||
* @see #setSchedulerName
|
||||
*/
|
||||
public class SchedulerAccessorBean extends SchedulerAccessor implements BeanFactoryAware, InitializingBean {
|
||||
|
||||
private String schedulerName;
|
||||
|
||||
private Scheduler scheduler;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the Quartz Scheduler to operate on via its scheduler name in the Spring
|
||||
* application context or also in the Quartz {@link org.quartz.impl.SchedulerRepository}.
|
||||
* <p>Schedulers can be registered in the repository through custom bootstrapping,
|
||||
* e.g. via the {@link org.quartz.impl.StdSchedulerFactory} or
|
||||
* {@link org.quartz.impl.DirectSchedulerFactory} factory classes.
|
||||
* However, in general, it's preferable to use Spring's {@link SchedulerFactoryBean}
|
||||
* which includes the job/trigger/listener capabilities of this accessor as well.
|
||||
*/
|
||||
public void setSchedulerName(String schedulerName) {
|
||||
this.schedulerName = schedulerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the Quartz Scheduler instance to operate on.
|
||||
*/
|
||||
public void setScheduler(Scheduler scheduler) {
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Quartz Scheduler instance that this accessor operates on.
|
||||
*/
|
||||
public Scheduler getScheduler() {
|
||||
return this.scheduler;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws SchedulerException {
|
||||
if (this.scheduler == null) {
|
||||
if (this.schedulerName != null) {
|
||||
this.scheduler = findScheduler(this.schedulerName);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("No Scheduler specified");
|
||||
}
|
||||
}
|
||||
registerListeners();
|
||||
registerJobsAndTriggers();
|
||||
}
|
||||
|
||||
protected Scheduler findScheduler(String schedulerName) throws SchedulerException {
|
||||
if (this.beanFactory instanceof ListableBeanFactory) {
|
||||
ListableBeanFactory lbf = (ListableBeanFactory) this.beanFactory;
|
||||
String[] beanNames = lbf.getBeanNamesForType(Scheduler.class);
|
||||
for (int i = 0; i < beanNames.length; i++) {
|
||||
Scheduler schedulerBean = (Scheduler) lbf.getBean(beanNames[i]);
|
||||
if (schedulerName.equals(schedulerBean.getSchedulerName())) {
|
||||
return schedulerBean;
|
||||
}
|
||||
}
|
||||
}
|
||||
Scheduler schedulerInRepo = SchedulerRepository.getInstance().lookup(schedulerName);
|
||||
if (schedulerInRepo == null) {
|
||||
throw new IllegalStateException("No Scheduler named '" + schedulerName + "' found");
|
||||
}
|
||||
return schedulerInRepo;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import org.quartz.SchedulerContext;
|
||||
|
||||
/**
|
||||
* Callback interface to be implemented by Spring-managed
|
||||
* Quartz artifacts that need access to the SchedulerContext
|
||||
* (without having natural access to it).
|
||||
*
|
||||
* <p>Currently only supported for custom JobFactory implementations
|
||||
* that are passed in via Spring's SchedulerFactoryBean.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see org.quartz.spi.JobFactory
|
||||
* @see SchedulerFactoryBean#setJobFactory
|
||||
*/
|
||||
public interface SchedulerContextAware {
|
||||
|
||||
/**
|
||||
* Set the SchedulerContext of the current Quartz Scheduler.
|
||||
* @see org.quartz.Scheduler#getContext()
|
||||
*/
|
||||
void setSchedulerContext(SchedulerContext schedulerContext);
|
||||
|
||||
}
|
|
@ -0,0 +1,728 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.quartz.SchedulerFactory;
|
||||
import org.quartz.impl.RemoteScheduler;
|
||||
import org.quartz.impl.SchedulerRepository;
|
||||
import org.quartz.impl.StdSchedulerFactory;
|
||||
import org.quartz.simpl.SimpleThreadPool;
|
||||
import org.quartz.spi.JobFactory;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.scheduling.SchedulingException;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} that creates and configures a Quartz {@link org.quartz.Scheduler},
|
||||
* manages its lifecycle as part of the Spring application context, and exposes the
|
||||
* Scheduler as bean reference for dependency injection.
|
||||
*
|
||||
* <p>Allows registration of JobDetails, Calendars and Triggers, automatically
|
||||
* starting the scheduler on initialization and shutting it down on destruction.
|
||||
* In scenarios that just require static registration of jobs at startup, there
|
||||
* is no need to access the Scheduler instance itself in application code.
|
||||
*
|
||||
* <p>For dynamic registration of jobs at runtime, use a bean reference to
|
||||
* this SchedulerFactoryBean to get direct access to the Quartz Scheduler
|
||||
* (<code>org.quartz.Scheduler</code>). This allows you to create new jobs
|
||||
* and triggers, and also to control and monitor the entire Scheduler.
|
||||
*
|
||||
* <p>Note that Quartz instantiates a new Job for each execution, in
|
||||
* contrast to Timer which uses a TimerTask instance that is shared
|
||||
* between repeated executions. Just JobDetail descriptors are shared.
|
||||
*
|
||||
* <p>When using persistent jobs, it is strongly recommended to perform all
|
||||
* operations on the Scheduler within Spring-managed (or plain JTA) transactions.
|
||||
* Else, database locking will not properly work and might even break.
|
||||
* (See {@link #setDataSource setDataSource} javadoc for details.)
|
||||
*
|
||||
* <p>The preferred way to achieve transactional execution is to demarcate
|
||||
* declarative transactions at the business facade level, which will
|
||||
* automatically apply to Scheduler operations performed within those scopes.
|
||||
* Alternatively, you may add transactional advice for the Scheduler itself.
|
||||
*
|
||||
* <p><b>Note:</b> This version of Spring's SchedulerFactoryBean requires
|
||||
* Quartz 1.5.x or 1.6.x. The "jobSchedulingDataLocation" feature requires
|
||||
* Quartz 1.6.1 or higher (as of Spring 2.5.5).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 18.02.2004
|
||||
* @see #setDataSource
|
||||
* @see org.quartz.Scheduler
|
||||
* @see org.quartz.SchedulerFactory
|
||||
* @see org.quartz.impl.StdSchedulerFactory
|
||||
* @see org.springframework.transaction.interceptor.TransactionProxyFactoryBean
|
||||
*/
|
||||
public class SchedulerFactoryBean extends SchedulerAccessor
|
||||
implements FactoryBean, BeanNameAware, ApplicationContextAware, InitializingBean, DisposableBean, Lifecycle {
|
||||
|
||||
public static final String PROP_THREAD_COUNT = "org.quartz.threadPool.threadCount";
|
||||
|
||||
public static final int DEFAULT_THREAD_COUNT = 10;
|
||||
|
||||
|
||||
private static final ThreadLocal configTimeResourceLoaderHolder = new ThreadLocal();
|
||||
|
||||
private static final ThreadLocal configTimeTaskExecutorHolder = new ThreadLocal();
|
||||
|
||||
private static final ThreadLocal configTimeDataSourceHolder = new ThreadLocal();
|
||||
|
||||
private static final ThreadLocal configTimeNonTransactionalDataSourceHolder = new ThreadLocal();
|
||||
|
||||
/**
|
||||
* Return the ResourceLoader for the currently configured Quartz Scheduler,
|
||||
* to be used by ResourceLoaderClassLoadHelper.
|
||||
* <p>This instance will be set before initialization of the corresponding
|
||||
* Scheduler, and reset immediately afterwards. It is thus only available
|
||||
* during configuration.
|
||||
* @see #setApplicationContext
|
||||
* @see ResourceLoaderClassLoadHelper
|
||||
*/
|
||||
public static ResourceLoader getConfigTimeResourceLoader() {
|
||||
return (ResourceLoader) configTimeResourceLoaderHolder.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the TaskExecutor for the currently configured Quartz Scheduler,
|
||||
* to be used by LocalTaskExecutorThreadPool.
|
||||
* <p>This instance will be set before initialization of the corresponding
|
||||
* Scheduler, and reset immediately afterwards. It is thus only available
|
||||
* during configuration.
|
||||
* @see #setTaskExecutor
|
||||
* @see LocalTaskExecutorThreadPool
|
||||
*/
|
||||
public static TaskExecutor getConfigTimeTaskExecutor() {
|
||||
return (TaskExecutor) configTimeTaskExecutorHolder.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DataSource for the currently configured Quartz Scheduler,
|
||||
* to be used by LocalDataSourceJobStore.
|
||||
* <p>This instance will be set before initialization of the corresponding
|
||||
* Scheduler, and reset immediately afterwards. It is thus only available
|
||||
* during configuration.
|
||||
* @see #setDataSource
|
||||
* @see LocalDataSourceJobStore
|
||||
*/
|
||||
public static DataSource getConfigTimeDataSource() {
|
||||
return (DataSource) configTimeDataSourceHolder.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the non-transactional DataSource for the currently configured
|
||||
* Quartz Scheduler, to be used by LocalDataSourceJobStore.
|
||||
* <p>This instance will be set before initialization of the corresponding
|
||||
* Scheduler, and reset immediately afterwards. It is thus only available
|
||||
* during configuration.
|
||||
* @see #setNonTransactionalDataSource
|
||||
* @see LocalDataSourceJobStore
|
||||
*/
|
||||
public static DataSource getConfigTimeNonTransactionalDataSource() {
|
||||
return (DataSource) configTimeNonTransactionalDataSourceHolder.get();
|
||||
}
|
||||
|
||||
|
||||
private Class schedulerFactoryClass = StdSchedulerFactory.class;
|
||||
|
||||
private String schedulerName;
|
||||
|
||||
private Resource configLocation;
|
||||
|
||||
private Properties quartzProperties;
|
||||
|
||||
|
||||
private TaskExecutor taskExecutor;
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
private DataSource nonTransactionalDataSource;
|
||||
|
||||
|
||||
private Map schedulerContextMap;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private String applicationContextSchedulerContextKey;
|
||||
|
||||
private JobFactory jobFactory;
|
||||
|
||||
private boolean jobFactorySet = false;
|
||||
|
||||
|
||||
private boolean autoStartup = true;
|
||||
|
||||
private int startupDelay = 0;
|
||||
|
||||
private boolean exposeSchedulerInRepository = false;
|
||||
|
||||
private boolean waitForJobsToCompleteOnShutdown = false;
|
||||
|
||||
|
||||
private Scheduler scheduler;
|
||||
|
||||
|
||||
/**
|
||||
* Set the Quartz SchedulerFactory implementation to use.
|
||||
* <p>Default is StdSchedulerFactory, reading in the standard
|
||||
* <code>quartz.properties</code> from <code>quartz.jar</code>.
|
||||
* To use custom Quartz properties, specify the "configLocation"
|
||||
* or "quartzProperties" bean property on this FactoryBean.
|
||||
* @see org.quartz.impl.StdSchedulerFactory
|
||||
* @see #setConfigLocation
|
||||
* @see #setQuartzProperties
|
||||
*/
|
||||
public void setSchedulerFactoryClass(Class schedulerFactoryClass) {
|
||||
if (schedulerFactoryClass == null || !SchedulerFactory.class.isAssignableFrom(schedulerFactoryClass)) {
|
||||
throw new IllegalArgumentException("schedulerFactoryClass must implement [org.quartz.SchedulerFactory]");
|
||||
}
|
||||
this.schedulerFactoryClass = schedulerFactoryClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the Scheduler to create via the SchedulerFactory.
|
||||
* <p>If not specified, the bean name will be used as default scheduler name.
|
||||
* @see #setBeanName
|
||||
* @see org.quartz.SchedulerFactory#getScheduler()
|
||||
* @see org.quartz.SchedulerFactory#getScheduler(String)
|
||||
*/
|
||||
public void setSchedulerName(String schedulerName) {
|
||||
this.schedulerName = schedulerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the location of the Quartz properties config file, for example
|
||||
* as classpath resource "classpath:quartz.properties".
|
||||
* <p>Note: Can be omitted when all necessary properties are specified
|
||||
* locally via this bean, or when relying on Quartz' default configuration.
|
||||
* @see #setQuartzProperties
|
||||
*/
|
||||
public void setConfigLocation(Resource configLocation) {
|
||||
this.configLocation = configLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Quartz properties, like "org.quartz.threadPool.class".
|
||||
* <p>Can be used to override values in a Quartz properties config file,
|
||||
* or to specify all necessary properties locally.
|
||||
* @see #setConfigLocation
|
||||
*/
|
||||
public void setQuartzProperties(Properties quartzProperties) {
|
||||
this.quartzProperties = quartzProperties;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the Spring TaskExecutor to use as Quartz backend.
|
||||
* Exposed as thread pool through the Quartz SPI.
|
||||
* <p>Can be used to assign a JDK 1.5 ThreadPoolExecutor or a CommonJ
|
||||
* WorkManager as Quartz backend, to avoid Quartz's manual thread creation.
|
||||
* <p>By default, a Quartz SimpleThreadPool will be used, configured through
|
||||
* the corresponding Quartz properties.
|
||||
* @see #setQuartzProperties
|
||||
* @see LocalTaskExecutorThreadPool
|
||||
* @see org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
|
||||
* @see org.springframework.scheduling.commonj.WorkManagerTaskExecutor
|
||||
*/
|
||||
public void setTaskExecutor(TaskExecutor taskExecutor) {
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default DataSource to be used by the Scheduler. If set,
|
||||
* this will override corresponding settings in Quartz properties.
|
||||
* <p>Note: If this is set, the Quartz settings should not define
|
||||
* a job store "dataSource" to avoid meaningless double configuration.
|
||||
* <p>A Spring-specific subclass of Quartz' JobStoreCMT will be used.
|
||||
* It is therefore strongly recommended to perform all operations on
|
||||
* the Scheduler within Spring-managed (or plain JTA) transactions.
|
||||
* Else, database locking will not properly work and might even break
|
||||
* (e.g. if trying to obtain a lock on Oracle without a transaction).
|
||||
* <p>Supports both transactional and non-transactional DataSource access.
|
||||
* With a non-XA DataSource and local Spring transactions, a single DataSource
|
||||
* argument is sufficient. In case of an XA DataSource and global JTA transactions,
|
||||
* SchedulerFactoryBean's "nonTransactionalDataSource" property should be set,
|
||||
* passing in a non-XA DataSource that will not participate in global transactions.
|
||||
* @see #setNonTransactionalDataSource
|
||||
* @see #setQuartzProperties
|
||||
* @see #setTransactionManager
|
||||
* @see LocalDataSourceJobStore
|
||||
*/
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DataSource to be used by the Scheduler <i>for non-transactional access</i>.
|
||||
* <p>This is only necessary if the default DataSource is an XA DataSource that will
|
||||
* always participate in transactions: A non-XA version of that DataSource should
|
||||
* be specified as "nonTransactionalDataSource" in such a scenario.
|
||||
* <p>This is not relevant with a local DataSource instance and Spring transactions.
|
||||
* Specifying a single default DataSource as "dataSource" is sufficient there.
|
||||
* @see #setDataSource
|
||||
* @see LocalDataSourceJobStore
|
||||
*/
|
||||
public void setNonTransactionalDataSource(DataSource nonTransactionalDataSource) {
|
||||
this.nonTransactionalDataSource = nonTransactionalDataSource;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register objects in the Scheduler context via a given Map.
|
||||
* These objects will be available to any Job that runs in this Scheduler.
|
||||
* <p>Note: When using persistent Jobs whose JobDetail will be kept in the
|
||||
* database, do not put Spring-managed beans or an ApplicationContext
|
||||
* reference into the JobDataMap but rather into the SchedulerContext.
|
||||
* @param schedulerContextAsMap Map with String keys and any objects as
|
||||
* values (for example Spring-managed beans)
|
||||
* @see JobDetailBean#setJobDataAsMap
|
||||
*/
|
||||
public void setSchedulerContextAsMap(Map schedulerContextAsMap) {
|
||||
this.schedulerContextMap = schedulerContextAsMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the key of an ApplicationContext reference to expose in the
|
||||
* SchedulerContext, for example "applicationContext". Default is none.
|
||||
* Only applicable when running in a Spring ApplicationContext.
|
||||
* <p>Note: When using persistent Jobs whose JobDetail will be kept in the
|
||||
* database, do not put an ApplicationContext reference into the JobDataMap
|
||||
* but rather into the SchedulerContext.
|
||||
* <p>In case of a QuartzJobBean, the reference will be applied to the Job
|
||||
* instance as bean property. An "applicationContext" attribute will
|
||||
* correspond to a "setApplicationContext" method in that scenario.
|
||||
* <p>Note that BeanFactory callback interfaces like ApplicationContextAware
|
||||
* are not automatically applied to Quartz Job instances, because Quartz
|
||||
* itself is reponsible for the lifecycle of its Jobs.
|
||||
* @see JobDetailBean#setApplicationContextJobDataKey
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
*/
|
||||
public void setApplicationContextSchedulerContextKey(String applicationContextSchedulerContextKey) {
|
||||
this.applicationContextSchedulerContextKey = applicationContextSchedulerContextKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Quartz JobFactory to use for this Scheduler.
|
||||
* <p>Default is Spring's {@link AdaptableJobFactory}, which supports
|
||||
* {@link java.lang.Runnable} objects as well as standard Quartz
|
||||
* {@link org.quartz.Job} instances. Note that this default only applies
|
||||
* to a <i>local</i> Scheduler, not to a RemoteScheduler (where setting
|
||||
* a custom JobFactory is not supported by Quartz).
|
||||
* <p>Specify an instance of Spring's {@link SpringBeanJobFactory} here
|
||||
* (typically as an inner bean definition) to automatically populate a job's
|
||||
* bean properties from the specified job data map and scheduler context.
|
||||
* @see AdaptableJobFactory
|
||||
* @see SpringBeanJobFactory
|
||||
*/
|
||||
public void setJobFactory(JobFactory jobFactory) {
|
||||
this.jobFactory = jobFactory;
|
||||
this.jobFactorySet = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to automatically start the scheduler after initialization.
|
||||
* <p>Default is "true"; set this to "false" to allow for manual startup.
|
||||
*/
|
||||
public void setAutoStartup(boolean autoStartup) {
|
||||
this.autoStartup = autoStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of seconds to wait after initialization before
|
||||
* starting the scheduler asynchronously. Default is 0, meaning
|
||||
* immediate synchronous startup on initialization of this bean.
|
||||
* <p>Setting this to 10 or 20 seconds makes sense if no jobs
|
||||
* should be run before the entire application has started up.
|
||||
*/
|
||||
public void setStartupDelay(int startupDelay) {
|
||||
this.startupDelay = startupDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to expose the Spring-managed {@link Scheduler} instance in the
|
||||
* Quartz {@link SchedulerRepository}. Default is "false", since the Spring-managed
|
||||
* Scheduler is usually exclusively intended for access within the Spring context.
|
||||
* <p>Switch this flag to "true" in order to expose the Scheduler globally.
|
||||
* This is not recommended unless you have an existing Spring application that
|
||||
* relies on this behavior. Note that such global exposure was the accidental
|
||||
* default in earlier Spring versions; this has been fixed as of Spring 2.5.6.
|
||||
*/
|
||||
public void setExposeSchedulerInRepository(boolean exposeSchedulerInRepository) {
|
||||
this.exposeSchedulerInRepository = exposeSchedulerInRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to wait for running jobs to complete on shutdown.
|
||||
* <p>Default is "false". Switch this to "true" if you prefer
|
||||
* fully completed jobs at the expense of a longer shutdown phase.
|
||||
* @see org.quartz.Scheduler#shutdown(boolean)
|
||||
*/
|
||||
public void setWaitForJobsToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) {
|
||||
this.waitForJobsToCompleteOnShutdown = waitForJobsToCompleteOnShutdown;
|
||||
}
|
||||
|
||||
|
||||
public void setBeanName(String name) {
|
||||
if (this.schedulerName == null) {
|
||||
this.schedulerName = name;
|
||||
}
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of InitializingBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.applicationContext != null && this.resourceLoader == null) {
|
||||
this.resourceLoader = this.applicationContext;
|
||||
}
|
||||
|
||||
if (this.dataSource == null && this.nonTransactionalDataSource != null) {
|
||||
this.dataSource = this.nonTransactionalDataSource;
|
||||
}
|
||||
|
||||
// Create SchedulerFactory instance.
|
||||
SchedulerFactory schedulerFactory = (SchedulerFactory) BeanUtils.instantiateClass(this.schedulerFactoryClass);
|
||||
|
||||
initSchedulerFactory(schedulerFactory);
|
||||
|
||||
if (this.resourceLoader != null) {
|
||||
// Make given ResourceLoader available for SchedulerFactory configuration.
|
||||
configTimeResourceLoaderHolder.set(this.resourceLoader);
|
||||
}
|
||||
if (this.taskExecutor != null) {
|
||||
// Make given TaskExecutor available for SchedulerFactory configuration.
|
||||
configTimeTaskExecutorHolder.set(this.taskExecutor);
|
||||
}
|
||||
if (this.dataSource != null) {
|
||||
// Make given DataSource available for SchedulerFactory configuration.
|
||||
configTimeDataSourceHolder.set(this.dataSource);
|
||||
}
|
||||
if (this.nonTransactionalDataSource != null) {
|
||||
// Make given non-transactional DataSource available for SchedulerFactory configuration.
|
||||
configTimeNonTransactionalDataSourceHolder.set(this.nonTransactionalDataSource);
|
||||
}
|
||||
|
||||
|
||||
// Get Scheduler instance from SchedulerFactory.
|
||||
try {
|
||||
this.scheduler = createScheduler(schedulerFactory, this.schedulerName);
|
||||
populateSchedulerContext();
|
||||
|
||||
if (!this.jobFactorySet && !(this.scheduler instanceof RemoteScheduler)) {
|
||||
// Use AdaptableJobFactory as default for a local Scheduler, unless when
|
||||
// explicitly given a null value through the "jobFactory" bean property.
|
||||
this.jobFactory = new AdaptableJobFactory();
|
||||
}
|
||||
if (this.jobFactory != null) {
|
||||
if (this.jobFactory instanceof SchedulerContextAware) {
|
||||
((SchedulerContextAware) this.jobFactory).setSchedulerContext(this.scheduler.getContext());
|
||||
}
|
||||
this.scheduler.setJobFactory(this.jobFactory);
|
||||
}
|
||||
}
|
||||
|
||||
finally {
|
||||
if (this.resourceLoader != null) {
|
||||
configTimeResourceLoaderHolder.set(null);
|
||||
}
|
||||
if (this.taskExecutor != null) {
|
||||
configTimeTaskExecutorHolder.set(null);
|
||||
}
|
||||
if (this.dataSource != null) {
|
||||
configTimeDataSourceHolder.set(null);
|
||||
}
|
||||
if (this.nonTransactionalDataSource != null) {
|
||||
configTimeNonTransactionalDataSourceHolder.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
registerListeners();
|
||||
registerJobsAndTriggers();
|
||||
|
||||
// Start Scheduler immediately, if demanded.
|
||||
if (this.autoStartup) {
|
||||
startScheduler(this.scheduler, this.startupDelay);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load and/or apply Quartz properties to the given SchedulerFactory.
|
||||
* @param schedulerFactory the SchedulerFactory to initialize
|
||||
*/
|
||||
private void initSchedulerFactory(SchedulerFactory schedulerFactory)
|
||||
throws SchedulerException, IOException {
|
||||
|
||||
if (!(schedulerFactory instanceof StdSchedulerFactory)) {
|
||||
if (this.configLocation != null || this.quartzProperties != null ||
|
||||
this.taskExecutor != null || this.dataSource != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"StdSchedulerFactory required for applying Quartz properties: " + schedulerFactory);
|
||||
}
|
||||
// Otherwise assume that no initialization is necessary...
|
||||
return;
|
||||
}
|
||||
|
||||
Properties mergedProps = new Properties();
|
||||
|
||||
if (this.resourceLoader != null) {
|
||||
mergedProps.setProperty(StdSchedulerFactory.PROP_SCHED_CLASS_LOAD_HELPER_CLASS,
|
||||
ResourceLoaderClassLoadHelper.class.getName());
|
||||
}
|
||||
|
||||
if (this.taskExecutor != null) {
|
||||
mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS,
|
||||
LocalTaskExecutorThreadPool.class.getName());
|
||||
}
|
||||
else {
|
||||
// Set necessary default properties here, as Quartz will not apply
|
||||
// its default configuration when explicitly given properties.
|
||||
mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, SimpleThreadPool.class.getName());
|
||||
mergedProps.setProperty(PROP_THREAD_COUNT, Integer.toString(DEFAULT_THREAD_COUNT));
|
||||
}
|
||||
|
||||
if (this.configLocation != null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Loading Quartz config from [" + this.configLocation + "]");
|
||||
}
|
||||
PropertiesLoaderUtils.fillProperties(mergedProps, this.configLocation);
|
||||
}
|
||||
|
||||
CollectionUtils.mergePropertiesIntoMap(this.quartzProperties, mergedProps);
|
||||
|
||||
if (this.dataSource != null) {
|
||||
mergedProps.put(StdSchedulerFactory.PROP_JOB_STORE_CLASS, LocalDataSourceJobStore.class.getName());
|
||||
}
|
||||
|
||||
// Make sure to set the scheduler name as configured in the Spring configuration.
|
||||
if (this.schedulerName != null) {
|
||||
mergedProps.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, this.schedulerName);
|
||||
}
|
||||
|
||||
((StdSchedulerFactory) schedulerFactory).initialize(mergedProps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the Scheduler instance for the given factory and scheduler name.
|
||||
* Called by {@link #afterPropertiesSet}.
|
||||
* <p>The default implementation invokes SchedulerFactory's <code>getScheduler</code>
|
||||
* method. Can be overridden for custom Scheduler creation.
|
||||
* @param schedulerFactory the factory to create the Scheduler with
|
||||
* @param schedulerName the name of the scheduler to create
|
||||
* @return the Scheduler instance
|
||||
* @throws SchedulerException if thrown by Quartz methods
|
||||
* @see #afterPropertiesSet
|
||||
* @see org.quartz.SchedulerFactory#getScheduler
|
||||
*/
|
||||
protected Scheduler createScheduler(SchedulerFactory schedulerFactory, String schedulerName)
|
||||
throws SchedulerException {
|
||||
|
||||
// Override thread context ClassLoader to work around naive Quartz ClassLoadHelper loading.
|
||||
Thread currentThread = Thread.currentThread();
|
||||
ClassLoader threadContextClassLoader = currentThread.getContextClassLoader();
|
||||
boolean overrideClassLoader = (this.resourceLoader != null &&
|
||||
!this.resourceLoader.getClassLoader().equals(threadContextClassLoader));
|
||||
if (overrideClassLoader) {
|
||||
currentThread.setContextClassLoader(this.resourceLoader.getClassLoader());
|
||||
}
|
||||
try {
|
||||
SchedulerRepository repository = SchedulerRepository.getInstance();
|
||||
synchronized (repository) {
|
||||
Scheduler existingScheduler = (schedulerName != null ? repository.lookup(schedulerName) : null);
|
||||
Scheduler newScheduler = schedulerFactory.getScheduler();
|
||||
if (newScheduler == existingScheduler) {
|
||||
throw new IllegalStateException("Active Scheduler of name '" + schedulerName + "' already registered " +
|
||||
"in Quartz SchedulerRepository. Cannot create a new Spring-managed Scheduler of the same name!");
|
||||
}
|
||||
if (!this.exposeSchedulerInRepository) {
|
||||
// Need to remove it in this case, since Quartz shares the Scheduler instance by default!
|
||||
SchedulerRepository.getInstance().remove(newScheduler.getSchedulerName());
|
||||
}
|
||||
return newScheduler;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (overrideClassLoader) {
|
||||
// Reset original thread context ClassLoader.
|
||||
currentThread.setContextClassLoader(threadContextClassLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose the specified context attributes and/or the current
|
||||
* ApplicationContext in the Quartz SchedulerContext.
|
||||
*/
|
||||
private void populateSchedulerContext() throws SchedulerException {
|
||||
// Put specified objects into Scheduler context.
|
||||
if (this.schedulerContextMap != null) {
|
||||
this.scheduler.getContext().putAll(this.schedulerContextMap);
|
||||
}
|
||||
|
||||
// Register ApplicationContext in Scheduler context.
|
||||
if (this.applicationContextSchedulerContextKey != null) {
|
||||
if (this.applicationContext == null) {
|
||||
throw new IllegalStateException(
|
||||
"SchedulerFactoryBean needs to be set up in an ApplicationContext " +
|
||||
"to be able to handle an 'applicationContextSchedulerContextKey'");
|
||||
}
|
||||
this.scheduler.getContext().put(this.applicationContextSchedulerContextKey, this.applicationContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start the Quartz Scheduler, respecting the "startupDelay" setting.
|
||||
* @param scheduler the Scheduler to start
|
||||
* @param startupDelay the number of seconds to wait before starting
|
||||
* the Scheduler asynchronously
|
||||
*/
|
||||
protected void startScheduler(final Scheduler scheduler, final int startupDelay) throws SchedulerException {
|
||||
if (startupDelay <= 0) {
|
||||
logger.info("Starting Quartz Scheduler now");
|
||||
scheduler.start();
|
||||
}
|
||||
else {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Will start Quartz Scheduler [" + scheduler.getSchedulerName() +
|
||||
"] in " + startupDelay + " seconds");
|
||||
}
|
||||
Thread schedulerThread = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(startupDelay * 1000);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
// simply proceed
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Starting Quartz Scheduler now, after delay of " + startupDelay + " seconds");
|
||||
}
|
||||
try {
|
||||
scheduler.start();
|
||||
}
|
||||
catch (SchedulerException ex) {
|
||||
throw new SchedulingException("Could not start Quartz Scheduler after delay", ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
schedulerThread.setName("Quartz Scheduler [" + scheduler.getSchedulerName() + "]");
|
||||
schedulerThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of FactoryBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public Scheduler getScheduler() {
|
||||
return this.scheduler;
|
||||
}
|
||||
|
||||
public Object getObject() {
|
||||
return this.scheduler;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return (this.scheduler != null) ? this.scheduler.getClass() : Scheduler.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of Lifecycle interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void start() throws SchedulingException {
|
||||
if (this.scheduler != null) {
|
||||
try {
|
||||
this.scheduler.start();
|
||||
}
|
||||
catch (SchedulerException ex) {
|
||||
throw new SchedulingException("Could not start Quartz Scheduler", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() throws SchedulingException {
|
||||
if (this.scheduler != null) {
|
||||
try {
|
||||
this.scheduler.standby();
|
||||
}
|
||||
catch (SchedulerException ex) {
|
||||
throw new SchedulingException("Could not stop Quartz Scheduler", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() throws SchedulingException {
|
||||
if (this.scheduler != null) {
|
||||
try {
|
||||
return !this.scheduler.isInStandbyMode();
|
||||
}
|
||||
catch (SchedulerException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of DisposableBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Shut down the Quartz scheduler on bean factory shutdown,
|
||||
* stopping all scheduled jobs.
|
||||
*/
|
||||
public void destroy() throws SchedulerException {
|
||||
logger.info("Shutting down Quartz Scheduler");
|
||||
this.scheduler.shutdown(this.waitForJobsToCompleteOnShutdown);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import org.quartz.SchedulerConfigException;
|
||||
import org.quartz.simpl.SimpleThreadPool;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.scheduling.SchedulingException;
|
||||
import org.springframework.scheduling.SchedulingTaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Subclass of Quartz's SimpleThreadPool that implements Spring's
|
||||
* TaskExecutor interface and listens to Spring lifecycle callbacks.
|
||||
*
|
||||
* <p>Can be used as a thread-pooling TaskExecutor backend, in particular
|
||||
* on JDK <= 1.5 (where the JDK ThreadPoolExecutor isn't available yet).
|
||||
* Can be shared between a Quartz Scheduler (specified as "taskExecutor")
|
||||
* and other TaskExecutor users, or even used completely independent of
|
||||
* a Quartz Scheduler (as plain TaskExecutor backend).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see org.quartz.simpl.SimpleThreadPool
|
||||
* @see org.springframework.core.task.TaskExecutor
|
||||
* @see SchedulerFactoryBean#setTaskExecutor
|
||||
*/
|
||||
public class SimpleThreadPoolTaskExecutor extends SimpleThreadPool
|
||||
implements SchedulingTaskExecutor, InitializingBean, DisposableBean {
|
||||
|
||||
private boolean waitForJobsToCompleteOnShutdown = false;
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to wait for running jobs to complete on shutdown.
|
||||
* Default is "false".
|
||||
* @see org.quartz.simpl.SimpleThreadPool#shutdown(boolean)
|
||||
*/
|
||||
public void setWaitForJobsToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) {
|
||||
this.waitForJobsToCompleteOnShutdown = waitForJobsToCompleteOnShutdown;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws SchedulerConfigException {
|
||||
initialize();
|
||||
}
|
||||
|
||||
|
||||
public void execute(Runnable task) {
|
||||
Assert.notNull(task, "Runnable must not be null");
|
||||
if (!runInThread(task)) {
|
||||
throw new SchedulingException("Quartz SimpleThreadPool already shut down");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This task executor prefers short-lived work units.
|
||||
*/
|
||||
public boolean prefersShortLivedTasks() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void destroy() {
|
||||
shutdown(this.waitForJobsToCompleteOnShutdown);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SimpleTrigger;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Constants;
|
||||
|
||||
/**
|
||||
* Convenience subclass of Quartz's {@link org.quartz.SimpleTrigger}
|
||||
* class, making bean-style usage easier.
|
||||
*
|
||||
* <p>SimpleTrigger itself is already a JavaBean but lacks sensible defaults.
|
||||
* This class uses the Spring bean name as job name, the Quartz default group
|
||||
* ("DEFAULT") as job group, the current time as start time, and indefinite
|
||||
* repetition, if not specified.
|
||||
*
|
||||
* <p>This class will also register the trigger with the job name and group of
|
||||
* a given {@link org.quartz.JobDetail}. This allows {@link SchedulerFactoryBean}
|
||||
* to automatically register a trigger for the corresponding JobDetail,
|
||||
* instead of registering the JobDetail separately.
|
||||
*
|
||||
* <p><b>NOTE:</b> This convenience subclass does not work with trigger
|
||||
* persistence in Quartz 1.6, due to a change in Quartz's trigger handling.
|
||||
* Use Quartz 1.5 if you rely on trigger persistence based on this class,
|
||||
* or the standard Quartz {@link org.quartz.SimpleTrigger} class instead.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 18.02.2004
|
||||
* @see #setName
|
||||
* @see #setGroup
|
||||
* @see #setStartTime
|
||||
* @see #setJobName
|
||||
* @see #setJobGroup
|
||||
* @see #setJobDetail
|
||||
* @see SchedulerFactoryBean#setTriggers
|
||||
* @see SchedulerFactoryBean#setJobDetails
|
||||
* @see CronTriggerBean
|
||||
*/
|
||||
public class SimpleTriggerBean extends SimpleTrigger
|
||||
implements JobDetailAwareTrigger, BeanNameAware, InitializingBean {
|
||||
|
||||
/** Constants for the SimpleTrigger class */
|
||||
private static final Constants constants = new Constants(SimpleTrigger.class);
|
||||
|
||||
|
||||
private long startDelay = 0;
|
||||
|
||||
private JobDetail jobDetail;
|
||||
|
||||
private String beanName;
|
||||
|
||||
|
||||
public SimpleTriggerBean() {
|
||||
setRepeatCount(REPEAT_INDEFINITELY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register objects in the JobDataMap via a given Map.
|
||||
* <p>These objects will be available to this Trigger only,
|
||||
* in contrast to objects in the JobDetail's data map.
|
||||
* @param jobDataAsMap Map with String keys and any objects as values
|
||||
* (for example Spring-managed beans)
|
||||
* @see JobDetailBean#setJobDataAsMap
|
||||
*/
|
||||
public void setJobDataAsMap(Map jobDataAsMap) {
|
||||
getJobDataMap().putAll(jobDataAsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the misfire instruction via the name of the corresponding
|
||||
* constant in the {@link org.quartz.SimpleTrigger} class.
|
||||
* Default is <code>MISFIRE_INSTRUCTION_SMART_POLICY</code>.
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_FIRE_NOW
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
|
||||
* @see org.quartz.Trigger#MISFIRE_INSTRUCTION_SMART_POLICY
|
||||
*/
|
||||
public void setMisfireInstructionName(String constantName) {
|
||||
setMisfireInstruction(constants.asNumber(constantName).intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a list of TriggerListener names for this job, referring to
|
||||
* non-global TriggerListeners registered with the Scheduler.
|
||||
* <p>A TriggerListener name always refers to the name returned
|
||||
* by the TriggerListener implementation.
|
||||
* @see SchedulerFactoryBean#setTriggerListeners
|
||||
* @see org.quartz.TriggerListener#getName
|
||||
*/
|
||||
public void setTriggerListenerNames(String[] names) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
addTriggerListener(names[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the delay before starting the job for the first time.
|
||||
* The given number of milliseconds will be added to the current
|
||||
* time to calculate the start time. Default is 0.
|
||||
* <p>This delay will just be applied if no custom start time was
|
||||
* specified. However, in typical usage within a Spring context,
|
||||
* the start time will be the container startup time anyway.
|
||||
* Specifying a relative delay is appropriate in that case.
|
||||
* @see #setStartTime
|
||||
*/
|
||||
public void setStartDelay(long startDelay) {
|
||||
this.startDelay = startDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JobDetail that this trigger should be associated with.
|
||||
* <p>This is typically used with a bean reference if the JobDetail
|
||||
* is a Spring-managed bean. Alternatively, the trigger can also
|
||||
* be associated with a job by name and group.
|
||||
* @see #setJobName
|
||||
* @see #setJobGroup
|
||||
*/
|
||||
public void setJobDetail(JobDetail jobDetail) {
|
||||
this.jobDetail = jobDetail;
|
||||
}
|
||||
|
||||
public JobDetail getJobDetail() {
|
||||
return this.jobDetail;
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws ParseException {
|
||||
if (getName() == null) {
|
||||
setName(this.beanName);
|
||||
}
|
||||
if (getGroup() == null) {
|
||||
setGroup(Scheduler.DEFAULT_GROUP);
|
||||
}
|
||||
if (getStartTime() == null) {
|
||||
setStartTime(new Date(System.currentTimeMillis() + this.startDelay));
|
||||
}
|
||||
if (this.jobDetail != null) {
|
||||
setJobName(this.jobDetail.getName());
|
||||
setJobGroup(this.jobDetail.getGroup());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
import org.quartz.SchedulerContext;
|
||||
import org.quartz.spi.TriggerFiredBundle;
|
||||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyAccessorFactory;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
/**
|
||||
* Subclass of {@link AdaptableJobFactory} that also supports Spring-style
|
||||
* dependency injection on bean properties. This is essentially the direct
|
||||
* equivalent of Spring's {@link QuartzJobBean} in the shape of a
|
||||
* Quartz 1.5 {@link org.quartz.spi.JobFactory}.
|
||||
*
|
||||
* <p>Applies scheduler context, job data map and trigger data map entries
|
||||
* as bean property values. If no matching bean property is found, the entry
|
||||
* is by default simply ignored. This is analogous to QuartzJobBean's behavior.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see SchedulerFactoryBean#setJobFactory
|
||||
* @see QuartzJobBean
|
||||
*/
|
||||
public class SpringBeanJobFactory extends AdaptableJobFactory implements SchedulerContextAware {
|
||||
|
||||
private String[] ignoredUnknownProperties;
|
||||
|
||||
private SchedulerContext schedulerContext;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the unknown properties (not found in the bean) that should be ignored.
|
||||
* <p>Default is <code>null</code>, indicating that all unknown properties
|
||||
* should be ignored. Specify an empty array to throw an exception in case
|
||||
* of any unknown properties, or a list of property names that should be
|
||||
* ignored if there is no corresponding property found on the particular
|
||||
* job class (all other unknown properties will still trigger an exception).
|
||||
*/
|
||||
public void setIgnoredUnknownProperties(String[] ignoredUnknownProperties) {
|
||||
this.ignoredUnknownProperties = ignoredUnknownProperties;
|
||||
}
|
||||
|
||||
public void setSchedulerContext(SchedulerContext schedulerContext) {
|
||||
this.schedulerContext = schedulerContext;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the job instance, populating it with property values taken
|
||||
* from the scheduler context, job data map and trigger data map.
|
||||
*/
|
||||
protected Object createJobInstance(TriggerFiredBundle bundle) {
|
||||
Object job = BeanUtils.instantiateClass(bundle.getJobDetail().getJobClass());
|
||||
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job);
|
||||
if (isEligibleForPropertyPopulation(bw.getWrappedInstance())) {
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
if (this.schedulerContext != null) {
|
||||
pvs.addPropertyValues(this.schedulerContext);
|
||||
}
|
||||
pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap());
|
||||
pvs.addPropertyValues(bundle.getTrigger().getJobDataMap());
|
||||
if (this.ignoredUnknownProperties != null) {
|
||||
for (int i = 0; i < this.ignoredUnknownProperties.length; i++) {
|
||||
String propName = this.ignoredUnknownProperties[i];
|
||||
if (pvs.contains(propName) && !bw.isWritableProperty(propName)) {
|
||||
pvs.removePropertyValue(propName);
|
||||
}
|
||||
}
|
||||
bw.setPropertyValues(pvs);
|
||||
}
|
||||
else {
|
||||
bw.setPropertyValues(pvs, true);
|
||||
}
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given job object is eligible for having
|
||||
* its bean properties populated.
|
||||
* <p>The default implementation ignores {@link QuartzJobBean} instances,
|
||||
* which will inject bean properties themselves.
|
||||
* @param jobObject the job object to introspect
|
||||
* @see QuartzJobBean
|
||||
*/
|
||||
protected boolean isEligibleForPropertyPopulation(Object jobObject) {
|
||||
return (!(jobObject instanceof QuartzJobBean));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Support classes for the open source scheduler
|
||||
<a href="http://www.opensymphony.com/quartz">Quartz</a>,
|
||||
allowing to set up Quartz Schedulers, JobDetails and
|
||||
Triggers as beans in a Spring context. Also provides
|
||||
convenience classes for implementing Quartz Jobs.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,422 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.freemarker;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import freemarker.cache.FileTemplateLoader;
|
||||
import freemarker.cache.MultiTemplateLoader;
|
||||
import freemarker.cache.TemplateLoader;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.SimpleHash;
|
||||
import freemarker.template.TemplateException;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Factory that configures a FreeMarker Configuration. Can be used standalone, but
|
||||
* typically you will either use FreeMarkerConfigurationFactoryBean for preparing a
|
||||
* Configuration as bean reference, or FreeMarkerConfigurer for web views.
|
||||
*
|
||||
* <p>The optional "configLocation" property sets the location of a FreeMarker
|
||||
* properties file, within the current application. FreeMarker properties can be
|
||||
* overridden via "freemarkerSettings". All of these properties will be set by
|
||||
* calling FreeMarker's <code>Configuration.setSettings()</code> method and are
|
||||
* subject to constraints set by FreeMarker.
|
||||
*
|
||||
* <p>The "freemarkerVariables" property can be used to specify a Map of
|
||||
* shared variables that will be applied to the Configuration via the
|
||||
* <code>setAllSharedVariables()</code> method. Like <code>setSettings()</code>,
|
||||
* these entries are subject to FreeMarker constraints.
|
||||
*
|
||||
* <p>The simplest way to use this class is to specify a "templateLoaderPath";
|
||||
* FreeMarker does not need any further configuration then.
|
||||
*
|
||||
* <p>Note: Spring's FreeMarker support requires FreeMarker 2.3 or higher.
|
||||
*
|
||||
* @author Darren Davison
|
||||
* @author Juergen Hoeller
|
||||
* @since 03.03.2004
|
||||
* @see #setConfigLocation
|
||||
* @see #setFreemarkerSettings
|
||||
* @see #setFreemarkerVariables
|
||||
* @see #setTemplateLoaderPath
|
||||
* @see #createConfiguration
|
||||
* @see FreeMarkerConfigurationFactoryBean
|
||||
* @see org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer
|
||||
* @see freemarker.template.Configuration
|
||||
*/
|
||||
public class FreeMarkerConfigurationFactory {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Resource configLocation;
|
||||
|
||||
private Properties freemarkerSettings;
|
||||
|
||||
private Map freemarkerVariables;
|
||||
|
||||
private String defaultEncoding;
|
||||
|
||||
private final List templateLoaders = new ArrayList();
|
||||
|
||||
private List preTemplateLoaders;
|
||||
|
||||
private List postTemplateLoaders;
|
||||
|
||||
private String[] templateLoaderPaths;
|
||||
|
||||
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
private boolean preferFileSystemAccess = true;
|
||||
|
||||
|
||||
/**
|
||||
* Set the location of the FreeMarker config file.
|
||||
* Alternatively, you can specify all setting locally.
|
||||
* @see #setFreemarkerSettings
|
||||
* @see #setTemplateLoaderPath
|
||||
*/
|
||||
public void setConfigLocation(Resource resource) {
|
||||
configLocation = resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set properties that contain well-known FreeMarker keys which will be
|
||||
* passed to FreeMarker's <code>Configuration.setSettings</code> method.
|
||||
* @see freemarker.template.Configuration#setSettings
|
||||
*/
|
||||
public void setFreemarkerSettings(Properties settings) {
|
||||
this.freemarkerSettings = settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a Map that contains well-known FreeMarker objects which will be passed
|
||||
* to FreeMarker's <code>Configuration.setAllSharedVariables()</code> method.
|
||||
* @see freemarker.template.Configuration#setAllSharedVariables
|
||||
*/
|
||||
public void setFreemarkerVariables(Map variables) {
|
||||
this.freemarkerVariables = variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default encoding for the FreeMarker configuration.
|
||||
* If not specified, FreeMarker will use the platform file encoding.
|
||||
* <p>Used for template rendering unless there is an explicit encoding specified
|
||||
* for the rendering process (for example, on Spring's FreeMarkerView).
|
||||
* @see freemarker.template.Configuration#setDefaultEncoding
|
||||
* @see org.springframework.web.servlet.view.freemarker.FreeMarkerView#setEncoding
|
||||
*/
|
||||
public void setDefaultEncoding(String defaultEncoding) {
|
||||
this.defaultEncoding = defaultEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a List of <code>TemplateLoader<code>s that will be used to search
|
||||
* for templates. For example, one or more custom loaders such as database
|
||||
* loaders could be configured and injected here.
|
||||
* @deprecated as of Spring 2.0.1, in favor of the "preTemplateLoaders"
|
||||
* and "postTemplateLoaders" properties
|
||||
* @see #setPreTemplateLoaders
|
||||
* @see #setPostTemplateLoaders
|
||||
*/
|
||||
public void setTemplateLoaders(TemplateLoader[] templateLoaders) {
|
||||
if (templateLoaders != null) {
|
||||
this.templateLoaders.addAll(Arrays.asList(templateLoaders));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a List of <code>TemplateLoader<code>s that will be used to search
|
||||
* for templates. For example, one or more custom loaders such as database
|
||||
* loaders could be configured and injected here.
|
||||
* <p>The {@link TemplateLoader TemplateLoaders} specified here will be
|
||||
* registered <i>before</i> the default template loaders that this factory
|
||||
* registers (such as loaders for specified "templateLoaderPaths" or any
|
||||
* loaders registered in {@link #postProcessTemplateLoaders}).
|
||||
* @see #setTemplateLoaderPaths
|
||||
* @see #postProcessTemplateLoaders
|
||||
*/
|
||||
public void setPreTemplateLoaders(TemplateLoader[] preTemplateLoaders) {
|
||||
this.preTemplateLoaders = Arrays.asList(preTemplateLoaders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a List of <code>TemplateLoader<code>s that will be used to search
|
||||
* for templates. For example, one or more custom loaders such as database
|
||||
* loaders can be configured.
|
||||
* <p>The {@link TemplateLoader TemplateLoaders} specified here will be
|
||||
* registered <i>after</i> the default template loaders that this factory
|
||||
* registers (such as loaders for specified "templateLoaderPaths" or any
|
||||
* loaders registered in {@link #postProcessTemplateLoaders}).
|
||||
* @see #setTemplateLoaderPaths
|
||||
* @see #postProcessTemplateLoaders
|
||||
*/
|
||||
public void setPostTemplateLoaders(TemplateLoader[] postTemplateLoaders) {
|
||||
this.postTemplateLoaders = Arrays.asList(postTemplateLoaders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Freemarker template loader path via a Spring resource location.
|
||||
* See the "templateLoaderPaths" property for details on path handling.
|
||||
* @see #setTemplateLoaderPaths
|
||||
*/
|
||||
public void setTemplateLoaderPath(String templateLoaderPath) {
|
||||
this.templateLoaderPaths = new String[] {templateLoaderPath};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set multiple Freemarker template loader paths via Spring resource locations.
|
||||
* <p>When populated via a String, standard URLs like "file:" and "classpath:"
|
||||
* pseudo URLs are supported, as understood by ResourceEditor. Allows for
|
||||
* relative paths when running in an ApplicationContext.
|
||||
* <p>Will define a path for the default FreeMarker template loader.
|
||||
* If a specified resource cannot be resolved to a <code>java.io.File</code>,
|
||||
* a generic SpringTemplateLoader will be used, without modification detection.
|
||||
* <p>To enforce the use of SpringTemplateLoader, i.e. to not resolve a path
|
||||
* as file system resource in any case, turn off the "preferFileSystemAccess"
|
||||
* flag. See the latter's javadoc for details.
|
||||
* <p>If you wish to specify your own list of TemplateLoaders, do not set this
|
||||
* property and instead use <code>setTemplateLoaders(List templateLoaders)</code>
|
||||
* @see org.springframework.core.io.ResourceEditor
|
||||
* @see org.springframework.context.ApplicationContext#getResource
|
||||
* @see freemarker.template.Configuration#setDirectoryForTemplateLoading
|
||||
* @see SpringTemplateLoader
|
||||
* @see #setTemplateLoaders
|
||||
*/
|
||||
public void setTemplateLoaderPaths(String[] templateLoaderPaths) {
|
||||
this.templateLoaderPaths = templateLoaderPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Spring ResourceLoader to use for loading FreeMarker template files.
|
||||
* The default is DefaultResourceLoader. Will get overridden by the
|
||||
* ApplicationContext if running in a context.
|
||||
* @see org.springframework.core.io.DefaultResourceLoader
|
||||
*/
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Spring ResourceLoader to use for loading FreeMarker template files.
|
||||
*/
|
||||
protected ResourceLoader getResourceLoader() {
|
||||
return resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to prefer file system access for template loading.
|
||||
* File system access enables hot detection of template changes.
|
||||
* <p>If this is enabled, FreeMarkerConfigurationFactory will try to resolve
|
||||
* the specified "templateLoaderPath" as file system resource (which will work
|
||||
* for expanded class path resources and ServletContext resources too).
|
||||
* <p>Default is "true". Turn this off to always load via SpringTemplateLoader
|
||||
* (i.e. as stream, without hot detection of template changes), which might
|
||||
* be necessary if some of your templates reside in an expanded classes
|
||||
* directory while others reside in jar files.
|
||||
* @see #setTemplateLoaderPath
|
||||
*/
|
||||
public void setPreferFileSystemAccess(boolean preferFileSystemAccess) {
|
||||
this.preferFileSystemAccess = preferFileSystemAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to prefer file system access for template loading.
|
||||
*/
|
||||
protected boolean isPreferFileSystemAccess() {
|
||||
return preferFileSystemAccess;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare the FreeMarker Configuration and return it.
|
||||
* @return the FreeMarker Configuration object
|
||||
* @throws IOException if the config file wasn't found
|
||||
* @throws TemplateException on FreeMarker initialization failure
|
||||
*/
|
||||
public Configuration createConfiguration() throws IOException, TemplateException {
|
||||
Configuration config = newConfiguration();
|
||||
Properties props = new Properties();
|
||||
|
||||
// Load config file if specified.
|
||||
if (this.configLocation != null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Loading FreeMarker configuration from " + this.configLocation);
|
||||
}
|
||||
PropertiesLoaderUtils.fillProperties(props, this.configLocation);
|
||||
}
|
||||
|
||||
// Merge local properties if specified.
|
||||
if (this.freemarkerSettings != null) {
|
||||
props.putAll(this.freemarkerSettings);
|
||||
}
|
||||
|
||||
// FreeMarker will only accept known keys in its setSettings and
|
||||
// setAllSharedVariables methods.
|
||||
if (!props.isEmpty()) {
|
||||
config.setSettings(props);
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(this.freemarkerVariables)) {
|
||||
config.setAllSharedVariables(new SimpleHash(this.freemarkerVariables));
|
||||
}
|
||||
|
||||
if (this.defaultEncoding != null) {
|
||||
config.setDefaultEncoding(this.defaultEncoding);
|
||||
}
|
||||
|
||||
// Register template loaders that are supposed to kick in early.
|
||||
if (this.preTemplateLoaders != null) {
|
||||
this.templateLoaders.addAll(this.preTemplateLoaders);
|
||||
}
|
||||
|
||||
// Register default template loaders.
|
||||
if (this.templateLoaderPaths != null) {
|
||||
for (int i = 0; i < this.templateLoaderPaths.length; i++) {
|
||||
this.templateLoaders.add(getTemplateLoaderForPath(this.templateLoaderPaths[i]));
|
||||
}
|
||||
}
|
||||
postProcessTemplateLoaders(this.templateLoaders);
|
||||
|
||||
// Register template loaders that are supposed to kick in late.
|
||||
if (this.postTemplateLoaders != null) {
|
||||
this.templateLoaders.addAll(this.postTemplateLoaders);
|
||||
}
|
||||
|
||||
TemplateLoader loader = getAggregateTemplateLoader(this.templateLoaders);
|
||||
if (loader != null) {
|
||||
config.setTemplateLoader(loader);
|
||||
}
|
||||
|
||||
postProcessConfiguration(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new Configuration object. Subclasses can override this for
|
||||
* custom initialization, or for using a mock object for testing.
|
||||
* <p>Called by <code>createConfiguration()</code>.
|
||||
* @return the Configuration object
|
||||
* @throws IOException if a config file wasn't found
|
||||
* @throws TemplateException on FreeMarker initialization failure
|
||||
* @see #createConfiguration()
|
||||
*/
|
||||
protected Configuration newConfiguration() throws IOException, TemplateException {
|
||||
return new Configuration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a FreeMarker TemplateLoader for the given path.
|
||||
* <p>Default implementation creates either a FileTemplateLoader or
|
||||
* a SpringTemplateLoader.
|
||||
* @param templateLoaderPath the path to load templates from
|
||||
* @return an appropriate TemplateLoader
|
||||
* @see freemarker.cache.FileTemplateLoader
|
||||
* @see SpringTemplateLoader
|
||||
*/
|
||||
protected TemplateLoader getTemplateLoaderForPath(String templateLoaderPath) {
|
||||
if (isPreferFileSystemAccess()) {
|
||||
// Try to load via the file system, fall back to SpringTemplateLoader
|
||||
// (for hot detection of template changes, if possible).
|
||||
try {
|
||||
Resource path = getResourceLoader().getResource(templateLoaderPath);
|
||||
File file = path.getFile(); // will fail if not resolvable in the file system
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
"Template loader path [" + path + "] resolved to file path [" + file.getAbsolutePath() + "]");
|
||||
}
|
||||
return new FileTemplateLoader(file);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cannot resolve template loader path [" + templateLoaderPath +
|
||||
"] to [java.io.File]: using SpringTemplateLoader as fallback", ex);
|
||||
}
|
||||
return new SpringTemplateLoader(getResourceLoader(), templateLoaderPath);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Always load via SpringTemplateLoader (without hot detection of template changes).
|
||||
logger.debug("File system access not preferred: using SpringTemplateLoader");
|
||||
return new SpringTemplateLoader(getResourceLoader(), templateLoaderPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To be overridden by subclasses that want to to register custom
|
||||
* TemplateLoader instances after this factory created its default
|
||||
* template loaders.
|
||||
* <p>Called by <code>createConfiguration()</code>. Note that specified
|
||||
* "postTemplateLoaders" will be registered <i>after</i> any loaders
|
||||
* registered by this callback; as a consequence, they are are <i>not</i>
|
||||
* included in the given List.
|
||||
* @param templateLoaders the current List of TemplateLoader instances,
|
||||
* to be modified by a subclass
|
||||
* @see #createConfiguration()
|
||||
* @see #setPostTemplateLoaders
|
||||
*/
|
||||
protected void postProcessTemplateLoaders(List templateLoaders) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a TemplateLoader based on the given TemplateLoader list.
|
||||
* If more than one TemplateLoader has been registered, a FreeMarker
|
||||
* MultiTemplateLoader needs to be created.
|
||||
* @param templateLoaders the final List of TemplateLoader instances
|
||||
* @return the aggregate TemplateLoader
|
||||
*/
|
||||
protected TemplateLoader getAggregateTemplateLoader(List templateLoaders) {
|
||||
int loaderCount = templateLoaders.size();
|
||||
switch (loaderCount) {
|
||||
case 0:
|
||||
logger.info("No FreeMarker TemplateLoaders specified");
|
||||
return null;
|
||||
case 1:
|
||||
return (TemplateLoader) templateLoaders.get(0);
|
||||
default:
|
||||
TemplateLoader[] loaders = (TemplateLoader[]) templateLoaders.toArray(new TemplateLoader[loaderCount]);
|
||||
return new MultiTemplateLoader(loaders);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To be overridden by subclasses that want to to perform custom
|
||||
* post-processing of the Configuration object after this factory
|
||||
* performed its default initialization.
|
||||
* <p>Called by <code>createConfiguration()</code>.
|
||||
* @param config the current Configuration object
|
||||
* @throws IOException if a config file wasn't found
|
||||
* @throws TemplateException on FreeMarker initialization failure
|
||||
* @see #createConfiguration()
|
||||
*/
|
||||
protected void postProcessConfiguration(Configuration config) throws IOException, TemplateException {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.freemarker;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.TemplateException;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
|
||||
/**
|
||||
* Factory bean that creates a FreeMarker Configuration and provides it as
|
||||
* bean reference. This bean is intended for any kind of usage of FreeMarker
|
||||
* in application code, e.g. for generating email content. For web views,
|
||||
* FreeMarkerConfigurer is used to set up a FreeMarkerConfigurationFactory.
|
||||
*
|
||||
* The simplest way to use this class is to specify just a "templateLoaderPath";
|
||||
* you do not need any further configuration then. For example, in a web
|
||||
* application context:
|
||||
*
|
||||
* <pre class="code"> <bean id="freemarkerConfiguration" class="org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean">
|
||||
* <property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
|
||||
* </bean></pre>
|
||||
|
||||
* See the base class FreeMarkerConfigurationFactory for configuration details.
|
||||
*
|
||||
* <p>Note: Spring's FreeMarker support requires FreeMarker 2.3 or higher.
|
||||
*
|
||||
* @author Darren Davison
|
||||
* @since 03.03.2004
|
||||
* @see #setConfigLocation
|
||||
* @see #setFreemarkerSettings
|
||||
* @see #setTemplateLoaderPath
|
||||
* @see org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer
|
||||
*/
|
||||
public class FreeMarkerConfigurationFactoryBean extends FreeMarkerConfigurationFactory
|
||||
implements FactoryBean, InitializingBean, ResourceLoaderAware {
|
||||
|
||||
private Configuration configuration;
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws IOException, TemplateException {
|
||||
this.configuration = createConfiguration();
|
||||
}
|
||||
|
||||
|
||||
public Object getObject() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return Configuration.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.freemarker;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateException;
|
||||
|
||||
/**
|
||||
* Utility class for working with FreeMarker.
|
||||
* Provides convenience methods to process a FreeMarker template with a model.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 14.03.2004
|
||||
*/
|
||||
public abstract class FreeMarkerTemplateUtils {
|
||||
|
||||
/**
|
||||
* Process the specified FreeMarker template with the given model and write
|
||||
* the result to the given Writer.
|
||||
* <p>When using this method to prepare a text for a mail to be sent with Spring's
|
||||
* mail support, consider wrapping IO/TemplateException in MailPreparationException.
|
||||
* @param model the model object, typically a Map that contains model names
|
||||
* as keys and model objects as values
|
||||
* @return the result as String
|
||||
* @throws IOException if the template wasn't found or couldn't be read
|
||||
* @throws freemarker.template.TemplateException if rendering failed
|
||||
* @see org.springframework.mail.MailPreparationException
|
||||
*/
|
||||
public static String processTemplateIntoString(Template template, Object model)
|
||||
throws IOException, TemplateException {
|
||||
StringWriter result = new StringWriter();
|
||||
template.process(model, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.freemarker;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
|
||||
import freemarker.cache.TemplateLoader;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
/**
|
||||
* FreeMarker TemplateLoader adapter that loads via a Spring ResourceLoader.
|
||||
* Used by FreeMarkerConfigurationFactory for any resource loader path that
|
||||
* cannot be resolved to a java.io.File.
|
||||
*
|
||||
* <p>Note that this loader does not allow for modification detection:
|
||||
* Use FreeMarker's default TemplateLoader for java.io.File resources.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 14.03.2004
|
||||
* @see FreeMarkerConfigurationFactory#setTemplateLoaderPath
|
||||
* @see freemarker.template.Configuration#setDirectoryForTemplateLoading
|
||||
*/
|
||||
public class SpringTemplateLoader implements TemplateLoader {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
private final String templateLoaderPath;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SpringTemplateLoader.
|
||||
* @param resourceLoader the Spring ResourceLoader to use
|
||||
* @param templateLoaderPath the template loader path to use
|
||||
*/
|
||||
public SpringTemplateLoader(ResourceLoader resourceLoader, String templateLoaderPath) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
if (!templateLoaderPath.endsWith("/")) {
|
||||
templateLoaderPath += "/";
|
||||
}
|
||||
this.templateLoaderPath = templateLoaderPath;
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("SpringTemplateLoader for FreeMarker: using resource loader [" + this.resourceLoader +
|
||||
"] and template loader path [" + this.templateLoaderPath + "]");
|
||||
}
|
||||
}
|
||||
|
||||
public Object findTemplateSource(String name) throws IOException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for FreeMarker template with name [" + name + "]");
|
||||
}
|
||||
Resource resource = this.resourceLoader.getResource(this.templateLoaderPath + name);
|
||||
return (resource.exists() ? resource : null);
|
||||
}
|
||||
|
||||
public Reader getReader(Object templateSource, String encoding) throws IOException {
|
||||
Resource resource = (Resource) templateSource;
|
||||
try {
|
||||
return new InputStreamReader(resource.getInputStream(), encoding);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not find FreeMarker template: " + resource);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public long getLastModified(Object templateSource) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void closeTemplateSource(Object templateSource) throws IOException {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Support classes for setting up
|
||||
<a href="http://www.freemarker.org">FreeMarker</a>
|
||||
within a Spring application context.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.jasperreports;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.jasperreports.engine.JRDataSource;
|
||||
import net.sf.jasperreports.engine.JRException;
|
||||
import net.sf.jasperreports.engine.JRExporter;
|
||||
import net.sf.jasperreports.engine.JRExporterParameter;
|
||||
import net.sf.jasperreports.engine.JasperFillManager;
|
||||
import net.sf.jasperreports.engine.JasperPrint;
|
||||
import net.sf.jasperreports.engine.JasperReport;
|
||||
import net.sf.jasperreports.engine.data.JRBeanArrayDataSource;
|
||||
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
|
||||
import net.sf.jasperreports.engine.export.JRCsvExporter;
|
||||
import net.sf.jasperreports.engine.export.JRHtmlExporter;
|
||||
import net.sf.jasperreports.engine.export.JRPdfExporter;
|
||||
import net.sf.jasperreports.engine.export.JRXlsExporter;
|
||||
|
||||
/**
|
||||
* Utility methods for working with JasperReports. Provides a set of convenience
|
||||
* methods for generating reports in a CSV, HTML, PDF and XLS formats.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.3
|
||||
*/
|
||||
public abstract class JasperReportsUtils {
|
||||
|
||||
/**
|
||||
* Convert the given report data value to a <code>JRDataSource</code>.
|
||||
* <p>In the default implementation, a <code>JRDataSource</code>,
|
||||
* <code>java.util.Collection</code> or object array is detected.
|
||||
* The latter are converted to <code>JRBeanCollectionDataSource</code>
|
||||
* or <code>JRBeanArrayDataSource</code>, respectively.
|
||||
* @param value the report data value to convert
|
||||
* @return the JRDataSource (never <code>null</code>)
|
||||
* @throws IllegalArgumentException if the value could not be converted
|
||||
* @see net.sf.jasperreports.engine.JRDataSource
|
||||
* @see net.sf.jasperreports.engine.data.JRBeanCollectionDataSource
|
||||
* @see net.sf.jasperreports.engine.data.JRBeanArrayDataSource
|
||||
*/
|
||||
public static JRDataSource convertReportData(Object value) throws IllegalArgumentException {
|
||||
if (value instanceof JRDataSource) {
|
||||
return (JRDataSource) value;
|
||||
}
|
||||
else if (value instanceof Collection) {
|
||||
return new JRBeanCollectionDataSource((Collection) value);
|
||||
}
|
||||
else if (value instanceof Object[]) {
|
||||
return new JRBeanArrayDataSource((Object[]) value);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Value [" + value + "] cannot be converted to a JRDataSource");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the supplied <code>JasperPrint</code> instance using the
|
||||
* supplied <code>JRAbstractExporter</code> instance and write the results
|
||||
* to the supplied <code>Writer</code>.
|
||||
* <p>Make sure that the <code>JRAbstractExporter</code> implementation
|
||||
* you supply is capable of writing to a <code>Writer</code>.
|
||||
* @param exporter the <code>JRAbstractExporter</code> to use to render the report
|
||||
* @param print the <code>JasperPrint</code> instance to render
|
||||
* @param writer the <code>Writer</code> to write the result to
|
||||
* @throws JRException if rendering failed
|
||||
*/
|
||||
public static void render(JRExporter exporter, JasperPrint print, Writer writer)
|
||||
throws JRException {
|
||||
|
||||
exporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
|
||||
exporter.setParameter(JRExporterParameter.OUTPUT_WRITER, writer);
|
||||
exporter.exportReport();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the supplied <code>JasperPrint</code> instance using the
|
||||
* supplied <code>JRAbstractExporter</code> instance and write the results
|
||||
* to the supplied <code>OutputStream</code>.
|
||||
* <p>Make sure that the <code>JRAbstractExporter</code> implementation you
|
||||
* supply is capable of writing to a <code>OutputStream</code>.
|
||||
* @param exporter the <code>JRAbstractExporter</code> to use to render the report
|
||||
* @param print the <code>JasperPrint</code> instance to render
|
||||
* @param outputStream the <code>OutputStream</code> to write the result to
|
||||
* @throws JRException if rendering failed
|
||||
*/
|
||||
public static void render(JRExporter exporter, JasperPrint print, OutputStream outputStream)
|
||||
throws JRException {
|
||||
|
||||
exporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
|
||||
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, outputStream);
|
||||
exporter.exportReport();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in CSV format using the supplied report data.
|
||||
* Writes the results to the supplied <code>Writer</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param writer the <code>Writer</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsCsv(JasperReport report, Map parameters, Object reportData, Writer writer)
|
||||
throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
render(new JRCsvExporter(), print, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in CSV format using the supplied report data.
|
||||
* Writes the results to the supplied <code>Writer</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param writer the <code>Writer</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @param exporterParameters a {@link Map} of {@link JRExporterParameter exporter parameters}
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsCsv(JasperReport report, Map parameters, Object reportData, Writer writer,
|
||||
Map exporterParameters) throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
JRCsvExporter exporter = new JRCsvExporter();
|
||||
exporter.setParameters(exporterParameters);
|
||||
render(exporter, print, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in HTML format using the supplied report data.
|
||||
* Writes the results to the supplied <code>Writer</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param writer the <code>Writer</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsHtml(JasperReport report, Map parameters, Object reportData, Writer writer)
|
||||
throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
render(new JRHtmlExporter(), print, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in HTML format using the supplied report data.
|
||||
* Writes the results to the supplied <code>Writer</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param writer the <code>Writer</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @param exporterParameters a {@link Map} of {@link JRExporterParameter exporter parameters}
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsHtml(JasperReport report, Map parameters, Object reportData, Writer writer,
|
||||
Map exporterParameters) throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
JRHtmlExporter exporter = new JRHtmlExporter();
|
||||
exporter.setParameters(exporterParameters);
|
||||
render(exporter, print, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in PDF format using the supplied report data.
|
||||
* Writes the results to the supplied <code>OutputStream</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param stream the <code>OutputStream</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsPdf(JasperReport report, Map parameters, Object reportData, OutputStream stream)
|
||||
throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
render(new JRPdfExporter(), print, stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in PDF format using the supplied report data.
|
||||
* Writes the results to the supplied <code>OutputStream</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param stream the <code>OutputStream</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @param exporterParameters a {@link Map} of {@link JRExporterParameter exporter parameters}
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsPdf(JasperReport report, Map parameters, Object reportData, OutputStream stream,
|
||||
Map exporterParameters) throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
JRPdfExporter exporter = new JRPdfExporter();
|
||||
exporter.setParameters(exporterParameters);
|
||||
render(exporter, print, stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in XLS format using the supplied report data.
|
||||
* Writes the results to the supplied <code>OutputStream</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param stream the <code>OutputStream</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsXls(JasperReport report, Map parameters, Object reportData, OutputStream stream)
|
||||
throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
render(new JRXlsExporter(), print, stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in XLS format using the supplied report data.
|
||||
* Writes the results to the supplied <code>OutputStream</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param stream the <code>OutputStream</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @param exporterParameters a {@link Map} of {@link JRExporterParameter exporter parameters}
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsXls(JasperReport report, Map parameters, Object reportData, OutputStream stream,
|
||||
Map exporterParameters) throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
JRXlsExporter exporter = new JRXlsExporter();
|
||||
exporter.setParameters(exporterParameters);
|
||||
render(exporter, print, stream);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Support classes for
|
||||
<a href="http://jasperreports.sourceforge.net">JasperReports</a>.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.velocity;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.velocity.app.VelocityEngine;
|
||||
import org.apache.velocity.runtime.RuntimeServices;
|
||||
import org.apache.velocity.runtime.log.LogSystem;
|
||||
|
||||
/**
|
||||
* Velocity LogSystem implementation for Jakarta Commons Logging.
|
||||
* Used by VelocityConfigurer to redirect log output.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 07.08.2003
|
||||
* @see VelocityEngineFactoryBean
|
||||
*/
|
||||
public class CommonsLoggingLogSystem implements LogSystem {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(VelocityEngine.class);
|
||||
|
||||
public void init(RuntimeServices runtimeServices) {
|
||||
}
|
||||
|
||||
public void logVelocityMessage(int type, String msg) {
|
||||
switch (type) {
|
||||
case ERROR_ID:
|
||||
logger.error(msg);
|
||||
break;
|
||||
case WARN_ID:
|
||||
logger.warn(msg);
|
||||
break;
|
||||
case INFO_ID:
|
||||
logger.info(msg);
|
||||
break;
|
||||
case DEBUG_ID:
|
||||
logger.debug(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.velocity;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.collections.ExtendedProperties;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.velocity.exception.ResourceNotFoundException;
|
||||
import org.apache.velocity.runtime.resource.Resource;
|
||||
import org.apache.velocity.runtime.resource.loader.ResourceLoader;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Velocity ResourceLoader adapter that loads via a Spring ResourceLoader.
|
||||
* Used by VelocityEngineFactory for any resource loader path that cannot
|
||||
* be resolved to a <code>java.io.File</code>.
|
||||
*
|
||||
* <p>Note that this loader does not allow for modification detection:
|
||||
* Use Velocity's default FileResourceLoader for <code>java.io.File</code>
|
||||
* resources.
|
||||
*
|
||||
* <p>Expects "spring.resource.loader" and "spring.resource.loader.path"
|
||||
* application attributes in the Velocity runtime: the former of type
|
||||
* <code>org.springframework.core.io.ResourceLoader</code>, the latter a String.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 14.03.2004
|
||||
* @see VelocityEngineFactory#setResourceLoaderPath
|
||||
* @see org.springframework.core.io.ResourceLoader
|
||||
* @see org.apache.velocity.runtime.resource.loader.FileResourceLoader
|
||||
*/
|
||||
public class SpringResourceLoader extends ResourceLoader {
|
||||
|
||||
public static final String NAME = "spring";
|
||||
|
||||
public static final String SPRING_RESOURCE_LOADER_CLASS = "spring.resource.loader.class";
|
||||
|
||||
public static final String SPRING_RESOURCE_LOADER_CACHE = "spring.resource.loader.cache";
|
||||
|
||||
public static final String SPRING_RESOURCE_LOADER = "spring.resource.loader";
|
||||
|
||||
public static final String SPRING_RESOURCE_LOADER_PATH = "spring.resource.loader.path";
|
||||
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private org.springframework.core.io.ResourceLoader resourceLoader;
|
||||
|
||||
private String[] resourceLoaderPaths;
|
||||
|
||||
|
||||
public void init(ExtendedProperties configuration) {
|
||||
this.resourceLoader = (org.springframework.core.io.ResourceLoader)
|
||||
this.rsvc.getApplicationAttribute(SPRING_RESOURCE_LOADER);
|
||||
String resourceLoaderPath = (String) this.rsvc.getApplicationAttribute(SPRING_RESOURCE_LOADER_PATH);
|
||||
if (this.resourceLoader == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"'resourceLoader' application attribute must be present for SpringResourceLoader");
|
||||
}
|
||||
if (resourceLoaderPath == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"'resourceLoaderPath' application attribute must be present for SpringResourceLoader");
|
||||
}
|
||||
this.resourceLoaderPaths = StringUtils.commaDelimitedListToStringArray(resourceLoaderPath);
|
||||
for (int i = 0; i < this.resourceLoaderPaths.length; i++) {
|
||||
String path = this.resourceLoaderPaths[i];
|
||||
if (!path.endsWith("/")) {
|
||||
this.resourceLoaderPaths[i] = path + "/";
|
||||
}
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("SpringResourceLoader for Velocity: using resource loader [" + this.resourceLoader +
|
||||
"] and resource loader paths " + Arrays.asList(this.resourceLoaderPaths));
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream getResourceStream(String source) throws ResourceNotFoundException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for Velocity resource with name [" + source + "]");
|
||||
}
|
||||
for (int i = 0; i < this.resourceLoaderPaths.length; i++) {
|
||||
org.springframework.core.io.Resource resource =
|
||||
this.resourceLoader.getResource(this.resourceLoaderPaths[i] + source);
|
||||
try {
|
||||
return resource.getInputStream();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not find Velocity resource: " + resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new ResourceNotFoundException(
|
||||
"Could not find resource [" + source + "] in Spring resource loader path");
|
||||
}
|
||||
|
||||
public boolean isSourceModified(Resource resource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public long getLastModified(Resource resource) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.velocity;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.velocity.app.VelocityEngine;
|
||||
import org.apache.velocity.exception.VelocityException;
|
||||
import org.apache.velocity.runtime.RuntimeConstants;
|
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Factory that configures a VelocityEngine. Can be used standalone,
|
||||
* but typically you will either use {@link VelocityEngineFactoryBean}
|
||||
* for preparing a VelocityEngine as bean reference, or
|
||||
* {@link org.springframework.web.servlet.view.velocity.VelocityConfigurer}
|
||||
* for web views.
|
||||
*
|
||||
* <p>The optional "configLocation" property sets the location of the Velocity
|
||||
* properties file, within the current application. Velocity properties can be
|
||||
* overridden via "velocityProperties", or even completely specified locally,
|
||||
* avoiding the need for an external properties file.
|
||||
*
|
||||
* <p>The "resourceLoaderPath" property can be used to specify the Velocity
|
||||
* resource loader path via Spring's Resource abstraction, possibly relative
|
||||
* to the Spring application context.
|
||||
*
|
||||
* <p>If "overrideLogging" is true (the default), the VelocityEngine will be
|
||||
* configured to log via Commons Logging, that is, using the Spring-provided
|
||||
* {@link CommonsLoggingLogSystem} as log system.
|
||||
*
|
||||
* <p>The simplest way to use this class is to specify a
|
||||
* {@link #setResourceLoaderPath(String) "resourceLoaderPath"}; the
|
||||
* VelocityEngine typically then does not need any further configuration.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @see #setConfigLocation
|
||||
* @see #setVelocityProperties
|
||||
* @see #setResourceLoaderPath
|
||||
* @see #setOverrideLogging
|
||||
* @see #createVelocityEngine
|
||||
* @see CommonsLoggingLogSystem
|
||||
* @see VelocityEngineFactoryBean
|
||||
* @see org.springframework.web.servlet.view.velocity.VelocityConfigurer
|
||||
* @see org.apache.velocity.app.VelocityEngine
|
||||
*/
|
||||
public class VelocityEngineFactory {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Resource configLocation;
|
||||
|
||||
private final Map velocityProperties = new HashMap();
|
||||
|
||||
private String resourceLoaderPath;
|
||||
|
||||
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
private boolean preferFileSystemAccess = true;
|
||||
|
||||
private boolean overrideLogging = true;
|
||||
|
||||
|
||||
/**
|
||||
* Set the location of the Velocity config file.
|
||||
* Alternatively, you can specify all properties locally.
|
||||
* @see #setVelocityProperties
|
||||
* @see #setResourceLoaderPath
|
||||
*/
|
||||
public void setConfigLocation(Resource configLocation) {
|
||||
this.configLocation = configLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Velocity properties, like "file.resource.loader.path".
|
||||
* Can be used to override values in a Velocity config file,
|
||||
* or to specify all necessary properties locally.
|
||||
* <p>Note that the Velocity resource loader path also be set to any
|
||||
* Spring resource location via the "resourceLoaderPath" property.
|
||||
* Setting it here is just necessary when using a non-file-based
|
||||
* resource loader.
|
||||
* @see #setVelocityPropertiesMap
|
||||
* @see #setConfigLocation
|
||||
* @see #setResourceLoaderPath
|
||||
*/
|
||||
public void setVelocityProperties(Properties velocityProperties) {
|
||||
setVelocityPropertiesMap(velocityProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Velocity properties as Map, to allow for non-String values
|
||||
* like "ds.resource.loader.instance".
|
||||
* @see #setVelocityProperties
|
||||
*/
|
||||
public void setVelocityPropertiesMap(Map velocityPropertiesMap) {
|
||||
if (velocityPropertiesMap != null) {
|
||||
this.velocityProperties.putAll(velocityPropertiesMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Velocity resource loader path via a Spring resource location.
|
||||
* Accepts multiple locations in Velocity's comma-separated path style.
|
||||
* <p>When populated via a String, standard URLs like "file:" and "classpath:"
|
||||
* pseudo URLs are supported, as understood by ResourceLoader. Allows for
|
||||
* relative paths when running in an ApplicationContext.
|
||||
* <p>Will define a path for the default Velocity resource loader with the name
|
||||
* "file". If the specified resource cannot be resolved to a <code>java.io.File</code>,
|
||||
* a generic SpringResourceLoader will be used under the name "spring", without
|
||||
* modification detection.
|
||||
* <p>Note that resource caching will be enabled in any case. With the file
|
||||
* resource loader, the last-modified timestamp will be checked on access to
|
||||
* detect changes. With SpringResourceLoader, the resource will be cached
|
||||
* forever (for example for class path resources).
|
||||
* <p>To specify a modification check interval for files, use Velocity's
|
||||
* standard "file.resource.loader.modificationCheckInterval" property. By default,
|
||||
* the file timestamp is checked on every access (which is surprisingly fast).
|
||||
* Of course, this just applies when loading resources from the file system.
|
||||
* <p>To enforce the use of SpringResourceLoader, i.e. to not resolve a path
|
||||
* as file system resource in any case, turn off the "preferFileSystemAccess"
|
||||
* flag. See the latter's javadoc for details.
|
||||
* @see #setResourceLoader
|
||||
* @see #setVelocityProperties
|
||||
* @see #setPreferFileSystemAccess
|
||||
* @see SpringResourceLoader
|
||||
* @see org.apache.velocity.runtime.resource.loader.FileResourceLoader
|
||||
*/
|
||||
public void setResourceLoaderPath(String resourceLoaderPath) {
|
||||
this.resourceLoaderPath = resourceLoaderPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Spring ResourceLoader to use for loading Velocity template files.
|
||||
* The default is DefaultResourceLoader. Will get overridden by the
|
||||
* ApplicationContext if running in a context.
|
||||
* @see org.springframework.core.io.DefaultResourceLoader
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
*/
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Spring ResourceLoader to use for loading Velocity template files.
|
||||
*/
|
||||
protected ResourceLoader getResourceLoader() {
|
||||
return this.resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to prefer file system access for template loading.
|
||||
* File system access enables hot detection of template changes.
|
||||
* <p>If this is enabled, VelocityEngineFactory will try to resolve the
|
||||
* specified "resourceLoaderPath" as file system resource (which will work
|
||||
* for expanded class path resources and ServletContext resources too).
|
||||
* <p>Default is "true". Turn this off to always load via SpringResourceLoader
|
||||
* (i.e. as stream, without hot detection of template changes), which might
|
||||
* be necessary if some of your templates reside in an expanded classes
|
||||
* directory while others reside in jar files.
|
||||
* @see #setResourceLoaderPath
|
||||
*/
|
||||
public void setPreferFileSystemAccess(boolean preferFileSystemAccess) {
|
||||
this.preferFileSystemAccess = preferFileSystemAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to prefer file system access for template loading.
|
||||
*/
|
||||
protected boolean isPreferFileSystemAccess() {
|
||||
return this.preferFileSystemAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether Velocity should log via Commons Logging, i.e. whether Velocity's
|
||||
* log system should be set to CommonsLoggingLogSystem. Default value is true.
|
||||
* @see CommonsLoggingLogSystem
|
||||
*/
|
||||
public void setOverrideLogging(boolean overrideLogging) {
|
||||
this.overrideLogging = overrideLogging;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare the VelocityEngine instance and return it.
|
||||
* @return the VelocityEngine instance
|
||||
* @throws IOException if the config file wasn't found
|
||||
* @throws VelocityException on Velocity initialization failure
|
||||
*/
|
||||
public VelocityEngine createVelocityEngine() throws IOException, VelocityException {
|
||||
VelocityEngine velocityEngine = newVelocityEngine();
|
||||
Properties props = new Properties();
|
||||
|
||||
// Load config file if set.
|
||||
if (this.configLocation != null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Loading Velocity config from [" + this.configLocation + "]");
|
||||
}
|
||||
PropertiesLoaderUtils.fillProperties(props, this.configLocation);
|
||||
}
|
||||
|
||||
// Merge local properties if set.
|
||||
if (!this.velocityProperties.isEmpty()) {
|
||||
props.putAll(this.velocityProperties);
|
||||
}
|
||||
|
||||
// Set a resource loader path, if required.
|
||||
if (this.resourceLoaderPath != null) {
|
||||
initVelocityResourceLoader(velocityEngine, this.resourceLoaderPath);
|
||||
}
|
||||
|
||||
// Log via Commons Logging?
|
||||
if (this.overrideLogging) {
|
||||
velocityEngine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new CommonsLoggingLogSystem());
|
||||
}
|
||||
|
||||
// Apply properties to VelocityEngine.
|
||||
for (Iterator it = props.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
if (!(entry.getKey() instanceof String)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Illegal property key [" + entry.getKey() + "]: only Strings allowed");
|
||||
}
|
||||
velocityEngine.setProperty((String) entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
postProcessVelocityEngine(velocityEngine);
|
||||
|
||||
try {
|
||||
// Perform actual initialization.
|
||||
velocityEngine.init();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (VelocityException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.error("Why does VelocityEngine throw a generic checked exception, after all?", ex);
|
||||
throw new VelocityException(ex.toString());
|
||||
}
|
||||
|
||||
return velocityEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new VelocityEngine. Subclasses can override this for
|
||||
* custom initialization, or for using a mock object for testing.
|
||||
* <p>Called by <code>createVelocityEngine()</code>.
|
||||
* @return the VelocityEngine instance
|
||||
* @throws IOException if a config file wasn't found
|
||||
* @throws VelocityException on Velocity initialization failure
|
||||
* @see #createVelocityEngine()
|
||||
*/
|
||||
protected VelocityEngine newVelocityEngine() throws IOException, VelocityException {
|
||||
return new VelocityEngine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a Velocity resource loader for the given VelocityEngine:
|
||||
* either a standard Velocity FileResourceLoader or a SpringResourceLoader.
|
||||
* <p>Called by <code>createVelocityEngine()</code>.
|
||||
* @param velocityEngine the VelocityEngine to configure
|
||||
* @param resourceLoaderPath the path to load Velocity resources from
|
||||
* @see org.apache.velocity.runtime.resource.loader.FileResourceLoader
|
||||
* @see SpringResourceLoader
|
||||
* @see #initSpringResourceLoader
|
||||
* @see #createVelocityEngine()
|
||||
*/
|
||||
protected void initVelocityResourceLoader(VelocityEngine velocityEngine, String resourceLoaderPath) {
|
||||
if (isPreferFileSystemAccess()) {
|
||||
// Try to load via the file system, fall back to SpringResourceLoader
|
||||
// (for hot detection of template changes, if possible).
|
||||
try {
|
||||
StringBuffer resolvedPath = new StringBuffer();
|
||||
String[] paths = StringUtils.commaDelimitedListToStringArray(resourceLoaderPath);
|
||||
for (int i = 0; i < paths.length; i++) {
|
||||
String path = paths[i];
|
||||
Resource resource = getResourceLoader().getResource(path);
|
||||
File file = resource.getFile(); // will fail if not resolvable in the file system
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Resource loader path [" + path + "] resolved to file [" + file.getAbsolutePath() + "]");
|
||||
}
|
||||
resolvedPath.append(file.getAbsolutePath());
|
||||
if (i < paths.length - 1) {
|
||||
resolvedPath.append(',');
|
||||
}
|
||||
}
|
||||
velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "file");
|
||||
velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_CACHE, "true");
|
||||
velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, resolvedPath.toString());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cannot resolve resource loader path [" + resourceLoaderPath +
|
||||
"] to [java.io.File]: using SpringResourceLoader", ex);
|
||||
}
|
||||
initSpringResourceLoader(velocityEngine, resourceLoaderPath);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Always load via SpringResourceLoader
|
||||
// (without hot detection of template changes).
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("File system access not preferred: using SpringResourceLoader");
|
||||
}
|
||||
initSpringResourceLoader(velocityEngine, resourceLoaderPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a SpringResourceLoader for the given VelocityEngine.
|
||||
* <p>Called by <code>initVelocityResourceLoader</code>.
|
||||
* @param velocityEngine the VelocityEngine to configure
|
||||
* @param resourceLoaderPath the path to load Velocity resources from
|
||||
* @see SpringResourceLoader
|
||||
* @see #initVelocityResourceLoader
|
||||
*/
|
||||
protected void initSpringResourceLoader(VelocityEngine velocityEngine, String resourceLoaderPath) {
|
||||
velocityEngine.setProperty(
|
||||
RuntimeConstants.RESOURCE_LOADER, SpringResourceLoader.NAME);
|
||||
velocityEngine.setProperty(
|
||||
SpringResourceLoader.SPRING_RESOURCE_LOADER_CLASS, SpringResourceLoader.class.getName());
|
||||
velocityEngine.setProperty(
|
||||
SpringResourceLoader.SPRING_RESOURCE_LOADER_CACHE, "true");
|
||||
velocityEngine.setApplicationAttribute(
|
||||
SpringResourceLoader.SPRING_RESOURCE_LOADER, getResourceLoader());
|
||||
velocityEngine.setApplicationAttribute(
|
||||
SpringResourceLoader.SPRING_RESOURCE_LOADER_PATH, resourceLoaderPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* To be implemented by subclasses that want to to perform custom
|
||||
* post-processing of the VelocityEngine after this FactoryBean
|
||||
* performed its default configuration (but before VelocityEngine.init).
|
||||
* <p>Called by <code>createVelocityEngine()</code>.
|
||||
* @param velocityEngine the current VelocityEngine
|
||||
* @throws IOException if a config file wasn't found
|
||||
* @throws VelocityException on Velocity initialization failure
|
||||
* @see #createVelocityEngine()
|
||||
* @see org.apache.velocity.app.VelocityEngine#init
|
||||
*/
|
||||
protected void postProcessVelocityEngine(VelocityEngine velocityEngine)
|
||||
throws IOException, VelocityException {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.velocity;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.velocity.app.VelocityEngine;
|
||||
import org.apache.velocity.exception.VelocityException;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
|
||||
/**
|
||||
* Factory bean that configures a VelocityEngine and provides it as bean
|
||||
* reference. This bean is intended for any kind of usage of Velocity in
|
||||
* application code, e.g. for generating email content. For web views,
|
||||
* VelocityConfigurer is used to set up a VelocityEngine for views.
|
||||
*
|
||||
* <p>The simplest way to use this class is to specify a "resourceLoaderPath";
|
||||
* you do not need any further configuration then. For example, in a web
|
||||
* application context:
|
||||
*
|
||||
* <pre class="code"> <bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
|
||||
* <property name="resourceLoaderPath" value="/WEB-INF/velocity/"/>
|
||||
* </bean></pre>
|
||||
*
|
||||
* See the base class VelocityEngineFactory for configuration details.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @see #setConfigLocation
|
||||
* @see #setVelocityProperties
|
||||
* @see #setResourceLoaderPath
|
||||
* @see org.springframework.web.servlet.view.velocity.VelocityConfigurer
|
||||
*/
|
||||
public class VelocityEngineFactoryBean extends VelocityEngineFactory
|
||||
implements FactoryBean, InitializingBean, ResourceLoaderAware {
|
||||
|
||||
private VelocityEngine velocityEngine;
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws IOException, VelocityException {
|
||||
this.velocityEngine = createVelocityEngine();
|
||||
}
|
||||
|
||||
|
||||
public Object getObject() {
|
||||
return this.velocityEngine;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return VelocityEngine.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ui.velocity;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.velocity.VelocityContext;
|
||||
import org.apache.velocity.app.VelocityEngine;
|
||||
import org.apache.velocity.exception.VelocityException;
|
||||
|
||||
/**
|
||||
* Utility class for working with a VelocityEngine.
|
||||
* Provides convenience methods to merge a Velocity template with a model.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 22.01.2004
|
||||
*/
|
||||
public abstract class VelocityEngineUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(VelocityEngineUtils.class);
|
||||
|
||||
|
||||
/**
|
||||
* Merge the specified Velocity template with the given model and write
|
||||
* the result to the given Writer.
|
||||
* @param velocityEngine VelocityEngine to work with
|
||||
* @param templateLocation the location of template, relative to Velocity's
|
||||
* resource loader path
|
||||
* @param model the Map that contains model names as keys and model objects
|
||||
* as values
|
||||
* @param writer the Writer to write the result to
|
||||
* @throws VelocityException if the template wasn't found or rendering failed
|
||||
*/
|
||||
public static void mergeTemplate(
|
||||
VelocityEngine velocityEngine, String templateLocation, Map model, Writer writer)
|
||||
throws VelocityException {
|
||||
|
||||
try {
|
||||
VelocityContext velocityContext = new VelocityContext(model);
|
||||
velocityEngine.mergeTemplate(templateLocation, velocityContext, writer);
|
||||
}
|
||||
catch (VelocityException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.error("Why does VelocityEngine throw a generic checked exception, after all?", ex);
|
||||
throw new VelocityException(ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the specified Velocity template with the given model and write
|
||||
* the result to the given Writer.
|
||||
* @param velocityEngine VelocityEngine to work with
|
||||
* @param templateLocation the location of template, relative to Velocity's
|
||||
* resource loader path
|
||||
* @param encoding the encoding of the template file
|
||||
* @param model the Map that contains model names as keys and model objects
|
||||
* as values
|
||||
* @param writer the Writer to write the result to
|
||||
* @throws VelocityException if the template wasn't found or rendering failed
|
||||
*/
|
||||
public static void mergeTemplate(
|
||||
VelocityEngine velocityEngine, String templateLocation, String encoding, Map model, Writer writer)
|
||||
throws VelocityException {
|
||||
|
||||
try {
|
||||
VelocityContext velocityContext = new VelocityContext(model);
|
||||
velocityEngine.mergeTemplate(templateLocation, encoding, velocityContext, writer);
|
||||
}
|
||||
catch (VelocityException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.error("Why does VelocityEngine throw a generic checked exception, after all?", ex);
|
||||
throw new VelocityException(ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the specified Velocity template with the given model into a String.
|
||||
* <p>When using this method to prepare a text for a mail to be sent with Spring's
|
||||
* mail support, consider wrapping VelocityException in MailPreparationException.
|
||||
* @param velocityEngine VelocityEngine to work with
|
||||
* @param templateLocation the location of template, relative to Velocity's
|
||||
* resource loader path
|
||||
* @param model the Map that contains model names as keys and model objects
|
||||
* as values
|
||||
* @return the result as String
|
||||
* @throws VelocityException if the template wasn't found or rendering failed
|
||||
* @see org.springframework.mail.MailPreparationException
|
||||
*/
|
||||
public static String mergeTemplateIntoString(
|
||||
VelocityEngine velocityEngine, String templateLocation, Map model)
|
||||
throws VelocityException {
|
||||
|
||||
StringWriter result = new StringWriter();
|
||||
mergeTemplate(velocityEngine, templateLocation, model, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the specified Velocity template with the given model into a String.
|
||||
* <p>When using this method to prepare a text for a mail to be sent with Spring's
|
||||
* mail support, consider wrapping VelocityException in MailPreparationException.
|
||||
* @param velocityEngine VelocityEngine to work with
|
||||
* @param templateLocation the location of template, relative to Velocity's
|
||||
* resource loader path
|
||||
* @param encoding the encoding of the template file
|
||||
* @param model the Map that contains model names as keys and model objects
|
||||
* as values
|
||||
* @return the result as String
|
||||
* @throws VelocityException if the template wasn't found or rendering failed
|
||||
* @see org.springframework.mail.MailPreparationException
|
||||
*/
|
||||
public static String mergeTemplateIntoString(
|
||||
VelocityEngine velocityEngine, String templateLocation, String encoding, Map model)
|
||||
throws VelocityException {
|
||||
|
||||
StringWriter result = new StringWriter();
|
||||
mergeTemplate(velocityEngine, templateLocation, encoding, model, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Support classes for setting up
|
||||
<a href="http://velocity.apache.org">Velocity</a>
|
||||
within a Spring application context.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<body>
|
||||
<p>
|
||||
The Spring Data Binding framework, an internal library used by Spring Web Flow.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
||||
|
||||
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
|
||||
|
||||
<!-- Appenders -->
|
||||
<appender name="console" class="org.apache.log4j.ConsoleAppender">
|
||||
<param name="Target" value="System.out" />
|
||||
<layout class="org.apache.log4j.PatternLayout">
|
||||
<param name="ConversionPattern" value="%-5p: %c - %m%n" />
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework.beans">
|
||||
<level value="warn" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.binding">
|
||||
<level value="debug" />
|
||||
</logger>
|
||||
|
||||
<!-- Root Logger -->
|
||||
<root>
|
||||
<priority value="warn" />
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
</log4j:configuration>
|
|
@ -0,0 +1,31 @@
|
|||
Bundle-SymbolicName: org.springframework.context.support
|
||||
Bundle-Name: Spring Context Support
|
||||
Bundle-Vendor: SpringSource
|
||||
Bundle-ManifestVersion: 2
|
||||
Import-Template:
|
||||
commonj.*;version="[1.1.0, 2.0.0)";resolution:=optional,
|
||||
freemarker.*;version="[2.3.12, 3.0.0)";resolution:=optional,
|
||||
javax.activation.*;version="[1.1.0, 2.0.0)";resolution:=optional,
|
||||
javax.mail.*;version="[1.4.0, 2.0.0)";resolution:=optional,
|
||||
net.sf.ehcache.*;version="[1.3.0, 2.0.0)";resolution:=optional,
|
||||
net.sf.jasperreports.*;version="[2.0.5, 3.0.0)";resolution:=optional,
|
||||
org.apache.commons.collections.*;version="[3.2.0, 4.0.0)";resolution:=optional,
|
||||
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)";resolution:=optional,
|
||||
org.apache.velocity.*;version="[1.5.0, 2.0.0)";resolution:=optional,
|
||||
org.quartz.*;version="[1.6.0, 2.0.0)";resolution:=optional,
|
||||
org.springframework.beans.*;version="[2.5.5.A, 2.5.5.A]";resolution:=optional,
|
||||
org.springframework.context.*;version="[2.5.5.A, 2.5.5.A]",
|
||||
org.springframework.core.*;version="[2.5.5.A, 2.5.5.A]",
|
||||
org.springframework.jdbc.datasource.*;version="[2.5.5.A, 2.5.5.A]";resolution:=optional,
|
||||
org.springframework.jndi.*;version="[2.5.5.A, 2.5.5.A]";resolution:=optional,
|
||||
org.springframework.scheduling.*;version="[2.5.5.A, 2.5.5.A]";resolution:=optional,
|
||||
org.springframework.transaction.*;version="[2.5.5.A, 2.5.5.A]";resolution:=optional,
|
||||
org.springframework.util.*;version="[2.5.5.A, 2.5.5.A]"
|
||||
Unversioned-Imports:
|
||||
javax.naming.*,
|
||||
javax.sql.*
|
||||
Ignored-Existing-Headers:
|
||||
Bnd-LastModified,
|
||||
Import-Package,
|
||||
Export-Package,
|
||||
Tool
|
Loading…
Reference in New Issue