org.archive.spring.Sheet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of heritrix-commons Show documentation
Show all versions of heritrix-commons Show documentation
The Archive Commons Code Libraries project contains general Java utility
libraries, as used by the Heritrix crawler and other projects.
The newest version!
/*
* This file is part of the Heritrix web crawler (crawler.archive.org).
*
* Licensed to the Internet Archive (IA) by one or more individual
* contributors.
*
* The IA licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.archive.spring;
import java.beans.PropertyChangeEvent;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
/**
* Collection of overrides: alternative values for object properties
* that should apply in some contexts. The target is specified as an
* arbitrarily-long property-path, a string describing how to access
* the property starting from a beanName in a BeanFactory.
*
* Once a Sheet has all its mappings, and all beans that could be
* affected by its mappings have been instantiated, the sheet must
* be 'primed' with respect to the bean factory. This step lets
* every target of the overlay values know the full bean-path(s)
* that it should check for overlays. (Otherwise, beans -- especially
* unnamed inner beans -- may not know the full-paths that lead to
* their properties.) Also, this step catches in advance type
* mismatches, or attempts to overlay non-overlayable properties.
*
*/
public class Sheet implements BeanFactoryAware, BeanNameAware {
@SuppressWarnings("unused")
private static final long serialVersionUID = 9129011082185864377L;
/**
* unique name of this Sheet; if Sheet has a beanName from original
* configuration, that is always the name -- but the name might
* also be another string, in the case of Sheets added after
* initial container wiring
*/
protected String name;
protected BeanFactory beanFactory;
/** map of full property-paths (from BeanFactory to individual
* property) and their changed value when this Sheet of overrides
* is in effect
*/
protected Map map = new ConcurrentHashMap();
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public void setBeanName(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
/**
* Return map of full bean-path (starting with a target bean-name)
* to the alternate value for that targeted property
*/
public Map getMap() {
return map;
}
/**
* Set map of property full bean-path (starting with a target
* bean-name) to alternate values. Note: provided map is copied
* into a local concurrent map, rather than used directly.
* @param m
*/
@Required
public void setMap(Map m) {
this.map.clear();
this.map.putAll(m);
}
/**
* Ensure any properties targeted by this Sheet know to
* check the right property paths for overrides at lookup time,
* and that the override values are compatible types for their
* destination properties.
*
* Should be done as soon as all possible targets are
* constructed (ApplicationListener ContextRefreshedEvent)
*
* TODO: consider if an 'un-priming' also needs to occur to
* prevent confusing side-effects.
* TODO: consider if priming should move to another class
*/
public void prime() {
for (String fullpath : map.keySet()) {
int lastDot = fullpath.lastIndexOf(".");
String beanPath = fullpath.substring(0,lastDot);
String terminalProp = fullpath.substring(lastDot+1);
Object value = map.get(fullpath);
int i = beanPath.indexOf(".");
Object bean;
HasKeyedProperties hkp;
if (i < 0) {
bean = beanFactory.getBean(beanPath);
} else {
String beanName = beanPath.substring(0,i);
String propPath = beanPath.substring(i+1);
BeanWrapperImpl wrapper = new BeanWrapperImpl(beanFactory.getBean(beanName));
bean = wrapper.getPropertyValue(propPath);
}
try {
hkp = (HasKeyedProperties) bean;
} catch (ClassCastException cce) {
// targeted bean has no overridable properties
throw new TypeMismatchException(bean,HasKeyedProperties.class,cce);
}
// install knowledge of this path
hkp.getKeyedProperties().addExternalPath(beanPath);
// verify type-compatibility
BeanWrapperImpl wrapper = new BeanWrapperImpl(hkp);
Class> requiredType = wrapper.getPropertyType(terminalProp);
try {
// convert for destination type
map.put(fullpath, wrapper.convertForProperty(value,terminalProp));
} catch(TypeMismatchException tme) {
TypeMismatchException tme2 =
new TypeMismatchException(
new PropertyChangeEvent(
hkp,
fullpath,
wrapper.getPropertyValue(terminalProp),
value), requiredType, tme);
throw tme2;
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy