/*
* Copyright 2004-2015 the Seasar Foundation and the Others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.seasar.framework.container.impl;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javassist.runtime.Desc;
import javassist.util.proxy.ProxyFactory;
import javassist.util.proxy.ProxyFactory.ClassLoaderProvider;
import ognl.OgnlRuntime;
import org.seasar.framework.container.ComponentDef;
import org.seasar.framework.container.ComponentNotFoundRuntimeException;
import org.seasar.framework.container.ContainerConstants;
import org.seasar.framework.container.ContainerNotRegisteredRuntimeException;
import org.seasar.framework.container.CyclicReferenceRuntimeException;
import org.seasar.framework.container.ExternalContext;
import org.seasar.framework.container.ExternalContextComponentDefRegister;
import org.seasar.framework.container.MetaDef;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.TooManyRegistrationComponentDef;
import org.seasar.framework.container.ognl.S2ContainerPropertyAccessor;
import org.seasar.framework.container.util.MetaDefSupport;
import org.seasar.framework.container.util.S2ContainerUtil;
import org.seasar.framework.container.util.Traversal;
import org.seasar.framework.log.Logger;
import org.seasar.framework.util.CaseInsensitiveMap;
import org.seasar.framework.util.StringUtil;
/**
* {@link S2Container}の実装クラスです。
*
* @author higa
*
*/
public class S2ContainerImpl implements S2Container, ContainerConstants {
private static final Logger logger = Logger
.getLogger(S2ContainerImpl.class);
private Map componentDefMap = new HashMap();
private List componentDefList = new ArrayList();
private String namespace;
private String path;
private boolean initializeOnCreate;
private List children = new ArrayList();
private Map childPositions = new HashMap();
private List parents = new ArrayList();
private CaseInsensitiveMap descendants = new CaseInsensitiveMap();
private S2Container root;
private ExternalContext externalContext;
private ExternalContextComponentDefRegister externalContextComponentDefRegister;
private MetaDefSupport metaDefSupport = new MetaDefSupport(this);
private boolean inited = false;
private ClassLoader classLoader = null;
static {
OgnlRuntime.setPropertyAccessor(S2Container.class,
new S2ContainerPropertyAccessor());
Desc.useContextClassLoader = true;
ProxyFactory.classLoaderProvider = new ClassLoaderProvider() {
public ClassLoader get(ProxyFactory proxyFactory) {
return Thread.currentThread().getContextClassLoader();
}
};
}
/**
* {@link S2ContainerImpl}を作成します。
*/
public S2ContainerImpl() {
root = this;
register0(new SimpleComponentDef(this, CONTAINER_NAME));
classLoader = Thread.currentThread().getContextClassLoader();
}
public S2Container getRoot() {
return root;
}
public void setRoot(S2Container root) {
this.root = root != null ? root : this;
}
/**
* @see org.seasar.framework.container.S2Container#getComponent(java.lang.Object)
*/
public Object getComponent(Object componentKey) {
assertParameterIsNotNull(componentKey, "componentKey");
ComponentDef cd = S2ContainerBehavior.acquireFromGetComponent(this,
componentKey);
if (cd == null) {
return null;
}
return cd.getComponent();
}
/**
* @see org.seasar.framework.container.S2Container#getComponent(java.lang.Object)
*/
public Object[] findComponents(Object componentKey) {
assertParameterIsNotNull(componentKey, "componentKey");
ComponentDef[] componentDefs = findComponentDefs(componentKey);
return toComponentArray(componentKey, componentDefs);
}
/*
* (non-Javadoc)
*
* @see org.seasar.framework.container.S2Container#findAllComponents(java.lang.Object)
*/
public Object[] findAllComponents(Object componentKey)
throws CyclicReferenceRuntimeException {
assertParameterIsNotNull(componentKey, "componentKey");
ComponentDef[] componentDefs = findAllComponentDefs(componentKey);
return toComponentArray(componentKey, componentDefs);
}
/**
* @see org.seasar.framework.container.S2Container#findLocalComponents(java.lang.Object)
*/
public Object[] findLocalComponents(Object componentKey)
throws CyclicReferenceRuntimeException {
assertParameterIsNotNull(componentKey, "componentKey");
ComponentDef[] componentDefs = findLocalComponentDefs(componentKey);
return toComponentArray(componentKey, componentDefs);
}
/**
* コンポーネントの配列に変換します。
*
* @param componentKey
* @param componentDefs
* @return コンポーネントの配列
*/
protected Object[] toComponentArray(Object componentKey,
ComponentDef[] componentDefs) {
int length = componentDefs.length;
Object[] components = (componentKey instanceof Class) ? (Object[]) Array
.newInstance((Class) componentKey, length)
: new Object[length];
for (int i = 0; i < length; ++i) {
components[i] = componentDefs[i].getComponent();
}
return components;
}
/**
* @see org.seasar.framework.container.S2Container#injectDependency(java.lang.Object)
*/
public void injectDependency(Object outerComponent) {
injectDependency(outerComponent, outerComponent.getClass());
}
/**
* @see org.seasar.framework.container.S2Container#injectDependency(java.lang.Object,
* java.lang.Class)
*/
public void injectDependency(Object outerComponent, Class componentClass) {
assertParameterIsNotNull(outerComponent, "outerComponent");
assertParameterIsNotNull(componentClass, "componentClass");
ComponentDef cd = S2ContainerBehavior.acquireFromInjectDependency(this,
componentClass);
if (cd != null) {
cd.injectDependency(outerComponent);
}
}
/**
* @see org.seasar.framework.container.S2Container#injectDependency(java.lang.Object,
* java.lang.String)
*/
public void injectDependency(Object outerComponent, String componentName) {
assertParameterIsNotNull(outerComponent, "outerComponent");
assertParameterIsNotEmpty(componentName, "componentName");
ComponentDef cd = S2ContainerBehavior.acquireFromInjectDependency(this,
componentName);
if (cd != null) {
cd.injectDependency(outerComponent);
}
}
/**
* @see org.seasar.framework.container.S2Container#register(java.lang.Object)
*/
public void register(Object component) {
assertParameterIsNotNull(component, "component");
register(new SimpleComponentDef(component));
}
public void register(Object component, String componentName) {
assertParameterIsNotNull(component, "component");
assertParameterIsNotEmpty(componentName, "componentName");
register(new SimpleComponentDef(component, componentName));
}
/**
* @see org.seasar.framework.container.S2Container#register(java.lang.Class)
*/
public void register(Class componentClass) {
assertParameterIsNotNull(componentClass, "componentClass");
register(new ComponentDefImpl(componentClass));
}
/**
* @see org.seasar.framework.container.S2Container#register(java.lang.Class,
* java.lang.String)
*/
public void register(Class componentClass, String componentName) {
assertParameterIsNotNull(componentClass, "componentClass");
assertParameterIsNotEmpty(componentName, "componentName");
register(new ComponentDefImpl(componentClass, componentName));
}
/**
* @see org.seasar.framework.container.S2Container#register(org.seasar.framework.container.ComponentDef)
*/
public void register(ComponentDef componentDef) {
assertParameterIsNotNull(componentDef, "componentDef");
register0(componentDef);
componentDefList.add(componentDef);
}
/**
* {@link ComponentDef}を登録します。
*
* @param componentDef
*/
public void register0(ComponentDef componentDef) {
if (componentDef.getContainer() == null) {
componentDef.setContainer(this);
}
registerByClass(componentDef);
registerByName(componentDef);
}
/**
* {@link Class}をキーにして {@link ComponentDef}を登録します。
*
* @param componentDef
*/
protected void registerByClass(ComponentDef componentDef) {
Class[] classes = S2ContainerUtil.getAssignableClasses(componentDef
.getComponentClass());
for (int i = 0; i < classes.length; ++i) {
registerMap(classes[i], componentDef);
}
}
/**
* 名前をキーにして {@link ComponentDef}を登録します。
*
* @param componentDef
*/
protected void registerByName(ComponentDef componentDef) {
String componentName = componentDef.getComponentName();
if (componentName != null) {
registerMap(componentName, componentDef);
}
}
/**
* キャッシュに {@link ComponentDef}を登録します。
*
* @param key
* @param componentDef
*/
protected void registerMap(Object key, ComponentDef componentDef) {
registerMap(key, componentDef, this);
}
public void registerMap(Object key, ComponentDef componentDef,
S2Container container) {
int position = getContainerPosition(container);
ComponentDefHolder holder = (ComponentDefHolder) componentDefMap
.get(key);
if (holder == null) {
holder = new ComponentDefHolder(position, componentDef);
componentDefMap.put(key, holder);
} else if (position > holder.getPosition()) {
return;
} else if (position < holder.getPosition()) {
holder.setPosition(position);
holder.setComponentDef(componentDef);
} else if (container != this) {
holder.setComponentDef(componentDef);
} else {
holder.setComponentDef(createTooManyRegistration(key, holder
.getComponentDef(), componentDef));
}
registerParent(key, holder.getComponentDef());
}
/**
* {@link S2Container}のネストが深くなってもパフォーマンスが落ちないように親に {@link ComponentDef}を登録します。
*
* @param key
* @param componentDef
*/
protected void registerParent(Object key, ComponentDef componentDef) {
for (int i = 0; i < getParentSize(); i++) {
S2Container parent = (S2Container) getParent(i);
parent.registerMap(key, componentDef, this);
if (isNeedNS(key, componentDef)) {
parent
.registerMap(namespace + NS_SEP + key, componentDef,
this);
}
}
}
/**
* @see org.seasar.framework.container.S2Container#getComponentDefSize()
*/
public int getComponentDefSize() {
return componentDefList.size();
}
/**
* @see org.seasar.framework.container.S2Container#getComponentDef(int)
*/
public ComponentDef getComponentDef(int index) {
return (ComponentDef) componentDefList.get(index);
}
/**
* @see org.seasar.framework.container.S2Container#getComponentDef(java.lang.Object)
*/
public ComponentDef getComponentDef(Object key)
throws ComponentNotFoundRuntimeException {
assertParameterIsNotNull(key, "key");
return S2ContainerBehavior.acquireFromGetComponentDef(this, key);
}
/**
* @see org.seasar.framework.container.S2Container#findComponentDefs(java.lang.Object)
*/
public ComponentDef[] findComponentDefs(Object key)
throws ComponentNotFoundRuntimeException {
assertParameterIsNotNull(key, "key");
ComponentDef cd = internalGetComponentDef(key);
return toComponentDefArray(cd);
}
/**
* @see org.seasar.framework.container.S2Container#findAllComponentDefs(java.lang.Object)
*/
public ComponentDef[] findAllComponentDefs(final Object componentKey) {
assertParameterIsNotNull(componentKey, "componentKey");
final List componentDefs = new ArrayList();
Traversal.forEachContainer(this, new Traversal.S2ContainerHandler() {
public Object processContainer(S2Container container) {
componentDefs.addAll(Arrays.asList(container
.findLocalComponentDefs(componentKey)));
return null;
}
});
return (ComponentDef[]) componentDefs
.toArray(new ComponentDef[componentDefs.size()]);
}
/**
* @see org.seasar.framework.container.S2Container#findLocalComponentDefs(java.lang.Object)
*/
public ComponentDef[] findLocalComponentDefs(Object componentKey) {
ComponentDefHolder holder = (ComponentDefHolder) componentDefMap
.get(componentKey);
if (holder == null || holder.getPosition() > 0) {
return new ComponentDef[0];
}
return toComponentDefArray(holder.getComponentDef());
}
/**
* {@link ComponentDef}の配列に変換します。
*
* @param cd
* @return {@link ComponentDef}の配列
*/
protected ComponentDef[] toComponentDefArray(ComponentDef cd) {
if (cd == null) {
return new ComponentDef[0];
} else if (cd instanceof TooManyRegistrationComponentDefImpl) {
return ((TooManyRegistrationComponentDefImpl) cd)
.getComponentDefs();
}
return new ComponentDef[] { cd };
}
/**
* 内部的なgetComponentDefの実装です。
*
* @param key
* @return {@link ComponentDef}
*/
protected ComponentDef internalGetComponentDef(Object key) {
ComponentDefHolder holder = (ComponentDefHolder) componentDefMap
.get(key);
if (holder != null) {
return holder.getComponentDef();
}
if (key instanceof String) {
String name = (String) key;
int index = name.indexOf(NS_SEP);
if (index > 0) {
String ns = name.substring(0, index);
name = name.substring(index + 1);
if (ns.equals(namespace)) {
return internalGetComponentDef(name);
}
}
}
return null;
}
/**
* @see org.seasar.framework.container.S2Container#hasComponentDef(java.lang.Object)
*/
public boolean hasComponentDef(Object componentKey) {
assertParameterIsNotNull(componentKey, "componentKey");
return S2ContainerBehavior.acquireFromHasComponentDef(this,
componentKey) != null;
}
/**
* @see org.seasar.framework.container.S2Container#hasDescendant(java.lang.String)
*/
public boolean hasDescendant(String path) {
assertParameterIsNotEmpty(path, "path");
return descendants.containsKey(path);
}
public S2Container getDescendant(String path) {
S2Container descendant = (S2Container) descendants.get(path);
if (descendant != null) {
return descendant;
}
throw new ContainerNotRegisteredRuntimeException(path);
}
public void registerDescendant(S2Container descendant) {
assertParameterIsNotNull(descendant, "descendant");
descendants.put(descendant.getPath(), descendant);
}
/**
* @see org.seasar.framework.container.S2Container#include(org.seasar.framework.container.S2Container)
*/
public void include(S2Container child) {
assertParameterIsNotNull(child, "child");
children.add(child);
childPositions.put(child, new Integer(children.size()));
child.setRoot(getRoot());
child.addParent(this);
}
/**
* 子供の位置を返します。
*
* @param container
* @return 子供の位置
*/
protected int getContainerPosition(S2Container container) {
if (container == this) {
return 0;
}
return ((Integer) childPositions.get(container)).intValue();
}
/**
* 名前空間が必要かどうかを返します。
*
* @param key
* @param cd
* @return 名前空間が必要かどうか
*/
protected boolean isNeedNS(Object key, ComponentDef cd) {
return key instanceof String && namespace != null;
}
public int getChildSize() {
return children.size();
}
public S2Container getChild(int index) {
return (S2Container) children.get(index);
}
public int getParentSize() {
return parents.size();
}
public S2Container getParent(int index) {
return (S2Container) parents.get(index);
}
public void addParent(S2Container parent) {
parents.add(parent);
for (Iterator it = componentDefMap.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
Object key = entry.getKey();
ComponentDefHolder holder = (ComponentDefHolder) entry.getValue();
ComponentDef cd = holder.getComponentDef();
parent.registerMap(key, cd, this);
if (isNeedNS(key, cd)) {
parent.registerMap(namespace + NS_SEP + key, cd, this);
}
}
}
public void init() {
if (inited) {
return;
}
final ExternalContextComponentDefRegister register = getRoot()
.getExternalContextComponentDefRegister();
if (register != null) {
register.registerComponentDefs(this);
}
final ClassLoader currentLoader = Thread.currentThread()
.getContextClassLoader();
Thread.currentThread().setContextClassLoader(classLoader);
try {
for (int i = 0; i < getChildSize(); ++i) {
getChild(i).init();
}
for (int i = 0; i < getComponentDefSize(); ++i) {
getComponentDef(i).init();
}
inited = true;
} finally {
Thread.currentThread().setContextClassLoader(currentLoader);
}
}
public void destroy() {
if (!inited) {
return;
}
final ClassLoader currentLoader = Thread.currentThread()
.getContextClassLoader();
Thread.currentThread().setContextClassLoader(classLoader);
try {
for (int i = getComponentDefSize() - 1; 0 <= i; --i) {
try {
getComponentDef(i).destroy();
} catch (Throwable t) {
logger.error("ESSR0017", t);
}
}
for (int i = getChildSize() - 1; 0 <= i; --i) {
getChild(i).destroy();
}
componentDefMap = null;
componentDefList = null;
namespace = null;
path = null;
children = null;
childPositions = null;
parents = null;
descendants = null;
externalContext = null;
externalContextComponentDefRegister = null;
metaDefSupport = null;
classLoader = null;
root = this;
inited = false;
} finally {
Thread.currentThread().setContextClassLoader(currentLoader);
}
}
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
componentDefMap.remove(namespace);
this.namespace = namespace;
registerMap(namespace, new S2ContainerComponentDef(this, namespace));
}
public boolean isInitializeOnCreate() {
return initializeOnCreate;
}
public void setInitializeOnCreate(boolean initializeOnCreate) {
this.initializeOnCreate = initializeOnCreate;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public ExternalContext getExternalContext() {
return externalContext;
}
public void setExternalContext(ExternalContext externalContext) {
this.externalContext = externalContext;
}
public ExternalContextComponentDefRegister getExternalContextComponentDefRegister() {
return externalContextComponentDefRegister;
}
public void setExternalContextComponentDefRegister(
ExternalContextComponentDefRegister register) {
this.externalContextComponentDefRegister = register;
}
public void addMetaDef(MetaDef metaDef) {
metaDefSupport.addMetaDef(metaDef);
}
public MetaDef getMetaDef(int index) {
return metaDefSupport.getMetaDef(index);
}
public MetaDef getMetaDef(String name) {
return metaDefSupport.getMetaDef(name);
}
public MetaDef[] getMetaDefs(String name) {
return metaDefSupport.getMetaDefs(name);
}
public int getMetaDefSize() {
return metaDefSupport.getMetaDefSize();
}
public ClassLoader getClassLoader() {
return classLoader;
}
public void setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* {@link TooManyRegistrationComponentDef}を作成します。
*
* @param key
* @param currentComponentDef
* @param newComponentDef
* @return {@link TooManyRegistrationComponentDef}
*/
public static ComponentDef createTooManyRegistration(Object key,
ComponentDef currentComponentDef, ComponentDef newComponentDef) {
if (currentComponentDef instanceof TooManyRegistrationComponentDef) {
((TooManyRegistrationComponentDef) currentComponentDef)
.addComponentDef(newComponentDef);
return currentComponentDef;
} else {
TooManyRegistrationComponentDef tmrcf = new TooManyRegistrationComponentDefImpl(
key);
tmrcf.addComponentDef(currentComponentDef);
tmrcf.addComponentDef(newComponentDef);
return tmrcf;
}
}
/**
* パラメータが<code>null</code>でないことを表明します。
*
* @param parameter
* @param name
*/
protected void assertParameterIsNotNull(Object parameter, String name) {
if (parameter == null) {
throw new IllegalArgumentException(name);
}
}
/**
* パラメータが空でないことを表明します。
*
* @param parameter
* @param name
*/
protected void assertParameterIsNotEmpty(String parameter, String name) {
if (StringUtil.isEmpty(parameter)) {
throw new IllegalArgumentException(name);
}
}
/**
* 子供の位置を保持するクラスです。
*/
static class ComponentDefHolder {
private int position;
private ComponentDef componentDef;
/**
* {@link ComponentDefHolder}を作成します。
*
* @param position
* @param componentDef
*/
public ComponentDefHolder(int position, ComponentDef componentDef) {
this.position = position;
this.componentDef = componentDef;
}
/**
* 位置を返します。
*
* @return 位置
*/
public int getPosition() {
return position;
}
/**
* 位置を設定します。
*
* @param position
*/
public void setPosition(int position) {
this.position = position;
}
/**
* {@link ComponentDef}を返します。
*
* @return {@link ComponentDef}
*/
public ComponentDef getComponentDef() {
return componentDef;
}
/**
* {@link ComponentDef}を設定します。
*
* @param componentDef
*/
public void setComponentDef(ComponentDef componentDef) {
this.componentDef = componentDef;
}
}
}