/* * 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; /** * {@link org.seasar.framework.container.S2Container S2コンテナ}を構築するためのファクトリクラスです。 *
* S2コンテナファクトリは、 diconファイルなどの設定ファイルから新たにS2コンテナを構築する機能を提供します。 S2コンテナの構築時には、 * 指定されたクラスローダをスレッドのコンテキストクラスローダとして設定します。 そのため、 S2コンテナによってロードされるクラスは、 * そのクラスローダに登録されます。 *
** 実際にS2コンテナを構築する処理は、 {@link S2ContainerFactory.Provider}を実装したクラスに委譲します。 * デフォルトの実装として、 {@link S2ContainerFactory.DefaultProvider}を提供します。 別の実装を使いたい場合は、 * コンフィグレーションdiconファイルに{@link S2ContainerFactory.Provider}を実装したクラスをコンポーネントとして登録します。 *
*
* コンフィグレーションdiconファイルとは、 S2コンテナおよびS2コンテナファクトリの振る舞いを変更するためのものです。 デフォルトでは、
* クラスパス上のs2container.dicon
を読み込みます。 システムプロパティのorg.seasar.framework.container.factory.config
にdiconファイルを指定することで、
* そのファイルをコンフィグレーションdiconファイルとして使うことが出来ます。
*
* コンフィグレーションdiconファイルは、 {@link S2ContainerFactory.Configurator}を実装したクラスによって処理されます。 * デフォルトの実装として{@link S2ContainerFactory.DefaultConfigurator}を提供します。 * コンフィグレーションdiconファイルに{@link S2ContainerFactory.Configurator}を実装したクラスをコンポーネントとして登録することで、 * コンフィグレーションdiconファイルの処理方法を変更することが出来ます。 *
* * @author higa * @author jundu */ public final class S2ContainerFactory { /** * コンフィグレーションdiconファイルを指定するためのシステムプロパティ名を表す定数です。 */ public static final String FACTORY_CONFIG_KEY = "org.seasar.framework.container.factory.config"; /** * コンフィグレーションdiconファイルのデフォルトのファイル名を表す定数です。 */ public static final String FACTORY_CONFIG_PATH = "s2container.dicon"; /** * コンフィグレーションdiconファイルに登録する、 デフォルトの{@link S2ContainerBuilder 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コンテナを構築して返します。 ** S2コンテナの構築には、 スレッドのコンテキストクラスローダを使用します。 *
* * @param path * 設定ファイルのパス * @return 構築したS2コンテナ * * @see S2ContainerFactory.Provider#create(String) */ public static synchronized S2Container create(final String path) { if (!initialized) { configure(); } return getProvider().create(path); } /** * 指定された設定ファイルに基づき、 指定されたクラスローダを使用してS2コンテナを構築して返します。 * * @param path * 設定ファイルのパス * @param classLoader * S2コンテナの構築に使用するクラスローダ * @return 構築したS2コンテナ * * @see S2ContainerFactory.Provider#create(String, ClassLoader) */ 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コンテナ * * @see S2ContainerFactory.Provider#include(S2Container, String) */ public static S2Container include(final S2Container parent, final String path) { if (!initialized) { configure(); } return getProvider().include(parent, path); } /** * コンフィグレーションdiconファイルに基づいて、 S2コンテナファクトリを構成します。 *
* コンフィグレーションdiconファイルとして、 クラスパス上のs2container.dicon
を使用します。
* ただし、 システムプロパティorg.seasar.framework.container.factory.config
にdiconファイルが指定されていた場合、
* そのdiconファイルを使用します。
*
* S2コンテナファクトリは、 {@link org.seasar.framework.container.S2Container S2コンテナ}の構築時に、 * このインターフェースを実装したクラスに処理を委譲します。 *
* * @author jundu */ public interface Provider { /** * 指定された設定ファイルに基づき、 S2コンテナを構築して返します。 ** 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); } /** * {@link S2ContainerFactory S2コンテナファクトリ}の振る舞いを提供する、 デフォルトの実装クラスです。 ** このクラスでは{@link org.seasar.framework.container.S2Container S2コンテナ}の構築時に指定された設定ファイルの拡張子から、 * その設定ファイルを処理するための拡張子と一致するコンポーネント名を持った{@link S2ContainerBuilder}を実装したコンポーネントをコンフィグレーションS2コンテナから取得しようとします。 * 取得に成功した場合は、 そのコンポーネントを使ってS2コンテナの構築を実行します。 取得できなかった場合は、 * デフォルトのS2コンテナビルダを利用します。 *
*
* デフォルトのS2コンテナビルダとして、 defaultBuilder
という名前でコンフィグレーションS2コンテナに登録されたコンポーネントを使用します。
*
* S2コンテナを構築した後、 * {@link org.seasar.framework.container.ExternalContext 外部コンテキスト}および{@link org.seasar.framework.container.ExternalContextComponentDefRegister 外部コンテキスト用コンポーネント定義を登録するオブジェクト}をコンテナへ登録します。 *
* * @author jundu */ public static class DefaultProvider implements Provider { /** * プロパティpathResolver
のための定数アノテーションです。
*/
public static final String pathResolver_BINDING = "bindingType=may";
/**
* プロパティexternalContext
のための定数アノテーションです。
*/
public static final String externalContext_BINDING = "bindingType=may";
/**
* プロパティexternalContextComponentDefRegister
のための定数アノテーションです。
*/
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;
}
}
/**
* {@link org.seasar.framework.container.S2Container S2コンテナ}および{@link S2ContainerFactory S2コンテナファクトリ}の振る舞いを構成します。
* * {@link S2ContainerFactory#configure}から呼び出されて、 * S2コンテナおよびS2コンテナファクトリを構成します。 *
* * @author jundu */ public interface Configurator { /** * S2コンテナおよびS2コンテナファクトリの構成をします。 ** 引数には、 コンフィグレーション用のコンポーネントを含むS2コンテナを指定します。 *
* * @param configurationContainer * コンフィグレーションS2コンテナ */ void configure(S2Container configurationContainer); } /** * {@link org.seasar.framework.container.S2Container S2コンテナ}および{@link S2ContainerFactory S2コンテナファクトリ}の振る舞いを構成するデフォルトの実装クラスです。 ** コンフィグレーションS2コンテナに登録されているコンポーネントに基づき、 S2コンテナおよびS2コンテナファクトリを構成します。 * S2コンテナについては、 以下の設定を行います。 *
** また、 S2コンテナファクトリについては、 以下の設定を行います。 *
*defaultBuilder
という名前のコンポーネントがあれば、
* デフォルトのS2コンテナビルダとして設定します。