Java tutorial
/* * Copyright 2014-2015 the original author or authors. * * 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.dbflute.utflute.spring; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; import java.util.Map; import javax.annotation.Resource; import org.dbflute.utflute.core.InjectionTestCase; import org.dbflute.utflute.core.binding.BindingAnnotationRule; import org.dbflute.utflute.core.transaction.TransactionResource; import org.dbflute.util.DfReflectionUtil; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.access.BeanFactoryLocator; import org.springframework.beans.factory.access.BeanFactoryReference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.access.ContextSingletonBeanFactoryLocator; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; /** * @author jflute * @since 0.1.1 (2011/07/25 Monday) */ public abstract class SpringTestCase extends InjectionTestCase { // =================================================================================== // Attribute // ========= // ----------------------------------------------------- // Static Cache // ------------ /** The cached configuration files of DI container. (NullAllowed: null means beginning or ending) */ protected static String[] _xcachedConfigFiles; /** The cached application context for DI container. (NullAllowed: null means beginning or ending) */ protected static ApplicationContext _xcachedContext; // ----------------------------------------------------- // Spring Object // ------------- /** The current active context of application. {Spring Object} */ protected ApplicationContext _xcurrentActiveContext; // =================================================================================== // Settings // ======== // ----------------------------------------------------- // Prepare Container // ----------------- @Override protected void xprepareTestCaseContainer() { final String[] configFiles = xdoPrepareTestCaseContainer(); xsaveCachedInstance(configFiles); } protected String[] xdoPrepareTestCaseContainer() { if (isUseOneTimeContainer()) { xdestroyContainer(); } final String[] configFiles = prepareConfigFiles(); if (xisInitializedContainer()) { // already exists if (xcanRecycleContainer(configFiles)) { // no change log("...Recycling spring: " + Arrays.asList(configFiles)); xrecycleContainerInstance(configFiles); return configFiles; // no need to initialize } else { // changed xdestroyContainer(); } } xinitializeContainer(configFiles); return configFiles; } protected boolean xcanRecycleContainer(String[] configFiles) { return xconfigCanAcceptContainerRecycle(configFiles); } protected boolean xconfigCanAcceptContainerRecycle(String[] configFiles) { return configFiles != null && Arrays.asList(configFiles).equals(Arrays.asList(_xcachedConfigFiles)); } protected void xrecycleContainerInstance(String[] configFiles) { _xcurrentActiveContext = _xcachedContext; } protected void xsaveCachedInstance(String[] configFiles) { _xcachedConfigFiles = configFiles; } /** * Prepare configuration files for Spring Framework. * @return The array of pure file name. (NotNull) */ protected String[] prepareConfigFiles() { // customize point return new String[] {}; // as default } /** * Create application context for Spring Framework. * @param confs The array of configuration file names. (NotNull) * @return The new-created instance of application context. (NotNull) */ protected ApplicationContext createApplicationContext(String[] confs) { return new ClassPathXmlApplicationContext(confs); } @Override protected void xclearCachedContainer() { _xcachedContext = null; } // =================================================================================== // Transaction // =========== /** * {@inheritDoc} */ @Override protected TransactionResource beginNewTransaction() { // user method final String defaultManagerName = "transactionManager"; final List<String> managerNameList = newArrayList(); { managerNameList.add(defaultManagerName); final String[] additionals = prepareAdditionalTransactionManagerNames(); for (String name : additionals) { managerNameList.add(name); } } SpringTransactionResource resource = null; for (String name : managerNameList) { if (!hasComponent(name)) { if (name.equals(defaultManagerName)) { // default transaction continue; } String msg = "Not found the transaction mamanger: " + name; throw new IllegalStateException(msg); } if (resource == null) { resource = new SpringTransactionResource(); } xregisterTransaction(resource, name); } return resource; // for cannonball's transaction or manual transaction } protected String[] prepareAdditionalTransactionManagerNames() { // customize point #extPoint return new String[] {}; // as default } protected SpringTransactionResource xregisterTransaction(SpringTransactionResource resource, final String managerKey) { final PlatformTransactionManager manager = getComponent(managerKey); final DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); final TransactionStatus status = manager.getTransaction(def); resource.registerTransaction(manager, status); return resource; } // =================================================================================== // Component Binding // ================= @Override protected Map<Class<? extends Annotation>, BindingAnnotationRule> xprovideBindingAnnotationRuleMap() { final Map<Class<? extends Annotation>, BindingAnnotationRule> ruleMap = newHashMap(); ruleMap.put(Resource.class, new BindingAnnotationRule().byNameOnly()); ruleMap.put(Autowired.class, new BindingAnnotationRule().byTypeOnly()); return ruleMap; } // =================================================================================== // Spring Handling // =============== // ----------------------------------------------------- // Initialize // ---------- protected boolean xisInitializedContainer() { return _xcachedContext != null; } protected void xinitializeContainer(String[] configFiles) { if (configFiles != null && configFiles.length > 0) { log("...Initializing spring by configuration files: " + Arrays.asList(configFiles)); _xcurrentActiveContext = createApplicationContext(configFiles); } else { log("...Initializing spring by provided default context."); _xcurrentActiveContext = provideDefaultApplicationContext(); } _xcachedContext = _xcurrentActiveContext; } protected ApplicationContext provideDefaultApplicationContext() { final BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(); final BeanFactoryReference ref = locator.useBeanFactory("context"); return (ApplicationContext) ref.getFactory(); } // ----------------------------------------------------- // Destroy // ------- protected void xdestroyContainer() { xreleaseClassPathContext(); xreleaseLocatorContextCache(); _xcachedContext = null; _xcurrentActiveContext = null; } protected void xreleaseClassPathContext() { final ApplicationContext cachedContext = _xcachedContext; if (cachedContext != null && cachedContext instanceof ClassPathXmlApplicationContext) { ((ClassPathXmlApplicationContext) cachedContext).destroy(); } } protected void xreleaseLocatorContextCache() { final Class<ContextSingletonBeanFactoryLocator> locatorType = ContextSingletonBeanFactoryLocator.class; final String cacheMapName = "instances"; final Field cacheMapField = DfReflectionUtil.getWholeField(locatorType, cacheMapName); final Map<?, ?> instances = (Map<?, ?>) DfReflectionUtil.getValueForcedly(cacheMapField, null); instances.clear(); } // ----------------------------------------------------- // Component // --------- /** * {@inheritDoc} */ protected <COMPONENT> COMPONENT getComponent(Class<COMPONENT> type) { // user method return _xcurrentActiveContext.getBean(type); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") protected <COMPONENT> COMPONENT getComponent(String name) { // user method return (COMPONENT) _xcurrentActiveContext.getBean(name); } /** * {@inheritDoc} */ protected boolean hasComponent(Class<?> type) { // user method try { getComponent(type); return true; } catch (NoSuchBeanDefinitionException e) { return false; } } /** * {@inheritDoc} */ protected boolean hasComponent(String name) { // user method try { getComponent(name); return true; } catch (NoSuchBeanDefinitionException e) { return false; } } // ----------------------------------------------------- // Application Context // ------------------- protected ApplicationContext getApplicationContext() { return _xcurrentActiveContext; } }