Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src-api/org/seasar/mayaa/FactoryFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,25 @@ public abstract class FactoryFactory {
private static PageSourceFactory _pageSourceFactory;
private static ProviderFactory _providerFactory;
private static CycleFactory _cycleFactory;
// Override set from servlet init-param to control backward/forward load order.
private static volatile Boolean _overrideEnableBackwardOrderLoading;

public static boolean isDisabledBackwardOrderLoading() {
// Servlet init-param override: when set, use it; otherwise default to forward order (backward disabled)
if (_overrideEnableBackwardOrderLoading != null) {
return !_overrideEnableBackwardOrderLoading.booleanValue();
}
// Default: backward order is disabled (forward order is used)
return true;
}

/**
* Overrides the backward/forward load order switch using a servlet init-param.
* @param enableBackwardOrder when {@code Boolean.TRUE}, backward order is enabled; when {@code Boolean.FALSE}, disabled
*/
public static void setEnableBackwardOrderLoadingOverride(Boolean enableBackwardOrder) {
_overrideEnableBackwardOrderLoading = enableBackwardOrder;
}

/**
* ファクトリの初期化。
Expand Down
134 changes: 120 additions & 14 deletions src-impl/org/seasar/mayaa/impl/FactoryFactoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ protected <T extends UnifiedFactory> T marshallFactory(

@Override
protected <T extends UnifiedFactory> T getFactory(Class<T> interfaceClass, Object context) {
// If the switch is ON, force forward order without compatibility fallback.
if (FactoryFactory.isDisabledBackwardOrderLoading()) {
return getFactory(interfaceClass, context, false);
}
try {
return getFactory(interfaceClass, context, true);
} catch (NeedCompatibilityException e) {
Expand All @@ -96,6 +100,37 @@ protected <T extends UnifiedFactory> T getFactory(Class<T> interfaceClass, Objec
}
}

static class Pair {
SourceDescriptor source;
String location;
Pair(SourceDescriptor source, String location) {
this.source = source;
this.location = location;
}
}

/**
* UnifiedFactoryの最低限の妥当性チェックを行う。
* getServiceClass()が呼び出せることを確認する。
*
* @param source 読み込んだソース
* @param factory チェック対象のファクトリ
* @throws IllegalStateException ファクトリが適切に初期化されていない場合
*/
private <T extends UnifiedFactory> void validateFactory(
SourceDescriptor source, T factory) throws IllegalStateException {
if (factory == null) {
throw new IllegalStateException("Factory is null after loading: " + source.getSystemID());
}
try {
factory.getServiceClass(); // 最低限の動作確認
} catch (IllegalStateException e) {
throw new IllegalStateException(
"Factory is not properly initialized: " + source.getSystemID() +
" - " + e.getMessage(), e);
}
}

/**
* org.seasar.mayaa.provider.ServiceProviderファイルに定義されている内容で{@code ServiceProvider}を生成する。
* v1.2.1まではビルトイン、ロード中のMETA-INF内、WEB-INF内にそれぞれ生成しており、後から生成されたもので無効になる(設定が継承されるわけではない)。
Expand All @@ -114,39 +149,110 @@ private <T extends UnifiedFactory> T getFactory(Class<T> interfaceClass, Object
if (checkInterface(interfaceClass) == false || context == null) {
throw new IllegalArgumentException();
}
String systemID = interfaceClass.getName();
List<SourceDescriptor> sources = new ArrayList<>();

List<Pair> sources = collectSources(interfaceClass);

if (loadBackwardWay) {
Collections.reverse(sources);
return loadFactoryBackward(interfaceClass, context, sources);
} else {
return loadFactoryForward(interfaceClass, context, sources);
}
}

// Collect source files
/**
* ソースファイルを収集する。
* Built-in → META-INF → WEB-INF の順序で収集される。
*
* @param interfaceClass ファクトリインタフェースクラス
* @return 存在するソースファイルのリスト
*/
private List<Pair> collectSources(Class<?> interfaceClass) {
String systemID = interfaceClass.getName();
List<Pair> sources = new ArrayList<>();

// Mayaa Built-in source file
SourceDescriptor source = MarshallUtil.getDefaultSource(systemID, UnifiedFactoryHandler.class);
if (source.exists()) {
sources.add(source);
sources.add(new Pair(source, "[Built-in]"));
LOG.info("FOUND [Built-in] " + source.getSystemID());
}
//

// 各META-INF/org.seasar.mayaa.provider.ServiceProvider を列挙する。順序は不定。
Iterator<SourceDescriptor> it = MarshallUtil.iterateMetaInfSources(systemID);
while (it.hasNext()) {
source = it.next();
if (source.exists()) {
sources.add(source);
sources.add(new Pair(source, "META-INF"));
LOG.info("FOUND META-INF " + source.getSystemID());
}
}

// WEB-INF
source = getBootstrapSource(ApplicationSourceDescriptor.WEB_INF, systemID);
if (source.exists()) {
sources.add(source);
sources.add(new Pair(source, "WEB-INF"));
LOG.info("FOUND WEB-INF " + source.getSystemID());
}

return sources;
}

if (loadBackwardWay) {
Collections.reverse(sources);
/**
* Backward順序でファクトリをロード(最初に見つかった有効なものを返す)。
* 各ソースを順に試し、最初に妥当性検証を通過したファクトリを返す。
*
* @param interfaceClass ファクトリインタフェースクラス
* @param context アプリケーションコンテキスト
* @param sources ソースファイルのリスト(既にリバース済み)
* @return 最初に有効なファクトリ、または null
*/
private <T extends UnifiedFactory> T loadFactoryBackward(
Class<T> interfaceClass, Object context, List<Pair> sources) {
for (Pair s : sources) {
LOG.info("LOADING " + s.location + " " + s.source.getSystemID());
T factory = marshallFactory(interfaceClass, context, s.source, null);

if (factory != null) {
try {
validateFactory(s.source, factory);
LOG.info("LOADED " + s.location + " " + s.source.getSystemID());
return factory;
} catch (IllegalStateException e) {
LOG.warn("Factory validation failed for " + s.location + " " +
s.source.getSystemID() + ", trying next: " + e.getMessage());
// 次のソースを試す
}
}
}
return null;
}

/**
* Forward順序でファクトリをロード(チェーン的に上書き)。
* 全てのソースを順に読み込み、前のファクトリを引き継ぎながら処理する。
*
* @param interfaceClass ファクトリインタフェースクラス
* @param context アプリケーションコンテキスト
* @param sources ソースファイルのリスト
* @return 最終的なファクトリ、または null
*/
private <T extends UnifiedFactory> T loadFactoryForward(
Class<T> interfaceClass, Object context, List<Pair> sources) {
T factory = null;
for (SourceDescriptor s: sources) {
factory = marshallFactory(interfaceClass, context, s, factory);
if (loadBackwardWay && factory != null) {
return factory;
}
Pair lastSource = null;

for (Pair s : sources) {
LOG.info("LOADING " + s.location + " " + s.source.getSystemID());
factory = marshallFactory(interfaceClass, context, s.source, factory);
lastSource = s;
}

if (factory != null && lastSource != null) {
validateFactory(lastSource.source, factory);
LOG.info("LOADED " + lastSource.location + " " + lastSource.source.getSystemID());
}

return factory;
}

Expand Down
25 changes: 25 additions & 0 deletions src-impl/org/seasar/mayaa/impl/MayaaServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public void init() {
LOG.info("init start");
FactoryFactory.setInstance(new FactoryFactoryImpl());
FactoryFactory.setContext(getServletContext());
// Allow overriding load order switch via web.xml init-param
applyInitParams();
_initialized = true;
}
LOG.info("prepareLibraries start");
Expand All @@ -66,6 +68,29 @@ public void init() {
}
}

/**
* Apply Servlet init-params to framework configuration.
* Currently supports:
* - enableBackwardOrderLoading: when true, use backward order (WEB-INF → META-INF → built-in)
* for ServiceProvider/factory resolution. When false, force forward order.
* If unset, defaults to forward order (backward disabled).
*/
protected void applyInitParams() {
if (getServletConfig() == null) {
return;
}
// Support both a concise name and the full property-like name
String v = getServletConfig().getInitParameter("enableBackwardOrderLoading");
if (!StringUtil.hasValue(v)) {
v = getServletConfig().getInitParameter("org.seasar.mayaa.provider.enableBackwardOrderLoading");
}
if (StringUtil.hasValue(v)) {
boolean enable = ObjectUtil.booleanValue(v, false);
FactoryFactory.setEnableBackwardOrderLoadingOverride(Boolean.valueOf(enable));
LOG.info("Servlet init-param: enableBackwardOrderLoading=" + enable);
}
}

/**
* AutoPageBuilderを初期化する。
*/
Expand Down
96 changes: 79 additions & 17 deletions src-impl/org/seasar/mayaa/impl/provider/ProviderFactoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ protected ServiceProvider marshallServiceProvider(
}

protected ServiceProvider getServiceProvider(Object context) {
// If the switch is ON, force forward order without compatibility fallback.
if (FactoryFactory.isDisabledBackwardOrderLoading()) {
return getServiceProvider(context, false);
}
try {
return getServiceProvider(context, true);
} catch (NeedCompatibilityException e) {
Expand All @@ -76,7 +80,7 @@ protected ServiceProvider getServiceProvider(Object context) {
}
}

class Pair {
static class Pair {
SourceDescriptor source;
String location;
Pair(SourceDescriptor source, String location) {
Expand All @@ -101,47 +105,105 @@ class Pair {
* @throws IllegalStateException ファイルに記述された内容不正などで必要なオブジェクトが生成されなかった時
*/
private ServiceProvider getServiceProvider(Object context, boolean loadBackwardWay) throws IllegalStateException {
final String systemID = "org.seasar.mayaa.provider.ServiceProvider";
List<Pair> sources = collectSources();

if (loadBackwardWay) {
Collections.reverse(sources);
return loadProviderBackward(sources);
} else {
return loadProviderForward(sources);
}
}

/**
* ソースファイルを収集する。
* Built-in → META-INF → WEB-INF の順序で収集される。
*
* @return 存在するソースファイルのリスト
*/
private List<Pair> collectSources() {
final String systemID = "org.seasar.mayaa.provider.ServiceProvider";
List<Pair> sources = new ArrayList<>();
// Collect source files

// Mayaa Built-in source file
SourceDescriptor source = MarshallUtil.getDefaultSource(systemID, ServiceProviderHandler.class);
if (source.exists()) {
sources.add(new Pair(source, "[Built-in]"));
LOG.info("FOUND " + "[Built-in]" + source.getSystemID());
LOG.info("FOUND [Built-in] " + source.getSystemID());
}

// 各META-INF/org.seasar.mayaa.provider.ServiceProvider を列挙する。順序は不定。
Iterator<SourceDescriptor> it = MarshallUtil.iterateMetaInfSources(systemID);
while (it.hasNext()) {
source = it.next();
if (source.exists()) {
sources.add(new Pair(source, "META-INF"));
LOG.info("FOUND " + "META-INF" + source.getSystemID());
LOG.info("FOUND META-INF " + source.getSystemID());
}
}

// WEB-INF
source = FactoryFactory.getBootstrapSource(ApplicationSourceDescriptor.WEB_INF, systemID);
if (source.exists()) {
sources.add(new Pair(source, "WEB-INF"));
LOG.info("FOUND " + "WEB-INF" + source.getSystemID());
LOG.info("FOUND WEB-INF " + source.getSystemID());
}

return sources;
}

if (loadBackwardWay) {
Collections.reverse(sources);
/**
* Backward順序でServiceProviderをロード(最初に見つかった有効なものを返す)。
* 各ソースを順に試し、最初に妥当性検証を通過したプロバイダを返す。
*
* @param sources ソースファイルのリスト(既にリバース済み)
* @return 最初に有効なServiceProvider、または null
*/
private ServiceProvider loadProviderBackward(List<Pair> sources) {
ServiceProvider provider = null;

for (Pair s : sources) {
LOG.info("LOADING " + s.location + " " + s.source.getSystemID());
provider = marshallServiceProvider(s.source, null);

if (provider != null) {
try {
validate(s.source, provider);
LOG.info("LOADED " + s.location + " " + s.source.getSystemID());
return provider;
} catch (IllegalStateException e) {
LOG.warn("ServiceProvider validation failed for " + s.location + " " +
s.source.getSystemID() + ", trying next: " + e.getMessage());
// 次のソースを試す
provider = null;
}
}
}
return provider;
}

/**
* Forward順序でServiceProviderをロード(チェーン的に上書き)。
* 全てのソースを順に読み込み、前のプロバイダを引き継ぎながら処理する。
*
* @param sources ソースファイルのリスト
* @return 最終的なServiceProvider、または null
*/
private ServiceProvider loadProviderForward(List<Pair> sources) {
ServiceProvider provider = null;
for (Pair s: sources) {
LOG.info("LOADING " + s.location + s.source.getSystemID());
Pair lastSource = null;

for (Pair s : sources) {
LOG.info("LOADING " + s.location + " " + s.source.getSystemID());
provider = marshallServiceProvider(s.source, provider);
validate(s.source, provider);
if (loadBackwardWay && provider != null) {
LOG.info("LOADED " + s.location + s.source.getSystemID());
return provider;
}
lastSource = s;
}

if (provider != null && lastSource != null) {
validate(lastSource.source, provider);
LOG.info("LOADED " + lastSource.location + " " + lastSource.source.getSystemID());
}
Pair last = sources.get(sources.size() - 1);
LOG.info("LOADED " + last.location + last.source.getSystemID());

return provider;
}
Comment thread
mitonize marked this conversation as resolved.

Expand Down
Loading