diff --git a/src-api/org/seasar/mayaa/FactoryFactory.java b/src-api/org/seasar/mayaa/FactoryFactory.java index eed613ce..5d2b3a1a 100644 --- a/src-api/org/seasar/mayaa/FactoryFactory.java +++ b/src-api/org/seasar/mayaa/FactoryFactory.java @@ -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; + } /** * ファクトリの初期化。 diff --git a/src-impl/org/seasar/mayaa/impl/FactoryFactoryImpl.java b/src-impl/org/seasar/mayaa/impl/FactoryFactoryImpl.java index 61db1a1f..2017cb38 100644 --- a/src-impl/org/seasar/mayaa/impl/FactoryFactoryImpl.java +++ b/src-impl/org/seasar/mayaa/impl/FactoryFactoryImpl.java @@ -88,6 +88,10 @@ protected T marshallFactory( @Override protected T getFactory(Class 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) { @@ -96,6 +100,37 @@ protected T getFactory(Class 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 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内にそれぞれ生成しており、後から生成されたもので無効になる(設定が継承されるわけではない)。 @@ -114,39 +149,110 @@ private T getFactory(Class interfaceClass, Object if (checkInterface(interfaceClass) == false || context == null) { throw new IllegalArgumentException(); } - String systemID = interfaceClass.getName(); - List sources = new ArrayList<>(); + + List 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 collectSources(Class interfaceClass) { + String systemID = interfaceClass.getName(); + List 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 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 loadFactoryBackward( + Class interfaceClass, Object context, List 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 loadFactoryForward( + Class interfaceClass, Object context, List 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; } diff --git a/src-impl/org/seasar/mayaa/impl/MayaaServlet.java b/src-impl/org/seasar/mayaa/impl/MayaaServlet.java index c321d32b..50e81920 100644 --- a/src-impl/org/seasar/mayaa/impl/MayaaServlet.java +++ b/src-impl/org/seasar/mayaa/impl/MayaaServlet.java @@ -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"); @@ -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を初期化する。 */ diff --git a/src-impl/org/seasar/mayaa/impl/provider/ProviderFactoryImpl.java b/src-impl/org/seasar/mayaa/impl/provider/ProviderFactoryImpl.java index bf1ee5a2..c2bf3274 100644 --- a/src-impl/org/seasar/mayaa/impl/provider/ProviderFactoryImpl.java +++ b/src-impl/org/seasar/mayaa/impl/provider/ProviderFactoryImpl.java @@ -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) { @@ -76,7 +80,7 @@ protected ServiceProvider getServiceProvider(Object context) { } } - class Pair { + static class Pair { SourceDescriptor source; String location; Pair(SourceDescriptor source, String location) { @@ -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 sources = collectSources(); + + if (loadBackwardWay) { + Collections.reverse(sources); + return loadProviderBackward(sources); + } else { + return loadProviderForward(sources); + } + } + /** + * ソースファイルを収集する。 + * Built-in → META-INF → WEB-INF の順序で収集される。 + * + * @return 存在するソースファイルのリスト + */ + private List collectSources() { + final String systemID = "org.seasar.mayaa.provider.ServiceProvider"; List 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 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 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 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; } diff --git a/src/test/java/org/seasar/mayaa/impl/FactoryFactoryImplTest.java b/src/test/java/org/seasar/mayaa/impl/FactoryFactoryImplTest.java index 87f18d59..9ac97033 100644 --- a/src/test/java/org/seasar/mayaa/impl/FactoryFactoryImplTest.java +++ b/src/test/java/org/seasar/mayaa/impl/FactoryFactoryImplTest.java @@ -1,76 +1,112 @@ -/* - * Copyright 2004-2011 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.mayaa.impl; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; -import org.seasar.mayaa.UnifiedFactory; -import org.seasar.mayaa.cycle.CycleFactory; -import org.seasar.mayaa.cycle.ServiceCycle; -import org.seasar.mayaa.cycle.scope.ApplicationScope; -import org.seasar.mayaa.impl.cycle.CycleFactoryImpl; -import org.seasar.mayaa.impl.source.ApplicationSourceDescriptor; -import org.seasar.mayaa.impl.source.ClassLoaderSourceDescriptor; -import org.seasar.mayaa.source.SourceDescriptor; -import org.springframework.mock.web.MockServletContext; - -/** - * @author Masataka Kurihara (Gluegent, Inc.) - */ -public class FactoryFactoryImplTest { - - @Test - public void testCheckInterface() { - FactoryFactoryImpl factory = new FactoryFactoryImpl(); - assertTrue(factory.checkInterface(CycleFactory.class)); - assertFalse(factory.checkInterface(CycleFactoryImpl.class)); - assertFalse(factory.checkInterface(null)); - } - - @Test - public void testGetBootstrapApplication() { - FactoryFactoryImpl factory = new FactoryFactoryImpl(); - ApplicationScope application = factory.getApplicationScope(new MockServletContext((String)null)); - assertNotNull(application); - } - - @Test - public void testGetBootstrapSource() { - FactoryFactoryImpl factory = new FactoryFactoryImpl(); - SourceDescriptor source = factory.getBootstrapSource(ApplicationSourceDescriptor.WEB_INF, "/testID", - new MockServletContext((String)null)); - assertNotNull(source); - assertEquals("/testID", source.getSystemID()); - } - - @Test - public void testMarshallFactory() { - FactoryFactoryImpl factory = new FactoryFactoryImpl(); - CycleFactoryImpl before = new CycleFactoryImpl(); - before.setServiceClass(ServiceCycle.class); - ClassLoaderSourceDescriptor source = new ClassLoaderSourceDescriptor(); - source.setSystemID("/test.factory"); - source.setNeighborClass(FactoryFactoryImpl.class); - UnifiedFactory current = factory.marshallFactory(CycleFactory.class, new MockServletContext((String)null), - source, before); - assertEquals(ServiceCycle.class, current.getServiceClass()); - } - -} +/* + * Copyright 2004-2011 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.mayaa.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.seasar.mayaa.FactoryFactory; +import org.seasar.mayaa.UnifiedFactory; +import org.seasar.mayaa.cycle.CycleFactory; +import org.seasar.mayaa.cycle.ServiceCycle; +import org.seasar.mayaa.cycle.scope.ApplicationScope; +import org.seasar.mayaa.impl.cycle.CycleFactoryImpl; +import org.seasar.mayaa.impl.source.ApplicationSourceDescriptor; +import org.seasar.mayaa.impl.source.ClassLoaderSourceDescriptor; +import org.seasar.mayaa.source.SourceDescriptor; +import org.springframework.mock.web.MockServletContext; + +/** + * @author Masataka Kurihara (Gluegent, Inc.) + */ +public class FactoryFactoryImplTest { + + @BeforeEach + public void setUp() { + // Ensure override is cleared before each test + FactoryFactory.setEnableBackwardOrderLoadingOverride(null); + } + + @AfterEach + public void tearDown() { + // Always clear override after each test to avoid leakage + FactoryFactory.setEnableBackwardOrderLoadingOverride(null); + } + + @Test + public void testCheckInterface() { + FactoryFactoryImpl factory = new FactoryFactoryImpl(); + assertTrue(factory.checkInterface(CycleFactory.class)); + assertFalse(factory.checkInterface(CycleFactoryImpl.class)); + assertFalse(factory.checkInterface(null)); + } + + @Test + public void testGetBootstrapApplication() { + FactoryFactoryImpl factory = new FactoryFactoryImpl(); + ApplicationScope application = factory.getApplicationScope(new MockServletContext((String)null)); + assertNotNull(application); + } + + @Test + public void testGetBootstrapSource() { + FactoryFactoryImpl factory = new FactoryFactoryImpl(); + SourceDescriptor source = factory.getBootstrapSource(ApplicationSourceDescriptor.WEB_INF, "/testID", + new MockServletContext((String)null)); + assertNotNull(source); + assertEquals("/testID", source.getSystemID()); + } + + @Test + public void testMarshallFactory() { + FactoryFactoryImpl factory = new FactoryFactoryImpl(); + CycleFactoryImpl before = new CycleFactoryImpl(); + before.setServiceClass(ServiceCycle.class); + ClassLoaderSourceDescriptor source = new ClassLoaderSourceDescriptor(); + source.setSystemID("/test.factory"); + source.setNeighborClass(FactoryFactoryImpl.class); + UnifiedFactory current = factory.marshallFactory(CycleFactory.class, new MockServletContext((String)null), + source, before); + assertEquals(ServiceCycle.class, current.getServiceClass()); + } + + @Test + public void testOverrideTrue_EnablesBackwardOrder() { + FactoryFactory.setEnableBackwardOrderLoadingOverride(Boolean.TRUE); + assertFalse(FactoryFactory.isDisabledBackwardOrderLoading(), + "Override=TRUE should enable backward order"); + } + + @Test + public void testOverrideFalse_DisablesBackwardOrder() { + FactoryFactory.setEnableBackwardOrderLoadingOverride(Boolean.FALSE); + assertTrue(FactoryFactory.isDisabledBackwardOrderLoading(), + "Override=FALSE should disable backward order"); + } + + @Test + public void testOverrideNull_DefaultsToForwardOrder() { + FactoryFactory.setEnableBackwardOrderLoadingOverride(null); + assertTrue(FactoryFactory.isDisabledBackwardOrderLoading(), + "With no override, should default to forward order (backward disabled)"); + } + +} diff --git a/src/test/java/org/seasar/mayaa/impl/cycle/web/MockServiceCycleForLoadOrderTest.java b/src/test/java/org/seasar/mayaa/impl/cycle/web/MockServiceCycleForLoadOrderTest.java new file mode 100644 index 00000000..7960e8a2 --- /dev/null +++ b/src/test/java/org/seasar/mayaa/impl/cycle/web/MockServiceCycleForLoadOrderTest.java @@ -0,0 +1,33 @@ +/* + * Copyright 2004-2012 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.mayaa.impl.cycle.web; + +/** + * LoadOrderTestで読み込み順序を確認するためのマーカークラス。 + * WEB-INF設定ファイルから参照される。 + * + * Built-in: ServiceCycleImpl + * WEB-INF: MockServiceCycleForLoadOrderTest + * + * Forward順序では WEB-INF (最後) が Built-in を上書き → MockServiceCycleForLoadOrderTest + * Backward順序では WEB-INF (最初) が返される → MockServiceCycleForLoadOrderTest + * + * @author Test + */ +public class MockServiceCycleForLoadOrderTest extends ServiceCycleImpl { + // ServiceCycleImplを継承することで動作可能 + // クラス名が異なることで、どの設定ファイルが読み込まれたか判別可能 +} diff --git a/src/test/java/org/seasar/mayaa/impl/provider/LoadOrderTest.java b/src/test/java/org/seasar/mayaa/impl/provider/LoadOrderTest.java new file mode 100644 index 00000000..dcbad086 --- /dev/null +++ b/src/test/java/org/seasar/mayaa/impl/provider/LoadOrderTest.java @@ -0,0 +1,248 @@ +/* + * Copyright 2004-2012 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.mayaa.impl.provider; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.seasar.mayaa.FactoryFactory; +import org.seasar.mayaa.impl.FactoryFactoryImpl; +import org.springframework.mock.web.MockServletContext; + +/** + * Forward/Backwardの読み込み順序による動作の違いをテストする。 + * + * @author Test + */ +public class LoadOrderTest { + + private MockServletContext mockServletContext; + + @BeforeEach + public void setUp() { + // テスト用のWEB-INFディレクトリを参照するMockServletContextを設定 + String workingDir = System.getProperty("user.dir"); + String srcPath = workingDir + "/src/test/java/org/seasar/mayaa/impl/provider"; + mockServletContext = new MockServletContext() { + @Override + public String getRealPath(String path) { + if ("/WEB-INF".equals(path) || "WEB-INF".equals(path)) { + return srcPath + "/WEB-INF"; + } + return super.getRealPath(path); + } + + @Override + public java.net.URL getResource(String path) throws java.net.MalformedURLException { + // WEB-INF配下のリソースは対応できるようにする + if (path != null && path.startsWith("/WEB-INF")) { + String filePath = srcPath + "/WEB-INF" + path.substring("/WEB-INF".length()); + java.io.File file = new java.io.File(filePath); + if (file.exists()) { + return file.toURI().toURL(); + } + } + return super.getResource(path); + } + }; + // デフォルト状態: Forward順序(Backwardは disabled) + FactoryFactory.setEnableBackwardOrderLoadingOverride(null); + } + + @AfterEach + public void tearDown() { + // クリーンアップ + FactoryFactory.setEnableBackwardOrderLoadingOverride(null); + } + + /** + * Forward順序がデフォルト(Backwardが disabled)であることを確認。 + */ + @Test + public void testDefaultIsForwardOrder() { + assertTrue(FactoryFactory.isDisabledBackwardOrderLoading(), + "Default should be Forward order (Backward disabled)"); + } + + /** + * Backward順序を有効にできることを確認。 + */ + @Test + public void testEnableBackwardOrder() { + FactoryFactory.setEnableBackwardOrderLoadingOverride(Boolean.TRUE); + assertFalse(FactoryFactory.isDisabledBackwardOrderLoading(), + "Backward order should be enabled when override=TRUE"); + } + + /** + * Forward順序を明示的に指定できることを確認。 + */ + @Test + public void testExplicitlyDisableBackwardOrder() { + FactoryFactory.setEnableBackwardOrderLoadingOverride(Boolean.FALSE); + assertTrue(FactoryFactory.isDisabledBackwardOrderLoading(), + "Backward order should be disabled when override=FALSE"); + } + + /** + * Overrideを null に設定するとデフォルトに戻ることを確認。 + */ + @Test + public void testResetOverrideToDefault() { + FactoryFactory.setEnableBackwardOrderLoadingOverride(Boolean.TRUE); + assertFalse(FactoryFactory.isDisabledBackwardOrderLoading(), + "Backward should be enabled"); + + FactoryFactory.setEnableBackwardOrderLoadingOverride(null); + assertTrue(FactoryFactory.isDisabledBackwardOrderLoading(), + "After reset, should return to default (Forward order)"); + } + + /** + * Forward順序でのFactoryFactory初期化を確認。 + * Built-in → META-INF → WEB-INF の順で探索し、 + * 全てを読み込みながら後のものが前のものを上書き。 + */ + @Test + public void testFactoryFactoryForwardOrder() { + FactoryFactory.setEnableBackwardOrderLoadingOverride(Boolean.FALSE); + + // FactoryFactoryImpl を初期化 + FactoryFactory.setInstance(new FactoryFactoryImpl()); + FactoryFactory.setContext(mockServletContext); + + // Forward順序で読み込まれることを確認 + assertTrue(FactoryFactory.isDisabledBackwardOrderLoading(), + "Forward order should be active"); + + // CycleFactory が取得でき、正しく初期化されていることを確認 + assertNotNull(FactoryFactory.getCycleFactory(), + "CycleFactory should be loaded"); + + // Forward順序では WEB-INF の設定が最後に読み込まれ Built-in を上書きする + assertEquals("org.seasar.mayaa.impl.cycle.web.MockServiceCycleForLoadOrderTest", + FactoryFactory.getCycleFactory().getServiceClass().getName(), + "Forward order: WEB-INF config should override Built-in (last wins)"); + } + + /** + * Backward順序でのFactoryFactory初期化を確認。 + * WEB-INF → META-INF → Built-in の順で探索し、 + * 最初に見つかった有効なものを返す。 + */ + @Test + public void testFactoryFactoryBackwardOrder() { + FactoryFactory.setEnableBackwardOrderLoadingOverride(Boolean.TRUE); + + // FactoryFactoryImpl を初期化 + FactoryFactory.setInstance(new FactoryFactoryImpl()); + FactoryFactory.setContext(mockServletContext); + + // Backward順序で読み込まれることを確認 + assertFalse(FactoryFactory.isDisabledBackwardOrderLoading(), + "Backward order should be active"); + + // CycleFactory が取得でき、正しく初期化されていることを確認 + assertNotNull(FactoryFactory.getCycleFactory(), + "CycleFactory should be loaded"); + + // Backward順序では WEB-INF の設定が最初に有効と判定され、それを返す + assertEquals("org.seasar.mayaa.impl.cycle.web.MockServiceCycleForLoadOrderTest", + FactoryFactory.getCycleFactory().getServiceClass().getName(), + "Backward order: WEB-INF config should be used (first valid wins)"); + } + + /** + * ServiceProvider の読み込みが Forward順序で動作することを確認。 + * Engine, LibraryManager など必須コンポーネントが正しく読み込まれることを検証。 + */ + @Test + public void testServiceProviderLoadingWithForwardOrder() { + FactoryFactory.setEnableBackwardOrderLoadingOverride(Boolean.FALSE); + FactoryFactory.setInstance(new FactoryFactoryImpl()); + FactoryFactory.setContext(mockServletContext); + + // Forward順序での ProviderFactory 取得 + assertNotNull(FactoryFactory.getProviderFactory(), + "ProviderFactory should be accessible"); + + // Engine が読み込まれていることを確認 + assertNotNull(FactoryFactory.getProviderFactory().getServiceProvider().getEngine(), + "Engine should be loaded in ServiceProvider"); + + // LibraryManager が読み込まれていることを確認 + assertNotNull(FactoryFactory.getProviderFactory().getServiceProvider().getLibraryManager(), + "LibraryManager should be loaded in ServiceProvider"); + + // TemplateBuilder が読み込まれていることを確認 + assertNotNull(FactoryFactory.getProviderFactory().getServiceProvider().getTemplateBuilder(), + "TemplateBuilder should be loaded in ServiceProvider"); + } + + /** + * ServiceProvider の読み込みが Backward順序で動作することを確認。 + * Engine, LibraryManager など必須コンポーネントが正しく読み込まれることを検証。 + */ + @Test + public void testServiceProviderLoadingWithBackwardOrder() { + FactoryFactory.setEnableBackwardOrderLoadingOverride(Boolean.TRUE); + FactoryFactory.setInstance(new FactoryFactoryImpl()); + FactoryFactory.setContext(mockServletContext); + + // Backward順序での ProviderFactory 取得 + assertNotNull(FactoryFactory.getProviderFactory(), + "ProviderFactory should be accessible"); + + // Engine が読み込まれていることを確認 + assertNotNull(FactoryFactory.getProviderFactory().getServiceProvider().getEngine(), + "Engine should be loaded in ServiceProvider"); + + // LibraryManager が読み込まれていることを確認 + assertNotNull(FactoryFactory.getProviderFactory().getServiceProvider().getLibraryManager(), + "LibraryManager should be loaded in ServiceProvider"); + + // TemplateBuilder が読み込まれていることを確認 + assertNotNull(FactoryFactory.getProviderFactory().getServiceProvider().getTemplateBuilder(), + "TemplateBuilder should be loaded in ServiceProvider"); + } + + /** + * isDisabledBackwardOrderLoading() の状態遷移を検証。 + */ + @Test + public void testBackwardOrderLoadingStateTransitions() { + // 初期状態: Forward(Backward disabled = true) + assertTrue(FactoryFactory.isDisabledBackwardOrderLoading()); + + // Backward有効化 + FactoryFactory.setEnableBackwardOrderLoadingOverride(Boolean.TRUE); + assertFalse(FactoryFactory.isDisabledBackwardOrderLoading()); + + // Forward明示指定 + FactoryFactory.setEnableBackwardOrderLoadingOverride(Boolean.FALSE); + assertTrue(FactoryFactory.isDisabledBackwardOrderLoading()); + + // リセット + FactoryFactory.setEnableBackwardOrderLoadingOverride(null); + assertTrue(FactoryFactory.isDisabledBackwardOrderLoading(), + "After reset, should default to Forward"); + } +} diff --git a/src/test/java/org/seasar/mayaa/impl/provider/WEB-INF/org.seasar.mayaa.cycle.CycleFactory b/src/test/java/org/seasar/mayaa/impl/provider/WEB-INF/org.seasar.mayaa.cycle.CycleFactory new file mode 100644 index 00000000..ca6e32c5 --- /dev/null +++ b/src/test/java/org/seasar/mayaa/impl/provider/WEB-INF/org.seasar.mayaa.cycle.CycleFactory @@ -0,0 +1,8 @@ + + + + +