Browse Source
git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@132 50f2f4bb-b051-0410-bef5-90022cba6387pull/1/head
66 changed files with 9477 additions and 0 deletions
@ -0,0 +1,6 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,10 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,8 @@
|
||||
<html> |
||||
<body> |
||||
|
||||
Spring's generic mail infrastructure. |
||||
Concrete implementations are provided in the subpackages. |
||||
|
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,54 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,8 @@
|
||||
<html> |
||||
<body> |
||||
|
||||
Support classes for |
||||
<a href="http://jasperreports.sourceforge.net">JasperReports</a>. |
||||
|
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,57 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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