org.broadleafcommerce.common.extensibility.context.merge.AbstractMergeBeanPostProcessor Maven / Gradle / Ivy
Show all versions of broadleaf-common Show documentation
/*
* #%L
* BroadleafCommerce Common Libraries
* %%
* Copyright (C) 2009 - 2013 Broadleaf Commerce
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package org.broadleafcommerce.common.extensibility.context.merge;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ListFactoryBean;
import org.springframework.beans.factory.config.MapFactoryBean;
import org.springframework.beans.factory.config.SetFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
* Contains useful processing code for merge bean post processors. The BeanPostProcessor instances can
* be used to merge additional collection members into collections declared elsewhere. In effect, this allows
* an implementer to only declare the collection members they're currently interested in cause those members
* to be merged into a larger, pre-existing list. This is more desirable than a traditional, comprehensive
* override that would require re-declaring the original bean and all of its members in addition to the current
* members being considered.
*
*
* This code demonstrates using one of the concrete implementations, {@link LateStageMergeBeanPostProcessor}. The
* basic usage pattern is to specify the id of the collection you want to merge (collectionRef) and the id
* of the pre-existing, target collection (targetRef) that should receive the merge collection. The collection
* can be represented using ListFactoryBean, SetFactoryBean or MapFactoryBean.
*
*
* {@code
*
*
*
*
*
*
*
*
*
*
* }
*
*
* @see LateStageMergeBeanPostProcessor
* @see EarlyStageMergeBeanPostProcessor
* @author Jeff Fischer
*/
public abstract class AbstractMergeBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
protected static final Log LOG = LogFactory.getLog(AbstractMergeBeanPostProcessor.class);
protected String collectionRef;
protected String targetRef;
protected Placement placement = Placement.APPEND;
protected int position;
protected ApplicationContext applicationContext;
protected MergeBeanStatusProvider statusProvider;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (statusProvider != null && !statusProvider.isProcessingEnabled(bean, beanName, applicationContext)) {
if (LOG.isTraceEnabled()) {
LOG.trace(String.format("Not performing post-processing on targetRef [%s] because the registered " +
"status provider [%s] returned false", targetRef, statusProvider.getClass().getSimpleName()));
}
return bean;
}
if (beanName.equals(targetRef)) {
Object mergeCollection = applicationContext.getBean(collectionRef);
if (bean instanceof ListFactoryBean || bean instanceof List) {
try {
List mergeList = (List) mergeCollection;
List sourceList;
if (bean instanceof ListFactoryBean) {
Field field = ListFactoryBean.class.getDeclaredField("sourceList");
field.setAccessible(true);
sourceList = (List) field.get(bean);
} else {
sourceList = (List) bean;
}
switch (placement) {
case APPEND:
sourceList.addAll(mergeList);
break;
case PREPEND:
sourceList.addAll(0, mergeList);
break;
case SPECIFIC:
sourceList.addAll(position, mergeList);
break;
}
} catch (Exception e) {
throw new BeanCreationException(e.getMessage());
}
} else if (bean instanceof SetFactoryBean || bean instanceof Set) {
try {
Set mergeSet = (Set) mergeCollection;
Set sourceSet;
if (bean instanceof SetFactoryBean) {
Field field = SetFactoryBean.class.getDeclaredField("sourceSet");
field.setAccessible(true);
sourceSet = (Set) field.get(bean);
} else {
sourceSet = (Set)bean;
}
List tempList = new ArrayList(sourceSet);
switch (placement) {
case APPEND:
tempList.addAll(mergeSet);
break;
case PREPEND:
tempList.addAll(0, mergeSet);
break;
case SPECIFIC:
tempList.addAll(position, mergeSet);
break;
}
sourceSet.clear();
sourceSet.addAll(tempList);
} catch (Exception e) {
throw new BeanCreationException(e.getMessage());
}
} else if (bean instanceof MapFactoryBean || bean instanceof Map) {
try {
Map mergeMap = (Map) mergeCollection;
Map sourceMap;
if (bean instanceof MapFactoryBean) {
Field field = MapFactoryBean.class.getDeclaredField("sourceMap");
field.setAccessible(true);
sourceMap = (Map) field.get(bean);
} else {
sourceMap = (Map) bean;
}
LinkedHashMap tempMap = new LinkedHashMap();
switch (placement) {
case APPEND:
tempMap.putAll(sourceMap);
tempMap.putAll(mergeMap);
break;
case PREPEND:
tempMap.putAll(mergeMap);
tempMap.putAll(sourceMap);
break;
case SPECIFIC:
boolean added = false;
int j = 0;
for (Object key : sourceMap.keySet()) {
if (j == position) {
tempMap.putAll(mergeMap);
added = true;
}
tempMap.put(key, sourceMap.get(key));
j++;
}
if (!added) {
tempMap.putAll(mergeMap);
}
break;
}
sourceMap.clear();
sourceMap.putAll(tempMap);
} catch (Exception e) {
throw new BeanCreationException(e.getMessage());
}
} else {
throw new IllegalArgumentException("Bean (" + beanName + ") is specified as a merge target, " +
"but is not" +
" of type ListFactoryBean, SetFactoryBean or MapFactoryBean");
}
}
return bean;
}
/**
* Retrieve the id of the collection to be merged
*
* @return the id of the collection to be merged
*/
public String getCollectionRef() {
return collectionRef;
}
/**
* Set the id of the collection to be merged
*
* @param collectionRef the id of the collection to be merged
*/
public void setCollectionRef(String collectionRef) {
this.collectionRef = collectionRef;
}
/**
* Retrieve the id of the collection to receive the merge
*
* @return the id of the collection receiving the merge
*/
public String getTargetRef() {
return targetRef;
}
/**
* Set the id of the collection to receive the merge
*
* @param targetRef the id of the collection receiving the merge
*/
public void setTargetRef(String targetRef) {
this.targetRef = targetRef;
}
/**
* The position in the target collection to place the merge. This can be at the beginning,
* end or at an explicit position.
*
* @return the position in the target collection to place the merge
*/
public Placement getPlacement() {
return placement;
}
/**
* The position in the target collection to place the merge. This can be at the beginning,
* end or at an explicit position.
*
* @param placement the position in the target collection to place the merge
*/
public void setPlacement(Placement placement) {
this.placement = placement;
}
/**
* If a placement of type Placement.SPECIFIC is used, then this is the integer position in the target
* target collection at which the merge will be performed.
*
* @return the specific position in the target collection
*/
public int getPosition() {
return position;
}
/**
* If a placement of type Placement.SPECIFIC is used, then this is the integer position in the target
* target collection at which the merge will be performed.
*
* @param position the specific position in the target collection
*/
public void setPosition(int position) {
this.position = position;
}
/**
* Gets the status provider that is configured for this post processor
*
* @return the MergeStatusBeanProvider
*/
public MergeBeanStatusProvider getStatusProvider() {
return statusProvider;
}
/**
* Sets the MergeBeanStatusProvider, which controls whether or not this post processor is activated.
* If no statusProvider is set, then we will always execute.
*
* @param statusProvider
*/
public void setStatusProvider(MergeBeanStatusProvider statusProvider) {
this.statusProvider = statusProvider;
}
}