Browse Source
Upgrades many dependency declarations; removes old EJB 2.x support and outdated Servlet-based integrations (Commons FileUpload, FreeMarker JSP support, Tiles). Closes gh-22093 Closes gh-25354 Closes gh-26185 Closes gh-27423 See gh-27424pull/27435/head
1291 changed files with 4988 additions and 25318 deletions
@ -1,171 +0,0 @@
@@ -1,171 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.util.concurrent.Callable; |
||||
|
||||
import net.sf.ehcache.Ehcache; |
||||
import net.sf.ehcache.Element; |
||||
import net.sf.ehcache.Status; |
||||
|
||||
import org.springframework.cache.Cache; |
||||
import org.springframework.cache.support.SimpleValueWrapper; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* {@link Cache} implementation on top of an {@link Ehcache} instance. |
||||
* |
||||
* @author Costin Leau |
||||
* @author Juergen Hoeller |
||||
* @author Stephane Nicoll |
||||
* @since 3.1 |
||||
* @see EhCacheCacheManager |
||||
*/ |
||||
public class EhCacheCache implements Cache { |
||||
|
||||
private final Ehcache cache; |
||||
|
||||
|
||||
/** |
||||
* Create an {@link EhCacheCache} instance. |
||||
* @param ehcache the backing Ehcache instance |
||||
*/ |
||||
public EhCacheCache(Ehcache ehcache) { |
||||
Assert.notNull(ehcache, "Ehcache must not be null"); |
||||
Status status = ehcache.getStatus(); |
||||
if (!Status.STATUS_ALIVE.equals(status)) { |
||||
throw new IllegalArgumentException( |
||||
"An 'alive' Ehcache is required - current cache is " + status.toString()); |
||||
} |
||||
this.cache = ehcache; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public final String getName() { |
||||
return this.cache.getName(); |
||||
} |
||||
|
||||
@Override |
||||
public final Ehcache getNativeCache() { |
||||
return this.cache; |
||||
} |
||||
|
||||
@Override |
||||
@Nullable |
||||
public ValueWrapper get(Object key) { |
||||
Element element = lookup(key); |
||||
return toValueWrapper(element); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
@Override |
||||
@Nullable |
||||
public <T> T get(Object key, @Nullable Class<T> type) { |
||||
Element element = this.cache.get(key); |
||||
Object value = (element != null ? element.getObjectValue() : null); |
||||
if (value != null && type != null && !type.isInstance(value)) { |
||||
throw new IllegalStateException( |
||||
"Cached value is not of required type [" + type.getName() + "]: " + value); |
||||
} |
||||
return (T) value; |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
@Override |
||||
@Nullable |
||||
public <T> T get(Object key, Callable<T> valueLoader) { |
||||
Element element = lookup(key); |
||||
if (element != null) { |
||||
return (T) element.getObjectValue(); |
||||
} |
||||
else { |
||||
this.cache.acquireWriteLockOnKey(key); |
||||
try { |
||||
element = lookup(key); // one more attempt with the write lock
|
||||
if (element != null) { |
||||
return (T) element.getObjectValue(); |
||||
} |
||||
else { |
||||
return loadValue(key, valueLoader); |
||||
} |
||||
} |
||||
finally { |
||||
this.cache.releaseWriteLockOnKey(key); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private <T> T loadValue(Object key, Callable<T> valueLoader) { |
||||
T value; |
||||
try { |
||||
value = valueLoader.call(); |
||||
} |
||||
catch (Throwable ex) { |
||||
throw new ValueRetrievalException(key, valueLoader, ex); |
||||
} |
||||
put(key, value); |
||||
return value; |
||||
} |
||||
|
||||
@Override |
||||
public void put(Object key, @Nullable Object value) { |
||||
this.cache.put(new Element(key, value)); |
||||
} |
||||
|
||||
@Override |
||||
@Nullable |
||||
public ValueWrapper putIfAbsent(Object key, @Nullable Object value) { |
||||
Element existingElement = this.cache.putIfAbsent(new Element(key, value)); |
||||
return toValueWrapper(existingElement); |
||||
} |
||||
|
||||
@Override |
||||
public void evict(Object key) { |
||||
this.cache.remove(key); |
||||
} |
||||
|
||||
@Override |
||||
public boolean evictIfPresent(Object key) { |
||||
return this.cache.remove(key); |
||||
} |
||||
|
||||
@Override |
||||
public void clear() { |
||||
this.cache.removeAll(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean invalidate() { |
||||
boolean notEmpty = (this.cache.getSize() > 0); |
||||
this.cache.removeAll(); |
||||
return notEmpty; |
||||
} |
||||
|
||||
|
||||
@Nullable |
||||
private Element lookup(Object key) { |
||||
return this.cache.get(key); |
||||
} |
||||
|
||||
@Nullable |
||||
private ValueWrapper toValueWrapper(@Nullable Element element) { |
||||
return (element != null ? new SimpleValueWrapper(element.getObjectValue()) : null); |
||||
} |
||||
|
||||
} |
||||
@ -1,117 +0,0 @@
@@ -1,117 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.util.Collection; |
||||
import java.util.LinkedHashSet; |
||||
|
||||
import net.sf.ehcache.Ehcache; |
||||
import net.sf.ehcache.Status; |
||||
|
||||
import org.springframework.cache.Cache; |
||||
import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* CacheManager backed by an EhCache {@link net.sf.ehcache.CacheManager}. |
||||
* |
||||
* @author Costin Leau |
||||
* @author Juergen Hoeller |
||||
* @author Stephane Nicoll |
||||
* @since 3.1 |
||||
* @see EhCacheCache |
||||
*/ |
||||
public class EhCacheCacheManager extends AbstractTransactionSupportingCacheManager { |
||||
|
||||
@Nullable |
||||
private net.sf.ehcache.CacheManager cacheManager; |
||||
|
||||
|
||||
/** |
||||
* Create a new EhCacheCacheManager, setting the target EhCache CacheManager |
||||
* through the {@link #setCacheManager} bean property. |
||||
*/ |
||||
public EhCacheCacheManager() { |
||||
} |
||||
|
||||
/** |
||||
* Create a new EhCacheCacheManager for the given backing EhCache CacheManager. |
||||
* @param cacheManager the backing EhCache {@link net.sf.ehcache.CacheManager} |
||||
*/ |
||||
public EhCacheCacheManager(net.sf.ehcache.CacheManager cacheManager) { |
||||
this.cacheManager = cacheManager; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Set the backing EhCache {@link net.sf.ehcache.CacheManager}. |
||||
*/ |
||||
public void setCacheManager(@Nullable net.sf.ehcache.CacheManager cacheManager) { |
||||
this.cacheManager = cacheManager; |
||||
} |
||||
|
||||
/** |
||||
* Return the backing EhCache {@link net.sf.ehcache.CacheManager}. |
||||
*/ |
||||
@Nullable |
||||
public net.sf.ehcache.CacheManager getCacheManager() { |
||||
return this.cacheManager; |
||||
} |
||||
|
||||
@Override |
||||
public void afterPropertiesSet() { |
||||
if (getCacheManager() == null) { |
||||
setCacheManager(EhCacheManagerUtils.buildCacheManager()); |
||||
} |
||||
super.afterPropertiesSet(); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected Collection<Cache> loadCaches() { |
||||
net.sf.ehcache.CacheManager cacheManager = getCacheManager(); |
||||
Assert.state(cacheManager != null, "No CacheManager set"); |
||||
|
||||
Status status = cacheManager.getStatus(); |
||||
if (!Status.STATUS_ALIVE.equals(status)) { |
||||
throw new IllegalStateException( |
||||
"An 'alive' EhCache CacheManager is required - current cache is " + status.toString()); |
||||
} |
||||
|
||||
String[] names = getCacheManager().getCacheNames(); |
||||
Collection<Cache> caches = new LinkedHashSet<>(names.length); |
||||
for (String name : names) { |
||||
caches.add(new EhCacheCache(getCacheManager().getEhcache(name))); |
||||
} |
||||
return caches; |
||||
} |
||||
|
||||
@Override |
||||
protected Cache getMissingCache(String name) { |
||||
net.sf.ehcache.CacheManager cacheManager = getCacheManager(); |
||||
Assert.state(cacheManager != null, "No CacheManager set"); |
||||
|
||||
// Check the EhCache cache again (in case the cache was added at runtime)
|
||||
Ehcache ehcache = cacheManager.getEhcache(name); |
||||
if (ehcache != null) { |
||||
return new EhCacheCache(ehcache); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
||||
@ -1,329 +0,0 @@
@@ -1,329 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2018 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 |
||||
* |
||||
* https://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.util.Set; |
||||
|
||||
import net.sf.ehcache.Cache; |
||||
import net.sf.ehcache.CacheException; |
||||
import net.sf.ehcache.CacheManager; |
||||
import net.sf.ehcache.Ehcache; |
||||
import net.sf.ehcache.bootstrap.BootstrapCacheLoader; |
||||
import net.sf.ehcache.config.CacheConfiguration; |
||||
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.event.CacheEventListener; |
||||
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.lang.Nullable; |
||||
|
||||
/** |
||||
* {@link 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. |
||||
* |
||||
* <p>Note: As of Spring 5.0, Spring's EhCache support requires EhCache 2.10 or higher. |
||||
* |
||||
* @author Juergen Hoeller |
||||
* @author Dmitriy Kopylenko |
||||
* @since 1.1.1 |
||||
* @see #setCacheManager |
||||
* @see EhCacheManagerFactoryBean |
||||
* @see net.sf.ehcache.Cache |
||||
*/ |
||||
public class EhCacheFactoryBean extends CacheConfiguration implements FactoryBean<Ehcache>, BeanNameAware, InitializingBean { |
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass()); |
||||
|
||||
@Nullable |
||||
private CacheManager cacheManager; |
||||
|
||||
private boolean blocking = false; |
||||
|
||||
@Nullable |
||||
private CacheEntryFactory cacheEntryFactory; |
||||
|
||||
@Nullable |
||||
private BootstrapCacheLoader bootstrapCacheLoader; |
||||
|
||||
@Nullable |
||||
private Set<CacheEventListener> cacheEventListeners; |
||||
|
||||
private boolean disabled = false; |
||||
|
||||
@Nullable |
||||
private String beanName; |
||||
|
||||
@Nullable |
||||
private Ehcache cache; |
||||
|
||||
|
||||
public EhCacheFactoryBean() { |
||||
setMaxEntriesLocalHeap(10000); |
||||
setMaxEntriesLocalDisk(10000000); |
||||
setTimeToLiveSeconds(120); |
||||
setTimeToIdleSeconds(120); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Set a CacheManager from which to retrieve a named Cache instance. |
||||
* By default, {@code CacheManager.getInstance()} 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) { |
||||
setName(cacheName); |
||||
} |
||||
|
||||
/** |
||||
* Set the time to live. |
||||
* @see #setTimeToLiveSeconds(long) |
||||
*/ |
||||
public void setTimeToLive(int timeToLive) { |
||||
setTimeToLiveSeconds(timeToLive); |
||||
} |
||||
|
||||
/** |
||||
* Set the time to idle. |
||||
* @see #setTimeToIdleSeconds(long) |
||||
*/ |
||||
public void setTimeToIdle(int timeToIdle) { |
||||
setTimeToIdleSeconds(timeToIdle); |
||||
} |
||||
|
||||
/** |
||||
* Set the disk spool buffer size (in MB). |
||||
* @see #setDiskSpoolBufferSizeMB(int) |
||||
*/ |
||||
public void setDiskSpoolBufferSize(int diskSpoolBufferSize) { |
||||
setDiskSpoolBufferSizeMB(diskSpoolBufferSize); |
||||
} |
||||
|
||||
/** |
||||
* 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; |
||||
} |
||||
|
||||
/** |
||||
* Set an EhCache {@link net.sf.ehcache.bootstrap.BootstrapCacheLoader} |
||||
* for this cache, if any. |
||||
*/ |
||||
public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader) { |
||||
this.bootstrapCacheLoader = bootstrapCacheLoader; |
||||
} |
||||
|
||||
/** |
||||
* Specify EhCache {@link net.sf.ehcache.event.CacheEventListener cache event listeners} |
||||
* to registered with this cache. |
||||
*/ |
||||
public void setCacheEventListeners(Set<CacheEventListener> cacheEventListeners) { |
||||
this.cacheEventListeners = cacheEventListeners; |
||||
} |
||||
|
||||
/** |
||||
* Set whether this cache should be marked as disabled. |
||||
* @see net.sf.ehcache.Cache#setDisabled |
||||
*/ |
||||
public void setDisabled(boolean disabled) { |
||||
this.disabled = disabled; |
||||
} |
||||
|
||||
@Override |
||||
public void setBeanName(String name) { |
||||
this.beanName = name; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void afterPropertiesSet() throws CacheException { |
||||
// If no cache name given, use bean name as cache name.
|
||||
String cacheName = getName(); |
||||
if (cacheName == null) { |
||||
cacheName = this.beanName; |
||||
if (cacheName != null) { |
||||
setName(cacheName); |
||||
} |
||||
} |
||||
|
||||
// If no CacheManager given, fetch the default.
|
||||
if (this.cacheManager == null) { |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Using default EhCache CacheManager for cache region '" + cacheName + "'"); |
||||
} |
||||
this.cacheManager = CacheManager.getInstance(); |
||||
} |
||||
|
||||
synchronized (this.cacheManager) { |
||||
// Fetch cache region: If none with the given name exists, create one on the fly.
|
||||
Ehcache rawCache; |
||||
boolean cacheExists = this.cacheManager.cacheExists(cacheName); |
||||
|
||||
if (cacheExists) { |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Using existing EhCache cache region '" + cacheName + "'"); |
||||
} |
||||
rawCache = this.cacheManager.getEhcache(cacheName); |
||||
} |
||||
else { |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Creating new EhCache cache region '" + cacheName + "'"); |
||||
} |
||||
rawCache = createCache(); |
||||
rawCache.setBootstrapCacheLoader(this.bootstrapCacheLoader); |
||||
} |
||||
|
||||
if (this.cacheEventListeners != null) { |
||||
for (CacheEventListener listener : this.cacheEventListeners) { |
||||
rawCache.getCacheEventNotificationService().registerListener(listener); |
||||
} |
||||
} |
||||
|
||||
// Needs to happen after listener registration but before setStatisticsEnabled
|
||||
if (!cacheExists) { |
||||
this.cacheManager.addCache(rawCache); |
||||
} |
||||
|
||||
if (this.disabled) { |
||||
rawCache.setDisabled(true); |
||||
} |
||||
|
||||
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. |
||||
*/ |
||||
protected Cache createCache() { |
||||
return new Cache(this); |
||||
} |
||||
|
||||
/** |
||||
* 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; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
@Nullable |
||||
public Ehcache getObject() { |
||||
return this.cache; |
||||
} |
||||
|
||||
/** |
||||
* Predict the particular {@code Ehcache} implementation that will be returned from |
||||
* {@link #getObject()} based on logic in {@link #createCache()} and |
||||
* {@link #decorateCache(Ehcache)} as orchestrated by {@link #afterPropertiesSet()}. |
||||
*/ |
||||
@Override |
||||
public Class<? extends Ehcache> getObjectType() { |
||||
if (this.cache != null) { |
||||
return this.cache.getClass(); |
||||
} |
||||
if (this.cacheEntryFactory != null) { |
||||
if (this.cacheEntryFactory instanceof UpdatingCacheEntryFactory) { |
||||
return UpdatingSelfPopulatingCache.class; |
||||
} |
||||
else { |
||||
return SelfPopulatingCache.class; |
||||
} |
||||
} |
||||
if (this.blocking) { |
||||
return BlockingCache.class; |
||||
} |
||||
return Cache.class; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSingleton() { |
||||
return true; |
||||
} |
||||
|
||||
} |
||||
@ -1,199 +0,0 @@
@@ -1,199 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2021 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 |
||||
* |
||||
* https://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 net.sf.ehcache.CacheException; |
||||
import net.sf.ehcache.CacheManager; |
||||
import net.sf.ehcache.config.Configuration; |
||||
import net.sf.ehcache.config.ConfigurationFactory; |
||||
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; |
||||
import org.springframework.lang.Nullable; |
||||
|
||||
/** |
||||
* {@link 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 5.0, Spring's EhCache support requires EhCache 2.10 or higher. |
||||
* |
||||
* @author Juergen Hoeller |
||||
* @author Dmitriy Kopylenko |
||||
* @since 1.1.1 |
||||
* @see #setConfigLocation |
||||
* @see #setShared |
||||
* @see EhCacheFactoryBean |
||||
* @see net.sf.ehcache.CacheManager |
||||
*/ |
||||
public class EhCacheManagerFactoryBean implements FactoryBean<CacheManager>, InitializingBean, DisposableBean { |
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass()); |
||||
|
||||
@Nullable |
||||
private Resource configLocation; |
||||
|
||||
@Nullable |
||||
private String cacheManagerName; |
||||
|
||||
private boolean acceptExisting = false; |
||||
|
||||
private boolean shared = false; |
||||
|
||||
@Nullable |
||||
private CacheManager cacheManager; |
||||
|
||||
private boolean locallyManaged = true; |
||||
|
||||
|
||||
/** |
||||
* 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 the name of the EhCache CacheManager (if a specific name is desired). |
||||
* @see net.sf.ehcache.config.Configuration#setName(String) |
||||
*/ |
||||
public void setCacheManagerName(String cacheManagerName) { |
||||
this.cacheManagerName = cacheManagerName; |
||||
} |
||||
|
||||
/** |
||||
* Set whether an existing EhCache CacheManager of the same name will be accepted |
||||
* for this EhCacheManagerFactoryBean setup. Default is "false". |
||||
* <p>Typically used in combination with {@link #setCacheManagerName "cacheManagerName"} |
||||
* but will simply work with the default CacheManager name if none specified. |
||||
* All references to the same CacheManager name (or the same default) in the |
||||
* same ClassLoader space will share the specified CacheManager then. |
||||
* @see #setCacheManagerName |
||||
* #see #setShared |
||||
* @see net.sf.ehcache.CacheManager#getCacheManager(String) |
||||
* @see net.sf.ehcache.CacheManager#CacheManager() |
||||
*/ |
||||
public void setAcceptExisting(boolean acceptExisting) { |
||||
this.acceptExisting = acceptExisting; |
||||
} |
||||
|
||||
/** |
||||
* Set whether the EhCache CacheManager should be shared (as a singleton at the |
||||
* ClassLoader level) or independent (typically local within the application). |
||||
* Default is "false", creating an independent local instance. |
||||
* <p><b>NOTE:</b> This feature allows for sharing this EhCacheManagerFactoryBean's |
||||
* CacheManager with any code calling <code>CacheManager.create()</code> in the same |
||||
* ClassLoader space, with no need to agree on a specific CacheManager name. |
||||
* However, it only supports a single EhCacheManagerFactoryBean involved which will |
||||
* control the lifecycle of the underlying CacheManager (in particular, its shutdown). |
||||
* <p>This flag overrides {@link #setAcceptExisting "acceptExisting"} if both are set, |
||||
* since it indicates the 'stronger' mode of sharing. |
||||
* @see #setCacheManagerName |
||||
* @see #setAcceptExisting |
||||
* @see net.sf.ehcache.CacheManager#create() |
||||
* @see net.sf.ehcache.CacheManager#CacheManager() |
||||
*/ |
||||
public void setShared(boolean shared) { |
||||
this.shared = shared; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void afterPropertiesSet() throws CacheException { |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Initializing EhCache CacheManager" + |
||||
(this.cacheManagerName != null ? " '" + this.cacheManagerName + "'" : "")); |
||||
} |
||||
|
||||
Configuration configuration = (this.configLocation != null ? |
||||
EhCacheManagerUtils.parseConfiguration(this.configLocation) : ConfigurationFactory.parseConfiguration()); |
||||
if (this.cacheManagerName != null) { |
||||
configuration.setName(this.cacheManagerName); |
||||
} |
||||
|
||||
if (this.shared) { |
||||
// Old-school EhCache singleton sharing...
|
||||
// No way to find out whether we actually created a new CacheManager
|
||||
// or just received an existing singleton reference.
|
||||
this.cacheManager = CacheManager.create(configuration); |
||||
} |
||||
else if (this.acceptExisting) { |
||||
// EhCache 2.5+: Reusing an existing CacheManager of the same name.
|
||||
// Basically the same code as in CacheManager.getInstance(String),
|
||||
// just storing whether we're dealing with an existing instance.
|
||||
synchronized (CacheManager.class) { |
||||
this.cacheManager = CacheManager.getCacheManager(this.cacheManagerName); |
||||
if (this.cacheManager == null) { |
||||
this.cacheManager = new CacheManager(configuration); |
||||
} |
||||
else { |
||||
this.locallyManaged = false; |
||||
} |
||||
} |
||||
} |
||||
else { |
||||
// Throwing an exception if a CacheManager of the same name exists already...
|
||||
this.cacheManager = new CacheManager(configuration); |
||||
} |
||||
} |
||||
|
||||
|
||||
@Override |
||||
@Nullable |
||||
public CacheManager getObject() { |
||||
return this.cacheManager; |
||||
} |
||||
|
||||
@Override |
||||
public Class<? extends CacheManager> getObjectType() { |
||||
return (this.cacheManager != null ? this.cacheManager.getClass() : CacheManager.class); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSingleton() { |
||||
return true; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void destroy() { |
||||
if (this.cacheManager != null && this.locallyManaged) { |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Shutting down EhCache CacheManager" + |
||||
(this.cacheManagerName != null ? " '" + this.cacheManagerName + "'" : "")); |
||||
} |
||||
this.cacheManager.shutdown(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,125 +0,0 @@
@@ -1,125 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2014 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 |
||||
* |
||||
* https://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 java.io.InputStream; |
||||
|
||||
import net.sf.ehcache.CacheException; |
||||
import net.sf.ehcache.CacheManager; |
||||
import net.sf.ehcache.config.Configuration; |
||||
import net.sf.ehcache.config.ConfigurationFactory; |
||||
|
||||
import org.springframework.core.io.Resource; |
||||
|
||||
/** |
||||
* Convenient builder methods for EhCache 2.5+ {@link CacheManager} setup, |
||||
* providing easy programmatic bootstrapping from a Spring-provided resource. |
||||
* This is primarily intended for use within {@code @Bean} methods in a |
||||
* Spring configuration class. |
||||
* |
||||
* <p>These methods are a simple alternative to custom {@link CacheManager} setup |
||||
* code. For any advanced purposes, consider using {@link #parseConfiguration}, |
||||
* customizing the configuration object, and then calling the |
||||
* {@link CacheManager#CacheManager(Configuration)} constructor. |
||||
* |
||||
* @author Juergen Hoeller |
||||
* @since 4.1 |
||||
*/ |
||||
public abstract class EhCacheManagerUtils { |
||||
|
||||
/** |
||||
* Build an EhCache {@link CacheManager} from the default configuration. |
||||
* <p>The 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). |
||||
* If no configuration file can be found, a fail-safe fallback configuration will be used. |
||||
* @return the new EhCache CacheManager |
||||
* @throws CacheException in case of configuration parsing failure |
||||
*/ |
||||
public static CacheManager buildCacheManager() throws CacheException { |
||||
return new CacheManager(ConfigurationFactory.parseConfiguration()); |
||||
} |
||||
|
||||
/** |
||||
* Build an EhCache {@link CacheManager} from the default configuration. |
||||
* <p>The 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). |
||||
* If no configuration file can be found, a fail-safe fallback configuration will be used. |
||||
* @param name the desired name of the cache manager |
||||
* @return the new EhCache CacheManager |
||||
* @throws CacheException in case of configuration parsing failure |
||||
*/ |
||||
public static CacheManager buildCacheManager(String name) throws CacheException { |
||||
Configuration configuration = ConfigurationFactory.parseConfiguration(); |
||||
configuration.setName(name); |
||||
return new CacheManager(configuration); |
||||
} |
||||
|
||||
/** |
||||
* Build an EhCache {@link CacheManager} from the given configuration resource. |
||||
* @param configLocation the location of the configuration file (as a Spring resource) |
||||
* @return the new EhCache CacheManager |
||||
* @throws CacheException in case of configuration parsing failure |
||||
*/ |
||||
public static CacheManager buildCacheManager(Resource configLocation) throws CacheException { |
||||
return new CacheManager(parseConfiguration(configLocation)); |
||||
} |
||||
|
||||
/** |
||||
* Build an EhCache {@link CacheManager} from the given configuration resource. |
||||
* @param name the desired name of the cache manager |
||||
* @param configLocation the location of the configuration file (as a Spring resource) |
||||
* @return the new EhCache CacheManager |
||||
* @throws CacheException in case of configuration parsing failure |
||||
*/ |
||||
public static CacheManager buildCacheManager(String name, Resource configLocation) throws CacheException { |
||||
Configuration configuration = parseConfiguration(configLocation); |
||||
configuration.setName(name); |
||||
return new CacheManager(configuration); |
||||
} |
||||
|
||||
/** |
||||
* Parse EhCache configuration from the given resource, for further use with |
||||
* custom {@link CacheManager} creation. |
||||
* @param configLocation the location of the configuration file (as a Spring resource) |
||||
* @return the EhCache Configuration handle |
||||
* @throws CacheException in case of configuration parsing failure |
||||
* @see CacheManager#CacheManager(Configuration) |
||||
* @see CacheManager#create(Configuration) |
||||
*/ |
||||
public static Configuration parseConfiguration(Resource configLocation) throws CacheException { |
||||
InputStream is = null; |
||||
try { |
||||
is = configLocation.getInputStream(); |
||||
return ConfigurationFactory.parseConfiguration(is); |
||||
} |
||||
catch (IOException ex) { |
||||
throw new CacheException("Failed to parse EhCache configuration resource", ex); |
||||
} |
||||
finally { |
||||
if (is != null) { |
||||
try { |
||||
is.close(); |
||||
} |
||||
catch (IOException ex) { |
||||
// ignore
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,17 +0,0 @@
@@ -1,17 +0,0 @@
|
||||
/** |
||||
* Support classes for the open source cache |
||||
* <a href="https://www.ehcache.org/">EhCache 2.x</a>, |
||||
* allowing to set up an EhCache CacheManager and Caches |
||||
* as beans in a Spring context. |
||||
* |
||||
* <p>Note: EhCache 3.x lives in a different package namespace |
||||
* and is not covered by the traditional support classes here. |
||||
* Instead, consider using it through JCache (JSR-107), with |
||||
* Spring's support in {@code org.springframework.cache.jcache}. |
||||
*/ |
||||
@NonNullApi |
||||
@NonNullFields |
||||
package org.springframework.cache.ehcache; |
||||
|
||||
import org.springframework.lang.NonNullApi; |
||||
import org.springframework.lang.NonNullFields; |
||||
@ -1,58 +0,0 @@
@@ -1,58 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2012 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 |
||||
* |
||||
* https://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 |
||||
* @deprecated as of 5.1, in favor of EE 7's |
||||
* {@link org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler} |
||||
*/ |
||||
@Deprecated |
||||
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. |
||||
*/ |
||||
@Override |
||||
public void timerExpired(Timer timer) { |
||||
this.runnable.run(); |
||||
} |
||||
|
||||
} |
||||
@ -1,85 +0,0 @@
@@ -1,85 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2012 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 |
||||
* |
||||
* https://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 |
||||
* @deprecated as of 5.1, in favor of EE 7's |
||||
* {@link org.springframework.scheduling.concurrent.DefaultManagedTaskExecutor} |
||||
*/ |
||||
@Deprecated |
||||
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. |
||||
*/ |
||||
@Override |
||||
public void run() { |
||||
this.delegate.run(); |
||||
} |
||||
|
||||
/** |
||||
* This implementation delegates to |
||||
* {@link org.springframework.scheduling.SchedulingAwareRunnable#isLongLived()}, |
||||
* if available. |
||||
*/ |
||||
@Override |
||||
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. |
||||
*/ |
||||
@Override |
||||
public void release() { |
||||
} |
||||
|
||||
} |
||||
@ -1,229 +0,0 @@
@@ -1,229 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2017 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 |
||||
* |
||||
* https://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; |
||||
|
||||
import org.springframework.lang.Nullable; |
||||
|
||||
/** |
||||
* 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 |
||||
* @deprecated as of 5.1, in favor of EE 7's |
||||
* {@link org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler} |
||||
*/ |
||||
@Deprecated |
||||
public class ScheduledTimerListener { |
||||
|
||||
@Nullable |
||||
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(@Nullable TimerListener timerListener) { |
||||
this.timerListener = timerListener; |
||||
} |
||||
|
||||
/** |
||||
* Return the TimerListener to schedule. |
||||
*/ |
||||
@Nullable |
||||
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 in-between 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}). |
||||
* @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} 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; |
||||
} |
||||
|
||||
} |
||||
@ -1,192 +0,0 @@
@@ -1,192 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2017 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 |
||||
* |
||||
* https://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 javax.naming.NamingException; |
||||
|
||||
import commonj.timers.TimerManager; |
||||
|
||||
import org.springframework.beans.factory.DisposableBean; |
||||
import org.springframework.beans.factory.InitializingBean; |
||||
import org.springframework.context.Lifecycle; |
||||
import org.springframework.jndi.JndiLocatorSupport; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* Base class for classes that are accessing a CommonJ {@link commonj.timers.TimerManager} |
||||
* Defines common configuration settings and common lifecycle handling. |
||||
* |
||||
* @author Juergen Hoeller |
||||
* @since 3.0 |
||||
* @see commonj.timers.TimerManager |
||||
* @deprecated as of 5.1, in favor of EE 7's |
||||
* {@link org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler} |
||||
*/ |
||||
@Deprecated |
||||
public abstract class TimerManagerAccessor extends JndiLocatorSupport |
||||
implements InitializingBean, DisposableBean, Lifecycle { |
||||
|
||||
@Nullable |
||||
private TimerManager timerManager; |
||||
|
||||
@Nullable |
||||
private String timerManagerName; |
||||
|
||||
private boolean shared = false; |
||||
|
||||
|
||||
/** |
||||
* 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} of type {@code commonj.timers.TimerManager} |
||||
* in {@code web.xml}, with {@code res-sharing-scope} 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; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
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 = lookup(this.timerManagerName, TimerManager.class); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Return the configured TimerManager, if any. |
||||
* @return the TimerManager, or {@code null} if not available |
||||
*/ |
||||
@Nullable |
||||
protected final TimerManager getTimerManager() { |
||||
return this.timerManager; |
||||
} |
||||
|
||||
/** |
||||
* Obtain the TimerManager for actual use. |
||||
* @return the TimerManager (never {@code null}) |
||||
* @throws IllegalStateException in case of no TimerManager set |
||||
* @since 5.0 |
||||
*/ |
||||
protected TimerManager obtainTimerManager() { |
||||
Assert.notNull(this.timerManager, "No TimerManager set"); |
||||
return this.timerManager; |
||||
} |
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of Lifecycle interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/** |
||||
* Resumes the underlying TimerManager (if not shared). |
||||
* @see commonj.timers.TimerManager#resume() |
||||
*/ |
||||
@Override |
||||
public void start() { |
||||
if (!this.shared) { |
||||
obtainTimerManager().resume(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Suspends the underlying TimerManager (if not shared). |
||||
* @see commonj.timers.TimerManager#suspend() |
||||
*/ |
||||
@Override |
||||
public void stop() { |
||||
if (!this.shared) { |
||||
obtainTimerManager().suspend(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Considers the underlying TimerManager as running if it is |
||||
* neither suspending nor stopping. |
||||
* @see commonj.timers.TimerManager#isSuspending() |
||||
* @see commonj.timers.TimerManager#isStopping() |
||||
*/ |
||||
@Override |
||||
public boolean isRunning() { |
||||
TimerManager tm = obtainTimerManager(); |
||||
return (!tm.isSuspending() && !tm.isStopping()); |
||||
} |
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of DisposableBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/** |
||||
* Stops the underlying TimerManager (if not shared). |
||||
* @see commonj.timers.TimerManager#stop() |
||||
*/ |
||||
@Override |
||||
public void destroy() { |
||||
// Stop the entire TimerManager, if necessary.
|
||||
if (this.timerManager != null && !this.shared) { |
||||
// May return early, but at least we already cancelled all known Timers.
|
||||
this.timerManager.stop(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,165 +0,0 @@
@@ -1,165 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.ArrayList; |
||||
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.lang.Nullable; |
||||
|
||||
/** |
||||
* {@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 |
||||
* @deprecated as of 5.1, in favor of EE 7's |
||||
* {@link org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler} |
||||
*/ |
||||
@Deprecated |
||||
public class TimerManagerFactoryBean extends TimerManagerAccessor |
||||
implements FactoryBean<TimerManager>, InitializingBean, DisposableBean, Lifecycle { |
||||
|
||||
@Nullable |
||||
private ScheduledTimerListener[] scheduledTimerListeners; |
||||
|
||||
@Nullable |
||||
private List<Timer> timers; |
||||
|
||||
|
||||
/** |
||||
* 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
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
@Override |
||||
public void afterPropertiesSet() throws NamingException { |
||||
super.afterPropertiesSet(); |
||||
|
||||
if (this.scheduledTimerListeners != null) { |
||||
this.timers = new ArrayList<>(this.scheduledTimerListeners.length); |
||||
TimerManager timerManager = obtainTimerManager(); |
||||
for (ScheduledTimerListener scheduledTask : this.scheduledTimerListeners) { |
||||
Timer timer; |
||||
if (scheduledTask.isOneTimeTask()) { |
||||
timer = timerManager.schedule(scheduledTask.getTimerListener(), scheduledTask.getDelay()); |
||||
} |
||||
else { |
||||
if (scheduledTask.isFixedRate()) { |
||||
timer = timerManager.scheduleAtFixedRate( |
||||
scheduledTask.getTimerListener(), scheduledTask.getDelay(), scheduledTask.getPeriod()); |
||||
} |
||||
else { |
||||
timer = timerManager.schedule( |
||||
scheduledTask.getTimerListener(), scheduledTask.getDelay(), scheduledTask.getPeriod()); |
||||
} |
||||
} |
||||
this.timers.add(timer); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of FactoryBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
@Override |
||||
@Nullable |
||||
public TimerManager getObject() { |
||||
return getTimerManager(); |
||||
} |
||||
|
||||
@Override |
||||
public Class<? extends TimerManager> getObjectType() { |
||||
TimerManager timerManager = getTimerManager(); |
||||
return (timerManager != null ? timerManager.getClass() : TimerManager.class); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSingleton() { |
||||
return true; |
||||
} |
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// 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() |
||||
*/ |
||||
@Override |
||||
public void destroy() { |
||||
// Cancel all registered timers.
|
||||
if (this.timers != null) { |
||||
for (Timer timer : this.timers) { |
||||
try { |
||||
timer.cancel(); |
||||
} |
||||
catch (Throwable ex) { |
||||
logger.debug("Could not cancel CommonJ Timer", ex); |
||||
} |
||||
} |
||||
this.timers.clear(); |
||||
} |
||||
|
||||
// Stop the TimerManager itself.
|
||||
super.destroy(); |
||||
} |
||||
|
||||
} |
||||
@ -1,201 +0,0 @@
@@ -1,201 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2018 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 |
||||
* |
||||
* https://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.Date; |
||||
import java.util.concurrent.Delayed; |
||||
import java.util.concurrent.FutureTask; |
||||
import java.util.concurrent.ScheduledFuture; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import commonj.timers.Timer; |
||||
import commonj.timers.TimerListener; |
||||
|
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.scheduling.TaskScheduler; |
||||
import org.springframework.scheduling.Trigger; |
||||
import org.springframework.scheduling.support.SimpleTriggerContext; |
||||
import org.springframework.scheduling.support.TaskUtils; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ErrorHandler; |
||||
|
||||
/** |
||||
* Implementation of Spring's {@link TaskScheduler} interface, wrapping |
||||
* a CommonJ {@link commonj.timers.TimerManager}. |
||||
* |
||||
* @author Juergen Hoeller |
||||
* @author Mark Fisher |
||||
* @since 3.0 |
||||
* @deprecated as of 5.1, in favor of EE 7's |
||||
* {@link org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler} |
||||
*/ |
||||
@Deprecated |
||||
public class TimerManagerTaskScheduler extends TimerManagerAccessor implements TaskScheduler { |
||||
|
||||
@Nullable |
||||
private volatile ErrorHandler errorHandler; |
||||
|
||||
|
||||
/** |
||||
* Provide an {@link ErrorHandler} strategy. |
||||
*/ |
||||
public void setErrorHandler(ErrorHandler errorHandler) { |
||||
this.errorHandler = errorHandler; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
@Nullable |
||||
public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) { |
||||
return new ReschedulingTimerListener(errorHandlingTask(task, true), trigger).schedule(); |
||||
} |
||||
|
||||
@Override |
||||
public ScheduledFuture<?> schedule(Runnable task, Date startTime) { |
||||
TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, false)); |
||||
Timer timer = obtainTimerManager().schedule(futureTask, startTime); |
||||
futureTask.setTimer(timer); |
||||
return futureTask; |
||||
} |
||||
|
||||
@Override |
||||
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) { |
||||
TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, true)); |
||||
Timer timer = obtainTimerManager().scheduleAtFixedRate(futureTask, startTime, period); |
||||
futureTask.setTimer(timer); |
||||
return futureTask; |
||||
} |
||||
|
||||
@Override |
||||
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) { |
||||
TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, true)); |
||||
Timer timer = obtainTimerManager().scheduleAtFixedRate(futureTask, 0, period); |
||||
futureTask.setTimer(timer); |
||||
return futureTask; |
||||
} |
||||
|
||||
@Override |
||||
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay) { |
||||
TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, true)); |
||||
Timer timer = obtainTimerManager().schedule(futureTask, startTime, delay); |
||||
futureTask.setTimer(timer); |
||||
return futureTask; |
||||
} |
||||
|
||||
@Override |
||||
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay) { |
||||
TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, true)); |
||||
Timer timer = obtainTimerManager().schedule(futureTask, 0, delay); |
||||
futureTask.setTimer(timer); |
||||
return futureTask; |
||||
} |
||||
|
||||
private Runnable errorHandlingTask(Runnable delegate, boolean isRepeatingTask) { |
||||
return TaskUtils.decorateTaskWithErrorHandler(delegate, this.errorHandler, isRepeatingTask); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* ScheduledFuture adapter that wraps a CommonJ Timer. |
||||
*/ |
||||
private static class TimerScheduledFuture extends FutureTask<Object> implements TimerListener, ScheduledFuture<Object> { |
||||
|
||||
@Nullable |
||||
protected transient Timer timer; |
||||
|
||||
protected transient boolean cancelled = false; |
||||
|
||||
public TimerScheduledFuture(Runnable runnable) { |
||||
super(runnable, null); |
||||
} |
||||
|
||||
public void setTimer(Timer timer) { |
||||
this.timer = timer; |
||||
} |
||||
|
||||
@Override |
||||
public void timerExpired(Timer timer) { |
||||
runAndReset(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean cancel(boolean mayInterruptIfRunning) { |
||||
boolean result = super.cancel(mayInterruptIfRunning); |
||||
if (this.timer != null) { |
||||
this.timer.cancel(); |
||||
} |
||||
this.cancelled = true; |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public long getDelay(TimeUnit unit) { |
||||
Assert.state(this.timer != null, "No Timer available"); |
||||
return unit.convert(this.timer.getScheduledExecutionTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); |
||||
} |
||||
|
||||
@Override |
||||
public int compareTo(Delayed other) { |
||||
if (this == other) { |
||||
return 0; |
||||
} |
||||
long diff = getDelay(TimeUnit.MILLISECONDS) - other.getDelay(TimeUnit.MILLISECONDS); |
||||
return (diff == 0 ? 0 : ((diff < 0) ? -1 : 1)); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* ScheduledFuture adapter for trigger-based rescheduling. |
||||
*/ |
||||
private class ReschedulingTimerListener extends TimerScheduledFuture { |
||||
|
||||
private final Trigger trigger; |
||||
|
||||
private final SimpleTriggerContext triggerContext = new SimpleTriggerContext(); |
||||
|
||||
private volatile Date scheduledExecutionTime = new Date(); |
||||
|
||||
public ReschedulingTimerListener(Runnable runnable, Trigger trigger) { |
||||
super(runnable); |
||||
this.trigger = trigger; |
||||
} |
||||
|
||||
@Nullable |
||||
public ScheduledFuture<?> schedule() { |
||||
Date nextExecutionTime = this.trigger.nextExecutionTime(this.triggerContext); |
||||
if (nextExecutionTime == null) { |
||||
return null; |
||||
} |
||||
this.scheduledExecutionTime = nextExecutionTime; |
||||
setTimer(obtainTimerManager().schedule(this, this.scheduledExecutionTime)); |
||||
return this; |
||||
} |
||||
|
||||
@Override |
||||
public void timerExpired(Timer timer) { |
||||
Date actualExecutionTime = new Date(); |
||||
super.timerExpired(timer); |
||||
Date completionTime = new Date(); |
||||
this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime); |
||||
if (!this.cancelled) { |
||||
schedule(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,234 +0,0 @@
@@ -1,234 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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 java.util.concurrent.Callable; |
||||
import java.util.concurrent.Future; |
||||
import java.util.concurrent.FutureTask; |
||||
|
||||
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.AsyncListenableTaskExecutor; |
||||
import org.springframework.core.task.TaskDecorator; |
||||
import org.springframework.core.task.TaskRejectedException; |
||||
import org.springframework.jndi.JndiLocatorSupport; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.scheduling.SchedulingException; |
||||
import org.springframework.scheduling.SchedulingTaskExecutor; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.concurrent.ListenableFuture; |
||||
import org.springframework.util.concurrent.ListenableFutureTask; |
||||
|
||||
/** |
||||
* 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>Note: On EE 7/8 compliant versions of WebLogic and WebSphere, a |
||||
* {@link org.springframework.scheduling.concurrent.DefaultManagedTaskExecutor} |
||||
* should be preferred, following JSR-236 support in Java EE 7/8. |
||||
* |
||||
* @author Juergen Hoeller |
||||
* @since 2.0 |
||||
* @deprecated as of 5.1, in favor of the EE 7/8 based |
||||
* {@link org.springframework.scheduling.concurrent.DefaultManagedTaskExecutor} |
||||
*/ |
||||
@Deprecated |
||||
public class WorkManagerTaskExecutor extends JndiLocatorSupport |
||||
implements AsyncListenableTaskExecutor, SchedulingTaskExecutor, WorkManager, InitializingBean { |
||||
|
||||
@Nullable |
||||
private WorkManager workManager; |
||||
|
||||
@Nullable |
||||
private String workManagerName; |
||||
|
||||
@Nullable |
||||
private WorkListener workListener; |
||||
|
||||
@Nullable |
||||
private TaskDecorator taskDecorator; |
||||
|
||||
|
||||
/** |
||||
* 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; |
||||
} |
||||
|
||||
/** |
||||
* Specify a custom {@link TaskDecorator} to be applied to any {@link Runnable} |
||||
* about to be executed. |
||||
* <p>Note that such a decorator is not necessarily being applied to the |
||||
* user-supplied {@code Runnable}/{@code Callable} but rather to the actual |
||||
* execution callback (which may be a wrapper around the user-supplied task). |
||||
* <p>The primary use case is to set some execution context around the task's |
||||
* invocation, or to provide some monitoring/statistics for task execution. |
||||
* <p><b>NOTE:</b> Exception handling in {@code TaskDecorator} implementations |
||||
* is limited to plain {@code Runnable} execution via {@code execute} calls. |
||||
* In case of {@code #submit} calls, the exposed {@code Runnable} will be a |
||||
* {@code FutureTask} which does not propagate any exceptions; you might |
||||
* have to cast it and call {@code Future#get} to evaluate exceptions. |
||||
* @since 4.3 |
||||
*/ |
||||
public void setTaskDecorator(TaskDecorator taskDecorator) { |
||||
this.taskDecorator = taskDecorator; |
||||
} |
||||
|
||||
@Override |
||||
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 = lookup(this.workManagerName, WorkManager.class); |
||||
} |
||||
} |
||||
|
||||
private WorkManager obtainWorkManager() { |
||||
Assert.state(this.workManager != null, "No WorkManager specified"); |
||||
return this.workManager; |
||||
} |
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Implementation of the Spring SchedulingTaskExecutor interface
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
@Override |
||||
public void execute(Runnable task) { |
||||
Work work = new DelegatingWork(this.taskDecorator != null ? this.taskDecorator.decorate(task) : task); |
||||
try { |
||||
if (this.workListener != null) { |
||||
obtainWorkManager().schedule(work, this.workListener); |
||||
} |
||||
else { |
||||
obtainWorkManager().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); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void execute(Runnable task, long startTimeout) { |
||||
execute(task); |
||||
} |
||||
|
||||
@Override |
||||
public Future<?> submit(Runnable task) { |
||||
FutureTask<Object> future = new FutureTask<>(task, null); |
||||
execute(future); |
||||
return future; |
||||
} |
||||
|
||||
@Override |
||||
public <T> Future<T> submit(Callable<T> task) { |
||||
FutureTask<T> future = new FutureTask<>(task); |
||||
execute(future); |
||||
return future; |
||||
} |
||||
|
||||
@Override |
||||
public ListenableFuture<?> submitListenable(Runnable task) { |
||||
ListenableFutureTask<Object> future = new ListenableFutureTask<>(task, null); |
||||
execute(future); |
||||
return future; |
||||
} |
||||
|
||||
@Override |
||||
public <T> ListenableFuture<T> submitListenable(Callable<T> task) { |
||||
ListenableFutureTask<T> future = new ListenableFutureTask<>(task); |
||||
execute(future); |
||||
return future; |
||||
} |
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Implementation of the CommonJ WorkManager interface
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
@Override |
||||
public WorkItem schedule(Work work) throws WorkException, IllegalArgumentException { |
||||
return obtainWorkManager().schedule(work); |
||||
} |
||||
|
||||
@Override |
||||
public WorkItem schedule(Work work, WorkListener workListener) throws WorkException { |
||||
return obtainWorkManager().schedule(work, workListener); |
||||
} |
||||
|
||||
@Override |
||||
@SuppressWarnings("rawtypes") |
||||
public boolean waitForAll(Collection workItems, long timeout) throws InterruptedException { |
||||
return obtainWorkManager().waitForAll(workItems, timeout); |
||||
} |
||||
|
||||
@Override |
||||
@SuppressWarnings("rawtypes") |
||||
public Collection waitForAny(Collection workItems, long timeout) throws InterruptedException { |
||||
return obtainWorkManager().waitForAny(workItems, timeout); |
||||
} |
||||
|
||||
} |
||||
@ -1,10 +0,0 @@
@@ -1,10 +0,0 @@
|
||||
/** |
||||
* Convenience classes for scheduling based on the CommonJ WorkManager/TimerManager |
||||
* facility, as supported by IBM WebSphere 6.0+ and BEA WebLogic 9.0+. |
||||
*/ |
||||
@NonNullApi |
||||
@NonNullFields |
||||
package org.springframework.scheduling.commonj; |
||||
|
||||
import org.springframework.lang.NonNullApi; |
||||
import org.springframework.lang.NonNullFields; |
||||
@ -1,85 +0,0 @@
@@ -1,85 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2019 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 |
||||
* |
||||
* https://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 net.sf.ehcache.CacheManager; |
||||
import net.sf.ehcache.config.CacheConfiguration; |
||||
import net.sf.ehcache.config.Configuration; |
||||
import org.junit.jupiter.api.AfterEach; |
||||
import org.junit.jupiter.api.BeforeEach; |
||||
|
||||
import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManagerTests; |
||||
|
||||
/** |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
public class EhCacheCacheManagerTests extends AbstractTransactionSupportingCacheManagerTests<EhCacheCacheManager> { |
||||
|
||||
private CacheManager nativeCacheManager; |
||||
|
||||
private EhCacheCacheManager cacheManager; |
||||
|
||||
private EhCacheCacheManager transactionalCacheManager; |
||||
|
||||
|
||||
@BeforeEach |
||||
public void setup() { |
||||
nativeCacheManager = new CacheManager(new Configuration().name("EhCacheCacheManagerTests") |
||||
.defaultCache(new CacheConfiguration("default", 100))); |
||||
addNativeCache(CACHE_NAME); |
||||
|
||||
cacheManager = new EhCacheCacheManager(nativeCacheManager); |
||||
cacheManager.setTransactionAware(false); |
||||
cacheManager.afterPropertiesSet(); |
||||
|
||||
transactionalCacheManager = new EhCacheCacheManager(nativeCacheManager); |
||||
transactionalCacheManager.setTransactionAware(true); |
||||
transactionalCacheManager.afterPropertiesSet(); |
||||
} |
||||
|
||||
@AfterEach |
||||
public void shutdown() { |
||||
nativeCacheManager.shutdown(); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected EhCacheCacheManager getCacheManager(boolean transactionAware) { |
||||
if (transactionAware) { |
||||
return transactionalCacheManager; |
||||
} |
||||
else { |
||||
return cacheManager; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected Class<? extends org.springframework.cache.Cache> getCacheType() { |
||||
return EhCacheCache.class; |
||||
} |
||||
|
||||
@Override |
||||
protected void addNativeCache(String cacheName) { |
||||
nativeCacheManager.addCache(cacheName); |
||||
} |
||||
|
||||
@Override |
||||
protected void removeNativeCache(String cacheName) { |
||||
nativeCacheManager.removeCache(cacheName); |
||||
} |
||||
|
||||
} |
||||
@ -1,91 +0,0 @@
@@ -1,91 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2019 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 |
||||
* |
||||
* https://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 net.sf.ehcache.CacheManager; |
||||
import net.sf.ehcache.Ehcache; |
||||
import net.sf.ehcache.Element; |
||||
import net.sf.ehcache.config.CacheConfiguration; |
||||
import net.sf.ehcache.config.Configuration; |
||||
import org.junit.jupiter.api.AfterEach; |
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.context.testfixture.cache.AbstractCacheTests; |
||||
import org.springframework.core.testfixture.EnabledForTestGroups; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING; |
||||
|
||||
/** |
||||
* @author Costin Leau |
||||
* @author Stephane Nicoll |
||||
* @author Juergen Hoeller |
||||
*/ |
||||
public class EhCacheCacheTests extends AbstractCacheTests<EhCacheCache> { |
||||
|
||||
private CacheManager cacheManager; |
||||
|
||||
private Ehcache nativeCache; |
||||
|
||||
private EhCacheCache cache; |
||||
|
||||
|
||||
@BeforeEach |
||||
public void setup() { |
||||
cacheManager = new CacheManager(new Configuration().name("EhCacheCacheTests") |
||||
.defaultCache(new CacheConfiguration("default", 100))); |
||||
nativeCache = new net.sf.ehcache.Cache(new CacheConfiguration(CACHE_NAME, 100)); |
||||
cacheManager.addCache(nativeCache); |
||||
|
||||
cache = new EhCacheCache(nativeCache); |
||||
} |
||||
|
||||
@AfterEach |
||||
public void shutdown() { |
||||
cacheManager.shutdown(); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected EhCacheCache getCache() { |
||||
return cache; |
||||
} |
||||
|
||||
@Override |
||||
protected Ehcache getNativeCache() { |
||||
return nativeCache; |
||||
} |
||||
|
||||
|
||||
@Test |
||||
@EnabledForTestGroups(LONG_RUNNING) |
||||
public void testExpiredElements() throws Exception { |
||||
String key = "brancusi"; |
||||
String value = "constantin"; |
||||
Element brancusi = new Element(key, value); |
||||
// ttl = 10s
|
||||
brancusi.setTimeToLive(3); |
||||
nativeCache.put(brancusi); |
||||
|
||||
assertThat(cache.get(key).get()).isEqualTo(value); |
||||
// wait for the entry to expire
|
||||
Thread.sleep(5 * 1000); |
||||
assertThat(cache.get(key)).isNull(); |
||||
} |
||||
|
||||
} |
||||
@ -1,287 +0,0 @@
@@ -1,287 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2019 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 |
||||
* |
||||
* https://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 net.sf.ehcache.Cache; |
||||
import net.sf.ehcache.CacheException; |
||||
import net.sf.ehcache.CacheManager; |
||||
import net.sf.ehcache.Ehcache; |
||||
import net.sf.ehcache.config.CacheConfiguration; |
||||
import net.sf.ehcache.constructs.blocking.BlockingCache; |
||||
import net.sf.ehcache.constructs.blocking.SelfPopulatingCache; |
||||
import net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory; |
||||
import net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.core.io.ClassPathResource; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
|
||||
/** |
||||
* @author Juergen Hoeller |
||||
* @author Dmitriy Kopylenko |
||||
* @since 27.09.2004 |
||||
*/ |
||||
public class EhCacheSupportTests { |
||||
|
||||
@Test |
||||
public void testBlankCacheManager() { |
||||
EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean(); |
||||
cacheManagerFb.setCacheManagerName("myCacheManager"); |
||||
assertThat(cacheManagerFb.getObjectType()).isEqualTo(CacheManager.class); |
||||
assertThat(cacheManagerFb.isSingleton()).as("Singleton property").isTrue(); |
||||
cacheManagerFb.afterPropertiesSet(); |
||||
try { |
||||
CacheManager cm = cacheManagerFb.getObject(); |
||||
assertThat(cm.getCacheNames().length == 0).as("Loaded CacheManager with no caches").isTrue(); |
||||
Cache myCache1 = cm.getCache("myCache1"); |
||||
assertThat(myCache1 == null).as("No myCache1 defined").isTrue(); |
||||
} |
||||
finally { |
||||
cacheManagerFb.destroy(); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void testCacheManagerConflict() { |
||||
EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean(); |
||||
try { |
||||
cacheManagerFb.setCacheManagerName("myCacheManager"); |
||||
assertThat(cacheManagerFb.getObjectType()).isEqualTo(CacheManager.class); |
||||
assertThat(cacheManagerFb.isSingleton()).as("Singleton property").isTrue(); |
||||
cacheManagerFb.afterPropertiesSet(); |
||||
CacheManager cm = cacheManagerFb.getObject(); |
||||
assertThat(cm.getCacheNames().length == 0).as("Loaded CacheManager with no caches").isTrue(); |
||||
Cache myCache1 = cm.getCache("myCache1"); |
||||
assertThat(myCache1 == null).as("No myCache1 defined").isTrue(); |
||||
|
||||
EhCacheManagerFactoryBean cacheManagerFb2 = new EhCacheManagerFactoryBean(); |
||||
cacheManagerFb2.setCacheManagerName("myCacheManager"); |
||||
assertThatExceptionOfType(CacheException.class).as("because of naming conflict").isThrownBy( |
||||
cacheManagerFb2::afterPropertiesSet); |
||||
} |
||||
finally { |
||||
cacheManagerFb.destroy(); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void testAcceptExistingCacheManager() { |
||||
EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean(); |
||||
cacheManagerFb.setCacheManagerName("myCacheManager"); |
||||
assertThat(cacheManagerFb.getObjectType()).isEqualTo(CacheManager.class); |
||||
assertThat(cacheManagerFb.isSingleton()).as("Singleton property").isTrue(); |
||||
cacheManagerFb.afterPropertiesSet(); |
||||
try { |
||||
CacheManager cm = cacheManagerFb.getObject(); |
||||
assertThat(cm.getCacheNames().length == 0).as("Loaded CacheManager with no caches").isTrue(); |
||||
Cache myCache1 = cm.getCache("myCache1"); |
||||
assertThat(myCache1 == null).as("No myCache1 defined").isTrue(); |
||||
|
||||
EhCacheManagerFactoryBean cacheManagerFb2 = new EhCacheManagerFactoryBean(); |
||||
cacheManagerFb2.setCacheManagerName("myCacheManager"); |
||||
cacheManagerFb2.setAcceptExisting(true); |
||||
cacheManagerFb2.afterPropertiesSet(); |
||||
CacheManager cm2 = cacheManagerFb2.getObject(); |
||||
assertThat(cm2).isSameAs(cm); |
||||
cacheManagerFb2.destroy(); |
||||
} |
||||
finally { |
||||
cacheManagerFb.destroy(); |
||||
} |
||||
} |
||||
|
||||
public void testCacheManagerFromConfigFile() { |
||||
EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean(); |
||||
cacheManagerFb.setConfigLocation(new ClassPathResource("testEhcache.xml", getClass())); |
||||
cacheManagerFb.setCacheManagerName("myCacheManager"); |
||||
cacheManagerFb.afterPropertiesSet(); |
||||
try { |
||||
CacheManager cm = cacheManagerFb.getObject(); |
||||
assertThat(cm.getCacheNames().length == 1).as("Correct number of caches loaded").isTrue(); |
||||
Cache myCache1 = cm.getCache("myCache1"); |
||||
assertThat(myCache1.getCacheConfiguration().isEternal()).as("myCache1 is not eternal").isFalse(); |
||||
assertThat(myCache1.getCacheConfiguration().getMaxEntriesLocalHeap() == 300).as("myCache1.maxElements == 300").isTrue(); |
||||
} |
||||
finally { |
||||
cacheManagerFb.destroy(); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void testEhCacheFactoryBeanWithDefaultCacheManager() { |
||||
doTestEhCacheFactoryBean(false); |
||||
} |
||||
|
||||
@Test |
||||
public void testEhCacheFactoryBeanWithExplicitCacheManager() { |
||||
doTestEhCacheFactoryBean(true); |
||||
} |
||||
|
||||
private void doTestEhCacheFactoryBean(boolean useCacheManagerFb) { |
||||
Cache cache; |
||||
EhCacheManagerFactoryBean cacheManagerFb = null; |
||||
boolean cacheManagerFbInitialized = false; |
||||
try { |
||||
EhCacheFactoryBean cacheFb = new EhCacheFactoryBean(); |
||||
Class<? extends Ehcache> objectType = cacheFb.getObjectType(); |
||||
assertThat(Ehcache.class.isAssignableFrom(objectType)).isTrue(); |
||||
assertThat(cacheFb.isSingleton()).as("Singleton property").isTrue(); |
||||
if (useCacheManagerFb) { |
||||
cacheManagerFb = new EhCacheManagerFactoryBean(); |
||||
cacheManagerFb.setConfigLocation(new ClassPathResource("testEhcache.xml", getClass())); |
||||
cacheManagerFb.setCacheManagerName("cache"); |
||||
cacheManagerFb.afterPropertiesSet(); |
||||
cacheManagerFbInitialized = true; |
||||
cacheFb.setCacheManager(cacheManagerFb.getObject()); |
||||
} |
||||
|
||||
cacheFb.setCacheName("myCache1"); |
||||
cacheFb.afterPropertiesSet(); |
||||
cache = (Cache) cacheFb.getObject(); |
||||
Class<? extends Ehcache> objectType2 = cacheFb.getObjectType(); |
||||
assertThat(objectType2).isSameAs(objectType); |
||||
CacheConfiguration config = cache.getCacheConfiguration(); |
||||
assertThat(cache.getName()).isEqualTo("myCache1"); |
||||
if (useCacheManagerFb){ |
||||
assertThat(config.getMaxEntriesLocalHeap()).as("myCache1.maxElements").isEqualTo(300); |
||||
} |
||||
else { |
||||
assertThat(config.getMaxEntriesLocalHeap()).as("myCache1.maxElements").isEqualTo(10000); |
||||
} |
||||
|
||||
// Cache region is not defined. Should create one with default properties.
|
||||
cacheFb = new EhCacheFactoryBean(); |
||||
if (useCacheManagerFb) { |
||||
cacheFb.setCacheManager(cacheManagerFb.getObject()); |
||||
} |
||||
cacheFb.setCacheName("undefinedCache"); |
||||
cacheFb.afterPropertiesSet(); |
||||
cache = (Cache) cacheFb.getObject(); |
||||
config = cache.getCacheConfiguration(); |
||||
assertThat(cache.getName()).isEqualTo("undefinedCache"); |
||||
assertThat(config.getMaxEntriesLocalHeap() == 10000).as("default maxElements is correct").isTrue(); |
||||
assertThat(config.isEternal()).as("default eternal is correct").isFalse(); |
||||
assertThat(config.getTimeToLiveSeconds() == 120).as("default timeToLive is correct").isTrue(); |
||||
assertThat(config.getTimeToIdleSeconds() == 120).as("default timeToIdle is correct").isTrue(); |
||||
assertThat(config.getDiskExpiryThreadIntervalSeconds() == 120).as("default diskExpiryThreadIntervalSeconds is correct").isTrue(); |
||||
|
||||
// overriding the default properties
|
||||
cacheFb = new EhCacheFactoryBean(); |
||||
if (useCacheManagerFb) { |
||||
cacheFb.setCacheManager(cacheManagerFb.getObject()); |
||||
} |
||||
cacheFb.setBeanName("undefinedCache2"); |
||||
cacheFb.setMaxEntriesLocalHeap(5); |
||||
cacheFb.setTimeToLive(8); |
||||
cacheFb.setTimeToIdle(7); |
||||
cacheFb.setDiskExpiryThreadIntervalSeconds(10); |
||||
cacheFb.afterPropertiesSet(); |
||||
cache = (Cache) cacheFb.getObject(); |
||||
config = cache.getCacheConfiguration(); |
||||
|
||||
assertThat(cache.getName()).isEqualTo("undefinedCache2"); |
||||
assertThat(config.getMaxEntriesLocalHeap() == 5).as("overridden maxElements is correct").isTrue(); |
||||
assertThat(config.getTimeToLiveSeconds() == 8).as("default timeToLive is correct").isTrue(); |
||||
assertThat(config.getTimeToIdleSeconds() == 7).as("default timeToIdle is correct").isTrue(); |
||||
assertThat(config.getDiskExpiryThreadIntervalSeconds() == 10).as("overridden diskExpiryThreadIntervalSeconds is correct").isTrue(); |
||||
} |
||||
finally { |
||||
if (cacheManagerFbInitialized) { |
||||
cacheManagerFb.destroy(); |
||||
} |
||||
else { |
||||
CacheManager.getInstance().shutdown(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void testEhCacheFactoryBeanWithBlockingCache() { |
||||
EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean(); |
||||
cacheManagerFb.afterPropertiesSet(); |
||||
try { |
||||
CacheManager cm = cacheManagerFb.getObject(); |
||||
EhCacheFactoryBean cacheFb = new EhCacheFactoryBean(); |
||||
cacheFb.setCacheManager(cm); |
||||
cacheFb.setCacheName("myCache1"); |
||||
cacheFb.setBlocking(true); |
||||
assertThat(BlockingCache.class).isEqualTo(cacheFb.getObjectType()); |
||||
cacheFb.afterPropertiesSet(); |
||||
Ehcache myCache1 = cm.getEhcache("myCache1"); |
||||
boolean condition = myCache1 instanceof BlockingCache; |
||||
assertThat(condition).isTrue(); |
||||
} |
||||
finally { |
||||
cacheManagerFb.destroy(); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void testEhCacheFactoryBeanWithSelfPopulatingCache() { |
||||
EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean(); |
||||
cacheManagerFb.afterPropertiesSet(); |
||||
try { |
||||
CacheManager cm = cacheManagerFb.getObject(); |
||||
EhCacheFactoryBean cacheFb = new EhCacheFactoryBean(); |
||||
cacheFb.setCacheManager(cm); |
||||
cacheFb.setCacheName("myCache1"); |
||||
cacheFb.setCacheEntryFactory(key -> key); |
||||
assertThat(SelfPopulatingCache.class).isEqualTo(cacheFb.getObjectType()); |
||||
cacheFb.afterPropertiesSet(); |
||||
Ehcache myCache1 = cm.getEhcache("myCache1"); |
||||
boolean condition = myCache1 instanceof SelfPopulatingCache; |
||||
assertThat(condition).isTrue(); |
||||
assertThat(myCache1.get("myKey1").getObjectValue()).isEqualTo("myKey1"); |
||||
} |
||||
finally { |
||||
cacheManagerFb.destroy(); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void testEhCacheFactoryBeanWithUpdatingSelfPopulatingCache() { |
||||
EhCacheManagerFactoryBean cacheManagerFb = new EhCacheManagerFactoryBean(); |
||||
cacheManagerFb.afterPropertiesSet(); |
||||
try { |
||||
CacheManager cm = cacheManagerFb.getObject(); |
||||
EhCacheFactoryBean cacheFb = new EhCacheFactoryBean(); |
||||
cacheFb.setCacheManager(cm); |
||||
cacheFb.setCacheName("myCache1"); |
||||
cacheFb.setCacheEntryFactory(new UpdatingCacheEntryFactory() { |
||||
@Override |
||||
public Object createEntry(Object key) { |
||||
return key; |
||||
} |
||||
@Override |
||||
public void updateEntryValue(Object key, Object value) { |
||||
} |
||||
}); |
||||
assertThat(UpdatingSelfPopulatingCache.class).isEqualTo(cacheFb.getObjectType()); |
||||
cacheFb.afterPropertiesSet(); |
||||
Ehcache myCache1 = cm.getEhcache("myCache1"); |
||||
boolean condition = myCache1 instanceof UpdatingSelfPopulatingCache; |
||||
assertThat(condition).isTrue(); |
||||
assertThat(myCache1.get("myKey1").getObjectValue()).isEqualTo("myKey1"); |
||||
} |
||||
finally { |
||||
cacheManagerFb.destroy(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,35 +0,0 @@
@@ -1,35 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2017 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 |
||||
* |
||||
* https://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.jcache; |
||||
|
||||
import javax.cache.Caching; |
||||
import javax.cache.spi.CachingProvider; |
||||
|
||||
/** |
||||
* Just here to be run against EHCache 3, whereas the original JCacheEhCacheAnnotationTests |
||||
* runs against EhCache 2.x with the EhCache-JCache add-on. |
||||
* |
||||
* @author Juergen Hoeller |
||||
*/ |
||||
public class JCacheEhCache3AnnotationTests extends JCacheEhCacheAnnotationTests { |
||||
|
||||
@Override |
||||
protected CachingProvider getCachingProvider() { |
||||
return Caching.getCachingProvider("org.ehcache.jsr107.EhcacheCachingProvider"); |
||||
} |
||||
|
||||
} |
||||
@ -1,35 +0,0 @@
@@ -1,35 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2017 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 |
||||
* |
||||
* https://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.jcache; |
||||
|
||||
import javax.cache.Caching; |
||||
import javax.cache.spi.CachingProvider; |
||||
|
||||
/** |
||||
* Just here to be run against EHCache 3, whereas the original JCacheEhCacheAnnotationTests |
||||
* runs against EhCache 2.x with the EhCache-JCache add-on. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
public class JCacheEhCache3ApiTests extends JCacheEhCacheApiTests { |
||||
|
||||
@Override |
||||
protected CachingProvider getCachingProvider() { |
||||
return Caching.getCachingProvider("org.ehcache.jsr107.EhcacheCachingProvider"); |
||||
} |
||||
|
||||
} |
||||
@ -1,155 +0,0 @@
@@ -1,155 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2019 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 |
||||
* |
||||
* https://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.validation.beanvalidation2; |
||||
|
||||
import javax.annotation.PostConstruct; |
||||
import javax.validation.constraints.NotNull; |
||||
import javax.validation.constraints.Size; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.BeanCreationException; |
||||
import org.springframework.beans.factory.support.RootBeanDefinition; |
||||
import org.springframework.beans.testfixture.beans.TestBean; |
||||
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor; |
||||
import org.springframework.context.support.GenericApplicationContext; |
||||
import org.springframework.validation.beanvalidation.BeanValidationPostProcessor; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
|
||||
/** |
||||
* @author Juergen Hoeller |
||||
*/ |
||||
public class BeanValidationPostProcessorTests { |
||||
|
||||
@Test |
||||
public void testNotNullConstraint() { |
||||
GenericApplicationContext ac = new GenericApplicationContext(); |
||||
ac.registerBeanDefinition("bvpp", new RootBeanDefinition(BeanValidationPostProcessor.class)); |
||||
ac.registerBeanDefinition("capp", new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class)); |
||||
ac.registerBeanDefinition("bean", new RootBeanDefinition(NotNullConstrainedBean.class)); |
||||
assertThatExceptionOfType(BeanCreationException.class) |
||||
.isThrownBy(ac::refresh) |
||||
.havingRootCause() |
||||
.withMessageContainingAll("testBean", "invalid"); |
||||
ac.close(); |
||||
} |
||||
|
||||
@Test |
||||
public void testNotNullConstraintSatisfied() { |
||||
GenericApplicationContext ac = new GenericApplicationContext(); |
||||
ac.registerBeanDefinition("bvpp", new RootBeanDefinition(BeanValidationPostProcessor.class)); |
||||
ac.registerBeanDefinition("capp", new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class)); |
||||
RootBeanDefinition bd = new RootBeanDefinition(NotNullConstrainedBean.class); |
||||
bd.getPropertyValues().add("testBean", new TestBean()); |
||||
ac.registerBeanDefinition("bean", bd); |
||||
ac.refresh(); |
||||
ac.close(); |
||||
} |
||||
|
||||
@Test |
||||
public void testNotNullConstraintAfterInitialization() { |
||||
GenericApplicationContext ac = new GenericApplicationContext(); |
||||
RootBeanDefinition bvpp = new RootBeanDefinition(BeanValidationPostProcessor.class); |
||||
bvpp.getPropertyValues().add("afterInitialization", true); |
||||
ac.registerBeanDefinition("bvpp", bvpp); |
||||
ac.registerBeanDefinition("capp", new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class)); |
||||
ac.registerBeanDefinition("bean", new RootBeanDefinition(AfterInitConstraintBean.class)); |
||||
ac.refresh(); |
||||
ac.close(); |
||||
} |
||||
|
||||
@Test |
||||
public void testSizeConstraint() { |
||||
GenericApplicationContext ac = new GenericApplicationContext(); |
||||
ac.registerBeanDefinition("bvpp", new RootBeanDefinition(BeanValidationPostProcessor.class)); |
||||
RootBeanDefinition bd = new RootBeanDefinition(NotNullConstrainedBean.class); |
||||
bd.getPropertyValues().add("testBean", new TestBean()); |
||||
bd.getPropertyValues().add("stringValue", "s"); |
||||
ac.registerBeanDefinition("bean", bd); |
||||
assertThatExceptionOfType(BeanCreationException.class) |
||||
.isThrownBy(ac::refresh) |
||||
.havingRootCause() |
||||
.withMessageContainingAll("stringValue", "invalid"); |
||||
ac.close(); |
||||
} |
||||
|
||||
@Test |
||||
public void testSizeConstraintSatisfied() { |
||||
GenericApplicationContext ac = new GenericApplicationContext(); |
||||
ac.registerBeanDefinition("bvpp", new RootBeanDefinition(BeanValidationPostProcessor.class)); |
||||
RootBeanDefinition bd = new RootBeanDefinition(NotNullConstrainedBean.class); |
||||
bd.getPropertyValues().add("testBean", new TestBean()); |
||||
bd.getPropertyValues().add("stringValue", "ss"); |
||||
ac.registerBeanDefinition("bean", bd); |
||||
ac.refresh(); |
||||
ac.close(); |
||||
} |
||||
|
||||
|
||||
public static class NotNullConstrainedBean { |
||||
|
||||
@NotNull |
||||
private TestBean testBean; |
||||
|
||||
@Size(min = 2) |
||||
private String stringValue; |
||||
|
||||
public TestBean getTestBean() { |
||||
return testBean; |
||||
} |
||||
|
||||
public void setTestBean(TestBean testBean) { |
||||
this.testBean = testBean; |
||||
} |
||||
|
||||
public String getStringValue() { |
||||
return stringValue; |
||||
} |
||||
|
||||
public void setStringValue(String stringValue) { |
||||
this.stringValue = stringValue; |
||||
} |
||||
|
||||
@PostConstruct |
||||
public void init() { |
||||
assertThat(this.testBean).as("Shouldn't be here after constraint checking").isNotNull(); |
||||
} |
||||
} |
||||
|
||||
|
||||
public static class AfterInitConstraintBean { |
||||
|
||||
@NotNull |
||||
private TestBean testBean; |
||||
|
||||
public TestBean getTestBean() { |
||||
return testBean; |
||||
} |
||||
|
||||
public void setTestBean(TestBean testBean) { |
||||
this.testBean = testBean; |
||||
} |
||||
|
||||
@PostConstruct |
||||
public void init() { |
||||
this.testBean = new TestBean(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,218 +0,0 @@
@@ -1,218 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2019 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 |
||||
* |
||||
* https://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.validation.beanvalidation2; |
||||
|
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
|
||||
import javax.validation.ValidationException; |
||||
import javax.validation.Validator; |
||||
import javax.validation.constraints.Max; |
||||
import javax.validation.constraints.NotNull; |
||||
import javax.validation.groups.Default; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.aop.framework.ProxyFactory; |
||||
import org.springframework.beans.MutablePropertyValues; |
||||
import org.springframework.beans.factory.FactoryBean; |
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.Lazy; |
||||
import org.springframework.context.support.StaticApplicationContext; |
||||
import org.springframework.scheduling.annotation.Async; |
||||
import org.springframework.scheduling.annotation.AsyncAnnotationAdvisor; |
||||
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor; |
||||
import org.springframework.validation.annotation.Validated; |
||||
import org.springframework.validation.beanvalidation.CustomValidatorBean; |
||||
import org.springframework.validation.beanvalidation.MethodValidationInterceptor; |
||||
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
|
||||
/** |
||||
* @author Juergen Hoeller |
||||
*/ |
||||
public class MethodValidationTests { |
||||
|
||||
@Test |
||||
@SuppressWarnings("unchecked") |
||||
public void testMethodValidationInterceptor() { |
||||
MyValidBean bean = new MyValidBean(); |
||||
ProxyFactory proxyFactory = new ProxyFactory(bean); |
||||
proxyFactory.addAdvice(new MethodValidationInterceptor()); |
||||
proxyFactory.addAdvisor(new AsyncAnnotationAdvisor()); |
||||
doTestProxyValidation((MyValidInterface<String>) proxyFactory.getProxy()); |
||||
} |
||||
|
||||
@Test |
||||
@SuppressWarnings("unchecked") |
||||
public void testMethodValidationPostProcessor() { |
||||
StaticApplicationContext ac = new StaticApplicationContext(); |
||||
ac.registerSingleton("mvpp", MethodValidationPostProcessor.class); |
||||
MutablePropertyValues pvs = new MutablePropertyValues(); |
||||
pvs.add("beforeExistingAdvisors", false); |
||||
ac.registerSingleton("aapp", AsyncAnnotationBeanPostProcessor.class, pvs); |
||||
ac.registerSingleton("bean", MyValidBean.class); |
||||
ac.refresh(); |
||||
doTestProxyValidation(ac.getBean("bean", MyValidInterface.class)); |
||||
ac.close(); |
||||
} |
||||
|
||||
private void doTestProxyValidation(MyValidInterface<String> proxy) { |
||||
assertThat(proxy.myValidMethod("value", 5)).isNotNull(); |
||||
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> |
||||
proxy.myValidMethod("value", 15)); |
||||
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> |
||||
proxy.myValidMethod(null, 5)); |
||||
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> |
||||
proxy.myValidMethod("value", 0)); |
||||
proxy.myValidAsyncMethod("value", 5); |
||||
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> |
||||
proxy.myValidAsyncMethod("value", 15)); |
||||
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> |
||||
proxy.myValidAsyncMethod(null, 5)); |
||||
assertThat(proxy.myGenericMethod("myValue")).isEqualTo("myValue"); |
||||
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> |
||||
proxy.myGenericMethod(null)); |
||||
} |
||||
|
||||
@Test |
||||
public void testLazyValidatorForMethodValidation() { |
||||
@SuppressWarnings("resource") |
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( |
||||
LazyMethodValidationConfig.class, CustomValidatorBean.class, |
||||
MyValidBean.class, MyValidFactoryBean.class); |
||||
ctx.getBeansOfType(MyValidInterface.class).values().forEach(bean -> bean.myValidMethod("value", 5)); |
||||
} |
||||
|
||||
@Test |
||||
public void testLazyValidatorForMethodValidationWithProxyTargetClass() { |
||||
@SuppressWarnings("resource") |
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( |
||||
LazyMethodValidationConfigWithProxyTargetClass.class, CustomValidatorBean.class, |
||||
MyValidBean.class, MyValidFactoryBean.class); |
||||
ctx.getBeansOfType(MyValidInterface.class).values().forEach(bean -> bean.myValidMethod("value", 5)); |
||||
} |
||||
|
||||
|
||||
@MyStereotype |
||||
public static class MyValidBean implements MyValidInterface<String> { |
||||
|
||||
@Override |
||||
public Object myValidMethod(String arg1, int arg2) { |
||||
return (arg2 == 0 ? null : "value"); |
||||
} |
||||
|
||||
@Override |
||||
public void myValidAsyncMethod(String arg1, int arg2) { |
||||
} |
||||
|
||||
@Override |
||||
public String myGenericMethod(String value) { |
||||
return value; |
||||
} |
||||
} |
||||
|
||||
|
||||
@MyStereotype |
||||
public static class MyValidFactoryBean implements FactoryBean<String>, MyValidInterface<String> { |
||||
|
||||
@Override |
||||
public String getObject() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public Class<?> getObjectType() { |
||||
return String.class; |
||||
} |
||||
|
||||
@Override |
||||
public Object myValidMethod(String arg1, int arg2) { |
||||
return (arg2 == 0 ? null : "value"); |
||||
} |
||||
|
||||
@Override |
||||
public void myValidAsyncMethod(String arg1, int arg2) { |
||||
} |
||||
|
||||
@Override |
||||
public String myGenericMethod(String value) { |
||||
return value; |
||||
} |
||||
} |
||||
|
||||
|
||||
public interface MyValidInterface<T> { |
||||
|
||||
@NotNull Object myValidMethod(@NotNull(groups = MyGroup.class) String arg1, @Max(10) int arg2); |
||||
|
||||
@MyValid |
||||
@Async void myValidAsyncMethod(@NotNull(groups = OtherGroup.class) String arg1, @Max(10) int arg2); |
||||
|
||||
T myGenericMethod(@NotNull T value); |
||||
} |
||||
|
||||
|
||||
public interface MyGroup { |
||||
} |
||||
|
||||
|
||||
public interface OtherGroup { |
||||
} |
||||
|
||||
|
||||
@Validated({MyGroup.class, Default.class}) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
public @interface MyStereotype { |
||||
} |
||||
|
||||
|
||||
@Validated({OtherGroup.class, Default.class}) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
public @interface MyValid { |
||||
} |
||||
|
||||
|
||||
@Configuration |
||||
public static class LazyMethodValidationConfig { |
||||
|
||||
@Bean |
||||
public static MethodValidationPostProcessor methodValidationPostProcessor(@Lazy Validator validator) { |
||||
MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor(); |
||||
postProcessor.setValidator(validator); |
||||
return postProcessor; |
||||
} |
||||
} |
||||
|
||||
|
||||
@Configuration |
||||
public static class LazyMethodValidationConfigWithProxyTargetClass { |
||||
|
||||
@Bean |
||||
public static MethodValidationPostProcessor methodValidationPostProcessor(@Lazy Validator validator) { |
||||
MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor(); |
||||
postProcessor.setValidator(validator); |
||||
postProcessor.setProxyTargetClass(true); |
||||
return postProcessor; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,563 +0,0 @@
@@ -1,563 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.validation.beanvalidation2; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Inherited; |
||||
import java.lang.annotation.Repeatable; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
import java.lang.reflect.Field; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.HashMap; |
||||
import java.util.LinkedHashSet; |
||||
import java.util.List; |
||||
import java.util.Locale; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
|
||||
import javax.validation.Constraint; |
||||
import javax.validation.ConstraintValidator; |
||||
import javax.validation.ConstraintValidatorContext; |
||||
import javax.validation.ConstraintViolation; |
||||
import javax.validation.Payload; |
||||
import javax.validation.Valid; |
||||
import javax.validation.Validation; |
||||
import javax.validation.Validator; |
||||
import javax.validation.constraints.NotNull; |
||||
import javax.validation.constraints.Pattern; |
||||
import javax.validation.constraints.Size; |
||||
|
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.BeanWrapper; |
||||
import org.springframework.beans.BeanWrapperImpl; |
||||
import org.springframework.context.support.StaticMessageSource; |
||||
import org.springframework.core.testfixture.io.SerializationTestUtils; |
||||
import org.springframework.util.ObjectUtils; |
||||
import org.springframework.validation.BeanPropertyBindingResult; |
||||
import org.springframework.validation.FieldError; |
||||
import org.springframework.validation.beanvalidation.SpringValidatorAdapter; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* @author Kazuki Shimizu |
||||
* @author Juergen Hoeller |
||||
*/ |
||||
public class SpringValidatorAdapterTests { |
||||
|
||||
private final Validator nativeValidator = Validation.buildDefaultValidatorFactory().getValidator(); |
||||
|
||||
private final SpringValidatorAdapter validatorAdapter = new SpringValidatorAdapter(nativeValidator); |
||||
|
||||
private final StaticMessageSource messageSource = new StaticMessageSource(); |
||||
|
||||
|
||||
@BeforeEach |
||||
public void setupSpringValidatorAdapter() { |
||||
messageSource.addMessage("Size", Locale.ENGLISH, "Size of {0} must be between {2} and {1}"); |
||||
messageSource.addMessage("Same", Locale.ENGLISH, "{2} must be same value as {1}"); |
||||
messageSource.addMessage("password", Locale.ENGLISH, "Password"); |
||||
messageSource.addMessage("confirmPassword", Locale.ENGLISH, "Password(Confirm)"); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
public void testUnwrap() { |
||||
Validator nativeValidator = validatorAdapter.unwrap(Validator.class); |
||||
assertThat(nativeValidator).isSameAs(this.nativeValidator); |
||||
} |
||||
|
||||
@Test // SPR-13406
|
||||
public void testNoStringArgumentValue() throws Exception { |
||||
TestBean testBean = new TestBean(); |
||||
testBean.setPassword("pass"); |
||||
testBean.setConfirmPassword("pass"); |
||||
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(testBean, "testBean"); |
||||
validatorAdapter.validate(testBean, errors); |
||||
|
||||
assertThat(errors.getFieldErrorCount("password")).isEqualTo(1); |
||||
assertThat(errors.getFieldValue("password")).isEqualTo("pass"); |
||||
FieldError error = errors.getFieldError("password"); |
||||
assertThat(error).isNotNull(); |
||||
assertThat(messageSource.getMessage(error, Locale.ENGLISH)).isEqualTo("Size of Password must be between 8 and 128"); |
||||
assertThat(error.contains(ConstraintViolation.class)).isTrue(); |
||||
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("password"); |
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(error.toString())).isEqualTo(error.toString()); |
||||
} |
||||
|
||||
@Test // SPR-13406
|
||||
public void testApplyMessageSourceResolvableToStringArgumentValueWithResolvedLogicalFieldName() throws Exception { |
||||
TestBean testBean = new TestBean(); |
||||
testBean.setPassword("password"); |
||||
testBean.setConfirmPassword("PASSWORD"); |
||||
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(testBean, "testBean"); |
||||
validatorAdapter.validate(testBean, errors); |
||||
|
||||
assertThat(errors.getFieldErrorCount("password")).isEqualTo(1); |
||||
assertThat(errors.getFieldValue("password")).isEqualTo("password"); |
||||
FieldError error = errors.getFieldError("password"); |
||||
assertThat(error).isNotNull(); |
||||
assertThat(messageSource.getMessage(error, Locale.ENGLISH)).isEqualTo("Password must be same value as Password(Confirm)"); |
||||
assertThat(error.contains(ConstraintViolation.class)).isTrue(); |
||||
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("password"); |
||||
assertThat(SerializationTestUtils.serializeAndDeserialize(error.toString())).isEqualTo(error.toString()); |
||||
} |
||||
|
||||
@Test // SPR-13406
|
||||
public void testApplyMessageSourceResolvableToStringArgumentValueWithUnresolvedLogicalFieldName() { |
||||
TestBean testBean = new TestBean(); |
||||
testBean.setEmail("test@example.com"); |
||||
testBean.setConfirmEmail("TEST@EXAMPLE.IO"); |
||||
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(testBean, "testBean"); |
||||
validatorAdapter.validate(testBean, errors); |
||||
|
||||
assertThat(errors.getFieldErrorCount("email")).isEqualTo(1); |
||||
assertThat(errors.getFieldValue("email")).isEqualTo("test@example.com"); |
||||
assertThat(errors.getFieldErrorCount("confirmEmail")).isEqualTo(1); |
||||
FieldError error1 = errors.getFieldError("email"); |
||||
FieldError error2 = errors.getFieldError("confirmEmail"); |
||||
assertThat(error1).isNotNull(); |
||||
assertThat(error2).isNotNull(); |
||||
assertThat(messageSource.getMessage(error1, Locale.ENGLISH)).isEqualTo("email must be same value as confirmEmail"); |
||||
assertThat(messageSource.getMessage(error2, Locale.ENGLISH)).isEqualTo("Email required"); |
||||
assertThat(error1.contains(ConstraintViolation.class)).isTrue(); |
||||
assertThat(error1.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("email"); |
||||
assertThat(error2.contains(ConstraintViolation.class)).isTrue(); |
||||
assertThat(error2.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("confirmEmail"); |
||||
} |
||||
|
||||
@Test // SPR-15123
|
||||
public void testApplyMessageSourceResolvableToStringArgumentValueWithAlwaysUseMessageFormat() { |
||||
messageSource.setAlwaysUseMessageFormat(true); |
||||
|
||||
TestBean testBean = new TestBean(); |
||||
testBean.setEmail("test@example.com"); |
||||
testBean.setConfirmEmail("TEST@EXAMPLE.IO"); |
||||
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(testBean, "testBean"); |
||||
validatorAdapter.validate(testBean, errors); |
||||
|
||||
assertThat(errors.getFieldErrorCount("email")).isEqualTo(1); |
||||
assertThat(errors.getFieldValue("email")).isEqualTo("test@example.com"); |
||||
assertThat(errors.getFieldErrorCount("confirmEmail")).isEqualTo(1); |
||||
FieldError error1 = errors.getFieldError("email"); |
||||
FieldError error2 = errors.getFieldError("confirmEmail"); |
||||
assertThat(error1).isNotNull(); |
||||
assertThat(error2).isNotNull(); |
||||
assertThat(messageSource.getMessage(error1, Locale.ENGLISH)).isEqualTo("email must be same value as confirmEmail"); |
||||
assertThat(messageSource.getMessage(error2, Locale.ENGLISH)).isEqualTo("Email required"); |
||||
assertThat(error1.contains(ConstraintViolation.class)).isTrue(); |
||||
assertThat(error1.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("email"); |
||||
assertThat(error2.contains(ConstraintViolation.class)).isTrue(); |
||||
assertThat(error2.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("confirmEmail"); |
||||
} |
||||
|
||||
@Test |
||||
public void testPatternMessage() { |
||||
TestBean testBean = new TestBean(); |
||||
testBean.setEmail("X"); |
||||
testBean.setConfirmEmail("X"); |
||||
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(testBean, "testBean"); |
||||
validatorAdapter.validate(testBean, errors); |
||||
|
||||
assertThat(errors.getFieldErrorCount("email")).isEqualTo(1); |
||||
assertThat(errors.getFieldValue("email")).isEqualTo("X"); |
||||
FieldError error = errors.getFieldError("email"); |
||||
assertThat(error).isNotNull(); |
||||
assertThat(messageSource.getMessage(error, Locale.ENGLISH)).contains("[\\w.'-]{1,}@[\\w.'-]{1,}"); |
||||
assertThat(error.contains(ConstraintViolation.class)).isTrue(); |
||||
assertThat(error.unwrap(ConstraintViolation.class).getPropertyPath().toString()).isEqualTo("email"); |
||||
} |
||||
|
||||
@Test // SPR-16177
|
||||
public void testWithList() { |
||||
Parent parent = new Parent(); |
||||
parent.setName("Parent whit list"); |
||||
parent.getChildList().addAll(createChildren(parent)); |
||||
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(parent, "parent"); |
||||
validatorAdapter.validate(parent, errors); |
||||
|
||||
assertThat(errors.getErrorCount() > 0).isTrue(); |
||||
} |
||||
|
||||
@Test // SPR-16177
|
||||
public void testWithSet() { |
||||
Parent parent = new Parent(); |
||||
parent.setName("Parent with set"); |
||||
parent.getChildSet().addAll(createChildren(parent)); |
||||
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(parent, "parent"); |
||||
validatorAdapter.validate(parent, errors); |
||||
|
||||
assertThat(errors.getErrorCount() > 0).isTrue(); |
||||
} |
||||
|
||||
private List<Child> createChildren(Parent parent) { |
||||
Child child1 = new Child(); |
||||
child1.setName("Child1"); |
||||
child1.setAge(null); |
||||
child1.setParent(parent); |
||||
|
||||
Child child2 = new Child(); |
||||
child2.setName(null); |
||||
child2.setAge(17); |
||||
child2.setParent(parent); |
||||
|
||||
return Arrays.asList(child1, child2); |
||||
} |
||||
|
||||
@Test // SPR-15839
|
||||
public void testListElementConstraint() { |
||||
BeanWithListElementConstraint bean = new BeanWithListElementConstraint(); |
||||
bean.setProperty(Arrays.asList("no", "element", "can", "be", null)); |
||||
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(bean, "bean"); |
||||
validatorAdapter.validate(bean, errors); |
||||
|
||||
assertThat(errors.getFieldErrorCount("property[4]")).isEqualTo(1); |
||||
assertThat(errors.getFieldValue("property[4]")).isNull(); |
||||
} |
||||
|
||||
@Test // SPR-15839
|
||||
public void testMapValueConstraint() { |
||||
Map<String, String> property = new HashMap<>(); |
||||
property.put("no value can be", null); |
||||
|
||||
BeanWithMapEntryConstraint bean = new BeanWithMapEntryConstraint(); |
||||
bean.setProperty(property); |
||||
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(bean, "bean"); |
||||
validatorAdapter.validate(bean, errors); |
||||
|
||||
assertThat(errors.getFieldErrorCount("property[no value can be]")).isEqualTo(1); |
||||
assertThat(errors.getFieldValue("property[no value can be]")).isNull(); |
||||
} |
||||
|
||||
@Test // SPR-15839
|
||||
public void testMapEntryConstraint() { |
||||
Map<String, String> property = new HashMap<>(); |
||||
property.put(null, null); |
||||
|
||||
BeanWithMapEntryConstraint bean = new BeanWithMapEntryConstraint(); |
||||
bean.setProperty(property); |
||||
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(bean, "bean"); |
||||
validatorAdapter.validate(bean, errors); |
||||
|
||||
assertThat(errors.hasFieldErrors("property[]")).isTrue(); |
||||
assertThat(errors.getFieldValue("property[]")).isNull(); |
||||
} |
||||
|
||||
|
||||
@Same(field = "password", comparingField = "confirmPassword") |
||||
@Same(field = "email", comparingField = "confirmEmail") |
||||
static class TestBean { |
||||
|
||||
@Size(min = 8, max = 128) |
||||
private String password; |
||||
|
||||
private String confirmPassword; |
||||
|
||||
@Pattern(regexp = "[\\w.'-]{1,}@[\\w.'-]{1,}") |
||||
private String email; |
||||
|
||||
@Pattern(regexp = "[\\p{L} -]*", message = "Email required") |
||||
private String confirmEmail; |
||||
|
||||
public String getPassword() { |
||||
return password; |
||||
} |
||||
|
||||
public void setPassword(String password) { |
||||
this.password = password; |
||||
} |
||||
|
||||
public String getConfirmPassword() { |
||||
return confirmPassword; |
||||
} |
||||
|
||||
public void setConfirmPassword(String confirmPassword) { |
||||
this.confirmPassword = confirmPassword; |
||||
} |
||||
|
||||
public String getEmail() { |
||||
return email; |
||||
} |
||||
|
||||
public void setEmail(String email) { |
||||
this.email = email; |
||||
} |
||||
|
||||
public String getConfirmEmail() { |
||||
return confirmEmail; |
||||
} |
||||
|
||||
public void setConfirmEmail(String confirmEmail) { |
||||
this.confirmEmail = confirmEmail; |
||||
} |
||||
} |
||||
|
||||
|
||||
@Documented |
||||
@Constraint(validatedBy = {SameValidator.class}) |
||||
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Repeatable(SameGroup.class) |
||||
@interface Same { |
||||
|
||||
String message() default "{org.springframework.validation.beanvalidation.Same.message}"; |
||||
|
||||
Class<?>[] groups() default {}; |
||||
|
||||
Class<? extends Payload>[] payload() default {}; |
||||
|
||||
String field(); |
||||
|
||||
String comparingField(); |
||||
|
||||
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
@interface List { |
||||
Same[] value(); |
||||
} |
||||
} |
||||
|
||||
|
||||
@Documented |
||||
@Inherited |
||||
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@interface SameGroup { |
||||
|
||||
Same[] value(); |
||||
} |
||||
|
||||
|
||||
public static class SameValidator implements ConstraintValidator<Same, Object> { |
||||
|
||||
private String field; |
||||
|
||||
private String comparingField; |
||||
|
||||
private String message; |
||||
|
||||
@Override |
||||
public void initialize(Same constraintAnnotation) { |
||||
field = constraintAnnotation.field(); |
||||
comparingField = constraintAnnotation.comparingField(); |
||||
message = constraintAnnotation.message(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isValid(Object value, ConstraintValidatorContext context) { |
||||
BeanWrapper beanWrapper = new BeanWrapperImpl(value); |
||||
Object fieldValue = beanWrapper.getPropertyValue(field); |
||||
Object comparingFieldValue = beanWrapper.getPropertyValue(comparingField); |
||||
boolean matched = ObjectUtils.nullSafeEquals(fieldValue, comparingFieldValue); |
||||
if (matched) { |
||||
return true; |
||||
} |
||||
else { |
||||
context.disableDefaultConstraintViolation(); |
||||
context.buildConstraintViolationWithTemplate(message) |
||||
.addPropertyNode(field) |
||||
.addConstraintViolation(); |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
public static class Parent { |
||||
|
||||
private Integer id; |
||||
|
||||
@NotNull |
||||
private String name; |
||||
|
||||
@Valid |
||||
private Set<Child> childSet = new LinkedHashSet<>(); |
||||
|
||||
@Valid |
||||
private List<Child> childList = new ArrayList<>(); |
||||
|
||||
public Integer getId() { |
||||
return id; |
||||
} |
||||
|
||||
public void setId(Integer id) { |
||||
this.id = id; |
||||
} |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public Set<Child> getChildSet() { |
||||
return childSet; |
||||
} |
||||
|
||||
public void setChildSet(Set<Child> childSet) { |
||||
this.childSet = childSet; |
||||
} |
||||
|
||||
public List<Child> getChildList() { |
||||
return childList; |
||||
} |
||||
|
||||
public void setChildList(List<Child> childList) { |
||||
this.childList = childList; |
||||
} |
||||
} |
||||
|
||||
|
||||
@AnythingValid |
||||
public static class Child { |
||||
|
||||
private Integer id; |
||||
|
||||
@NotNull |
||||
private String name; |
||||
|
||||
@NotNull |
||||
private Integer age; |
||||
|
||||
@NotNull |
||||
private Parent parent; |
||||
|
||||
public Integer getId() { |
||||
return id; |
||||
} |
||||
|
||||
public void setId(Integer id) { |
||||
this.id = id; |
||||
} |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public Integer getAge() { |
||||
return age; |
||||
} |
||||
|
||||
public void setAge(Integer age) { |
||||
this.age = age; |
||||
} |
||||
|
||||
public Parent getParent() { |
||||
return parent; |
||||
} |
||||
|
||||
public void setParent(Parent parent) { |
||||
this.parent = parent; |
||||
} |
||||
} |
||||
|
||||
|
||||
@Constraint(validatedBy = AnythingValidator.class) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
public @interface AnythingValid { |
||||
|
||||
String message() default "{AnythingValid.message}"; |
||||
|
||||
Class<?>[] groups() default {}; |
||||
|
||||
Class<? extends Payload>[] payload() default {}; |
||||
} |
||||
|
||||
|
||||
public static class AnythingValidator implements ConstraintValidator<AnythingValid, Object> { |
||||
|
||||
private static final String ID = "id"; |
||||
|
||||
@Override |
||||
public void initialize(AnythingValid constraintAnnotation) { |
||||
} |
||||
|
||||
@Override |
||||
public boolean isValid(Object value, ConstraintValidatorContext context) { |
||||
List<Field> fieldsErrors = new ArrayList<>(); |
||||
Arrays.asList(value.getClass().getDeclaredFields()).forEach(field -> { |
||||
field.setAccessible(true); |
||||
try { |
||||
if (!field.getName().equals(ID) && field.get(value) == null) { |
||||
fieldsErrors.add(field); |
||||
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()) |
||||
.addPropertyNode(field.getName()) |
||||
.addConstraintViolation(); |
||||
} |
||||
} |
||||
catch (IllegalAccessException ex) { |
||||
throw new IllegalStateException(ex); |
||||
} |
||||
}); |
||||
return fieldsErrors.isEmpty(); |
||||
} |
||||
} |
||||
|
||||
|
||||
public class BeanWithListElementConstraint { |
||||
|
||||
@Valid |
||||
private List<@NotNull String> property; |
||||
|
||||
public List<String> getProperty() { |
||||
return property; |
||||
} |
||||
|
||||
public void setProperty(List<String> property) { |
||||
this.property = property; |
||||
} |
||||
} |
||||
|
||||
|
||||
public class BeanWithMapEntryConstraint { |
||||
|
||||
@Valid |
||||
private Map<@NotNull String, @NotNull String> property; |
||||
|
||||
public Map<String, String> getProperty() { |
||||
return property; |
||||
} |
||||
|
||||
public void setProperty(Map<String, String> property) { |
||||
this.property = property; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,505 +0,0 @@
@@ -1,505 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.validation.beanvalidation2; |
||||
|
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.Iterator; |
||||
import java.util.LinkedHashSet; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
import java.util.Set; |
||||
|
||||
import javax.validation.Constraint; |
||||
import javax.validation.ConstraintValidator; |
||||
import javax.validation.ConstraintValidatorContext; |
||||
import javax.validation.ConstraintViolation; |
||||
import javax.validation.Payload; |
||||
import javax.validation.Valid; |
||||
import javax.validation.Validator; |
||||
import javax.validation.ValidatorFactory; |
||||
import javax.validation.constraints.NotNull; |
||||
|
||||
import org.hibernate.validator.HibernateValidator; |
||||
import org.hibernate.validator.HibernateValidatorFactory; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||
import org.springframework.core.convert.support.DefaultConversionService; |
||||
import org.springframework.core.env.Environment; |
||||
import org.springframework.validation.BeanPropertyBindingResult; |
||||
import org.springframework.validation.Errors; |
||||
import org.springframework.validation.FieldError; |
||||
import org.springframework.validation.ObjectError; |
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* @author Juergen Hoeller |
||||
*/ |
||||
@SuppressWarnings("resource") |
||||
public class ValidatorFactoryTests { |
||||
|
||||
@Test |
||||
@SuppressWarnings("cast") |
||||
public void testSimpleValidation() { |
||||
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); |
||||
validator.afterPropertiesSet(); |
||||
|
||||
ValidPerson person = new ValidPerson(); |
||||
Set<ConstraintViolation<ValidPerson>> result = validator.validate(person); |
||||
assertThat(result.size()).isEqualTo(2); |
||||
for (ConstraintViolation<ValidPerson> cv : result) { |
||||
String path = cv.getPropertyPath().toString(); |
||||
assertThat(path).matches(actual -> "name".equals(actual) || "address.street".equals(actual)); |
||||
assertThat(cv.getConstraintDescriptor().getAnnotation()).isInstanceOf(NotNull.class); |
||||
} |
||||
|
||||
Validator nativeValidator = validator.unwrap(Validator.class); |
||||
assertThat(nativeValidator.getClass().getName().startsWith("org.hibernate")).isTrue(); |
||||
assertThat(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory).isTrue(); |
||||
assertThat(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory).isTrue(); |
||||
|
||||
validator.destroy(); |
||||
} |
||||
|
||||
@Test |
||||
@SuppressWarnings("cast") |
||||
public void testSimpleValidationWithCustomProvider() { |
||||
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); |
||||
validator.setProviderClass(HibernateValidator.class); |
||||
validator.afterPropertiesSet(); |
||||
|
||||
ValidPerson person = new ValidPerson(); |
||||
Set<ConstraintViolation<ValidPerson>> result = validator.validate(person); |
||||
assertThat(result.size()).isEqualTo(2); |
||||
for (ConstraintViolation<ValidPerson> cv : result) { |
||||
String path = cv.getPropertyPath().toString(); |
||||
assertThat(path).matches(actual -> "name".equals(actual) || "address.street".equals(actual)); |
||||
assertThat(cv.getConstraintDescriptor().getAnnotation()).isInstanceOf(NotNull.class); |
||||
} |
||||
|
||||
Validator nativeValidator = validator.unwrap(Validator.class); |
||||
assertThat(nativeValidator.getClass().getName().startsWith("org.hibernate")).isTrue(); |
||||
assertThat(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory).isTrue(); |
||||
assertThat(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory).isTrue(); |
||||
|
||||
validator.destroy(); |
||||
} |
||||
|
||||
@Test |
||||
public void testSimpleValidationWithClassLevel() { |
||||
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); |
||||
validator.afterPropertiesSet(); |
||||
|
||||
ValidPerson person = new ValidPerson(); |
||||
person.setName("Juergen"); |
||||
person.getAddress().setStreet("Juergen's Street"); |
||||
Set<ConstraintViolation<ValidPerson>> result = validator.validate(person); |
||||
assertThat(result.size()).isEqualTo(1); |
||||
Iterator<ConstraintViolation<ValidPerson>> iterator = result.iterator(); |
||||
ConstraintViolation<?> cv = iterator.next(); |
||||
assertThat(cv.getPropertyPath().toString()).isEqualTo(""); |
||||
assertThat(cv.getConstraintDescriptor().getAnnotation() instanceof NameAddressValid).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpringValidationFieldType() { |
||||
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); |
||||
validator.afterPropertiesSet(); |
||||
|
||||
ValidPerson person = new ValidPerson(); |
||||
person.setName("Phil"); |
||||
person.getAddress().setStreet("Phil's Street"); |
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(person, "person"); |
||||
validator.validate(person, errors); |
||||
assertThat(errors.getErrorCount()).isEqualTo(1); |
||||
assertThat(errors.getFieldError("address").getRejectedValue()).isInstanceOf(ValidAddress.class); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpringValidation() { |
||||
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); |
||||
validator.afterPropertiesSet(); |
||||
|
||||
ValidPerson person = new ValidPerson(); |
||||
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person"); |
||||
validator.validate(person, result); |
||||
assertThat(result.getErrorCount()).isEqualTo(2); |
||||
FieldError fieldError = result.getFieldError("name"); |
||||
assertThat(fieldError.getField()).isEqualTo("name"); |
||||
List<String> errorCodes = Arrays.asList(fieldError.getCodes()); |
||||
assertThat(errorCodes.size()).isEqualTo(4); |
||||
assertThat(errorCodes.contains("NotNull.person.name")).isTrue(); |
||||
assertThat(errorCodes.contains("NotNull.name")).isTrue(); |
||||
assertThat(errorCodes.contains("NotNull.java.lang.String")).isTrue(); |
||||
assertThat(errorCodes.contains("NotNull")).isTrue(); |
||||
fieldError = result.getFieldError("address.street"); |
||||
assertThat(fieldError.getField()).isEqualTo("address.street"); |
||||
errorCodes = Arrays.asList(fieldError.getCodes()); |
||||
assertThat(errorCodes.size()).isEqualTo(5); |
||||
assertThat(errorCodes.contains("NotNull.person.address.street")).isTrue(); |
||||
assertThat(errorCodes.contains("NotNull.address.street")).isTrue(); |
||||
assertThat(errorCodes.contains("NotNull.street")).isTrue(); |
||||
assertThat(errorCodes.contains("NotNull.java.lang.String")).isTrue(); |
||||
assertThat(errorCodes.contains("NotNull")).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpringValidationWithClassLevel() { |
||||
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); |
||||
validator.afterPropertiesSet(); |
||||
|
||||
ValidPerson person = new ValidPerson(); |
||||
person.setName("Juergen"); |
||||
person.getAddress().setStreet("Juergen's Street"); |
||||
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person"); |
||||
validator.validate(person, result); |
||||
assertThat(result.getErrorCount()).isEqualTo(1); |
||||
ObjectError globalError = result.getGlobalError(); |
||||
List<String> errorCodes = Arrays.asList(globalError.getCodes()); |
||||
assertThat(errorCodes.size()).isEqualTo(2); |
||||
assertThat(errorCodes.contains("NameAddressValid.person")).isTrue(); |
||||
assertThat(errorCodes.contains("NameAddressValid")).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpringValidationWithAutowiredValidator() { |
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext( |
||||
LocalValidatorFactoryBean.class); |
||||
LocalValidatorFactoryBean validator = ctx.getBean(LocalValidatorFactoryBean.class); |
||||
|
||||
ValidPerson person = new ValidPerson(); |
||||
person.expectsAutowiredValidator = true; |
||||
person.setName("Juergen"); |
||||
person.getAddress().setStreet("Juergen's Street"); |
||||
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person"); |
||||
validator.validate(person, result); |
||||
assertThat(result.getErrorCount()).isEqualTo(1); |
||||
ObjectError globalError = result.getGlobalError(); |
||||
List<String> errorCodes = Arrays.asList(globalError.getCodes()); |
||||
assertThat(errorCodes.size()).isEqualTo(2); |
||||
assertThat(errorCodes.contains("NameAddressValid.person")).isTrue(); |
||||
assertThat(errorCodes.contains("NameAddressValid")).isTrue(); |
||||
ctx.close(); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpringValidationWithErrorInListElement() { |
||||
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); |
||||
validator.afterPropertiesSet(); |
||||
|
||||
ValidPerson person = new ValidPerson(); |
||||
person.getAddressList().add(new ValidAddress()); |
||||
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person"); |
||||
validator.validate(person, result); |
||||
assertThat(result.getErrorCount()).isEqualTo(3); |
||||
FieldError fieldError = result.getFieldError("name"); |
||||
assertThat(fieldError.getField()).isEqualTo("name"); |
||||
fieldError = result.getFieldError("address.street"); |
||||
assertThat(fieldError.getField()).isEqualTo("address.street"); |
||||
fieldError = result.getFieldError("addressList[0].street"); |
||||
assertThat(fieldError.getField()).isEqualTo("addressList[0].street"); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpringValidationWithErrorInSetElement() { |
||||
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); |
||||
validator.afterPropertiesSet(); |
||||
|
||||
ValidPerson person = new ValidPerson(); |
||||
person.getAddressSet().add(new ValidAddress()); |
||||
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person"); |
||||
validator.validate(person, result); |
||||
assertThat(result.getErrorCount()).isEqualTo(3); |
||||
FieldError fieldError = result.getFieldError("name"); |
||||
assertThat(fieldError.getField()).isEqualTo("name"); |
||||
fieldError = result.getFieldError("address.street"); |
||||
assertThat(fieldError.getField()).isEqualTo("address.street"); |
||||
fieldError = result.getFieldError("addressSet[].street"); |
||||
assertThat(fieldError.getField()).isEqualTo("addressSet[].street"); |
||||
} |
||||
|
||||
@Test |
||||
public void testInnerBeanValidation() { |
||||
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); |
||||
validator.afterPropertiesSet(); |
||||
|
||||
MainBean mainBean = new MainBean(); |
||||
Errors errors = new BeanPropertyBindingResult(mainBean, "mainBean"); |
||||
validator.validate(mainBean, errors); |
||||
Object rejected = errors.getFieldValue("inner.value"); |
||||
assertThat(rejected).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void testValidationWithOptionalField() { |
||||
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); |
||||
validator.afterPropertiesSet(); |
||||
|
||||
MainBeanWithOptional mainBean = new MainBeanWithOptional(); |
||||
Errors errors = new BeanPropertyBindingResult(mainBean, "mainBean"); |
||||
validator.validate(mainBean, errors); |
||||
Object rejected = errors.getFieldValue("inner.value"); |
||||
assertThat(rejected).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
public void testListValidation() { |
||||
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); |
||||
validator.afterPropertiesSet(); |
||||
|
||||
ListContainer listContainer = new ListContainer(); |
||||
listContainer.addString("A"); |
||||
listContainer.addString("X"); |
||||
|
||||
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(listContainer, "listContainer"); |
||||
errors.initConversion(new DefaultConversionService()); |
||||
validator.validate(listContainer, errors); |
||||
|
||||
FieldError fieldError = errors.getFieldError("list[1]"); |
||||
assertThat(fieldError).isNotNull(); |
||||
assertThat(fieldError.getRejectedValue()).isEqualTo("X"); |
||||
assertThat(errors.getFieldValue("list[1]")).isEqualTo("X"); |
||||
} |
||||
|
||||
|
||||
@NameAddressValid |
||||
public static class ValidPerson { |
||||
|
||||
@NotNull |
||||
private String name; |
||||
|
||||
@Valid |
||||
private ValidAddress address = new ValidAddress(); |
||||
|
||||
@Valid |
||||
private List<ValidAddress> addressList = new ArrayList<>(); |
||||
|
||||
@Valid |
||||
private Set<ValidAddress> addressSet = new LinkedHashSet<>(); |
||||
|
||||
public boolean expectsAutowiredValidator = false; |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public ValidAddress getAddress() { |
||||
return address; |
||||
} |
||||
|
||||
public void setAddress(ValidAddress address) { |
||||
this.address = address; |
||||
} |
||||
|
||||
public List<ValidAddress> getAddressList() { |
||||
return addressList; |
||||
} |
||||
|
||||
public void setAddressList(List<ValidAddress> addressList) { |
||||
this.addressList = addressList; |
||||
} |
||||
|
||||
public Set<ValidAddress> getAddressSet() { |
||||
return addressSet; |
||||
} |
||||
|
||||
public void setAddressSet(Set<ValidAddress> addressSet) { |
||||
this.addressSet = addressSet; |
||||
} |
||||
} |
||||
|
||||
|
||||
public static class ValidAddress { |
||||
|
||||
@NotNull |
||||
private String street; |
||||
|
||||
public String getStreet() { |
||||
return street; |
||||
} |
||||
|
||||
public void setStreet(String street) { |
||||
this.street = street; |
||||
} |
||||
} |
||||
|
||||
|
||||
@Target(ElementType.TYPE) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Constraint(validatedBy = NameAddressValidator.class) |
||||
public @interface NameAddressValid { |
||||
|
||||
String message() default "Street must not contain name"; |
||||
|
||||
Class<?>[] groups() default {}; |
||||
|
||||
Class<?>[] payload() default {}; |
||||
} |
||||
|
||||
|
||||
public static class NameAddressValidator implements ConstraintValidator<NameAddressValid, ValidPerson> { |
||||
|
||||
@Autowired |
||||
private Environment environment; |
||||
|
||||
@Override |
||||
public void initialize(NameAddressValid constraintAnnotation) { |
||||
} |
||||
|
||||
@Override |
||||
public boolean isValid(ValidPerson value, ConstraintValidatorContext context) { |
||||
if (value.expectsAutowiredValidator) { |
||||
assertThat(this.environment).isNotNull(); |
||||
} |
||||
boolean valid = (value.name == null || !value.address.street.contains(value.name)); |
||||
if (!valid && "Phil".equals(value.name)) { |
||||
context.buildConstraintViolationWithTemplate( |
||||
context.getDefaultConstraintMessageTemplate()).addPropertyNode("address").addConstraintViolation().disableDefaultConstraintViolation(); |
||||
} |
||||
return valid; |
||||
} |
||||
} |
||||
|
||||
|
||||
public static class MainBean { |
||||
|
||||
@InnerValid |
||||
private InnerBean inner = new InnerBean(); |
||||
|
||||
public InnerBean getInner() { |
||||
return inner; |
||||
} |
||||
} |
||||
|
||||
|
||||
public static class MainBeanWithOptional { |
||||
|
||||
@InnerValid |
||||
private InnerBean inner = new InnerBean(); |
||||
|
||||
public Optional<InnerBean> getInner() { |
||||
return Optional.ofNullable(inner); |
||||
} |
||||
} |
||||
|
||||
|
||||
public static class InnerBean { |
||||
|
||||
private String value; |
||||
|
||||
public String getValue() { |
||||
return value; |
||||
} |
||||
public void setValue(String value) { |
||||
this.value = value; |
||||
} |
||||
} |
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.FIELD) |
||||
@Constraint(validatedBy=InnerValidator.class) |
||||
public static @interface InnerValid { |
||||
|
||||
String message() default "NOT VALID"; |
||||
|
||||
Class<?>[] groups() default { }; |
||||
|
||||
Class<? extends Payload>[] payload() default {}; |
||||
} |
||||
|
||||
|
||||
public static class InnerValidator implements ConstraintValidator<InnerValid, InnerBean> { |
||||
|
||||
@Override |
||||
public void initialize(InnerValid constraintAnnotation) { |
||||
} |
||||
|
||||
@Override |
||||
public boolean isValid(InnerBean bean, ConstraintValidatorContext context) { |
||||
context.disableDefaultConstraintViolation(); |
||||
if (bean.getValue() == null) { |
||||
context.buildConstraintViolationWithTemplate("NULL").addPropertyNode("value").addConstraintViolation(); |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
|
||||
public static class ListContainer { |
||||
|
||||
@NotXList |
||||
private List<String> list = new ArrayList<>(); |
||||
|
||||
public void addString(String value) { |
||||
list.add(value); |
||||
} |
||||
|
||||
public List<String> getList() { |
||||
return list; |
||||
} |
||||
} |
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Target(ElementType.FIELD) |
||||
@Constraint(validatedBy = NotXListValidator.class) |
||||
public @interface NotXList { |
||||
|
||||
String message() default "Should not be X"; |
||||
|
||||
Class<?>[] groups() default {}; |
||||
|
||||
Class<? extends Payload>[] payload() default {}; |
||||
} |
||||
|
||||
|
||||
public static class NotXListValidator implements ConstraintValidator<NotXList, List<String>> { |
||||
|
||||
@Override |
||||
public void initialize(NotXList constraintAnnotation) { |
||||
} |
||||
|
||||
@Override |
||||
public boolean isValid(List<String> list, ConstraintValidatorContext context) { |
||||
context.disableDefaultConstraintViolation(); |
||||
boolean valid = true; |
||||
for (int i = 0; i < list.size(); i++) { |
||||
if ("X".equals(list.get(i))) { |
||||
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()).addBeanNode().inIterable().atIndex(i).addConstraintViolation(); |
||||
valid = false; |
||||
} |
||||
} |
||||
return valid; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,213 +0,0 @@
@@ -1,213 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.ejb.access; |
||||
|
||||
import java.lang.reflect.InvocationTargetException; |
||||
import java.lang.reflect.Method; |
||||
import java.rmi.RemoteException; |
||||
|
||||
import javax.ejb.EJBHome; |
||||
import javax.ejb.EJBObject; |
||||
import javax.naming.NamingException; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
|
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.remoting.RemoteConnectFailureException; |
||||
import org.springframework.remoting.RemoteLookupFailureException; |
||||
|
||||
/** |
||||
* Base class for interceptors proxying remote Stateless Session Beans. |
||||
* Designed for EJB 2.x, but works for EJB 3 Session Beans as well. |
||||
* |
||||
* <p>Such an interceptor must be the last interceptor in the advice chain. |
||||
* In this case, there is no target object. |
||||
* |
||||
* @author Rod Johnson |
||||
* @author Juergen Hoeller |
||||
*/ |
||||
public abstract class AbstractRemoteSlsbInvokerInterceptor extends AbstractSlsbInvokerInterceptor { |
||||
|
||||
private boolean refreshHomeOnConnectFailure = false; |
||||
|
||||
private volatile boolean homeAsComponent; |
||||
|
||||
|
||||
|
||||
/** |
||||
* Set whether to refresh the EJB home on connect failure. |
||||
* Default is "false". |
||||
* <p>Can be turned on to allow for hot restart of the EJB server. |
||||
* If a cached EJB home throws an RMI exception that indicates a |
||||
* remote connect failure, a fresh home will be fetched and the |
||||
* invocation will be retried. |
||||
* @see java.rmi.ConnectException |
||||
* @see java.rmi.ConnectIOException |
||||
* @see java.rmi.NoSuchObjectException |
||||
*/ |
||||
public void setRefreshHomeOnConnectFailure(boolean refreshHomeOnConnectFailure) { |
||||
this.refreshHomeOnConnectFailure = refreshHomeOnConnectFailure; |
||||
} |
||||
|
||||
@Override |
||||
protected boolean isHomeRefreshable() { |
||||
return this.refreshHomeOnConnectFailure; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Check for EJB3-style home object that serves as EJB component directly. |
||||
*/ |
||||
@Override |
||||
protected Method getCreateMethod(Object home) throws EjbAccessException { |
||||
if (this.homeAsComponent) { |
||||
return null; |
||||
} |
||||
if (!(home instanceof EJBHome)) { |
||||
// An EJB3 Session Bean...
|
||||
this.homeAsComponent = true; |
||||
return null; |
||||
} |
||||
return super.getCreateMethod(home); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Fetches an EJB home object and delegates to {@code doInvoke}. |
||||
* <p>If configured to refresh on connect failure, it will call |
||||
* {@link #refreshAndRetry} on corresponding RMI exceptions. |
||||
* @see #getHome |
||||
* @see #doInvoke |
||||
* @see #refreshAndRetry |
||||
*/ |
||||
@Override |
||||
@Nullable |
||||
public Object invokeInContext(MethodInvocation invocation) throws Throwable { |
||||
try { |
||||
return doInvoke(invocation); |
||||
} |
||||
catch (RemoteConnectFailureException ex) { |
||||
return handleRemoteConnectFailure(invocation, ex); |
||||
} |
||||
catch (RemoteException ex) { |
||||
if (isConnectFailure(ex)) { |
||||
return handleRemoteConnectFailure(invocation, ex); |
||||
} |
||||
else { |
||||
throw ex; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Determine whether the given RMI exception indicates a connect failure. |
||||
* <p>The default implementation delegates to RmiClientInterceptorUtils. |
||||
* @param ex the RMI exception to check |
||||
* @return whether the exception should be treated as connect failure |
||||
* @see org.springframework.remoting.rmi.RmiClientInterceptorUtils#isConnectFailure |
||||
*/ |
||||
@SuppressWarnings("deprecation") |
||||
protected boolean isConnectFailure(RemoteException ex) { |
||||
return org.springframework.remoting.rmi.RmiClientInterceptorUtils.isConnectFailure(ex); |
||||
} |
||||
|
||||
@Nullable |
||||
private Object handleRemoteConnectFailure(MethodInvocation invocation, Exception ex) throws Throwable { |
||||
if (this.refreshHomeOnConnectFailure) { |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Could not connect to remote EJB [" + getJndiName() + "] - retrying", ex); |
||||
} |
||||
else if (logger.isWarnEnabled()) { |
||||
logger.warn("Could not connect to remote EJB [" + getJndiName() + "] - retrying"); |
||||
} |
||||
return refreshAndRetry(invocation); |
||||
} |
||||
else { |
||||
throw ex; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Refresh the EJB home object and retry the given invocation. |
||||
* Called by invoke on connect failure. |
||||
* @param invocation the AOP method invocation |
||||
* @return the invocation result, if any |
||||
* @throws Throwable in case of invocation failure |
||||
* @see #invoke |
||||
*/ |
||||
@Nullable |
||||
protected Object refreshAndRetry(MethodInvocation invocation) throws Throwable { |
||||
try { |
||||
refreshHome(); |
||||
} |
||||
catch (NamingException ex) { |
||||
throw new RemoteLookupFailureException("Failed to locate remote EJB [" + getJndiName() + "]", ex); |
||||
} |
||||
return doInvoke(invocation); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Perform the given invocation on the current EJB home. |
||||
* Template method to be implemented by subclasses. |
||||
* @param invocation the AOP method invocation |
||||
* @return the invocation result, if any |
||||
* @throws Throwable in case of invocation failure |
||||
* @see #getHome |
||||
* @see #newSessionBeanInstance |
||||
*/ |
||||
@Nullable |
||||
protected abstract Object doInvoke(MethodInvocation invocation) throws Throwable; |
||||
|
||||
|
||||
/** |
||||
* Return a new instance of the stateless session bean. |
||||
* To be invoked by concrete remote SLSB invoker subclasses. |
||||
* <p>Can be overridden to change the algorithm. |
||||
* @throws NamingException if thrown by JNDI |
||||
* @throws InvocationTargetException if thrown by the create method |
||||
* @see #create |
||||
*/ |
||||
protected Object newSessionBeanInstance() throws NamingException, InvocationTargetException { |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Trying to create reference to remote EJB"); |
||||
} |
||||
Object ejbInstance = create(); |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Obtained reference to remote EJB: " + ejbInstance); |
||||
} |
||||
return ejbInstance; |
||||
} |
||||
|
||||
/** |
||||
* Remove the given EJB instance. |
||||
* To be invoked by concrete remote SLSB invoker subclasses. |
||||
* @param ejb the EJB instance to remove |
||||
* @see javax.ejb.EJBObject#remove |
||||
*/ |
||||
protected void removeSessionBeanInstance(@Nullable EJBObject ejb) { |
||||
if (ejb != null && !this.homeAsComponent) { |
||||
try { |
||||
ejb.remove(); |
||||
} |
||||
catch (Throwable ex) { |
||||
logger.warn("Could not invoke 'remove' on remote EJB proxy", ex); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,238 +0,0 @@
@@ -1,238 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2018 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 |
||||
* |
||||
* https://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.ejb.access; |
||||
|
||||
import java.lang.reflect.InvocationTargetException; |
||||
import java.lang.reflect.Method; |
||||
|
||||
import javax.naming.Context; |
||||
import javax.naming.NamingException; |
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor; |
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
|
||||
import org.springframework.jndi.JndiObjectLocator; |
||||
import org.springframework.lang.Nullable; |
||||
|
||||
/** |
||||
* Base class for AOP interceptors invoking local or remote Stateless Session Beans. |
||||
* Designed for EJB 2.x, but works for EJB 3 Session Beans as well. |
||||
* |
||||
* <p>Such an interceptor must be the last interceptor in the advice chain. |
||||
* In this case, there is no direct target object: The call is handled in a |
||||
* special way, getting executed on an EJB instance retrieved via an EJB home. |
||||
* |
||||
* @author Rod Johnson |
||||
* @author Juergen Hoeller |
||||
*/ |
||||
public abstract class AbstractSlsbInvokerInterceptor extends JndiObjectLocator |
||||
implements MethodInterceptor { |
||||
|
||||
private boolean lookupHomeOnStartup = true; |
||||
|
||||
private boolean cacheHome = true; |
||||
|
||||
private boolean exposeAccessContext = false; |
||||
|
||||
/** |
||||
* The EJB's home object, potentially cached. |
||||
* The type must be Object as it could be either EJBHome or EJBLocalHome. |
||||
*/ |
||||
@Nullable |
||||
private Object cachedHome; |
||||
|
||||
/** |
||||
* The no-arg create() method required on EJB homes, potentially cached. |
||||
*/ |
||||
@Nullable |
||||
private Method createMethod; |
||||
|
||||
private final Object homeMonitor = new Object(); |
||||
|
||||
|
||||
/** |
||||
* Set whether to look up the EJB home object on startup. |
||||
* Default is "true". |
||||
* <p>Can be turned off to allow for late start of the EJB server. |
||||
* In this case, the EJB home object will be fetched on first access. |
||||
* @see #setCacheHome |
||||
*/ |
||||
public void setLookupHomeOnStartup(boolean lookupHomeOnStartup) { |
||||
this.lookupHomeOnStartup = lookupHomeOnStartup; |
||||
} |
||||
|
||||
/** |
||||
* Set whether to cache the EJB home object once it has been located. |
||||
* Default is "true". |
||||
* <p>Can be turned off to allow for hot restart of the EJB server. |
||||
* In this case, the EJB home object will be fetched for each invocation. |
||||
* @see #setLookupHomeOnStartup |
||||
*/ |
||||
public void setCacheHome(boolean cacheHome) { |
||||
this.cacheHome = cacheHome; |
||||
} |
||||
|
||||
/** |
||||
* Set whether to expose the JNDI environment context for all access to the target |
||||
* EJB, i.e. for all method invocations on the exposed object reference. |
||||
* <p>Default is "false", i.e. to only expose the JNDI context for object lookup. |
||||
* Switch this flag to "true" in order to expose the JNDI environment (including |
||||
* the authorization context) for each EJB invocation, as needed by WebLogic |
||||
* for EJBs with authorization requirements. |
||||
*/ |
||||
public void setExposeAccessContext(boolean exposeAccessContext) { |
||||
this.exposeAccessContext = exposeAccessContext; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Fetches EJB home on startup, if necessary. |
||||
* @see #setLookupHomeOnStartup |
||||
* @see #refreshHome |
||||
*/ |
||||
@Override |
||||
public void afterPropertiesSet() throws NamingException { |
||||
super.afterPropertiesSet(); |
||||
if (this.lookupHomeOnStartup) { |
||||
// look up EJB home and create method
|
||||
refreshHome(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Refresh the cached home object, if applicable. |
||||
* Also caches the create method on the home object. |
||||
* @throws NamingException if thrown by the JNDI lookup |
||||
* @see #lookup |
||||
* @see #getCreateMethod |
||||
*/ |
||||
protected void refreshHome() throws NamingException { |
||||
synchronized (this.homeMonitor) { |
||||
Object home = lookup(); |
||||
if (this.cacheHome) { |
||||
this.cachedHome = home; |
||||
this.createMethod = getCreateMethod(home); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Determine the create method of the given EJB home object. |
||||
* @param home the EJB home object |
||||
* @return the create method |
||||
* @throws EjbAccessException if the method couldn't be retrieved |
||||
*/ |
||||
@Nullable |
||||
protected Method getCreateMethod(Object home) throws EjbAccessException { |
||||
try { |
||||
// Cache the EJB create() method that must be declared on the home interface.
|
||||
return home.getClass().getMethod("create"); |
||||
} |
||||
catch (NoSuchMethodException ex) { |
||||
throw new EjbAccessException("EJB home [" + home + "] has no no-arg create() method"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Return the EJB home object to use. Called for each invocation. |
||||
* <p>Default implementation returns the home created on initialization, |
||||
* if any; else, it invokes lookup to get a new proxy for each invocation. |
||||
* <p>Can be overridden in subclasses, for example to cache a home object |
||||
* for a given amount of time before recreating it, or to test the home |
||||
* object whether it is still alive. |
||||
* @return the EJB home object to use for an invocation |
||||
* @throws NamingException if proxy creation failed |
||||
* @see #lookup |
||||
* @see #getCreateMethod |
||||
*/ |
||||
protected Object getHome() throws NamingException { |
||||
if (!this.cacheHome || (this.lookupHomeOnStartup && !isHomeRefreshable())) { |
||||
return (this.cachedHome != null ? this.cachedHome : lookup()); |
||||
} |
||||
else { |
||||
synchronized (this.homeMonitor) { |
||||
if (this.cachedHome == null) { |
||||
this.cachedHome = lookup(); |
||||
this.createMethod = getCreateMethod(this.cachedHome); |
||||
} |
||||
return this.cachedHome; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Return whether the cached EJB home object is potentially |
||||
* subject to on-demand refreshing. Default is "false". |
||||
*/ |
||||
protected boolean isHomeRefreshable() { |
||||
return false; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Prepares the thread context if necessary, and delegates to |
||||
* {@link #invokeInContext}. |
||||
*/ |
||||
@Override |
||||
@Nullable |
||||
public Object invoke(MethodInvocation invocation) throws Throwable { |
||||
Context ctx = (this.exposeAccessContext ? getJndiTemplate().getContext() : null); |
||||
try { |
||||
return invokeInContext(invocation); |
||||
} |
||||
finally { |
||||
getJndiTemplate().releaseContext(ctx); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Perform the given invocation on the current EJB home, |
||||
* within the thread context being prepared accordingly. |
||||
* Template method to be implemented by subclasses. |
||||
* @param invocation the AOP method invocation |
||||
* @return the invocation result, if any |
||||
* @throws Throwable in case of invocation failure |
||||
*/ |
||||
@Nullable |
||||
protected abstract Object invokeInContext(MethodInvocation invocation) throws Throwable; |
||||
|
||||
|
||||
/** |
||||
* Invokes the {@code create()} method on the cached EJB home object. |
||||
* @return a new EJBObject or EJBLocalObject |
||||
* @throws NamingException if thrown by JNDI |
||||
* @throws InvocationTargetException if thrown by the create method |
||||
*/ |
||||
protected Object create() throws NamingException, InvocationTargetException { |
||||
try { |
||||
Object home = getHome(); |
||||
Method createMethodToUse = this.createMethod; |
||||
if (createMethodToUse == null) { |
||||
createMethodToUse = getCreateMethod(home); |
||||
} |
||||
if (createMethodToUse == null) { |
||||
return home; |
||||
} |
||||
// Invoke create() method on EJB home object.
|
||||
return createMethodToUse.invoke(home, (Object[]) null); |
||||
} |
||||
catch (IllegalAccessException ex) { |
||||
throw new EjbAccessException("Could not access EJB home create() method", ex); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,47 +0,0 @@
@@ -1,47 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2012 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 |
||||
* |
||||
* https://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.ejb.access; |
||||
|
||||
import org.springframework.core.NestedRuntimeException; |
||||
|
||||
/** |
||||
* Exception that gets thrown when an EJB stub cannot be accessed properly. |
||||
* |
||||
* @author Juergen Hoeller |
||||
* @since 2.0 |
||||
*/ |
||||
@SuppressWarnings("serial") |
||||
public class EjbAccessException extends NestedRuntimeException { |
||||
|
||||
/** |
||||
* Constructor for EjbAccessException. |
||||
* @param msg the detail message |
||||
*/ |
||||
public EjbAccessException(String msg) { |
||||
super(msg); |
||||
} |
||||
|
||||
/** |
||||
* Constructor for EjbAccessException. |
||||
* @param msg the detail message |
||||
* @param cause the root cause |
||||
*/ |
||||
public EjbAccessException(String msg, Throwable cause) { |
||||
super(msg, cause); |
||||
} |
||||
|
||||
} |
||||
@ -1,179 +0,0 @@
@@ -1,179 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.ejb.access; |
||||
|
||||
import java.lang.reflect.InvocationTargetException; |
||||
import java.lang.reflect.Method; |
||||
|
||||
import javax.ejb.CreateException; |
||||
import javax.ejb.EJBLocalHome; |
||||
import javax.ejb.EJBLocalObject; |
||||
import javax.naming.NamingException; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
|
||||
import org.springframework.lang.Nullable; |
||||
|
||||
/** |
||||
* Invoker for a local Stateless Session Bean. |
||||
* Designed for EJB 2.x, but works for EJB 3 Session Beans as well. |
||||
* |
||||
* <p>Caches the home object, since a local EJB home can never go stale. |
||||
* See {@link org.springframework.jndi.JndiObjectLocator} for info on |
||||
* how to specify the JNDI location of the target EJB. |
||||
* |
||||
* <p>In a bean container, this class is normally best used as a singleton. However, |
||||
* if that bean container pre-instantiates singletons (as do the XML ApplicationContext |
||||
* variants) you may have a problem if the bean container is loaded before the EJB |
||||
* container loads the target EJB. That is because by default the JNDI lookup will be |
||||
* performed in the init method of this class and cached, but the EJB will not have been |
||||
* bound at the target location yet. The best solution is to set the lookupHomeOnStartup |
||||
* property to false, in which case the home will be fetched on first access to the EJB. |
||||
* (This flag is only true by default for backwards compatibility reasons). |
||||
* |
||||
* @author Rod Johnson |
||||
* @author Juergen Hoeller |
||||
* @see AbstractSlsbInvokerInterceptor#setLookupHomeOnStartup |
||||
* @see AbstractSlsbInvokerInterceptor#setCacheHome |
||||
*/ |
||||
public class LocalSlsbInvokerInterceptor extends AbstractSlsbInvokerInterceptor { |
||||
|
||||
private volatile boolean homeAsComponent; |
||||
|
||||
|
||||
/** |
||||
* This implementation "creates" a new EJB instance for each invocation. |
||||
* Can be overridden for custom invocation strategies. |
||||
* <p>Alternatively, override {@link #getSessionBeanInstance} and |
||||
* {@link #releaseSessionBeanInstance} to change EJB instance creation, |
||||
* for example to hold a single shared EJB instance. |
||||
*/ |
||||
@Override |
||||
@Nullable |
||||
public Object invokeInContext(MethodInvocation invocation) throws Throwable { |
||||
Object ejb = null; |
||||
try { |
||||
ejb = getSessionBeanInstance(); |
||||
Method method = invocation.getMethod(); |
||||
if (method.getDeclaringClass().isInstance(ejb)) { |
||||
// directly implemented
|
||||
return method.invoke(ejb, invocation.getArguments()); |
||||
} |
||||
else { |
||||
// not directly implemented
|
||||
Method ejbMethod = ejb.getClass().getMethod(method.getName(), method.getParameterTypes()); |
||||
return ejbMethod.invoke(ejb, invocation.getArguments()); |
||||
} |
||||
} |
||||
catch (InvocationTargetException ex) { |
||||
Throwable targetEx = ex.getTargetException(); |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Method of local EJB [" + getJndiName() + "] threw exception", targetEx); |
||||
} |
||||
if (targetEx instanceof CreateException) { |
||||
throw new EjbAccessException("Could not create local EJB [" + getJndiName() + "]", targetEx); |
||||
} |
||||
else { |
||||
throw targetEx; |
||||
} |
||||
} |
||||
catch (NamingException ex) { |
||||
throw new EjbAccessException("Failed to locate local EJB [" + getJndiName() + "]", ex); |
||||
} |
||||
catch (IllegalAccessException ex) { |
||||
throw new EjbAccessException("Could not access method [" + invocation.getMethod().getName() + |
||||
"] of local EJB [" + getJndiName() + "]", ex); |
||||
} |
||||
finally { |
||||
if (ejb instanceof EJBLocalObject) { |
||||
releaseSessionBeanInstance((EJBLocalObject) ejb); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Check for EJB3-style home object that serves as EJB component directly. |
||||
*/ |
||||
@Override |
||||
protected Method getCreateMethod(Object home) throws EjbAccessException { |
||||
if (this.homeAsComponent) { |
||||
return null; |
||||
} |
||||
if (!(home instanceof EJBLocalHome)) { |
||||
// An EJB3 Session Bean...
|
||||
this.homeAsComponent = true; |
||||
return null; |
||||
} |
||||
return super.getCreateMethod(home); |
||||
} |
||||
|
||||
/** |
||||
* Return an EJB instance to delegate the call to. |
||||
* Default implementation delegates to newSessionBeanInstance. |
||||
* @throws NamingException if thrown by JNDI |
||||
* @throws InvocationTargetException if thrown by the create method |
||||
* @see #newSessionBeanInstance |
||||
*/ |
||||
protected Object getSessionBeanInstance() throws NamingException, InvocationTargetException { |
||||
return newSessionBeanInstance(); |
||||
} |
||||
|
||||
/** |
||||
* Release the given EJB instance. |
||||
* Default implementation delegates to removeSessionBeanInstance. |
||||
* @param ejb the EJB instance to release |
||||
* @see #removeSessionBeanInstance |
||||
*/ |
||||
protected void releaseSessionBeanInstance(EJBLocalObject ejb) { |
||||
removeSessionBeanInstance(ejb); |
||||
} |
||||
|
||||
/** |
||||
* Return a new instance of the stateless session bean. |
||||
* Can be overridden to change the algorithm. |
||||
* @throws NamingException if thrown by JNDI |
||||
* @throws InvocationTargetException if thrown by the create method |
||||
* @see #create |
||||
*/ |
||||
protected Object newSessionBeanInstance() throws NamingException, InvocationTargetException { |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Trying to create reference to local EJB"); |
||||
} |
||||
Object ejbInstance = create(); |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Obtained reference to local EJB: " + ejbInstance); |
||||
} |
||||
return ejbInstance; |
||||
} |
||||
|
||||
/** |
||||
* Remove the given EJB instance. |
||||
* @param ejb the EJB instance to remove |
||||
* @see javax.ejb.EJBLocalObject#remove() |
||||
*/ |
||||
protected void removeSessionBeanInstance(@Nullable EJBLocalObject ejb) { |
||||
if (ejb != null && !this.homeAsComponent) { |
||||
try { |
||||
ejb.remove(); |
||||
} |
||||
catch (Throwable ex) { |
||||
logger.warn("Could not invoke 'remove' on local EJB proxy", ex); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,116 +0,0 @@
@@ -1,116 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2018 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 |
||||
* |
||||
* https://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.ejb.access; |
||||
|
||||
import javax.naming.NamingException; |
||||
|
||||
import org.springframework.aop.framework.ProxyFactory; |
||||
import org.springframework.beans.factory.BeanClassLoaderAware; |
||||
import org.springframework.beans.factory.FactoryBean; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.ClassUtils; |
||||
|
||||
/** |
||||
* Convenient {@link FactoryBean} for local Stateless Session Bean (SLSB) proxies. |
||||
* Designed for EJB 2.x, but works for EJB 3 Session Beans as well. |
||||
* |
||||
* <p>See {@link org.springframework.jndi.JndiObjectLocator} for info on |
||||
* how to specify the JNDI location of the target EJB. |
||||
* |
||||
* <p>If you want control over interceptor chaining, use an AOP ProxyFactoryBean |
||||
* with LocalSlsbInvokerInterceptor rather than rely on this class. |
||||
* |
||||
* <p>In a bean container, this class is normally best used as a singleton. However, |
||||
* if that bean container pre-instantiates singletons (as do the XML ApplicationContext |
||||
* variants) you may have a problem if the bean container is loaded before the EJB |
||||
* container loads the target EJB. That is because by default the JNDI lookup will be |
||||
* performed in the init method of this class and cached, but the EJB will not have been |
||||
* bound at the target location yet. The best solution is to set the "lookupHomeOnStartup" |
||||
* property to "false", in which case the home will be fetched on first access to the EJB. |
||||
* (This flag is only true by default for backwards compatibility reasons). |
||||
* |
||||
* @author Rod Johnson |
||||
* @author Colin Sampaleanu |
||||
* @since 09.05.2003 |
||||
* @see AbstractSlsbInvokerInterceptor#setLookupHomeOnStartup |
||||
* @see AbstractSlsbInvokerInterceptor#setCacheHome |
||||
*/ |
||||
public class LocalStatelessSessionProxyFactoryBean extends LocalSlsbInvokerInterceptor |
||||
implements FactoryBean<Object>, BeanClassLoaderAware { |
||||
|
||||
/** The business interface of the EJB we're proxying. */ |
||||
@Nullable |
||||
private Class<?> businessInterface; |
||||
|
||||
@Nullable |
||||
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); |
||||
|
||||
/** EJBLocalObject. */ |
||||
@Nullable |
||||
private Object proxy; |
||||
|
||||
|
||||
/** |
||||
* Set the business interface of the EJB we're proxying. |
||||
* This will normally be a super-interface of the EJB local component interface. |
||||
* Using a business methods interface is a best practice when implementing EJBs. |
||||
* @param businessInterface set the business interface of the EJB |
||||
*/ |
||||
public void setBusinessInterface(@Nullable Class<?> businessInterface) { |
||||
this.businessInterface = businessInterface; |
||||
} |
||||
|
||||
/** |
||||
* Return the business interface of the EJB we're proxying. |
||||
*/ |
||||
@Nullable |
||||
public Class<?> getBusinessInterface() { |
||||
return this.businessInterface; |
||||
} |
||||
|
||||
@Override |
||||
public void setBeanClassLoader(ClassLoader classLoader) { |
||||
this.beanClassLoader = classLoader; |
||||
} |
||||
|
||||
@Override |
||||
public void afterPropertiesSet() throws NamingException { |
||||
super.afterPropertiesSet(); |
||||
if (this.businessInterface == null) { |
||||
throw new IllegalArgumentException("businessInterface is required"); |
||||
} |
||||
this.proxy = new ProxyFactory(this.businessInterface, this).getProxy(this.beanClassLoader); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
@Nullable |
||||
public Object getObject() { |
||||
return this.proxy; |
||||
} |
||||
|
||||
@Override |
||||
public Class<?> getObjectType() { |
||||
return this.businessInterface; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSingleton() { |
||||
return true; |
||||
} |
||||
|
||||
} |
||||
@ -1,188 +0,0 @@
@@ -1,188 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2017 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 |
||||
* |
||||
* https://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.ejb.access; |
||||
|
||||
import java.lang.reflect.InvocationTargetException; |
||||
import java.rmi.RemoteException; |
||||
|
||||
import javax.ejb.CreateException; |
||||
import javax.ejb.EJBObject; |
||||
import javax.naming.NamingException; |
||||
|
||||
import org.aopalliance.intercept.MethodInvocation; |
||||
|
||||
import org.springframework.beans.factory.DisposableBean; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.remoting.RemoteLookupFailureException; |
||||
|
||||
/** |
||||
* Basic invoker for a remote Stateless Session Bean. |
||||
* Designed for EJB 2.x, but works for EJB 3 Session Beans as well. |
||||
* |
||||
* <p>"Creates" a new EJB instance for each invocation, or caches the session |
||||
* bean instance for all invocations (see {@link #setCacheSessionBean}). |
||||
* See {@link org.springframework.jndi.JndiObjectLocator} for info on |
||||
* how to specify the JNDI location of the target EJB. |
||||
* |
||||
* <p>In a bean container, this class is normally best used as a singleton. However, |
||||
* if that bean container pre-instantiates singletons (as do the XML ApplicationContext |
||||
* variants) you may have a problem if the bean container is loaded before the EJB |
||||
* container loads the target EJB. That is because by default the JNDI lookup will be |
||||
* performed in the init method of this class and cached, but the EJB will not have been |
||||
* bound at the target location yet. The best solution is to set the "lookupHomeOnStartup" |
||||
* property to "false", in which case the home will be fetched on first access to the EJB. |
||||
* (This flag is only true by default for backwards compatibility reasons). |
||||
* |
||||
* <p>This invoker is typically used with an RMI business interface, which serves |
||||
* as super-interface of the EJB component interface. Alternatively, this invoker |
||||
* can also proxy a remote SLSB with a matching non-RMI business interface, i.e. an |
||||
* interface that mirrors the EJB business methods but does not declare RemoteExceptions. |
||||
* In the latter case, RemoteExceptions thrown by the EJB stub will automatically get |
||||
* converted to Spring's unchecked RemoteAccessException. |
||||
* |
||||
* @author Rod Johnson |
||||
* @author Juergen Hoeller |
||||
* @since 09.05.2003 |
||||
* @see org.springframework.remoting.RemoteAccessException |
||||
* @see AbstractSlsbInvokerInterceptor#setLookupHomeOnStartup |
||||
* @see AbstractSlsbInvokerInterceptor#setCacheHome |
||||
* @see AbstractRemoteSlsbInvokerInterceptor#setRefreshHomeOnConnectFailure |
||||
*/ |
||||
public class SimpleRemoteSlsbInvokerInterceptor extends AbstractRemoteSlsbInvokerInterceptor |
||||
implements DisposableBean { |
||||
|
||||
private boolean cacheSessionBean = false; |
||||
|
||||
@Nullable |
||||
private Object beanInstance; |
||||
|
||||
private final Object beanInstanceMonitor = new Object(); |
||||
|
||||
|
||||
/** |
||||
* Set whether to cache the actual session bean object. |
||||
* <p>Off by default for standard EJB compliance. Turn this flag |
||||
* on to optimize session bean access for servers that are |
||||
* known to allow for caching the actual session bean object. |
||||
* @see #setCacheHome |
||||
*/ |
||||
public void setCacheSessionBean(boolean cacheSessionBean) { |
||||
this.cacheSessionBean = cacheSessionBean; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* This implementation "creates" a new EJB instance for each invocation. |
||||
* Can be overridden for custom invocation strategies. |
||||
* <p>Alternatively, override {@link #getSessionBeanInstance} and |
||||
* {@link #releaseSessionBeanInstance} to change EJB instance creation, |
||||
* for example to hold a single shared EJB component instance. |
||||
*/ |
||||
@Override |
||||
@Nullable |
||||
@SuppressWarnings("deprecation") |
||||
protected Object doInvoke(MethodInvocation invocation) throws Throwable { |
||||
Object ejb = null; |
||||
try { |
||||
ejb = getSessionBeanInstance(); |
||||
return org.springframework.remoting.rmi.RmiClientInterceptorUtils.invokeRemoteMethod(invocation, ejb); |
||||
} |
||||
catch (NamingException ex) { |
||||
throw new RemoteLookupFailureException("Failed to locate remote EJB [" + getJndiName() + "]", ex); |
||||
} |
||||
catch (InvocationTargetException ex) { |
||||
Throwable targetEx = ex.getTargetException(); |
||||
if (targetEx instanceof RemoteException) { |
||||
RemoteException rex = (RemoteException) targetEx; |
||||
throw org.springframework.remoting.rmi.RmiClientInterceptorUtils.convertRmiAccessException( |
||||
invocation.getMethod(), rex, isConnectFailure(rex), getJndiName()); |
||||
} |
||||
else if (targetEx instanceof CreateException) { |
||||
throw org.springframework.remoting.rmi.RmiClientInterceptorUtils.convertRmiAccessException( |
||||
invocation.getMethod(), targetEx, "Could not create remote EJB [" + getJndiName() + "]"); |
||||
} |
||||
throw targetEx; |
||||
} |
||||
finally { |
||||
if (ejb instanceof EJBObject) { |
||||
releaseSessionBeanInstance((EJBObject) ejb); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Return an EJB component instance to delegate the call to. |
||||
* <p>The default implementation delegates to {@link #newSessionBeanInstance}. |
||||
* @return the EJB component instance |
||||
* @throws NamingException if thrown by JNDI |
||||
* @throws InvocationTargetException if thrown by the create method |
||||
* @see #newSessionBeanInstance |
||||
*/ |
||||
protected Object getSessionBeanInstance() throws NamingException, InvocationTargetException { |
||||
if (this.cacheSessionBean) { |
||||
synchronized (this.beanInstanceMonitor) { |
||||
if (this.beanInstance == null) { |
||||
this.beanInstance = newSessionBeanInstance(); |
||||
} |
||||
return this.beanInstance; |
||||
} |
||||
} |
||||
else { |
||||
return newSessionBeanInstance(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Release the given EJB instance. |
||||
* <p>The default implementation delegates to {@link #removeSessionBeanInstance}. |
||||
* @param ejb the EJB component instance to release |
||||
* @see #removeSessionBeanInstance |
||||
*/ |
||||
protected void releaseSessionBeanInstance(EJBObject ejb) { |
||||
if (!this.cacheSessionBean) { |
||||
removeSessionBeanInstance(ejb); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Reset the cached session bean instance, if necessary. |
||||
*/ |
||||
@Override |
||||
protected void refreshHome() throws NamingException { |
||||
super.refreshHome(); |
||||
if (this.cacheSessionBean) { |
||||
synchronized (this.beanInstanceMonitor) { |
||||
this.beanInstance = null; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Remove the cached session bean instance, if necessary. |
||||
*/ |
||||
@Override |
||||
public void destroy() { |
||||
if (this.cacheSessionBean) { |
||||
synchronized (this.beanInstanceMonitor) { |
||||
if (this.beanInstance instanceof EJBObject) { |
||||
removeSessionBeanInstance((EJBObject) this.beanInstance); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,130 +0,0 @@
@@ -1,130 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2018 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 |
||||
* |
||||
* https://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.ejb.access; |
||||
|
||||
import javax.naming.NamingException; |
||||
|
||||
import org.springframework.aop.framework.ProxyFactory; |
||||
import org.springframework.beans.factory.BeanClassLoaderAware; |
||||
import org.springframework.beans.factory.FactoryBean; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.ClassUtils; |
||||
|
||||
/** |
||||
* Convenient {@link FactoryBean} for remote SLSB proxies. |
||||
* Designed for EJB 2.x, but works for EJB 3 Session Beans as well. |
||||
* |
||||
* <p>See {@link org.springframework.jndi.JndiObjectLocator} for info on |
||||
* how to specify the JNDI location of the target EJB. |
||||
* |
||||
* <p>If you want control over interceptor chaining, use an AOP ProxyFactoryBean |
||||
* with SimpleRemoteSlsbInvokerInterceptor rather than rely on this class. |
||||
* |
||||
* <p>In a bean container, this class is normally best used as a singleton. However, |
||||
* if that bean container pre-instantiates singletons (as do the XML ApplicationContext |
||||
* variants) you may have a problem if the bean container is loaded before the EJB |
||||
* container loads the target EJB. That is because by default the JNDI lookup will be |
||||
* performed in the init method of this class and cached, but the EJB will not have been |
||||
* bound at the target location yet. The best solution is to set the lookupHomeOnStartup |
||||
* property to false, in which case the home will be fetched on first access to the EJB. |
||||
* (This flag is only true by default for backwards compatibility reasons). |
||||
* |
||||
* <p>This proxy factory is typically used with an RMI business interface, which serves |
||||
* as super-interface of the EJB component interface. Alternatively, this factory |
||||
* can also proxy a remote SLSB with a matching non-RMI business interface, i.e. an |
||||
* interface that mirrors the EJB business methods but does not declare RemoteExceptions. |
||||
* In the latter case, RemoteExceptions thrown by the EJB stub will automatically get |
||||
* converted to Spring's unchecked RemoteAccessException. |
||||
* |
||||
* @author Rod Johnson |
||||
* @author Colin Sampaleanu |
||||
* @author Juergen Hoeller |
||||
* @since 09.05.2003 |
||||
* @see org.springframework.remoting.RemoteAccessException |
||||
* @see AbstractSlsbInvokerInterceptor#setLookupHomeOnStartup |
||||
* @see AbstractSlsbInvokerInterceptor#setCacheHome |
||||
* @see AbstractRemoteSlsbInvokerInterceptor#setRefreshHomeOnConnectFailure |
||||
*/ |
||||
public class SimpleRemoteStatelessSessionProxyFactoryBean extends SimpleRemoteSlsbInvokerInterceptor |
||||
implements FactoryBean<Object>, BeanClassLoaderAware { |
||||
|
||||
/** The business interface of the EJB we're proxying. */ |
||||
@Nullable |
||||
private Class<?> businessInterface; |
||||
|
||||
@Nullable |
||||
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); |
||||
|
||||
/** EJBObject. */ |
||||
@Nullable |
||||
private Object proxy; |
||||
|
||||
|
||||
/** |
||||
* Set the business interface of the EJB we're proxying. |
||||
* This will normally be a super-interface of the EJB remote component interface. |
||||
* Using a business methods interface is a best practice when implementing EJBs. |
||||
* <p>You can also specify a matching non-RMI business interface, i.e. an interface
|
||||
* that mirrors the EJB business methods but does not declare RemoteExceptions. |
||||
* In this case, RemoteExceptions thrown by the EJB stub will automatically get |
||||
* converted to Spring's generic RemoteAccessException. |
||||
* @param businessInterface the business interface of the EJB |
||||
*/ |
||||
public void setBusinessInterface(@Nullable Class<?> businessInterface) { |
||||
this.businessInterface = businessInterface; |
||||
} |
||||
|
||||
/** |
||||
* Return the business interface of the EJB we're proxying. |
||||
*/ |
||||
@Nullable |
||||
public Class<?> getBusinessInterface() { |
||||
return this.businessInterface; |
||||
} |
||||
|
||||
@Override |
||||
public void setBeanClassLoader(ClassLoader classLoader) { |
||||
this.beanClassLoader = classLoader; |
||||
} |
||||
|
||||
@Override |
||||
public void afterPropertiesSet() throws NamingException { |
||||
super.afterPropertiesSet(); |
||||
if (this.businessInterface == null) { |
||||
throw new IllegalArgumentException("businessInterface is required"); |
||||
} |
||||
this.proxy = new ProxyFactory(this.businessInterface, this).getProxy(this.beanClassLoader); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
@Nullable |
||||
public Object getObject() { |
||||
return this.proxy; |
||||
} |
||||
|
||||
@Override |
||||
public Class<?> getObjectType() { |
||||
return this.businessInterface; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSingleton() { |
||||
return true; |
||||
} |
||||
|
||||
} |
||||
@ -1,27 +0,0 @@
@@ -1,27 +0,0 @@
|
||||
/** |
||||
* This package contains classes that allow easy access to EJBs. |
||||
* The basis are AOP interceptors run before and after the EJB invocation. |
||||
* In particular, the classes in this package allow transparent access |
||||
* to stateless session beans (SLSBs) with local interfaces, avoiding |
||||
* the need for application code using them to use EJB-specific APIs |
||||
* and JNDI lookups, and work with business interfaces that could be |
||||
* implemented without using EJB. This provides a valuable decoupling |
||||
* of client (such as web components) and business objects (which may |
||||
* or may not be EJBs). This gives us the choice of introducing EJB |
||||
* into an application (or removing EJB from an application) without |
||||
* affecting code using business objects. |
||||
* |
||||
* <p>The motivation for the classes in this package is discussed in Chapter 11 of |
||||
* <a href="https://www.amazon.com/exec/obidos/tg/detail/-/0764543857/">Expert One-On-One J2EE Design and Development</a> |
||||
* by Rod Johnson (Wrox, 2002). |
||||
* |
||||
* <p>However, the implementation and naming of classes in this package has changed. |
||||
* It now uses FactoryBeans and AOP, rather than the custom bean definitions described in |
||||
* <i>Expert One-on-One J2EE</i>. |
||||
*/ |
||||
@NonNullApi |
||||
@NonNullFields |
||||
package org.springframework.ejb.access; |
||||
|
||||
import org.springframework.lang.NonNullApi; |
||||
import org.springframework.lang.NonNullFields; |
||||
@ -1,117 +0,0 @@
@@ -1,117 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2018 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 |
||||
* |
||||
* https://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.instrument.classloading.weblogic; |
||||
|
||||
import java.lang.instrument.ClassFileTransformer; |
||||
import java.lang.reflect.Constructor; |
||||
import java.lang.reflect.InvocationHandler; |
||||
import java.lang.reflect.InvocationTargetException; |
||||
import java.lang.reflect.Method; |
||||
import java.lang.reflect.Proxy; |
||||
|
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* Reflective wrapper around a WebLogic 10 class loader. Used to |
||||
* encapsulate the classloader-specific methods (discovered and |
||||
* called through reflection) from the load-time weaver. |
||||
* |
||||
* @author Costin Leau |
||||
* @author Juergen Hoeller |
||||
* @since 2.5 |
||||
*/ |
||||
class WebLogicClassLoaderAdapter { |
||||
|
||||
private static final String GENERIC_CLASS_LOADER_NAME = "weblogic.utils.classloaders.GenericClassLoader"; |
||||
|
||||
private static final String CLASS_PRE_PROCESSOR_NAME = "weblogic.utils.classloaders.ClassPreProcessor"; |
||||
|
||||
|
||||
private final ClassLoader classLoader; |
||||
|
||||
private final Class<?> wlPreProcessorClass; |
||||
|
||||
private final Method addPreProcessorMethod; |
||||
|
||||
private final Method getClassFinderMethod; |
||||
|
||||
private final Method getParentMethod; |
||||
|
||||
private final Constructor<?> wlGenericClassLoaderConstructor; |
||||
|
||||
|
||||
public WebLogicClassLoaderAdapter(ClassLoader classLoader) { |
||||
Class<?> wlGenericClassLoaderClass; |
||||
try { |
||||
wlGenericClassLoaderClass = classLoader.loadClass(GENERIC_CLASS_LOADER_NAME); |
||||
this.wlPreProcessorClass = classLoader.loadClass(CLASS_PRE_PROCESSOR_NAME); |
||||
this.addPreProcessorMethod = classLoader.getClass().getMethod( |
||||
"addInstanceClassPreProcessor", this.wlPreProcessorClass); |
||||
this.getClassFinderMethod = classLoader.getClass().getMethod("getClassFinder"); |
||||
this.getParentMethod = classLoader.getClass().getMethod("getParent"); |
||||
this.wlGenericClassLoaderConstructor = wlGenericClassLoaderClass.getConstructor( |
||||
this.getClassFinderMethod.getReturnType(), ClassLoader.class); |
||||
} |
||||
catch (Throwable ex) { |
||||
throw new IllegalStateException( |
||||
"Could not initialize WebLogic LoadTimeWeaver because WebLogic 10 API classes are not available", ex); |
||||
} |
||||
|
||||
if (!wlGenericClassLoaderClass.isInstance(classLoader)) { |
||||
throw new IllegalArgumentException( |
||||
"ClassLoader must be an instance of [" + wlGenericClassLoaderClass.getName() + "]: " + classLoader); |
||||
} |
||||
this.classLoader = classLoader; |
||||
} |
||||
|
||||
|
||||
public void addTransformer(ClassFileTransformer transformer) { |
||||
Assert.notNull(transformer, "ClassFileTransformer must not be null"); |
||||
try { |
||||
InvocationHandler adapter = new WebLogicClassPreProcessorAdapter(transformer, this.classLoader); |
||||
Object adapterInstance = Proxy.newProxyInstance(this.wlPreProcessorClass.getClassLoader(), |
||||
new Class<?>[] {this.wlPreProcessorClass}, adapter); |
||||
this.addPreProcessorMethod.invoke(this.classLoader, adapterInstance); |
||||
} |
||||
catch (InvocationTargetException ex) { |
||||
throw new IllegalStateException("WebLogic addInstanceClassPreProcessor method threw exception", ex.getCause()); |
||||
} |
||||
catch (Throwable ex) { |
||||
throw new IllegalStateException("Could not invoke WebLogic addInstanceClassPreProcessor method", ex); |
||||
} |
||||
} |
||||
|
||||
public ClassLoader getClassLoader() { |
||||
return this.classLoader; |
||||
} |
||||
|
||||
public ClassLoader getThrowawayClassLoader() { |
||||
try { |
||||
Object classFinder = this.getClassFinderMethod.invoke(this.classLoader); |
||||
Object parent = this.getParentMethod.invoke(this.classLoader); |
||||
// arguments for 'clone'-like method
|
||||
return (ClassLoader) this.wlGenericClassLoaderConstructor.newInstance(classFinder, parent); |
||||
} |
||||
catch (InvocationTargetException ex) { |
||||
throw new IllegalStateException("WebLogic GenericClassLoader constructor failed", ex.getCause()); |
||||
} |
||||
catch (Throwable ex) { |
||||
throw new IllegalStateException("Could not construct WebLogic GenericClassLoader", ex); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,97 +0,0 @@
@@ -1,97 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2017 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 |
||||
* |
||||
* https://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.instrument.classloading.weblogic; |
||||
|
||||
import java.lang.instrument.ClassFileTransformer; |
||||
import java.lang.instrument.IllegalClassFormatException; |
||||
import java.lang.reflect.InvocationHandler; |
||||
import java.lang.reflect.Method; |
||||
import java.util.Hashtable; |
||||
|
||||
import org.springframework.lang.Nullable; |
||||
|
||||
/** |
||||
* Adapter that implements WebLogic ClassPreProcessor interface, delegating to a |
||||
* standard JDK {@link ClassFileTransformer} underneath. |
||||
* |
||||
* <p>To avoid compile time checks again the vendor API, a dynamic proxy is |
||||
* being used. |
||||
* |
||||
* @author Costin Leau |
||||
* @author Juergen Hoeller |
||||
* @since 2.5 |
||||
*/ |
||||
class WebLogicClassPreProcessorAdapter implements InvocationHandler { |
||||
|
||||
private final ClassFileTransformer transformer; |
||||
|
||||
private final ClassLoader loader; |
||||
|
||||
|
||||
/** |
||||
* Construct a new {@link WebLogicClassPreProcessorAdapter}. |
||||
*/ |
||||
public WebLogicClassPreProcessorAdapter(ClassFileTransformer transformer, ClassLoader loader) { |
||||
this.transformer = transformer; |
||||
this.loader = loader; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
@Nullable |
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
||||
String name = method.getName(); |
||||
if ("equals".equals(name)) { |
||||
return (proxy == args[0]); |
||||
} |
||||
else if ("hashCode".equals(name)) { |
||||
return hashCode(); |
||||
} |
||||
else if ("toString".equals(name)) { |
||||
return toString(); |
||||
} |
||||
else if ("initialize".equals(name)) { |
||||
initialize((Hashtable<?, ?>) args[0]); |
||||
return null; |
||||
} |
||||
else if ("preProcess".equals(name)) { |
||||
return preProcess((String) args[0], (byte[]) args[1]); |
||||
} |
||||
else { |
||||
throw new IllegalArgumentException("Unknown method: " + method); |
||||
} |
||||
} |
||||
|
||||
public void initialize(Hashtable<?, ?> params) { |
||||
} |
||||
|
||||
public byte[] preProcess(String className, byte[] classBytes) { |
||||
try { |
||||
byte[] result = this.transformer.transform(this.loader, className, null, null, classBytes); |
||||
return (result != null ? result : classBytes); |
||||
} |
||||
catch (IllegalClassFormatException ex) { |
||||
throw new IllegalStateException("Cannot transform due to illegal class format", ex); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getName() + " for transformer: " + this.transformer; |
||||
} |
||||
|
||||
} |
||||
@ -1,78 +0,0 @@
@@ -1,78 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2016 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 |
||||
* |
||||
* https://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.instrument.classloading.weblogic; |
||||
|
||||
import java.lang.instrument.ClassFileTransformer; |
||||
|
||||
import org.springframework.core.OverridingClassLoader; |
||||
import org.springframework.instrument.classloading.LoadTimeWeaver; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ClassUtils; |
||||
|
||||
/** |
||||
* {@link LoadTimeWeaver} implementation for WebLogic's instrumentable |
||||
* ClassLoader. |
||||
* |
||||
* <p><b>NOTE:</b> Requires BEA WebLogic version 10 or higher. |
||||
* |
||||
* @author Costin Leau |
||||
* @author Juergen Hoeller |
||||
* @since 2.5 |
||||
*/ |
||||
public class WebLogicLoadTimeWeaver implements LoadTimeWeaver { |
||||
|
||||
private final WebLogicClassLoaderAdapter classLoader; |
||||
|
||||
|
||||
/** |
||||
* Creates a new instance of the {@link WebLogicLoadTimeWeaver} class using |
||||
* the default {@link ClassLoader class loader}. |
||||
* @see org.springframework.util.ClassUtils#getDefaultClassLoader() |
||||
*/ |
||||
public WebLogicLoadTimeWeaver() { |
||||
this(ClassUtils.getDefaultClassLoader()); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new instance of the {@link WebLogicLoadTimeWeaver} class using |
||||
* the supplied {@link ClassLoader}. |
||||
* @param classLoader the {@code ClassLoader} to delegate to for weaving |
||||
*/ |
||||
public WebLogicLoadTimeWeaver(@Nullable ClassLoader classLoader) { |
||||
Assert.notNull(classLoader, "ClassLoader must not be null"); |
||||
this.classLoader = new WebLogicClassLoaderAdapter(classLoader); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void addTransformer(ClassFileTransformer transformer) { |
||||
this.classLoader.addTransformer(transformer); |
||||
} |
||||
|
||||
@Override |
||||
public ClassLoader getInstrumentableClassLoader() { |
||||
return this.classLoader.getClassLoader(); |
||||
} |
||||
|
||||
@Override |
||||
public ClassLoader getThrowawayClassLoader() { |
||||
return new OverridingClassLoader(this.classLoader.getClassLoader(), |
||||
this.classLoader.getThrowawayClassLoader()); |
||||
} |
||||
|
||||
} |
||||
@ -1,9 +0,0 @@
@@ -1,9 +0,0 @@
|
||||
/** |
||||
* Support for class instrumentation on BEA WebLogic 10+. |
||||
*/ |
||||
@NonNullApi |
||||
@NonNullFields |
||||
package org.springframework.instrument.classloading.weblogic; |
||||
|
||||
import org.springframework.lang.NonNullApi; |
||||
import org.springframework.lang.NonNullFields; |
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue