/*
* Copyright 2004-2006 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.factory;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;
import org.seasar.framework.container.ExtensionNotFoundRuntimeException;
import org.seasar.framework.container.ExternalContext;
import org.seasar.framework.container.ExternalContextComponentDefRegister;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.assembler.AssemblerFactory;
import org.seasar.framework.container.deployer.ComponentDeployerFactory;
import org.seasar.framework.container.impl.S2ContainerBehavior;
import org.seasar.framework.util.Disposable;
import org.seasar.framework.util.DisposableUtil;
import org.seasar.framework.util.ResourceUtil;
/**
* S2コンテナを構築するためのクラスです。
*
* S2コンテナファクトリは、 diconファイルなどの設定ファイルから新たにS2コンテナを構築する機能を提供します。 S2コンテナの構築方法は、
* 初期化用diconファイルによって指定することが出来ます。 初期化用diconファイルは、 システムプロパティorg.seasar.framework.container.factory.config
に指定されたdiconファイルか、
* 指定が無ければクラスパス上のs2container.dicon
が読み込まれます。
*
*
* @author higa
* @author jundu
*/
public final class S2ContainerFactory {
/**
* S2コンテナファクトリの初期化用diconファイルを指定するためのシステムプロパティ名を表す定数です。
*/
public static final String FACTORY_CONFIG_KEY = "org.seasar.framework.container.factory.config";
/**
* S2コンテナファクトリの初期化用diconファイルがシステムプロパティに指定されなかった場合の規定のファイルを表す定数です。
*/
public static final String FACTORY_CONFIG_PATH = "s2container.dicon";
/**
* S2コンテナファクトリの初期化用diconファイルに指定する、 既定のS2コンテナビルダのコンポーネント名を表す定数です。
*/
public static final String DEFAULT_BUILDER_NAME = "defaultBuilder";
protected static boolean initialized;
protected static boolean configuring = false;
protected static S2Container configurationContainer;
protected static Provider provider;
protected static S2ContainerBuilder defaultBuilder;
protected static ThreadLocal processingPaths = new ThreadLocal() {
protected Object initialValue() {
return new LinkedHashSet();
}
};
static {
configure();
}
/**
* 指定された設定ファイルに基づき、 S2コンテナを構築して返します。
*
* @param path
* 設定ファイルのパス
* @return 構築済みのS2コンテナ
*/
public static synchronized S2Container create(final String path) {
if (!initialized) {
configure();
}
return getProvider().create(path);
}
/**
* 指定された設定ファイルに基づき、 指定されたクラスローダ上にS2コンテナを構築して返します。
*
* @param path
* 設定ファイルのパス
* @param classLoader
* S2コンテナを構築する対象のクラスローダ
* @return 構築済みのS2コンテナ
*/
public static synchronized S2Container create(final String path,
final ClassLoader classLoader) {
if (!initialized) {
configure();
}
return getProvider().create(path, classLoader);
}
/**
* 指定された設定ファイルからS2コンテナを構築し、 指定された親S2コンテナに対してインクルードした状態で返します。
*
* @param parent
* 親となるS2コンテナ
* @param path
* 設定ファイルのパス
* @return 構築済みのS2コンテナ
*/
public static S2Container include(final S2Container parent,
final String path) {
if (!initialized) {
configure();
}
return getProvider().include(parent, path);
}
/**
* システムプロパティに指定された初期化用diconファイルに基づいて、 S2コンテナファクトリを構成します。
*
* システムプロパティorg.seasar.framework.container.factory.config
が指定されていなかった場合は、
* クラスパス上のs2container.dicon
を初期化用diconファイルとして利用します。
*
*/
public static void configure() {
final String configFile = System.getProperty(FACTORY_CONFIG_KEY,
FACTORY_CONFIG_PATH);
configure(configFile);
}
/**
* 指定された初期化用diconファイルに基づき、 S2コンテナファクトリを設定します。
*
* @param configFile
* 初期化用diconファイル
*/
public static synchronized void configure(final String configFile) {
if (configuring) {
return;
}
configuring = true;
if (provider == null) {
provider = new DefaultProvider();
}
if (defaultBuilder == null) {
defaultBuilder = new XmlS2ContainerBuilder();
}
if (ResourceUtil.isExist(configFile)) {
final S2ContainerBuilder builder = new XmlS2ContainerBuilder();
configurationContainer = builder.build(configFile);
configurationContainer.init();
Configurator configurator;
if (configurationContainer.hasComponentDef(Configurator.class)) {
configurator = (Configurator) configurationContainer
.getComponent(Configurator.class);
} else {
configurator = new DefaultConfigurator();
}
configurator.configure(configurationContainer);
}
DisposableUtil.add(new Disposable() {
public void dispose() {
S2ContainerFactory.destroy();
}
});
configuring = false;
initialized = true;
}
/**
* S2コンテナファクトリの設定をクリアして、 初期化前の状態に戻します。
*/
public static synchronized void destroy() {
defaultBuilder = null;
provider = null;
if (configurationContainer != null) {
configurationContainer.destroy();
}
configurationContainer = null;
initialized = false;
}
protected static Provider getProvider() {
return provider;
}
protected static void setProvider(final Provider p) {
provider = p;
}
protected static S2ContainerBuilder getDefaultBuilder() {
return defaultBuilder;
}
protected static void setDefaultBuilder(final S2ContainerBuilder builder) {
defaultBuilder = builder;
}
protected static void enter(final String path) {
final Set paths = (Set) processingPaths.get();
if (paths.contains(path)) {
throw new CircularIncludeRuntimeException(path, paths);
}
paths.add(path);
}
protected static void leave(final String path) {
final Set paths = (Set) processingPaths.get();
paths.remove(path);
}
protected static void assertCircularInclude(final S2Container container,
final String path) {
assertCircularInclude(container, path, new LinkedList());
}
protected static void assertCircularInclude(final S2Container container,
final String path, LinkedList paths) {
paths.addFirst(container.getPath());
try {
if (path.equals(container.getPath())) {
throw new CircularIncludeRuntimeException(path, new ArrayList(
paths));
}
for (int i = 0; i < container.getParentSize(); ++i) {
assertCircularInclude(container.getParent(i), path, paths);
}
} finally {
paths.removeFirst();
}
}
/**
* S2コンテナの構築に関する機能を提供するインターフェースです。
*
* {@link S2ContainerFactory S2コンテナファクトリ}は、
* このインターフェースを実装したクラスを呼び出すことでS2コンテナの構築を行います。
*
*
* @author jundu
*/
public interface Provider {
/**
* 指定された設定ファイルに基づき、 S2コンテナを構築して返します。
*
* @param path
* 設定ファイルのパス
* @return 構築済みのS2コンテナ
*/
S2Container create(String path);
/**
* 指定された設定ファイルに基づき、 指定されたクラスローダ上にS2コンテナを構築して返します。
*
* @param path
* 設定ファイルのパス
* @param classLoader
* S2コンテナを構築する対象のクラスローダ
* @return 構築済みのS2コンテナ
*/
S2Container create(String path, ClassLoader classLoader);
/**
* 指定された設定ファイルからS2コンテナを構築し、 指定された親S2コンテナに対してインクルードした状態で返します。
*
* @param parent
* 親となるS2コンテナ
* @param path
* 設定ファイルのパス
* @return 構築済みのS2コンテナ
*/
S2Container include(S2Container parent, String path);
}
/**
* S2コンテナファクトリに対して、 既定のS2コンテナ構築機能を提供するクラスです。
*
* このクラスでは、 S2コンテナビルダを呼び出してS2コンテナを構築した後、
* 外部コンテキストおよび外部コンテキスト用コンポーネント定義を登録するオブジェクトをコンテナへ登録します。
*
*
* @author jundu
*/
public static class DefaultProvider implements Provider {
public static final String pathResolver_BINDING = "bindingType=may";
public static final String externalContext_BINDING = "bindingType=may";
public static final String externalContextComponentDefRegister_BINDING = "bindingType=may";
protected PathResolver pathResolver = new SimplePathResolver();
protected boolean hotswapMode;
protected ExternalContext externalContext;
protected ExternalContextComponentDefRegister externalContextComponentDefRegister;
/**
* パス名から絶対パスを取得するためのパスリゾルバを返します。
*
* @return パスリゾルバ
*/
public PathResolver getPathResolver() {
return pathResolver;
}
/**
* パス名から絶対パスを取得するためのパスリゾルバを設定します。
*
* @param pathResolver
* パスリゾルバ
*/
public void setPathResolver(final PathResolver pathResolver) {
this.pathResolver = pathResolver;
}
/**
* 設定済みの外部コンテキストを返します。
*
* @return 外部コンテキスト
*/
public ExternalContext getExternalContext() {
return externalContext;
}
/**
* 外部コンテキストを設定します。
*
* @param externalContext
* 外部コンテキスト
*/
public void setExternalContext(ExternalContext externalContext) {
this.externalContext = externalContext;
}
/**
* 外部コンテキスト用コンポーネント定義を登録するためのオブジェクトを返します。
*
* @return 外部コンテキスト用コンポーネント定義を登録するためのオブジェクト
*/
public ExternalContextComponentDefRegister getExternalContextComponentDefRegister() {
return externalContextComponentDefRegister;
}
/**
* 外部コンテキスト用コンポーネント定義を登録するためのオブジェクトを設定します。
*
* @param externalContextComponentDefRegister
* 外部コンテキスト用コンポーネント定義を登録するためのオブジェクト
*/
public void setExternalContextComponentDefRegister(
ExternalContextComponentDefRegister externalContextComponentDefRegister) {
this.externalContextComponentDefRegister = externalContextComponentDefRegister;
}
public S2Container create(final String path) {
ClassLoader classLoader;
if (configurationContainer != null
&& configurationContainer
.hasComponentDef(ClassLoader.class)) {
classLoader = (ClassLoader) configurationContainer
.getComponent(ClassLoader.class);
} else {
classLoader = Thread.currentThread().getContextClassLoader();
}
return build(path, classLoader);
}
public S2Container create(final String path,
final ClassLoader classLoader) {
final ClassLoader oldLoader = Thread.currentThread()
.getContextClassLoader();
try {
if (classLoader != null) {
Thread.currentThread().setContextClassLoader(classLoader);
}
return create(path);
} finally {
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
public S2Container include(final S2Container parent, final String path) {
final String realPath = pathResolver.resolvePath(parent.getPath(),
path);
assertCircularInclude(parent, realPath);
enter(realPath);
try {
final S2Container root = parent.getRoot();
S2Container child = null;
synchronized (root) {
if (root.hasDescendant(realPath)) {
child = root.getDescendant(realPath);
parent.include(child);
} else {
final String ext = getExtension(realPath);
final S2ContainerBuilder builder = getBuilder(ext);
child = builder.include(parent, realPath);
root.registerDescendant(child);
}
}
return child;
} finally {
leave(realPath);
}
}
protected S2Container build(final String path,
final ClassLoader classLoader) {
final String realPath = pathResolver.resolvePath(null, path);
enter(realPath);
try {
final String ext = getExtension(realPath);
final S2Container container = getBuilder(ext).build(realPath,
classLoader);
container.setExternalContext(externalContext);
container
.setExternalContextComponentDefRegister(externalContextComponentDefRegister);
return container;
} finally {
leave(realPath);
}
}
protected String getExtension(final String path) {
final String ext = ResourceUtil.getExtension(path);
if (ext == null) {
throw new ExtensionNotFoundRuntimeException(path);
}
return ext;
}
protected S2ContainerBuilder getBuilder(final String ext) {
if (configurationContainer != null
&& configurationContainer.hasComponentDef(ext)) {
return (S2ContainerBuilder) configurationContainer
.getComponent(ext);
}
return defaultBuilder;
}
}
/**
* S2コンテナファクトリを設定するためのインターフェースです。
*
* @author jundu
*/
public interface Configurator {
/**
* S2コンテナファクトリの設定をします。
*
* 引数には、 初期化用のコンポーネントを含むS2コンテナを指定します。
*
*
* @param configurationContainer
* 初期化用S2コンテナ
*/
void configure(S2Container configurationContainer);
}
/**
* S2コンテナファクトリの既定の設定方法を提供するクラスです。
*
* 設定用S2コンテナに登録されているコンポーネントに基づき、 S2コンテナファクトリを設定します。 このクラスでは、 以下の設定を行います。
*
*
* - ファクトリプロバイダ
* - {@link S2ContainerFactory.Provider}を実装したコンポーネントがあれば、
* それをファクトリプロバイダとして実行するように設定します。 それ以外の場合は、
* {@link S2ContainerFactory.DefaultProvider 既定のファクトリプロバイダ}を設定します。
* - パスリゾルバ
* - 既定のファクトリプロバイダが利用されている場合、
* {@link org.seasar.framework.container.factory.PathResolver}を実装したコンポーネントがあれば、
* パスリゾルバとして設定します。
* - 外部コンテキスト
* - 既定のファクトリプロバイダが利用されている場合、
* {@link org.seasar.framework.container.ExternalContext}を実装したコンポーネントがあれば、
* 外部コンテキストとして設定します。
* - 外部コンテキスト用コンポーネント定義を登録するオブジェクト
* - 既定のファクトリプロバイダが利用されている場合、
* {@link org.seasar.framework.container.ExternalContextComponentDefRegister}を実装したコンポーネントがあれば、
* 外部コンテキスト用コンポーネントを登録するオブジェクトとして設定します。
* - 既定のS2コンテナビルダ
* defaultBuilder
という名前のコンポーネントがあれば、
* 既定のS2コンテナビルダとして設定します。
* - リソースリゾルバ
* - defaultBuilderというコンポーネントがなく{@link AbstractS2ContainerBuilder}を継承したS2コンテナビルダが設定されてる場合は、
* {@link org.seasar.framework.container.factory.ResourceResolver}を実装したコンポーネントがあればリソースリゾルバとして設定します。
* - ビヘイビアプロバイダ
* - {@link org.seasar.framework.container.impl.S2ContainerBehavior.Provider}を実装したコンポーネントがあれば、
* ビヘイビアプロバイダとして設定します。
* - デプロイヤファクトリプロバイダ
* - {@link org.seasar.framework.container.deployer.ComponentDeployerFactory.Provider}を実装したコンポーネントがあれば、
* デプロイヤファクトリプロバイダとして設定します。
* - アセンブラファクトリプロバイダ
* - {@link org.seasar.framework.container.assembler.AssemblerFactory.Provider}を実装したコンポーネントがあれば、
* アセンブラファクトリプロバイダとして設定します。
*
*
* @author jundu
*/
public static class DefaultConfigurator implements Configurator {
public void configure(final S2Container configurationContainer) {
if (configurationContainer.hasComponentDef(Provider.class)) {
provider = (Provider) configurationContainer
.getComponent(Provider.class);
} else if (provider instanceof DefaultProvider) {
DefaultProvider dp = (DefaultProvider) provider;
if (configurationContainer.hasComponentDef(PathResolver.class)) {
dp.setPathResolver((PathResolver) configurationContainer
.getComponent(PathResolver.class));
}
if (configurationContainer
.hasComponentDef(ExternalContext.class)) {
dp
.setExternalContext((ExternalContext) configurationContainer
.getComponent(ExternalContext.class));
}
if (configurationContainer
.hasComponentDef(ExternalContextComponentDefRegister.class)) {
dp
.setExternalContextComponentDefRegister((ExternalContextComponentDefRegister) configurationContainer
.getComponent(ExternalContextComponentDefRegister.class));
}
}
if (configurationContainer.hasComponentDef(DEFAULT_BUILDER_NAME)) {
defaultBuilder = (S2ContainerBuilder) configurationContainer
.getComponent(DEFAULT_BUILDER_NAME);
} else if (configurationContainer
.hasComponentDef(ResourceResolver.class)
&& defaultBuilder instanceof AbstractS2ContainerBuilder) {
((AbstractS2ContainerBuilder) defaultBuilder)
.setResourceResolver((ResourceResolver) configurationContainer
.getComponent(ResourceResolver.class));
}
if (configurationContainer
.hasComponentDef(S2ContainerBehavior.Provider.class)) {
S2ContainerBehavior
.setProvider((S2ContainerBehavior.Provider) configurationContainer
.getComponent(S2ContainerBehavior.Provider.class));
}
if (configurationContainer
.hasComponentDef(ComponentDeployerFactory.Provider.class)) {
ComponentDeployerFactory
.setProvider((ComponentDeployerFactory.Provider) configurationContainer
.getComponent(ComponentDeployerFactory.Provider.class));
}
if (configurationContainer
.hasComponentDef(AssemblerFactory.Provider.class)) {
AssemblerFactory
.setProvider((AssemblerFactory.Provider) configurationContainer
.getComponent(AssemblerFactory.Provider.class));
}
}
}
}