Jakarta EE 9 migration
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-27424
This commit is contained in:
@@ -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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/**
|
||||
* 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;
|
||||
@@ -104,7 +104,7 @@ public class MailSendException extends MailException {
|
||||
* be available after serialization as well.
|
||||
* @return the Map of failed messages as keys and thrown exceptions as values
|
||||
* @see SimpleMailMessage
|
||||
* @see javax.mail.internet.MimeMessage
|
||||
* @see jakarta.mail.internet.MimeMessage
|
||||
*/
|
||||
public final Map<Object, Exception> getFailedMessages() {
|
||||
return this.failedMessages;
|
||||
|
||||
@@ -20,8 +20,8 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.activation.MimetypesFileTypeMap;
|
||||
import jakarta.activation.FileTypeMap;
|
||||
import jakarta.activation.MimetypesFileTypeMap;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
@@ -58,7 +58,7 @@ import org.springframework.lang.Nullable;
|
||||
* @since 1.2
|
||||
* @see #setMappingLocation
|
||||
* @see #setMappings
|
||||
* @see javax.activation.MimetypesFileTypeMap
|
||||
* @see jakarta.activation.MimetypesFileTypeMap
|
||||
*/
|
||||
public class ConfigurableMimeFileTypeMap extends FileTypeMap implements InitializingBean {
|
||||
|
||||
@@ -140,8 +140,8 @@ public class ConfigurableMimeFileTypeMap extends FileTypeMap implements Initiali
|
||||
* @param mappings an array of MIME type mapping lines (can be {@code null})
|
||||
* @return the compiled FileTypeMap
|
||||
* @throws IOException if resource access failed
|
||||
* @see javax.activation.MimetypesFileTypeMap#MimetypesFileTypeMap(java.io.InputStream)
|
||||
* @see javax.activation.MimetypesFileTypeMap#addMimeTypes(String)
|
||||
* @see jakarta.activation.MimetypesFileTypeMap#MimetypesFileTypeMap(java.io.InputStream)
|
||||
* @see jakarta.activation.MimetypesFileTypeMap#addMimeTypes(String)
|
||||
*/
|
||||
protected FileTypeMap createFileTypeMap(@Nullable Resource mappingLocation, @Nullable String[] mappings) throws IOException {
|
||||
MimetypesFileTypeMap fileTypeMap = null;
|
||||
|
||||
@@ -18,8 +18,8 @@ package org.springframework.mail.javamail;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2.3
|
||||
* @see javax.mail.internet.InternetAddress
|
||||
* @see jakarta.mail.internet.InternetAddress
|
||||
*/
|
||||
public class InternetAddressEditor extends PropertyEditorSupport {
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.mail.javamail;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.mail.MailException;
|
||||
import org.springframework.mail.MailSender;
|
||||
@@ -40,7 +40,7 @@ import org.springframework.mail.MailSender;
|
||||
* mechanism, possibly using a {@link MimeMessageHelper} for populating the message.
|
||||
* See {@link MimeMessageHelper MimeMessageHelper's javadoc} for an example.
|
||||
*
|
||||
* <p>The entire JavaMail {@link javax.mail.Session} management is abstracted
|
||||
* <p>The entire JavaMail {@link jakarta.mail.Session} management is abstracted
|
||||
* by the JavaMailSender. Client code should not deal with a Session in any way,
|
||||
* rather leave the entire JavaMail configuration and resource handling to the
|
||||
* JavaMailSender implementation. This also increases testability.
|
||||
@@ -54,8 +54,8 @@ import org.springframework.mail.MailSender;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 07.10.2003
|
||||
* @see javax.mail.internet.MimeMessage
|
||||
* @see javax.mail.Session
|
||||
* @see jakarta.mail.internet.MimeMessage
|
||||
* @see jakarta.mail.Session
|
||||
* @see JavaMailSenderImpl
|
||||
* @see MimeMessagePreparator
|
||||
* @see MimeMessageHelper
|
||||
|
||||
@@ -24,14 +24,14 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.mail.Address;
|
||||
import javax.mail.AuthenticationFailedException;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.NoSuchProviderException;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.Transport;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import jakarta.activation.FileTypeMap;
|
||||
import jakarta.mail.Address;
|
||||
import jakarta.mail.AuthenticationFailedException;
|
||||
import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.NoSuchProviderException;
|
||||
import jakarta.mail.Session;
|
||||
import jakarta.mail.Transport;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.mail.MailAuthenticationException;
|
||||
@@ -49,7 +49,7 @@ import org.springframework.util.Assert;
|
||||
* plain {@link org.springframework.mail.MailSender} implementation.
|
||||
*
|
||||
* <p>Allows for defining all settings locally as bean properties.
|
||||
* Alternatively, a pre-configured JavaMail {@link javax.mail.Session} can be
|
||||
* Alternatively, a pre-configured JavaMail {@link jakarta.mail.Session} can be
|
||||
* specified, possibly pulled from an application server's JNDI environment.
|
||||
*
|
||||
* <p>Non-default properties in this object will always override the settings
|
||||
@@ -59,8 +59,8 @@ import org.springframework.util.Assert;
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
* @since 10.09.2003
|
||||
* @see javax.mail.internet.MimeMessage
|
||||
* @see javax.mail.Session
|
||||
* @see jakarta.mail.internet.MimeMessage
|
||||
* @see jakarta.mail.Session
|
||||
* @see #setSession
|
||||
* @see #setJavaMailProperties
|
||||
* @see #setHost
|
||||
@@ -523,7 +523,7 @@ public class JavaMailSenderImpl implements JavaMailSender {
|
||||
* Obtain a Transport object from the given JavaMail Session,
|
||||
* using the configured protocol.
|
||||
* <p>Can be overridden in subclasses, e.g. to return a mock Transport object.
|
||||
* @see javax.mail.Session#getTransport(String)
|
||||
* @see jakarta.mail.Session#getTransport(String)
|
||||
* @see #getSession()
|
||||
* @see #getProtocol()
|
||||
*/
|
||||
|
||||
@@ -18,8 +18,8 @@ package org.springframework.mail.javamail;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.mail.MailMessage;
|
||||
import org.springframework.mail.MailParseException;
|
||||
@@ -35,7 +35,7 @@ import org.springframework.mail.MailParseException;
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.5
|
||||
* @see MimeMessageHelper
|
||||
* @see javax.mail.internet.MimeMessage
|
||||
* @see jakarta.mail.internet.MimeMessage
|
||||
*/
|
||||
public class MimeMailMessage implements MailMessage {
|
||||
|
||||
|
||||
@@ -23,20 +23,20 @@ import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.activation.DataHandler;
|
||||
import javax.activation.DataSource;
|
||||
import javax.activation.FileDataSource;
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.mail.BodyPart;
|
||||
import javax.mail.Message;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.mail.internet.MimeBodyPart;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import javax.mail.internet.MimeMultipart;
|
||||
import javax.mail.internet.MimePart;
|
||||
import javax.mail.internet.MimeUtility;
|
||||
import jakarta.activation.DataHandler;
|
||||
import jakarta.activation.DataSource;
|
||||
import jakarta.activation.FileDataSource;
|
||||
import jakarta.activation.FileTypeMap;
|
||||
import jakarta.mail.BodyPart;
|
||||
import jakarta.mail.Message;
|
||||
import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.mail.internet.MimeBodyPart;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
import jakarta.mail.internet.MimeMultipart;
|
||||
import jakarta.mail.internet.MimePart;
|
||||
import jakarta.mail.internet.MimeUtility;
|
||||
|
||||
import org.springframework.core.io.InputStreamSource;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -44,7 +44,7 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Helper class for populating a {@link javax.mail.internet.MimeMessage}.
|
||||
* Helper class for populating a {@link jakarta.mail.internet.MimeMessage}.
|
||||
*
|
||||
* <p>Mirrors the simple setters of {@link org.springframework.mail.SimpleMailMessage},
|
||||
* directly applying the values to the underlying MimeMessage. Allows for defining
|
||||
@@ -186,8 +186,8 @@ public class MimeMessageHelper {
|
||||
* the passed-in MimeMessage object, if carried there. Else,
|
||||
* JavaMail's default encoding will be used.
|
||||
* @param mimeMessage the mime message to work on
|
||||
* @see #MimeMessageHelper(javax.mail.internet.MimeMessage, boolean)
|
||||
* @see #getDefaultEncoding(javax.mail.internet.MimeMessage)
|
||||
* @see #MimeMessageHelper(jakarta.mail.internet.MimeMessage, boolean)
|
||||
* @see #getDefaultEncoding(jakarta.mail.internet.MimeMessage)
|
||||
* @see JavaMailSenderImpl#setDefaultEncoding
|
||||
*/
|
||||
public MimeMessageHelper(MimeMessage mimeMessage) {
|
||||
@@ -200,7 +200,7 @@ public class MimeMessageHelper {
|
||||
* i.e. no alternative texts and no inline elements or attachments).
|
||||
* @param mimeMessage the mime message to work on
|
||||
* @param encoding the character encoding to use for the message
|
||||
* @see #MimeMessageHelper(javax.mail.internet.MimeMessage, boolean)
|
||||
* @see #MimeMessageHelper(jakarta.mail.internet.MimeMessage, boolean)
|
||||
*/
|
||||
public MimeMessageHelper(MimeMessage mimeMessage, @Nullable String encoding) {
|
||||
this.mimeMessage = mimeMessage;
|
||||
@@ -223,8 +223,8 @@ public class MimeMessageHelper {
|
||||
* supports alternative texts, inline elements and attachments
|
||||
* (corresponds to MULTIPART_MODE_MIXED_RELATED)
|
||||
* @throws MessagingException if multipart creation failed
|
||||
* @see #MimeMessageHelper(javax.mail.internet.MimeMessage, int)
|
||||
* @see #getDefaultEncoding(javax.mail.internet.MimeMessage)
|
||||
* @see #MimeMessageHelper(jakarta.mail.internet.MimeMessage, int)
|
||||
* @see #getDefaultEncoding(jakarta.mail.internet.MimeMessage)
|
||||
* @see JavaMailSenderImpl#setDefaultEncoding
|
||||
*/
|
||||
public MimeMessageHelper(MimeMessage mimeMessage, boolean multipart) throws MessagingException {
|
||||
@@ -244,7 +244,7 @@ public class MimeMessageHelper {
|
||||
* (corresponds to MULTIPART_MODE_MIXED_RELATED)
|
||||
* @param encoding the character encoding to use for the message
|
||||
* @throws MessagingException if multipart creation failed
|
||||
* @see #MimeMessageHelper(javax.mail.internet.MimeMessage, int, String)
|
||||
* @see #MimeMessageHelper(jakarta.mail.internet.MimeMessage, int, String)
|
||||
*/
|
||||
public MimeMessageHelper(MimeMessage mimeMessage, boolean multipart, @Nullable String encoding)
|
||||
throws MessagingException {
|
||||
@@ -267,7 +267,7 @@ public class MimeMessageHelper {
|
||||
* @see #MULTIPART_MODE_MIXED
|
||||
* @see #MULTIPART_MODE_RELATED
|
||||
* @see #MULTIPART_MODE_MIXED_RELATED
|
||||
* @see #getDefaultEncoding(javax.mail.internet.MimeMessage)
|
||||
* @see #getDefaultEncoding(jakarta.mail.internet.MimeMessage)
|
||||
* @see JavaMailSenderImpl#setDefaultEncoding
|
||||
*/
|
||||
public MimeMessageHelper(MimeMessage mimeMessage, int multipartMode) throws MessagingException {
|
||||
@@ -388,7 +388,7 @@ public class MimeMessageHelper {
|
||||
* @throws IllegalStateException if this helper is not in multipart mode
|
||||
* @see #isMultipart
|
||||
* @see #getMimeMessage
|
||||
* @see javax.mail.internet.MimeMultipart#addBodyPart
|
||||
* @see jakarta.mail.internet.MimeMultipart#addBodyPart
|
||||
*/
|
||||
public final MimeMultipart getRootMimeMultipart() throws IllegalStateException {
|
||||
if (this.rootMimeMultipart == null) {
|
||||
@@ -407,7 +407,7 @@ public class MimeMessageHelper {
|
||||
* @throws IllegalStateException if this helper is not in multipart mode
|
||||
* @see #isMultipart
|
||||
* @see #getRootMimeMultipart
|
||||
* @see javax.mail.internet.MimeMultipart#addBodyPart
|
||||
* @see jakarta.mail.internet.MimeMultipart#addBodyPart
|
||||
*/
|
||||
public final MimeMultipart getMimeMultipart() throws IllegalStateException {
|
||||
if (this.mimeMultipart == null) {
|
||||
@@ -469,9 +469,9 @@ public class MimeMessageHelper {
|
||||
* {@code FileTypeMap} instance else.
|
||||
* @see #addInline
|
||||
* @see #addAttachment
|
||||
* @see #getDefaultFileTypeMap(javax.mail.internet.MimeMessage)
|
||||
* @see #getDefaultFileTypeMap(jakarta.mail.internet.MimeMessage)
|
||||
* @see JavaMailSenderImpl#setDefaultFileTypeMap
|
||||
* @see javax.activation.FileTypeMap#getDefaultFileTypeMap
|
||||
* @see jakarta.activation.FileTypeMap#getDefaultFileTypeMap
|
||||
* @see ConfigurableMimeFileTypeMap
|
||||
*/
|
||||
public void setFileTypeMap(@Nullable FileTypeMap fileTypeMap) {
|
||||
@@ -538,7 +538,7 @@ public class MimeMessageHelper {
|
||||
* @param address the address to validate
|
||||
* @throws AddressException if validation failed
|
||||
* @see #isValidateAddresses()
|
||||
* @see javax.mail.internet.InternetAddress#validate()
|
||||
* @see jakarta.mail.internet.InternetAddress#validate()
|
||||
*/
|
||||
protected void validateAddress(InternetAddress address) throws AddressException {
|
||||
if (isValidateAddresses()) {
|
||||
@@ -889,7 +889,7 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an inline element to the MimeMessage, taking the content from a
|
||||
* {@code javax.activation.DataSource}.
|
||||
* {@code jakarta.activation.DataSource}.
|
||||
* <p>Note that the InputStream returned by the DataSource implementation
|
||||
* needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
|
||||
* {@code getInputStream()} multiple times.
|
||||
@@ -898,7 +898,7 @@ public class MimeMessageHelper {
|
||||
* @param contentId the content ID to use. Will end up as "Content-ID" header
|
||||
* in the body part, surrounded by angle brackets: e.g. "myId" -> "<myId>".
|
||||
* Can be referenced in HTML source via src="cid:myId" expressions.
|
||||
* @param dataSource the {@code javax.activation.DataSource} to take
|
||||
* @param dataSource the {@code jakarta.activation.DataSource} to take
|
||||
* the content from, determining the InputStream and the content type
|
||||
* @throws MessagingException in case of errors
|
||||
* @see #addInline(String, java.io.File)
|
||||
@@ -929,7 +929,7 @@ public class MimeMessageHelper {
|
||||
* @throws MessagingException in case of errors
|
||||
* @see #setText
|
||||
* @see #addInline(String, org.springframework.core.io.Resource)
|
||||
* @see #addInline(String, javax.activation.DataSource)
|
||||
* @see #addInline(String, jakarta.activation.DataSource)
|
||||
*/
|
||||
public void addInline(String contentId, File file) throws MessagingException {
|
||||
Assert.notNull(file, "File must not be null");
|
||||
@@ -956,7 +956,7 @@ public class MimeMessageHelper {
|
||||
* @throws MessagingException in case of errors
|
||||
* @see #setText
|
||||
* @see #addInline(String, java.io.File)
|
||||
* @see #addInline(String, javax.activation.DataSource)
|
||||
* @see #addInline(String, jakarta.activation.DataSource)
|
||||
*/
|
||||
public void addInline(String contentId, Resource resource) throws MessagingException {
|
||||
Assert.notNull(resource, "Resource must not be null");
|
||||
@@ -984,7 +984,7 @@ public class MimeMessageHelper {
|
||||
* @see #setText
|
||||
* @see #getFileTypeMap
|
||||
* @see #addInline(String, org.springframework.core.io.Resource)
|
||||
* @see #addInline(String, javax.activation.DataSource)
|
||||
* @see #addInline(String, jakarta.activation.DataSource)
|
||||
*/
|
||||
public void addInline(String contentId, InputStreamSource inputStreamSource, String contentType)
|
||||
throws MessagingException {
|
||||
@@ -1001,13 +1001,13 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an attachment to the MimeMessage, taking the content from a
|
||||
* {@code javax.activation.DataSource}.
|
||||
* {@code jakarta.activation.DataSource}.
|
||||
* <p>Note that the InputStream returned by the DataSource implementation
|
||||
* needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
|
||||
* {@code getInputStream()} multiple times.
|
||||
* @param attachmentFilename the name of the attachment as it will
|
||||
* appear in the mail (the content type will be determined by this)
|
||||
* @param dataSource the {@code javax.activation.DataSource} to take
|
||||
* @param dataSource the {@code jakarta.activation.DataSource} to take
|
||||
* the content from, determining the InputStream and the content type
|
||||
* @throws MessagingException in case of errors
|
||||
* @see #addAttachment(String, org.springframework.core.io.InputStreamSource)
|
||||
@@ -1040,7 +1040,7 @@ public class MimeMessageHelper {
|
||||
* @param file the File resource to take the content from
|
||||
* @throws MessagingException in case of errors
|
||||
* @see #addAttachment(String, org.springframework.core.io.InputStreamSource)
|
||||
* @see #addAttachment(String, javax.activation.DataSource)
|
||||
* @see #addAttachment(String, jakarta.activation.DataSource)
|
||||
*/
|
||||
public void addAttachment(String attachmentFilename, File file) throws MessagingException {
|
||||
Assert.notNull(file, "File must not be null");
|
||||
@@ -1064,7 +1064,7 @@ public class MimeMessageHelper {
|
||||
* (all of Spring's Resource implementations can be passed in here)
|
||||
* @throws MessagingException in case of errors
|
||||
* @see #addAttachment(String, java.io.File)
|
||||
* @see #addAttachment(String, javax.activation.DataSource)
|
||||
* @see #addAttachment(String, jakarta.activation.DataSource)
|
||||
* @see org.springframework.core.io.Resource
|
||||
*/
|
||||
public void addAttachment(String attachmentFilename, InputStreamSource inputStreamSource)
|
||||
@@ -1087,7 +1087,7 @@ public class MimeMessageHelper {
|
||||
* @param contentType the content type to use for the element
|
||||
* @throws MessagingException in case of errors
|
||||
* @see #addAttachment(String, java.io.File)
|
||||
* @see #addAttachment(String, javax.activation.DataSource)
|
||||
* @see #addAttachment(String, jakarta.activation.DataSource)
|
||||
* @see org.springframework.core.io.Resource
|
||||
*/
|
||||
public void addAttachment(
|
||||
@@ -1121,7 +1121,7 @@ public class MimeMessageHelper {
|
||||
}
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
throw new UnsupportedOperationException("Read-only javax.activation.DataSource");
|
||||
throw new UnsupportedOperationException("Read-only jakarta.activation.DataSource");
|
||||
}
|
||||
@Override
|
||||
public String getContentType() {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.springframework.mail.javamail;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
|
||||
/**
|
||||
* Callback interface for the preparation of JavaMail MIME messages.
|
||||
@@ -42,7 +42,7 @@ public interface MimeMessagePreparator {
|
||||
/**
|
||||
* Prepare the given new MimeMessage instance.
|
||||
* @param mimeMessage the message to prepare
|
||||
* @throws javax.mail.MessagingException passing any exceptions thrown by MimeMessage
|
||||
* @throws jakarta.mail.MessagingException passing any exceptions thrown by MimeMessage
|
||||
* methods through for automatic conversion to the MailException hierarchy
|
||||
* @throws java.io.IOException passing any exceptions thrown by MimeMessage methods
|
||||
* through for automatic conversion to the MailException hierarchy
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
package org.springframework.mail.javamail;
|
||||
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import jakarta.activation.FileTypeMap;
|
||||
import jakarta.mail.Session;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -34,8 +34,8 @@ import org.springframework.lang.Nullable;
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2
|
||||
* @see JavaMailSenderImpl#createMimeMessage()
|
||||
* @see MimeMessageHelper#getDefaultEncoding(javax.mail.internet.MimeMessage)
|
||||
* @see MimeMessageHelper#getDefaultFileTypeMap(javax.mail.internet.MimeMessage)
|
||||
* @see MimeMessageHelper#getDefaultEncoding(jakarta.mail.internet.MimeMessage)
|
||||
* @see MimeMessageHelper#getDefaultFileTypeMap(jakarta.mail.internet.MimeMessage)
|
||||
*/
|
||||
class SmartMimeMessage extends MimeMessage {
|
||||
|
||||
|
||||
@@ -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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/**
|
||||
* 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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/*
|
||||
* 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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -64,7 +64,7 @@ public class JCacheEhCacheAnnotationTests extends AbstractCacheAnnotationTests {
|
||||
}
|
||||
|
||||
protected CachingProvider getCachingProvider() {
|
||||
return Caching.getCachingProvider("org.ehcache.jcache.JCacheCachingProvider");
|
||||
return Caching.getCachingProvider("org.ehcache.jsr107.EhcacheCachingProvider");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
||||
@@ -54,7 +54,7 @@ public class JCacheEhCacheApiTests extends AbstractValueAdaptingCacheTests<JCach
|
||||
}
|
||||
|
||||
protected CachingProvider getCachingProvider() {
|
||||
return Caching.getCachingProvider("org.ehcache.jcache.JCacheCachingProvider");
|
||||
return Caching.getCachingProvider("org.ehcache.jsr107.EhcacheCachingProvider");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
||||
@@ -24,18 +24,17 @@ import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.mail.Address;
|
||||
import javax.mail.Message;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.NoSuchProviderException;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.Transport;
|
||||
import javax.mail.URLName;
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import jakarta.activation.FileTypeMap;
|
||||
import jakarta.mail.Address;
|
||||
import jakarta.mail.Message;
|
||||
import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.NoSuchProviderException;
|
||||
import jakarta.mail.Session;
|
||||
import jakarta.mail.Transport;
|
||||
import jakarta.mail.URLName;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.mail.MailParseException;
|
||||
|
||||
@@ -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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/*
|
||||
* 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 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user