Browse Source

first SAFlute migration

jflute 10 years ago
parent
commit
1ace1caabc
30 changed files with 3540 additions and 8 deletions
  1. 1 0
      .gitignore
  2. 15 0
      pom.xml
  3. 827 0
      src/main/java/org/codelibs/fess/action/base/FessBaseAction.java
  4. 125 0
      src/main/java/org/codelibs/fess/lasta/core/direction/FessConfig.java
  5. 260 0
      src/main/java/org/codelibs/fess/lasta/core/direction/FessEnv.java
  6. 276 0
      src/main/java/org/codelibs/fess/lasta/core/direction/FessFwAssistantDirector.java
  7. 73 0
      src/main/java/org/codelibs/fess/lasta/core/direction/sponsor/FessActionAdjustmentProvider.java
  8. 94 0
      src/main/java/org/codelibs/fess/lasta/core/direction/sponsor/FessTimeResourceProvider.java
  9. 65 0
      src/main/java/org/codelibs/fess/lasta/core/direction/sponsor/FessUserLocaleProcessProvider.java
  10. 77 0
      src/main/java/org/codelibs/fess/lasta/core/direction/sponsor/FessUserTimeZoneProcessProvider.java
  11. 404 0
      src/main/java/org/codelibs/fess/lasta/web/action/FessJspPath.java
  12. 32 0
      src/main/java/org/codelibs/fess/lasta/web/action/FessLoginRequired.java
  13. 721 0
      src/main/java/org/codelibs/fess/lasta/web/action/FessMessages.java
  14. 29 0
      src/main/java/org/codelibs/fess/lasta/web/action/FessPerformLogin.java
  15. 79 0
      src/main/java/org/codelibs/fess/lasta/web/action/FessUserBean.java
  16. 1 1
      src/main/java/org/codelibs/fess/struts/action/FessRequestProcessor.java
  17. 0 2
      src/main/resources/app.dicon
  18. 3 3
      src/main/resources/customizer.dicon
  19. 27 0
      src/main/resources/fess_config.properties
  20. 45 0
      src/main/resources/fess_env.properties
  21. 107 0
      src/main/resources/fess_message.properties
  22. 106 0
      src/main/resources/fess_message_ja.properties
  23. 6 0
      src/main/resources/s2container.dicon
  24. 9 0
      src/main/resources/saflute_assist++.dicon
  25. 1 1
      src/main/webapp/WEB-INF/cmd/resources/customizer.dicon
  26. 11 0
      src/main/webapp/WEB-INF/view/error/error_message.jsp
  27. 16 1
      src/main/webapp/WEB-INF/web.xml
  28. 35 0
      src/test/java/org/codelibs/fess/lasta/LastaEnvTest.java
  29. 94 0
      src/test/java/org/codelibs/fess/unit/UnitFessContainerTestCase.java
  30. 1 0
      src/test/resources/app_ut.dicon

+ 1 - 0
.gitignore

@@ -18,3 +18,4 @@
 *.iml
 .idea
 .DS_Store
+/target/

+ 15 - 0
pom.xml

@@ -467,6 +467,21 @@
         </exclusion>
       </exclusions>
     </dependency>
+    <dependency>
+      <groupId>org.seasar.cms.s2-classbuilder</groupId>
+      <artifactId>s2-classbuilder</artifactId>
+      <version>0.0.11</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.seasar.container</groupId>
+          <artifactId>s2-framework</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.seasar.container</groupId>
+          <artifactId>s2-extension</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
     <dependency>
       <groupId>org.codelibs.struts</groupId>
       <artifactId>cl-struts</artifactId>

+ 827 - 0
src/main/java/org/codelibs/fess/action/base/FessBaseAction.java

@@ -0,0 +1,827 @@
+/*
+
+ * 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.codelibs.fess.action.base;
+
+import java.lang.reflect.Method;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.action.ActionMessages;
+import org.codelibs.fess.entity.LoginInfo;
+import org.codelibs.fess.lasta.core.direction.FessConfig;
+import org.codelibs.fess.lasta.web.action.FessJspPath;
+import org.codelibs.fess.lasta.web.action.FessMessages;
+import org.codelibs.robot.dbflute.optional.OptionalObject;
+import org.codelibs.sastruts.core.SSCConstants;
+import org.dbflute.bhv.proposal.callback.ExecutedSqlCounter;
+import org.dbflute.bhv.proposal.callback.TraceableSqlAdditionalInfoProvider;
+import org.dbflute.exception.EntityAlreadyDeletedException;
+import org.dbflute.exception.EntityAlreadyExistsException;
+import org.dbflute.exception.EntityAlreadyUpdatedException;
+import org.dbflute.helper.HandyDate;
+import org.dbflute.hook.AccessContext;
+import org.dbflute.hook.CallbackContext;
+import org.dbflute.hook.SqlFireHook;
+import org.dbflute.hook.SqlStringFilter;
+import org.dbflute.saflute.core.exception.ApplicationBaseException;
+import org.dbflute.saflute.core.exception.ExceptionTranslator;
+import org.dbflute.saflute.core.magic.ThreadCacheContext;
+import org.dbflute.saflute.core.time.TimeManager;
+import org.dbflute.saflute.db.dbflute.accesscontext.AccessContextArranger;
+import org.dbflute.saflute.db.dbflute.accesscontext.AccessContextResource;
+import org.dbflute.saflute.db.dbflute.accesscontext.PreparedAccessContext;
+import org.dbflute.saflute.db.dbflute.callbackcontext.RomanticTraceableSqlFireHook;
+import org.dbflute.saflute.db.dbflute.callbackcontext.RomanticTraceableSqlStringFilter;
+import org.dbflute.saflute.web.action.RootAction;
+import org.dbflute.saflute.web.action.api.ApiManager;
+import org.dbflute.saflute.web.action.api.ApiResult;
+import org.dbflute.saflute.web.action.callback.ActionCallback;
+import org.dbflute.saflute.web.action.callback.ActionExecuteMeta;
+import org.dbflute.saflute.web.action.exception.ForcedIllegalTransitionApplicationException;
+import org.dbflute.saflute.web.action.exception.ForcedRequest404NotFoundException;
+import org.dbflute.saflute.web.action.exception.GetParameterNotFoundException;
+import org.dbflute.saflute.web.action.exception.MessageKeyApplicationException;
+import org.dbflute.saflute.web.servlet.request.RequestManager;
+import org.dbflute.saflute.web.servlet.session.SessionManager;
+import org.dbflute.saflute.web.servlet.taglib.MyErrorsTag;
+import org.dbflute.saflute.web.servlet.taglib.MyHtmlMessagesTag;
+import org.dbflute.util.DfTypeUtil;
+
+/**
+ * @author jflute
+ */
+public abstract class FessBaseAction extends RootAction implements ActionCallback, FessJspPath {
+
+    // ===================================================================================
+    //                                                                          Definition
+    //                                                                          ==========
+    private static final Log LOG = LogFactory.getLog(FessBaseAction.class);
+
+    // ===================================================================================
+    //                                                                           Attribute
+    //                                                                           =========
+    @Resource
+    protected FessConfig fessConfig;
+
+    /** The manager of time. (NotNull) */
+    @Resource
+    protected TimeManager timeManager;
+
+    /** The manager of session. (NotNull) */
+    @Resource
+    protected SessionManager sessionManager;
+
+    /** The translator of exception. (NotNull) */
+    @Resource
+    protected ExceptionTranslator exceptionTranslator;
+
+    /** The manager of API. (NotNull) */
+    @Resource
+    protected ApiManager apiManager;
+
+    // ===================================================================================
+    //                                                                              Login
+    //                                                                             =======
+    protected OptionalObject<LoginInfo> getLoginInfo() {
+        return OptionalObject.of(sessionManager.getAttribute(SSCConstants.USER_INFO));
+    }
+
+    // ===================================================================================
+    //                                                                             Message
+    //                                                                             =======
+    /**
+     * Create the action messages basically for session global errors or messages.
+     * @return The new-created action messages provided from Struts. (NotNull)
+     */
+    protected ActionMessages createActionMessages() { // should be overridden as type-safe properties
+        return new ActionMessages();
+    }
+
+    // -----------------------------------------------------
+    //                                                Errors
+    //                                                ------
+    /**
+     * Save message as (global) action errors. (after deleting existing messages) <br>
+     * This message will be deleted immediately after display if you use {@link MyErrorsTag}.
+     * @param messageKey The message key to be saved. (NotNull)
+     * @param args The varying array of arguments for the message. (NullAllowed, EmptyAllowed)
+     */
+    protected void saveErrors(String messageKey, Object... args) {
+        sessionManager.saveErrors(messageKey, args);
+    }
+
+    /**
+     * Save message as (global) action errors. (after deleting existing messages) <br>
+     * This message will be deleted immediately after display if you use {@link MyErrorsTag}.
+     * @param errors The action message for errors. (NotNull, EmptyAllowed: removes existing errors)
+     */
+    protected void saveErrors(ActionMessages errors) {
+        sessionManager.saveErrors(errors);
+    }
+
+    /**
+     * Add message as (global) action errors to rear of existing messages. <br>
+     * This message will be deleted immediately after display if you use {@link MyErrorsTag}.
+     * @param messageKey The message key to be added. (NotNull)
+     * @param args The varying array of arguments for the message. (NullAllowed, EmptyAllowed)
+     */
+    protected void addErrors(String messageKey, Object... args) {
+        sessionManager.addErrors(messageKey, args);
+    }
+
+    /**
+     * Does it have messages as (global or specified property) action errors at least one?
+     * @return The determination, true or false.
+     */
+    protected boolean hasErrors() {
+        return sessionManager.hasErrors();
+    }
+
+    /**
+     * Get action message from (global) action errors.
+     * @return The object for action message. (NullAllowed: if no errors in session)
+     */
+    protected ActionMessages getErrors() {
+        return sessionManager.getErrors();
+    }
+
+    /**
+     * Clear (global) action errors from session.
+     */
+    protected void clearErrors() {
+        sessionManager.clearErrors();
+    }
+
+    // -----------------------------------------------------
+    //                                              Messages
+    //                                              --------
+    /**
+     * Save message as (global) action messages. (after deleting existing messages) <br>
+     * This message will be deleted immediately after display if you use {@link MyHtmlMessagesTag}.
+     * @param messageKey The message key to be saved. (NotNull)
+     * @param args The varying array of arguments for the message. (NullAllowed, EmptyAllowed)
+     */
+    protected void saveMessages(String messageKey, Object... args) {
+        sessionManager.saveMessages(messageKey, args);
+    }
+
+    /**
+     * Save message as (global) action messages. (after deleting existing messages) <br>
+     * This message will be deleted immediately after display if you use {@link MyHtmlMessagesTag}.
+     * @param messages The action message for messages. (NotNull, EmptyAllowed: removes existing messages)
+     */
+    protected void saveMessages(ActionMessages messages) {
+        sessionManager.saveMessages(messages);
+    }
+
+    /**
+     * Add message as (global) action messages to rear of existing messages. <br>
+     * This message will be deleted immediately after display if you use {@link MyHtmlMessagesTag}.
+     * @param messageKey The message key to be added. (NotNull)
+     * @param args The varying array of arguments for the message. (NullAllowed, EmptyAllowed)
+     */
+    protected void addMessages(String messageKey, Object... args) {
+        sessionManager.addMessages(messageKey, args);
+    }
+
+    /**
+     * Does it have messages as (global or specified property) action messages at least one?
+     * @return The determination, true or false.
+     */
+    protected boolean hasMessages() {
+        return sessionManager.hasMessages();
+    }
+
+    /**
+     * Get action message from (global) action errors.
+     * @return The object for action message. (NullAllowed: if no messages in session)
+     */
+    protected ActionMessages getMessages() {
+        return sessionManager.getMessages();
+    }
+
+    /**
+     * Clear (global) action messages from session.
+     */
+    protected void clearMessages() {
+        sessionManager.clearMessages();
+    }
+
+    // ===================================================================================
+    //                                                                            Callback
+    //                                                                            ========
+    // [typical callback process]
+    // read the source code for the details
+    // (because of no comment here)
+    // -----------------------------------------------------
+    //                                                Before
+    //                                                ------
+    @Override
+    public final String godHandActionPrologue(final ActionExecuteMeta executeMeta) { // fixed process
+        arrangeThreadCacheContextBasicItem(executeMeta);
+        arrangePreparedAccessContext(executeMeta);
+        arrangeCallbackContext(executeMeta); // should be after access-context (using access context's info)
+        arrangeThreadCacheContextLoginItem(executeMeta);
+        return null;
+    }
+
+    protected void arrangeThreadCacheContextBasicItem(ActionExecuteMeta executeMeta) {
+        if (ThreadCacheContext.exists()) { // basically true, just in case
+            ThreadCacheContext.registerRequestPath(requestManager.getRoutingOriginRequestPathAndQuery());
+            ThreadCacheContext.registerEntryMethod(executeMeta.getActionMethod());
+        }
+    }
+
+    protected void arrangeThreadCacheContextLoginItem(ActionExecuteMeta executeMeta) {
+        if (ThreadCacheContext.exists()) { // basically true, just in case
+            ThreadCacheContext.registerUserBean(getLoginInfo().orElse(null)); // basically for asynchronous
+        }
+    }
+
+    @Override
+    public String godHandBefore(ActionExecuteMeta executeMeta) { // you can override
+        return null;
+    }
+
+    @Override
+    public String callbackBefore(ActionExecuteMeta executeMeta) { // you can override
+        return null;
+    }
+
+    // -----------------------------------------------------
+    //                                            on Success
+    //                                            ----------
+    @Override
+    public String godHandSuccessMonologue(ActionExecuteMeta executeMeta) {
+        return null;
+    }
+
+    // -----------------------------------------------------
+    //                                            on Failure
+    //                                            ----------
+    @Override
+    public String godHandExceptionMonologue(ActionExecuteMeta executeMeta) { // fixed process
+        return handleActionException(executeMeta);
+    }
+
+    protected String handleActionException(ActionExecuteMeta executeMeta) {
+        final RuntimeException cause = executeMeta.getFailureCause();
+        RuntimeException translated = null;
+        try {
+            translateException(cause);
+        } catch (RuntimeException e) {
+            translated = e;
+        }
+        final RuntimeException handlingEx = translated != null ? translated : cause;
+        final String nextPath = handleApplicationException(executeMeta, handlingEx);
+        if (nextPath != null) {
+            return nextPath;
+        }
+        if (translated != null) {
+            throw translated;
+        }
+        return null;
+    }
+
+    protected void translateException(RuntimeException cause) {
+        exceptionTranslator.translateException(cause);
+    }
+
+    // -----------------------------------------------------
+    //                                               Finally
+    //                                               -------
+    @Override
+    public void callbackFinally(ActionExecuteMeta executeMeta) { // you can override
+    }
+
+    @Override
+    public void godHandFinally(ActionExecuteMeta executeMeta) { // you can override
+    }
+
+    @Override
+    public final void godHandActionEpilogue(ActionExecuteMeta executeMeta) { // fixed process
+        if (executeMeta.isForwardToJsp()) {
+            arrangeNoCacheResponseWhenJsp(executeMeta);
+        }
+        handleSqlCount(executeMeta);
+        clearCallbackContext();
+        clearPreparedAccessContext();
+    }
+
+    protected void arrangeNoCacheResponseWhenJsp(ActionExecuteMeta executeMeta) {
+        responseManager.addNoCache();
+    }
+
+    // -----------------------------------------------------
+    //                                            Adjustment
+    //                                            ----------
+    @Override
+    protected String movedPermanently(String redirectUrl) {
+        // e.g. godHandBefore() needs dummy to stop execution
+        super.movedPermanently(redirectUrl);
+        return responseResolved();
+    }
+
+    /**
+     * Return as response resolved. <br>
+     * Basically used in action callback or action execute. <br>
+     * You should use this to stop execution in callback.
+     * <pre>
+     * ... // resolve response by other way
+     * return responseResolved(); // stop execution after here
+     * </pre>
+     * @return The dummy value that means resolved. (NotNull)
+     */
+    protected String responseResolved() {
+        return ActionCallback.RESPONSE_RESOLVED_DUMMY_FORWARD;
+    }
+
+    // ===================================================================================
+    //                                                                      Access Context
+    //                                                                      ==============
+    /**
+     * Arrange prepared access context for DBFlute, which is used for common columns setup. <br>
+     * This is called by callback process so you should NOT call this directly in your action.
+     * @param executeMeta The meta of action execute. (NotNull)
+     */
+    protected void arrangePreparedAccessContext(ActionExecuteMeta executeMeta) { // called by callback
+        final AccessContextArranger arranger = createAccessContextArranger();
+        final AccessContextResource resource = createAccessContextResource(executeMeta);
+        final AccessContext accessContext = arranger.arrangePreparedAccessContext(resource);
+        PreparedAccessContext.setAccessContextOnThread(accessContext);
+    }
+
+    /**
+     * Create the arranger of access context.
+     * @return The instance of arranger. (NotNull)
+     */
+    protected AccessContextArranger createAccessContextArranger() {
+        return new AccessContextArranger() {
+            public AccessContext arrangePreparedAccessContext(final AccessContextResource resource) {
+                final AccessContext context = new AccessContext();
+                // uses provider to synchronize it with transaction time
+                context.setAccessLocalDateTimeProvider(() -> {
+                    return timeManager.getCurrentLocalDateTime();
+                });
+                // uses provider to synchronize it with login status in session
+                context.setAccessUserProvider(() -> {
+                    return buildAccessUserTrace(resource);
+                });
+                return context;
+            }
+
+            private String buildAccessUserTrace(AccessContextResource resource) {
+                return getLoginInfo().map(info -> info.getUsername()).orElse("system");
+            }
+        };
+    }
+
+    /**
+     * Create the resource of access context.
+     * @param executeMeta The meta of action execute. (NotNull)
+     * @return The new-created resource of access context. (NotNull)
+     */
+    protected AccessContextResource createAccessContextResource(ActionExecuteMeta executeMeta) {
+        final Method method = executeMeta.getActionMethod();
+        final String classTitle = DfTypeUtil.toClassTitle(method.getDeclaringClass());
+        return new AccessContextResource(classTitle, method);
+    }
+
+    /**
+     * Clear prepared access context. <br>
+     * This is called by callback process so you should NOT call this directly in your action.
+     */
+    protected void clearPreparedAccessContext() { // called by callback
+        PreparedAccessContext.clearAccessContextOnThread();
+    }
+
+    // ===================================================================================
+    //                                                                    Callback Context
+    //                                                                    ================
+    /**
+     * Arrange callback context for DBFlute, which is used for several purpose. <br>
+     * This is called by callback process so you should NOT call this directly in your action.
+     * @param executeMeta The meta of action execute. (NotNull)
+     */
+    protected void arrangeCallbackContext(final ActionExecuteMeta executeMeta) {
+        final SqlFireHook sqlFireHook = createSqlFireHook(executeMeta);
+        CallbackContext.setSqlFireHookOnThread(sqlFireHook);
+        final SqlStringFilter filter = createSqlStringFilter(executeMeta);
+        CallbackContext.setSqlStringFilterOnThread(filter);
+    }
+
+    /**
+     * Create the filter of SQL string for DBFlute.
+     * @param executeMeta The meta of action execute. (NotNull)
+     * @return The hook of SQL fire. (NullAllowed: if null, no hook)
+     */
+    protected SqlFireHook createSqlFireHook(ActionExecuteMeta executeMeta) {
+        return newRomanticTraceableSqlFireHook();
+    }
+
+    protected RomanticTraceableSqlFireHook newRomanticTraceableSqlFireHook() {
+        return new RomanticTraceableSqlFireHook();
+    }
+
+    /**
+     * Create the filter of SQL string for DBFlute.
+     * @param executeMeta The meta of action execute. (NotNull)
+     * @return The filter of SQL string. (NullAllowed: if null, no filter)
+     */
+    protected SqlStringFilter createSqlStringFilter(final ActionExecuteMeta executeMeta) {
+        final Method actionMethod = executeMeta.getActionMethod();
+        return newRomanticTraceableSqlStringFilter(actionMethod, new TraceableSqlAdditionalInfoProvider() {
+            @Override
+            public String provide() { // lazy because it may be auto-login later
+                return buildSqlMarkingAdditionalInfo();
+            }
+        });
+    }
+
+    protected RomanticTraceableSqlStringFilter newRomanticTraceableSqlStringFilter(Method actionMethod,
+            TraceableSqlAdditionalInfoProvider additionalInfoProvider) {
+        return new RomanticTraceableSqlStringFilter(actionMethod, additionalInfoProvider);
+    }
+
+    /**
+     * Build string for additional info of SQL marking.
+     * @return The string expression of additional info. (NullAllowed: if null, no additional info)
+     */
+    protected String buildSqlMarkingAdditionalInfo() {
+        return "{" + getLoginInfo().map(info -> String.valueOf(info.isAdministrator())).orElse("*No login") + "}";
+        // it doesn't contain user ID for SQL cache in DBMS
+    }
+
+    /**
+     * Handle count of SQL execution in the request.
+     * @param executeMeta The meta of action execute. (NotNull)
+     */
+    protected void handleSqlCount(final ActionExecuteMeta executeMeta) {
+        final CallbackContext context = CallbackContext.getCallbackContextOnThread();
+        if (context == null) {
+            return;
+        }
+        final SqlStringFilter filter = context.getSqlStringFilter();
+        if (filter == null || !(filter instanceof ExecutedSqlCounter)) {
+            return;
+        }
+        final ExecutedSqlCounter counter = ((ExecutedSqlCounter) filter);
+        final int limitCountOfSql = getLimitCountOfSql(executeMeta);
+        if (limitCountOfSql >= 0 && counter.getTotalCountOfSql() > limitCountOfSql) {
+            handleTooManySqlExecution(executeMeta, counter);
+        }
+        final String exp = counter.toLineDisp();
+        requestManager.setAttribute(RequestManager.KEY_DBFLUTE_SQL_COUNT, exp); // logged by logging filter
+    }
+
+    /**
+     * Handle too many SQL executions.
+     * @param executeMeta The meta of action execute. (NotNull)
+     * @param sqlCounter The counter object for SQL executions. (NotNull)
+     */
+    protected void handleTooManySqlExecution(final ActionExecuteMeta executeMeta, final ExecutedSqlCounter sqlCounter) {
+        final String actionDisp = buildActionDisp(executeMeta);
+        LOG.warn("*Too many SQL executions: " + sqlCounter.getTotalCountOfSql() + " in " + actionDisp);
+    }
+
+    protected String buildActionDisp(ActionExecuteMeta executeMeta) {
+        final Method method = executeMeta.getActionMethod();
+        final Class<?> declaringClass = method.getDeclaringClass();
+        return declaringClass.getSimpleName() + "." + method.getName() + "()";
+    }
+
+    /**
+     * Get the limit count of SQL execution. <br>
+     * You can override if you need.
+     * @param executeMeta The meta of action execute. (NotNull)
+     * @return The max count allowed for SQL executions. (MinusAllowed: if minus, no check)
+     */
+    protected int getLimitCountOfSql(ActionExecuteMeta executeMeta) {
+        return 30; // as default
+    }
+
+    /**
+     * Clear callback context. <br>
+     * This is called by callback process so you should NOT call this directly in your action.
+     */
+    protected void clearCallbackContext() {
+        CallbackContext.clearSqlStringFilterOnThread();
+        CallbackContext.clearSqlFireHookOnThread();
+    }
+
+    // ===================================================================================
+    //                                                               Application Exception
+    //                                                               =====================
+    /**
+     * Handle the application exception thrown by (basically) action execute. <br>
+     * Though this is same as global-exceptions settings of Struts,
+     * There is more flexibility than the function so you can set it here. <br>
+     * This is called by callback process so you should NOT call this directly in your action.
+     * @param executeMeta The meta of action execute. (NotNull)
+     * @param cause The exception thrown by (basically) action execute, might be translated. (NotNull)
+     * @return The forward path. (NullAllowed: if not null, it goes to the path)
+     */
+    protected String handleApplicationException(ActionExecuteMeta executeMeta, RuntimeException cause) { // called by callback
+        final String forwardTo = doHandleApplicationException(executeMeta, cause);
+        if (forwardTo != null && executeMeta.isApiAction()) {
+            return dispatchApiApplicationException(executeMeta, cause);
+        }
+        return forwardTo;
+    }
+
+    protected String doHandleApplicationException(ActionExecuteMeta executeMeta, RuntimeException cause) {
+        String forwardTo = null;
+        if (cause instanceof ApplicationBaseException) {
+            final ApplicationBaseException appEx = (ApplicationBaseException) cause;
+            if (appEx instanceof GetParameterNotFoundException) {
+                forwardTo = handleGetParameterNotFoundException((GetParameterNotFoundException) appEx);
+            } else if (appEx instanceof MessageKeyApplicationException) {
+                forwardTo = handleErrorsApplicationException((MessageKeyApplicationException) appEx);
+            } else {
+                forwardTo = handleSpecialApplicationException(appEx);
+            }
+            if (forwardTo == null) {
+                forwardTo = handleUnknownApplicationException(appEx);
+            }
+            reflectEmbeddedApplicationMessagesIfExists(appEx); // override existing messages if exists
+        } else {
+            if (cause instanceof EntityAlreadyDeletedException) {
+                forwardTo = handleEntityAlreadyDeletedException((EntityAlreadyDeletedException) cause);
+            } else if (cause instanceof EntityAlreadyUpdatedException) {
+                forwardTo = handleEntityAlreadyUpdatedException((EntityAlreadyUpdatedException) cause);
+            } else if (cause instanceof EntityAlreadyExistsException) {
+                forwardTo = handleEntityAlreadyExistsException((EntityAlreadyExistsException) cause);
+            }
+        }
+        if (forwardTo != null) {
+            showApplicationExceptionHandling(cause, forwardTo);
+        }
+        return forwardTo;
+    }
+
+    protected void showApplicationExceptionHandling(RuntimeException cause, String forwardTo) {
+        if (LOG.isDebugEnabled()) {
+            // not show forwardTo because of forwarding log later
+            final StringBuilder sb = new StringBuilder();
+            sb.append("...Handling application exception:");
+            sb.append("\n_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/");
+            sb.append("\n[Application Exception]");
+            sb.append("\n").append(cause.getMessage());
+            final ActionMessages errors = getErrors();
+            if (errors != null) {
+                sb.append("\n").append(errors.toString());
+            }
+            buildApplicationExceptionStackTrace(cause, sb);
+            sb.append("\n_/_/_/_/_/_/_/_/_/_/");
+            LOG.debug(sb.toString());
+        }
+    }
+
+    protected void buildApplicationExceptionStackTrace(RuntimeException cause, StringBuilder sb) {
+        final StackTraceElement[] stackTrace = cause.getStackTrace();
+        if (stackTrace == null) { // just in case
+            return;
+        }
+        int index = 0;
+        for (StackTraceElement element : stackTrace) {
+            if (index > 10) { // not all because it's not error
+                break;
+            }
+            final String className = element.getClassName();
+            final String fileName = element.getFileName(); // might be null
+            final int lineNumber = element.getLineNumber();
+            final String methodName = element.getMethodName();
+            sb.append("\n at ").append(className).append(".").append(methodName);
+            sb.append("(").append(fileName);
+            if (lineNumber >= 0) {
+                sb.append(":").append(lineNumber);
+            }
+            sb.append(")");
+            ++index;
+        }
+    }
+
+    protected void reflectEmbeddedApplicationMessagesIfExists(ApplicationBaseException appEx) {
+        final String errorsKey = appEx.getErrorKey();
+        if (errorsKey != null) {
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("...Saving embedded application message as action error: " + errorsKey);
+            }
+            saveErrors(errorsKey, appEx.getErrorArgs());
+        }
+    }
+
+    protected String dispatchApiApplicationException(ActionExecuteMeta executeMeta, RuntimeException cause) {
+        final ActionMessages errors = getErrors(); // might be null in minor case
+        final ApiResult result = apiManager.prepareApplicationExceptionResult(errors, executeMeta, cause);
+        apiManager.writeJsonResponse(result);
+        return apiManager.forwardToApiResolvedDummy();
+    }
+
+    // -----------------------------------------------------
+    //                                        DBFlute Entity
+    //                                        --------------
+    protected String handleEntityAlreadyDeletedException(EntityAlreadyDeletedException cause) {
+        saveErrors(getErrorsAppAlreadyDeletedKey());
+        return getErrorMessageAlreadyDeletedJsp();
+    }
+
+    protected String getErrorsAppAlreadyDeletedKey() {
+        return FessMessages.ERRORS_APP_ALREADY_DELETED;
+    }
+
+    protected String getErrorMessageAlreadyDeletedJsp() {
+        return getErrorMessageJsp(); // as default
+    }
+
+    protected String handleEntityAlreadyUpdatedException(EntityAlreadyUpdatedException cause) {
+        saveErrors(getErrorsAppAlreadyUpdatedKey());
+        return getErrorMessageAlreadyUpdatedJsp();
+    }
+
+    protected String getErrorsAppAlreadyUpdatedKey() {
+        return FessMessages.ERRORS_APP_ALREADY_UPDATED;
+    }
+
+    protected String getErrorMessageAlreadyUpdatedJsp() {
+        return getErrorMessageJsp(); // as default
+    }
+
+    protected String handleEntityAlreadyExistsException(EntityAlreadyExistsException cause) {
+        saveErrors(getErrorsAppAlreadyExistsKey());
+        return getErrorMessageAlreadyExistsJsp();
+    }
+
+    protected String getErrorsAppAlreadyExistsKey() {
+        return FessMessages.ERRORS_APP_ALREADY_EXISTS;
+    }
+
+    protected String getErrorMessageAlreadyExistsJsp() {
+        return getErrorMessageJsp(); // as default
+    }
+
+    // -----------------------------------------------------
+    //                                         Get Parameter
+    //                                         -------------
+    protected String handleGetParameterNotFoundException(GetParameterNotFoundException appEx) {
+        saveErrors(getErrorsAppIllegalTransitionKey());
+        return getErrorMessageJsp();
+    }
+
+    protected String getErrorsAppIllegalTransitionKey() {
+        return FessMessages.ERRORS_APP_ILLEGAL_TRANSITION;
+    }
+
+    protected String getErrorMessageJsp() {
+        return path_Error_ErrorMessageJsp;
+    }
+
+    // -----------------------------------------------------
+    //                                           Message Key
+    //                                           -----------
+    protected String handleErrorsApplicationException(MessageKeyApplicationException appEx) {
+        // no save here because of saved later
+        //saveErrors(appEx.getErrors());
+        return getErrorMessageJsp();
+    }
+
+    // -----------------------------------------------------
+    //                                               Special
+    //                                               -------
+    protected String handleSpecialApplicationException(ApplicationBaseException appEx) { // you can override
+        return null;
+    }
+
+    // -----------------------------------------------------
+    //                                               Unknown
+    //                                               -------
+    protected String handleUnknownApplicationException(ApplicationBaseException appEx) {
+        return null; // basically no way
+    }
+
+    // ===================================================================================
+    //                                                                       Assert Helper
+    //                                                                       =============
+    /**
+     * Assert the condition is true or it throws illegal transition forcedly. <br>
+     * You can use this in your action process against strange request parameters.
+     * @param condition Your determination, true or false. (false: illegal transition)
+     */
+    protected void assertTrueOrForcedIllegalTransition(boolean condition) {
+        if (!condition) {
+            throw new ForcedIllegalTransitionApplicationException(getErrorsAppIllegalTransitionKey());
+        }
+    }
+
+    /**
+     * Assert the condition is true or it throws 404 not found forcedly. <br>
+     * You can use this in your action process against invalid URL parameters.
+     * @param condition Your determination, true or false. (false: 404 not found)
+     */
+    protected void assertTrueOrForcedRequest404NotFound(boolean condition) {
+        if (!condition) {
+            String msg = "from Forced 404 NotFound assertion"; // debug message
+            throw new ForcedRequest404NotFoundException(msg);
+        }
+    }
+
+    // ===================================================================================
+    //                                                                   Conversion Helper
+    //                                                                   =================
+    // -----------------------------------------------------
+    //                                            Collectors
+    //                                            ----------
+    protected <T> Collector<T, ?, List<T>> toList() {
+        return Collectors.toList();
+    }
+
+    protected <T, K, U> Collector<T, ?, Map<K, U>> toMap(Function<? super T, ? extends K> keyMapper,
+            Function<? super T, ? extends U> valueMapper) {
+        return Collectors.toMap(keyMapper, valueMapper);
+    }
+
+    // -----------------------------------------------------
+    //                                           String Date
+    //                                           -----------
+    protected String toStringDate(LocalDate localDate) {
+        return localDate != null ? doConvertToDisp(localDate) : null;
+    }
+
+    protected String toStringDate(LocalDateTime localDateTime) {
+        return localDateTime != null ? doConvertToStringDate(localDateTime) : null;
+    }
+
+    private String doConvertToDisp(LocalDate localDate) {
+        return new HandyDate(localDate, getConversionTimeZone()).toDisp(getStringDatePattern());
+    }
+
+    private String doConvertToStringDate(LocalDateTime localDateTime) {
+        return new HandyDate(localDateTime, getConversionTimeZone()).toDisp(getStringDatePattern());
+    }
+
+    protected String toStringDateTime(LocalDateTime localDateTime) {
+        return localDateTime != null ? doConvertToStringDateTime(localDateTime) : null;
+    }
+
+    private String doConvertToStringDateTime(LocalDateTime localDateTime) {
+        return new HandyDate(localDateTime, getConversionTimeZone()).toDisp(getStringDateTimePattern());
+    }
+
+    // -----------------------------------------------------
+    //                                            Local Date
+    //                                            ----------
+    protected LocalDate toLocalDate(String dateExp) {
+        if (dateExp == null || dateExp.isEmpty()) {
+            return null;
+        }
+        TimeZone userTimeZone = getConversionTimeZone();
+        return new HandyDate(dateExp, userTimeZone).getLocalDate();
+    }
+
+    protected LocalDateTime toLocalDateTime(String dateTimeExp) {
+        if (dateTimeExp == null || dateTimeExp.isEmpty()) {
+            return null;
+        }
+        TimeZone userTimeZone = getConversionTimeZone();
+        return new HandyDate(dateTimeExp, userTimeZone).getLocalDateTime();
+    }
+
+    // -----------------------------------------------------
+    //                                   Conversion Resource
+    //                                   -------------------
+    protected String getStringDatePattern() {
+        return "yyyy/MM/dd";
+    }
+
+    protected String getStringDateTimePattern() {
+        return "yyyy/MM/dd HH:mm:ss";
+    }
+
+    protected TimeZone getConversionTimeZone() {
+        return requestManager.getUserTimeZone();
+    }
+}

+ 125 - 0
src/main/java/org/codelibs/fess/lasta/core/direction/FessConfig.java

@@ -0,0 +1,125 @@
+package org.codelibs.fess.lasta.core.direction;
+
+import org.codelibs.fess.lasta.core.direction.FessEnv;
+
+/**
+ * @author FreeGen
+ */
+public interface FessConfig extends FessEnv {
+
+    /** The key of the configuration. e.g. Fess */
+    String DOMAIN_TITLE = "domain.title";
+
+    /** The key of the configuration. e.g. / */
+    String COOKIE_DEFAULT_PATH = "cookie.default.path";
+
+    /** The key of the configuration. e.g. 31556926 */
+    String COOKIE_DEFAULT_EXPIRE = "cookie.default.expire";
+
+    /** The key of the configuration. e.g. 315360000 */
+    String COOKIE_ETERNAL_EXPIRE = "cookie.eternal.expire";
+
+    /**
+     * Get the value of property as {@link String}.
+     * @param propertyKey The key of the property. (NotNull)
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String get(String propertyKey);
+
+    /**
+     * Is the property true?
+     * @param propertyKey The key of the property which is boolean type. (NotNull)
+     * @return The determination, true or false. (if the property can be true, returns true)
+     */
+    boolean is(String propertyKey);
+
+    /**
+     * Get the value for the key 'domain.title'. <br />
+     * The value is, e.g. Fess <br />
+     * comment: The title of domain the application for logging
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String getDomainTitle();
+
+    /**
+     * Get the value for the key 'cookie.default.path'. <br />
+     * The value is, e.g. / <br />
+     * comment: The default path of cookie (basically '/' if no context path)
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String getCookieDefaultPath();
+
+    /**
+     * Get the value for the key 'cookie.default.expire'. <br />
+     * The value is, e.g. 31556926 <br />
+     * comment: The default expire of cookie in seconds e.g. 31556926: one year, 86400: one day
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String getCookieDefaultExpire();
+
+    /**
+     * Get the value for the key 'cookie.default.expire' as {@link Integer}. <br />
+     * The value is, e.g. 31556926 <br />
+     * comment: The default expire of cookie in seconds e.g. 31556926: one year, 86400: one day
+     * @return The value of found property. (NullAllowed: if null, not found)
+     * @throws NumberFormatException When the property is not integer.
+     */
+    Integer getCookieDefaultExpireAsInteger();
+
+    /**
+     * Get the value for the key 'cookie.eternal.expire'. <br />
+     * The value is, e.g. 315360000 <br />
+     * comment: The eternal expire of cookie in seconds e.g. 315360000: ten year, 86400: one day
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String getCookieEternalExpire();
+
+    /**
+     * Get the value for the key 'cookie.eternal.expire' as {@link Integer}. <br />
+     * The value is, e.g. 315360000 <br />
+     * comment: The eternal expire of cookie in seconds e.g. 315360000: ten year, 86400: one day
+     * @return The value of found property. (NullAllowed: if null, not found)
+     * @throws NumberFormatException When the property is not integer.
+     */
+    Integer getCookieEternalExpireAsInteger();
+
+    /**
+     * The simple implementation for configuration.
+     * @author FreeGen
+     */
+    public static class SimpleImpl extends FessEnv.SimpleImpl implements FessConfig {
+
+        /** The serial version UID for object serialization. (Default) */
+        private static final long serialVersionUID = 1L;
+
+        /** {@inheritDoc} */
+        public String getDomainTitle() {
+            return get(FessConfig.DOMAIN_TITLE);
+        }
+
+        /** {@inheritDoc} */
+        public String getCookieDefaultPath() {
+            return get(FessConfig.COOKIE_DEFAULT_PATH);
+        }
+
+        /** {@inheritDoc} */
+        public String getCookieDefaultExpire() {
+            return get(FessConfig.COOKIE_DEFAULT_EXPIRE);
+        }
+
+        /** {@inheritDoc} */
+        public Integer getCookieDefaultExpireAsInteger() {
+            return getAsInteger(FessConfig.COOKIE_DEFAULT_EXPIRE);
+        }
+
+        /** {@inheritDoc} */
+        public String getCookieEternalExpire() {
+            return get(FessConfig.COOKIE_ETERNAL_EXPIRE);
+        }
+
+        /** {@inheritDoc} */
+        public Integer getCookieEternalExpireAsInteger() {
+            return getAsInteger(FessConfig.COOKIE_ETERNAL_EXPIRE);
+        }
+    }
+}

+ 260 - 0
src/main/java/org/codelibs/fess/lasta/core/direction/FessEnv.java

@@ -0,0 +1,260 @@
+package org.codelibs.fess.lasta.core.direction;
+
+import org.dbflute.saflute.core.direction.ObjectiveConfig;
+
+/**
+ * @author FreeGen
+ */
+public interface FessEnv {
+
+    /** The key of the configuration. e.g. true */
+    String DEVELOPMENT_HERE = "development.here";
+
+    /** The key of the configuration. e.g. Local Development */
+    String ENVIRONMENT_TITLE = "environment.title";
+
+    /** The key of the configuration. e.g. false */
+    String FRAMEWORK_DEBUG = "framework.debug";
+
+    /** The key of the configuration. e.g. 0 */
+    String TIME_ADJUST_TIME_MILLIS = "time.adjust.time.millis";
+
+    /** The key of the configuration. e.g. true */
+    String MAIL_SEND_MOCK = "mail.send.mock";
+
+    /** The key of the configuration. e.g. localhost:25 */
+    String MAIL_SMTP_SERVER_DEFAULT_HOST_AND_PORT = "mail.smtp.server.default.host.and.port";
+
+    /** The key of the configuration. e.g. jdbc:mysql://localhost:3306/maihamadb */
+    String JDBC_URL = "jdbc.url";
+
+    /** The key of the configuration. e.g. maihamauser */
+    String JDBC_USER = "jdbc.user";
+
+    /** The key of the configuration. e.g. maihamaword */
+    String JDBC_PASSWORD = "jdbc.password";
+
+    /** The key of the configuration. e.g. 10 */
+    String JDBC_CONNECTION_POOLING_SIZE = "jdbc.connection.pooling.size";
+
+    /**
+     * Get the value of property as {@link String}.
+     * @param propertyKey The key of the property. (NotNull)
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String get(String propertyKey);
+
+    /**
+     * Is the property true?
+     * @param propertyKey The key of the property which is boolean type. (NotNull)
+     * @return The determination, true or false. (if the property can be true, returns true)
+     */
+    boolean is(String propertyKey);
+
+    /**
+     * Get the value for the key 'development.here'. <br />
+     * The value is, e.g. true <br />
+     * comment: Is development environment here? (used for various purpose, you should set false if unknown)
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String getDevelopmentHere();
+
+    /**
+     * Is the property for the key 'development.here' true? <br />
+     * The value is, e.g. true <br />
+     * comment: Is development environment here? (used for various purpose, you should set false if unknown)
+     * @return The determination, true or false. (if the property can be true, returns true)
+     */
+    boolean isDevelopmentHere();
+
+    /**
+     * Get the value for the key 'environment.title'. <br />
+     * The value is, e.g. Local Development <br />
+     * comment: The title of environment (e.g. local or integartion or production)
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String getEnvironmentTitle();
+
+    /**
+     * Get the value for the key 'framework.debug'. <br />
+     * The value is, e.g. false <br />
+     * comment: Does it enable the Framework internal debug? (true only when emergency)
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String getFrameworkDebug();
+
+    /**
+     * Is the property for the key 'framework.debug' true? <br />
+     * The value is, e.g. false <br />
+     * comment: Does it enable the Framework internal debug? (true only when emergency)
+     * @return The determination, true or false. (if the property can be true, returns true)
+     */
+    boolean isFrameworkDebug();
+
+    /**
+     * Get the value for the key 'time.adjust.time.millis'. <br />
+     * The value is, e.g. 0 <br />
+     * comment: The milliseconds for (relative or absolute) adjust time (set only when test) @LongType *dynamic in development
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String getTimeAdjustTimeMillis();
+
+    /**
+     * Get the value for the key 'time.adjust.time.millis' as {@link Long}. <br />
+     * The value is, e.g. 0 <br />
+     * comment: The milliseconds for (relative or absolute) adjust time (set only when test) @LongType *dynamic in development
+     * @return The value of found property. (NullAllowed: if null, not found)
+     * @throws NumberFormatException When the property is not long.
+     */
+    Long getTimeAdjustTimeMillisAsLong();
+
+    /**
+     * Get the value for the key 'mail.send.mock'. <br />
+     * The value is, e.g. true <br />
+     * comment: Does it send mock mail? (true: no send actually, logging only)
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String getMailSendMock();
+
+    /**
+     * Is the property for the key 'mail.send.mock' true? <br />
+     * The value is, e.g. true <br />
+     * comment: Does it send mock mail? (true: no send actually, logging only)
+     * @return The determination, true or false. (if the property can be true, returns true)
+     */
+    boolean isMailSendMock();
+
+    /**
+     * Get the value for the key 'mail.smtp.server.default.host.and.port'. <br />
+     * The value is, e.g. localhost:25 <br />
+     * comment: SMTP server settings for default: host:port
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String getMailSmtpServerDefaultHostAndPort();
+
+    /**
+     * Get the value for the key 'jdbc.url'. <br />
+     * The value is, e.g. jdbc:mysql://localhost:3306/maihamadb <br />
+     * comment: The URL of database connection for JDBC
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String getJdbcUrl();
+
+    /**
+     * Get the value for the key 'jdbc.user'. <br />
+     * The value is, e.g. maihamauser <br />
+     * comment: The user of database connection for JDBC
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String getJdbcUser();
+
+    /**
+     * Get the value for the key 'jdbc.password'. <br />
+     * The value is, e.g. maihamaword <br />
+     * comment: @Secure The password of database connection for JDBC
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String getJdbcPassword();
+
+    /**
+     * Get the value for the key 'jdbc.connection.pooling.size'. <br />
+     * The value is, e.g. 10 <br />
+     * comment: The (max) pooling size of Seasar's connection pool
+     * @return The value of found property. (NullAllowed: if null, not found)
+     */
+    String getJdbcConnectionPoolingSize();
+
+    /**
+     * Get the value for the key 'jdbc.connection.pooling.size' as {@link Integer}. <br />
+     * The value is, e.g. 10 <br />
+     * comment: The (max) pooling size of Seasar's connection pool
+     * @return The value of found property. (NullAllowed: if null, not found)
+     * @throws NumberFormatException When the property is not integer.
+     */
+    Integer getJdbcConnectionPoolingSizeAsInteger();
+
+    /**
+     * The simple implementation for configuration.
+     * @author FreeGen
+     */
+    public static class SimpleImpl extends ObjectiveConfig implements FessEnv {
+
+        /** The serial version UID for object serialization. (Default) */
+        private static final long serialVersionUID = 1L;
+
+        /** {@inheritDoc} */
+        public String getDevelopmentHere() {
+            return get(FessEnv.DEVELOPMENT_HERE);
+        }
+
+        /** {@inheritDoc} */
+        public boolean isDevelopmentHere() {
+            return is(FessEnv.DEVELOPMENT_HERE);
+        }
+
+        /** {@inheritDoc} */
+        public String getEnvironmentTitle() {
+            return get(FessEnv.ENVIRONMENT_TITLE);
+        }
+
+        /** {@inheritDoc} */
+        public String getFrameworkDebug() {
+            return get(FessEnv.FRAMEWORK_DEBUG);
+        }
+
+        /** {@inheritDoc} */
+        public boolean isFrameworkDebug() {
+            return is(FessEnv.FRAMEWORK_DEBUG);
+        }
+
+        /** {@inheritDoc} */
+        public String getTimeAdjustTimeMillis() {
+            return get(FessEnv.TIME_ADJUST_TIME_MILLIS);
+        }
+
+        /** {@inheritDoc} */
+        public Long getTimeAdjustTimeMillisAsLong() {
+            return getAsLong(FessEnv.TIME_ADJUST_TIME_MILLIS);
+        }
+
+        /** {@inheritDoc} */
+        public String getMailSendMock() {
+            return get(FessEnv.MAIL_SEND_MOCK);
+        }
+
+        /** {@inheritDoc} */
+        public boolean isMailSendMock() {
+            return is(FessEnv.MAIL_SEND_MOCK);
+        }
+
+        /** {@inheritDoc} */
+        public String getMailSmtpServerDefaultHostAndPort() {
+            return get(FessEnv.MAIL_SMTP_SERVER_DEFAULT_HOST_AND_PORT);
+        }
+
+        /** {@inheritDoc} */
+        public String getJdbcUrl() {
+            return get(FessEnv.JDBC_URL);
+        }
+
+        /** {@inheritDoc} */
+        public String getJdbcUser() {
+            return get(FessEnv.JDBC_USER);
+        }
+
+        /** {@inheritDoc} */
+        public String getJdbcPassword() {
+            return get(FessEnv.JDBC_PASSWORD);
+        }
+
+        /** {@inheritDoc} */
+        public String getJdbcConnectionPoolingSize() {
+            return get(FessEnv.JDBC_CONNECTION_POOLING_SIZE);
+        }
+
+        /** {@inheritDoc} */
+        public Integer getJdbcConnectionPoolingSizeAsInteger() {
+            return getAsInteger(FessEnv.JDBC_CONNECTION_POOLING_SIZE);
+        }
+    }
+}

+ 276 - 0
src/main/java/org/codelibs/fess/lasta/core/direction/FessFwAssistantDirector.java

@@ -0,0 +1,276 @@
+/*
+ * 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.codelibs.fess.lasta.core.direction;
+
+import java.util.TimeZone;
+
+import javax.annotation.Resource;
+
+import org.codelibs.fess.lasta.core.direction.sponsor.FessActionAdjustmentProvider;
+import org.codelibs.fess.lasta.core.direction.sponsor.FessTimeResourceProvider;
+import org.codelibs.fess.lasta.core.direction.sponsor.FessUserLocaleProcessProvider;
+import org.codelibs.fess.lasta.core.direction.sponsor.FessUserTimeZoneProcessProvider;
+import org.dbflute.saflute.core.direction.BootProcessCallback;
+import org.dbflute.saflute.core.direction.CachedFwAssistantDirector;
+import org.dbflute.saflute.core.direction.FwAssistantDirector;
+import org.dbflute.saflute.core.direction.OptionalAssistDirection;
+import org.dbflute.saflute.core.direction.OptionalCoreDirection;
+import org.dbflute.saflute.core.security.InvertibleCipher;
+import org.dbflute.saflute.core.security.SecurityResourceProvider;
+import org.dbflute.saflute.db.dbflute.OptionalDBFluteDirection;
+import org.dbflute.saflute.web.action.OptionalActionDirection;
+import org.dbflute.saflute.web.servlet.OptionalServletDirection;
+import org.dbflute.saflute.web.servlet.cookie.CookieResourceProvider;
+import org.dbflute.saflute.web.task.OptionalTaskDirection;
+import org.dbflute.system.DBFluteSystem;
+import org.dbflute.system.provider.DfFinalTimeZoneProvider;
+import org.dbflute.util.DfTypeUtil;
+
+/**
+ * @author jflute
+ */
+public class FessFwAssistantDirector extends CachedFwAssistantDirector {
+
+    // ===================================================================================
+    //                                                                          Definition
+    //                                                                          ==========
+    public static final String FESS_CONFIG_FILE = "fess_config.properties";
+
+    public static final String FESS_ENV_FILE = "fess_env.properties";
+
+    // ===================================================================================
+    //                                                                           Attribute
+    //                                                                           =========
+    @Resource
+    protected FessConfig fessConfig;
+
+    // ===================================================================================
+    //                                                                              Assist
+    //                                                                              ======
+    @Override
+    protected OptionalAssistDirection prepareOptionalAssistDirection() {
+        final OptionalAssistDirection direction = new OptionalAssistDirection();
+        prepareConfiguration(direction);
+        return direction;
+    }
+
+    protected void prepareConfiguration(OptionalAssistDirection direction) {
+        direction.directConfiguration(getDomainConfigFile(),
+                getExtendsConfigFiles());
+    }
+
+    protected String getDomainConfigFile() {
+        return FESS_CONFIG_FILE;
+    }
+
+    protected String[] getExtendsConfigFiles() {
+        return new String[] { FESS_ENV_FILE };
+    }
+
+    // ===================================================================================
+    //                                                                                Core
+    //                                                                                ====
+    @Override
+    protected OptionalCoreDirection prepareOptionalCoreDirection() {
+        final OptionalCoreDirection direction = new OptionalCoreDirection();
+        prepareFramework(direction);
+        prepareSecurity(direction);
+        prepareTime(direction);
+        return direction;
+    }
+
+    // -----------------------------------------------------
+    //                                             Framework
+    //                                             ---------
+    protected void prepareFramework(OptionalCoreDirection direction) {
+        // this configuration is on fess_env.properties
+        // because this is true only when development
+        direction.directDevelopmentHere(fessConfig.isDevelopmentHere());
+
+        // titles are from configurations
+        direction.directLoggingTitle(fessConfig.getDomainTitle(),
+                fessConfig.getEnvironmentTitle());
+
+        // this configuration is on sea_env.properties
+        // because it has no influence to production
+        // even if you set true manually and forget to set false back
+        direction.directFrameworkDebug(fessConfig.isFrameworkDebug()); // basically false
+
+        // you can add your own process when your application is booting
+        direction.directBootProcessCallback(new BootProcessCallback() {
+            public void callback(FwAssistantDirector assistantDirector) {
+                processDBFluteSystem();
+            }
+        });
+    }
+
+    protected void processDBFluteSystem() {
+        DBFluteSystem.unlock();
+        DBFluteSystem.setFinalTimeZoneProvider(new DfFinalTimeZoneProvider() {
+            protected final TimeZone provided = FessUserTimeZoneProcessProvider.centralTimeZone;
+
+            public TimeZone provide() {
+                return provided;
+            }
+
+            @Override
+            public String toString() {
+                return DfTypeUtil.toClassTitle(this) + ":{" + provided.getID()
+                        + "}";
+            }
+        });
+        DBFluteSystem.lock();
+    }
+
+    // -----------------------------------------------------
+    //                                              Security
+    //                                              --------
+    protected void prepareSecurity(OptionalCoreDirection direction) {
+        final String key = getPrimarySecurityWord();
+        final InvertibleCipher primaryInvertibleCipher = InvertibleCipher
+                .createAesCipher(key); // AES for now
+        direction.directSecurity(new SecurityResourceProvider() {
+            public InvertibleCipher providePrimaryInvertibleCipher() {
+                return primaryInvertibleCipher;
+            }
+        });
+    }
+
+    protected String getPrimarySecurityWord() {
+        return "fess:fess"; // hard coding for now
+    }
+
+    // -----------------------------------------------------
+    //                                                  Time
+    //                                                  ----
+    protected void prepareTime(OptionalCoreDirection direction) {
+        direction.directTime(createTimeResourceProvider());
+    }
+
+    protected FessTimeResourceProvider createTimeResourceProvider() {
+        return new FessTimeResourceProvider(fessConfig);
+    }
+
+    // ===================================================================================
+    //                                                                                  DB
+    //                                                                                  ==
+    @Override
+    protected OptionalDBFluteDirection prepareOptionalDBFluteDirection() {
+        final OptionalDBFluteDirection direction = new OptionalDBFluteDirection();
+        return direction;
+    }
+
+    // ===================================================================================
+    //                                                                                 Web
+    //                                                                                 ===
+    // -----------------------------------------------------
+    //                                                Action
+    //                                                ------
+    @Override
+    protected OptionalActionDirection prepareOptionalActionDirection() {
+        final OptionalActionDirection direction = new OptionalActionDirection();
+        prepareAdjustment(direction);
+        prepareMessage(direction);
+        return direction;
+    }
+
+    protected void prepareAdjustment(OptionalActionDirection direction) {
+        direction.directAdjustment(createActionAdjustmentProvider());
+    }
+
+    protected FessActionAdjustmentProvider createActionAdjustmentProvider() {
+        return new FessActionAdjustmentProvider();
+    }
+
+    protected void prepareMessage(OptionalActionDirection direction) {
+        direction.directMessage(getDomainMessageName(),
+                getExtendsMessageNames());
+    }
+
+    protected String getDomainMessageName() {
+        return "fess_message";
+    }
+
+    protected String[] getExtendsMessageNames() {
+        return new String[] {};
+    }
+
+    // -----------------------------------------------------
+    //                                               Servlet
+    //                                               -------
+    @Override
+    protected OptionalServletDirection prepareOptionalServletDirection() {
+        final OptionalServletDirection direction = new OptionalServletDirection();
+        prepareRequest(direction);
+        prepareCookie(direction);
+        return direction;
+    }
+
+    protected OptionalServletDirection prepareRequest(
+            OptionalServletDirection direction) {
+        direction.directRequest(createUserLocaleProcessProvider(),
+                createUserTimeZoneProcessProvider());
+        return direction;
+    }
+
+    protected FessUserLocaleProcessProvider createUserLocaleProcessProvider() {
+        return new FessUserLocaleProcessProvider();
+    }
+
+    protected FessUserTimeZoneProcessProvider createUserTimeZoneProcessProvider() {
+        return new FessUserTimeZoneProcessProvider();
+    }
+
+    protected void prepareCookie(OptionalServletDirection direction) {
+        final String key = getCookieSecurityWord();
+        final String cookieDefaultPath = fessConfig.getCookieDefaultPath();
+        final Integer cookieDefaultExpire = fessConfig
+                .getCookieDefaultExpireAsInteger();
+        final InvertibleCipher cookieCipher = InvertibleCipher
+                .createAesCipher(key); // AES for now
+        direction.directCookie(new CookieResourceProvider() {
+            public String provideDefaultPath() {
+                return cookieDefaultPath;
+            }
+
+            public Integer provideDefaultExpire() {
+                return cookieDefaultExpire;
+            }
+
+            public InvertibleCipher provideCipher() {
+                return cookieCipher;
+            }
+
+            @Override
+            public String toString() {
+                return "{" + cookieDefaultPath + ", " + cookieDefaultExpire
+                        + "}";
+            }
+        });
+    }
+
+    protected String getCookieSecurityWord() {
+        return "fess:fess"; // hard coding for now
+    }
+
+    // -----------------------------------------------------
+    //                                                  Task
+    //                                                  ----
+    @Override
+    protected OptionalTaskDirection prepareOptionalTaskDirection() {
+        return new OptionalTaskDirection();
+    }
+}

+ 73 - 0
src/main/java/org/codelibs/fess/lasta/core/direction/sponsor/FessActionAdjustmentProvider.java

@@ -0,0 +1,73 @@
+/*
+ * 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.codelibs.fess.lasta.core.direction.sponsor;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.dbflute.saflute.web.action.processor.ActionAdjustmentProvider;
+import org.dbflute.saflute.web.action.processor.ActionMappingWrapper;
+import org.dbflute.util.DfTypeUtil;
+import org.seasar.struts.config.S2ExecuteConfig;
+
+/**
+ * @author jflute
+ */
+public class FessActionAdjustmentProvider implements ActionAdjustmentProvider {
+
+    private static final int INDEXED_PROPERTY_SIZE_LIMIT = 200; // hard coding for now
+
+    public int provideIndexedPropertySizeLimit() {
+        return INDEXED_PROPERTY_SIZE_LIMIT;
+    }
+
+    public String decodeUrlParameterPropertyValue(Object bean, String name,
+            String value) {
+        return null;
+    }
+
+    public String filterJspPath(String path,
+            ActionMappingWrapper actionMappingWrapper) {
+        return null;
+    }
+
+    public List<String> prepareJspRetryWordList(String requestPath,
+            List<String> wordList) {
+        return null;
+    }
+
+    public boolean isForcedRoutingTarget(HttpServletRequest request,
+            String requestPath) {
+        return false;
+    }
+
+    public boolean isForcedSuppressRedirectWithSlash(
+            HttpServletRequest request, String requestPath,
+            S2ExecuteConfig executeConfig) {
+        return false;
+    }
+
+    public String customizeActionMappingRequestPath(String requestPath) {
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return DfTypeUtil.toClassTitle(this) + ":{indexedLimit="
+                + INDEXED_PROPERTY_SIZE_LIMIT + "}";
+    }
+}

+ 94 - 0
src/main/java/org/codelibs/fess/lasta/core/direction/sponsor/FessTimeResourceProvider.java

@@ -0,0 +1,94 @@
+/*
+ * 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.codelibs.fess.lasta.core.direction.sponsor;
+
+import java.util.Date;
+
+import org.codelibs.fess.lasta.core.direction.FessConfig;
+import org.dbflute.saflute.core.time.BusinessTimeHandler;
+import org.dbflute.saflute.core.time.RelativeDateScript;
+import org.dbflute.saflute.core.time.TimeManager;
+import org.dbflute.saflute.core.time.TimeResourceProvider;
+import org.dbflute.saflute.core.time.TypicalBusinessTimeHandler;
+
+/**
+ * @author jflute
+ */
+public class FessTimeResourceProvider implements TimeResourceProvider {
+
+    // ===================================================================================
+    //                                                                           Attribute
+    //                                                                           =========
+    protected final FessConfig maihamaConfig;
+
+    protected final RelativeDateScript script = new RelativeDateScript();
+
+    // ===================================================================================
+    //                                                                         Constructor
+    //                                                                         ===========
+    public FessTimeResourceProvider(FessConfig maihamaConfig) {
+        this.maihamaConfig = maihamaConfig;
+    }
+
+    // ===================================================================================
+    //                                                                      Basic Handling
+    //                                                                      ==============
+    public BusinessTimeHandler provideBusinessTimeHandler(
+            TimeManager timeManager) {
+        return new TypicalBusinessTimeHandler(() -> {
+            return timeManager.getCurrentMillis();
+        }, () -> {
+            return FessUserTimeZoneProcessProvider.centralTimeZone;
+        });
+    }
+
+    public boolean isCurrentIgnoreTransaction() {
+        // this project uses transaction time for current date
+        return false; // fixedly
+    }
+
+    // ===================================================================================
+    //                                                                     Time Adjustment
+    //                                                                     ===============
+    public boolean isAdjustAbsoluteMode() { // *1
+        final String exp = maihamaConfig.getTimeAdjustTimeMillis();
+        return exp.startsWith("$"); // means absolute e.g. $(2014/07/10)
+    }
+
+    public long provideAdjustTimeMillis() { // *1
+        final String exp = maihamaConfig.getTimeAdjustTimeMillis();
+        try {
+            return doProvideAdjustTimeMillis(exp);
+        } catch (RuntimeException e) {
+            String msg = "Illegal property for time.adjust.time.millis: " + exp;
+            throw new IllegalStateException(msg);
+        }
+    }
+
+    protected long doProvideAdjustTimeMillis(final String exp) {
+        if (exp.startsWith("$")) { // absolute e.g. $(2014/07/10)
+            return script.resolveHardCodingDate(exp).getTime();
+        } else if (exp.contains("(")) { // relative e.g. addDay(3)
+            final long current = System.currentTimeMillis();
+            final Date resolved = script.resolveRelativeDate(exp, new Date(
+                    current));
+            return resolved.getTime() - current;
+        } else { // should be millisecond as relative
+            return maihamaConfig.getTimeAdjustTimeMillisAsLong();
+        }
+    }
+    // *1: called per called for dynamic change in development
+}

+ 65 - 0
src/main/java/org/codelibs/fess/lasta/core/direction/sponsor/FessUserLocaleProcessProvider.java

@@ -0,0 +1,65 @@
+/*
+ * 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.codelibs.fess.lasta.core.direction.sponsor;
+
+import java.util.Locale;
+
+import org.dbflute.saflute.web.action.callback.ActionExecuteMeta;
+import org.dbflute.saflute.web.servlet.request.RequestManager;
+import org.dbflute.saflute.web.servlet.request.UserLocaleProcessProvider;
+
+/**
+ * @author jflute
+ */
+public class FessUserLocaleProcessProvider implements UserLocaleProcessProvider {
+
+    public static final Locale centralLocale = Locale.getDefault(); // you can change it if you like
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isAcceptCookieLocale() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Locale findBusinessLocale(ActionExecuteMeta executeMeta,
+            RequestManager requestManager) {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Locale getRequestedLocale(RequestManager requestManager) {
+        return null; // null means browser default
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Locale getFallbackLocale() {
+        return centralLocale;
+    }
+
+    @Override
+    public String toString() {
+        return "{acceptCookieLocale=" + isAcceptCookieLocale()
+                + ", fallbackLocale=" + getFallbackLocale() + "}";
+    }
+}

+ 77 - 0
src/main/java/org/codelibs/fess/lasta/core/direction/sponsor/FessUserTimeZoneProcessProvider.java

@@ -0,0 +1,77 @@
+/*
+ * 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.codelibs.fess.lasta.core.direction.sponsor;
+
+import java.util.TimeZone;
+
+import org.dbflute.saflute.web.action.callback.ActionExecuteMeta;
+import org.dbflute.saflute.web.servlet.request.RequestManager;
+import org.dbflute.saflute.web.servlet.request.UserTimeZoneProcessProvider;
+
+/**
+ * @author jflute
+ */
+public class FessUserTimeZoneProcessProvider implements
+        UserTimeZoneProcessProvider {
+
+    public static final TimeZone centralTimeZone = TimeZone.getDefault(); // you can change it if you like
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isUseTimeZoneHandling() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isAcceptCookieTimeZone() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public TimeZone findBusinessTimeZone(ActionExecuteMeta executeMeta,
+            RequestManager requestManager) {
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public TimeZone getRequestedTimeZone(RequestManager requestManager) {
+        return centralTimeZone; // same as fall-back
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public TimeZone getFallbackTimeZone() {
+        return centralTimeZone;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("{useTimeZoneHandling=").append(isUseTimeZoneHandling());
+        sb.append(", acceptCookieTimeZone=").append(isAcceptCookieTimeZone());
+        sb.append(", fallbackTimeZone=").append(getFallbackTimeZone());
+        sb.append("}");
+        return sb.toString();
+    }
+}

+ 404 - 0
src/main/java/org/codelibs/fess/lasta/web/action/FessJspPath.java

@@ -0,0 +1,404 @@
+package org.codelibs.fess.lasta.web.action;
+
+/**
+ * The path definition of JSP.
+ * @author FreeGen
+ */
+public interface FessJspPath {
+
+    /** The path of the JSP: /admin/boostDocumentRule/confirm.jsp */
+    String path_AdminBoostDocumentRule_ConfirmJsp = "/admin/boostDocumentRule/confirm.jsp";
+
+    /** The path of the JSP: /admin/boostDocumentRule/edit.jsp */
+    String path_AdminBoostDocumentRule_EditJsp = "/admin/boostDocumentRule/edit.jsp";
+
+    /** The path of the JSP: /admin/boostDocumentRule/error.jsp */
+    String path_AdminBoostDocumentRule_ErrorJsp = "/admin/boostDocumentRule/error.jsp";
+
+    /** The path of the JSP: /admin/boostDocumentRule/index.jsp */
+    String path_AdminBoostDocumentRule_IndexJsp = "/admin/boostDocumentRule/index.jsp";
+
+    /** The path of the JSP: /admin/crawl/index.jsp */
+    String path_AdminCrawl_IndexJsp = "/admin/crawl/index.jsp";
+
+    /** The path of the JSP: /admin/crawlingSession/confirm.jsp */
+    String path_AdminCrawlingSession_ConfirmJsp = "/admin/crawlingSession/confirm.jsp";
+
+    /** The path of the JSP: /admin/crawlingSession/error.jsp */
+    String path_AdminCrawlingSession_ErrorJsp = "/admin/crawlingSession/error.jsp";
+
+    /** The path of the JSP: /admin/crawlingSession/index.jsp */
+    String path_AdminCrawlingSession_IndexJsp = "/admin/crawlingSession/index.jsp";
+
+    /** The path of the JSP: /admin/data/index.jsp */
+    String path_AdminData_IndexJsp = "/admin/data/index.jsp";
+
+    /** The path of the JSP: /admin/dataCrawlingConfig/confirm.jsp */
+    String path_AdminDataCrawlingConfig_ConfirmJsp = "/admin/dataCrawlingConfig/confirm.jsp";
+
+    /** The path of the JSP: /admin/dataCrawlingConfig/edit.jsp */
+    String path_AdminDataCrawlingConfig_EditJsp = "/admin/dataCrawlingConfig/edit.jsp";
+
+    /** The path of the JSP: /admin/dataCrawlingConfig/error.jsp */
+    String path_AdminDataCrawlingConfig_ErrorJsp = "/admin/dataCrawlingConfig/error.jsp";
+
+    /** The path of the JSP: /admin/dataCrawlingConfig/index.jsp */
+    String path_AdminDataCrawlingConfig_IndexJsp = "/admin/dataCrawlingConfig/index.jsp";
+
+    /** The path of the JSP: /admin/design/edit.jsp */
+    String path_AdminDesign_EditJsp = "/admin/design/edit.jsp";
+
+    /** The path of the JSP: /admin/design/index.jsp */
+    String path_AdminDesign_IndexJsp = "/admin/design/index.jsp";
+
+    /** The path of the JSP: /admin/dict/error.jsp */
+    String path_AdminDict_ErrorJsp = "/admin/dict/error.jsp";
+
+    /** The path of the JSP: /admin/dict/index.jsp */
+    String path_AdminDict_IndexJsp = "/admin/dict/index.jsp";
+
+    /** The path of the JSP: /admin/dict/synonym/confirm.jsp */
+    String path_AdminDictSynonym_ConfirmJsp = "/admin/dict/synonym/confirm.jsp";
+
+    /** The path of the JSP: /admin/dict/synonym/download.jsp */
+    String path_AdminDictSynonym_DownloadJsp = "/admin/dict/synonym/download.jsp";
+
+    /** The path of the JSP: /admin/dict/synonym/edit.jsp */
+    String path_AdminDictSynonym_EditJsp = "/admin/dict/synonym/edit.jsp";
+
+    /** The path of the JSP: /admin/dict/synonym/error.jsp */
+    String path_AdminDictSynonym_ErrorJsp = "/admin/dict/synonym/error.jsp";
+
+    /** The path of the JSP: /admin/dict/synonym/index.jsp */
+    String path_AdminDictSynonym_IndexJsp = "/admin/dict/synonym/index.jsp";
+
+    /** The path of the JSP: /admin/dict/synonym/upload.jsp */
+    String path_AdminDictSynonym_UploadJsp = "/admin/dict/synonym/upload.jsp";
+
+    /** The path of the JSP: /admin/dict/userDict/confirm.jsp */
+    String path_AdminDictUserDict_ConfirmJsp = "/admin/dict/userDict/confirm.jsp";
+
+    /** The path of the JSP: /admin/dict/userDict/download.jsp */
+    String path_AdminDictUserDict_DownloadJsp = "/admin/dict/userDict/download.jsp";
+
+    /** The path of the JSP: /admin/dict/userDict/edit.jsp */
+    String path_AdminDictUserDict_EditJsp = "/admin/dict/userDict/edit.jsp";
+
+    /** The path of the JSP: /admin/dict/userDict/error.jsp */
+    String path_AdminDictUserDict_ErrorJsp = "/admin/dict/userDict/error.jsp";
+
+    /** The path of the JSP: /admin/dict/userDict/index.jsp */
+    String path_AdminDictUserDict_IndexJsp = "/admin/dict/userDict/index.jsp";
+
+    /** The path of the JSP: /admin/dict/userDict/upload.jsp */
+    String path_AdminDictUserDict_UploadJsp = "/admin/dict/userDict/upload.jsp";
+
+    /** The path of the JSP: /admin/document/index.jsp */
+    String path_AdminDocument_IndexJsp = "/admin/document/index.jsp";
+
+    /** The path of the JSP: /admin/failureUrl/confirm.jsp */
+    String path_AdminFailureUrl_ConfirmJsp = "/admin/failureUrl/confirm.jsp";
+
+    /** The path of the JSP: /admin/failureUrl/error.jsp */
+    String path_AdminFailureUrl_ErrorJsp = "/admin/failureUrl/error.jsp";
+
+    /** The path of the JSP: /admin/failureUrl/index.jsp */
+    String path_AdminFailureUrl_IndexJsp = "/admin/failureUrl/index.jsp";
+
+    /** The path of the JSP: /admin/favoriteLog/confirm.jsp */
+    String path_AdminFavoriteLog_ConfirmJsp = "/admin/favoriteLog/confirm.jsp";
+
+    /** The path of the JSP: /admin/favoriteLog/error.jsp */
+    String path_AdminFavoriteLog_ErrorJsp = "/admin/favoriteLog/error.jsp";
+
+    /** The path of the JSP: /admin/favoriteLog/index.jsp */
+    String path_AdminFavoriteLog_IndexJsp = "/admin/favoriteLog/index.jsp";
+
+    /** The path of the JSP: /admin/fileAuthentication/confirm.jsp */
+    String path_AdminFileAuthentication_ConfirmJsp = "/admin/fileAuthentication/confirm.jsp";
+
+    /** The path of the JSP: /admin/fileAuthentication/edit.jsp */
+    String path_AdminFileAuthentication_EditJsp = "/admin/fileAuthentication/edit.jsp";
+
+    /** The path of the JSP: /admin/fileAuthentication/error.jsp */
+    String path_AdminFileAuthentication_ErrorJsp = "/admin/fileAuthentication/error.jsp";
+
+    /** The path of the JSP: /admin/fileAuthentication/index.jsp */
+    String path_AdminFileAuthentication_IndexJsp = "/admin/fileAuthentication/index.jsp";
+
+    /** The path of the JSP: /admin/fileCrawlingConfig/confirm.jsp */
+    String path_AdminFileCrawlingConfig_ConfirmJsp = "/admin/fileCrawlingConfig/confirm.jsp";
+
+    /** The path of the JSP: /admin/fileCrawlingConfig/edit.jsp */
+    String path_AdminFileCrawlingConfig_EditJsp = "/admin/fileCrawlingConfig/edit.jsp";
+
+    /** The path of the JSP: /admin/fileCrawlingConfig/error.jsp */
+    String path_AdminFileCrawlingConfig_ErrorJsp = "/admin/fileCrawlingConfig/error.jsp";
+
+    /** The path of the JSP: /admin/fileCrawlingConfig/index.jsp */
+    String path_AdminFileCrawlingConfig_IndexJsp = "/admin/fileCrawlingConfig/index.jsp";
+
+    /** The path of the JSP: /admin/jobLog/confirm.jsp */
+    String path_AdminJobLog_ConfirmJsp = "/admin/jobLog/confirm.jsp";
+
+    /** The path of the JSP: /admin/jobLog/error.jsp */
+    String path_AdminJobLog_ErrorJsp = "/admin/jobLog/error.jsp";
+
+    /** The path of the JSP: /admin/jobLog/index.jsp */
+    String path_AdminJobLog_IndexJsp = "/admin/jobLog/index.jsp";
+
+    /** The path of the JSP: /admin/keyMatch/confirm.jsp */
+    String path_AdminKeyMatch_ConfirmJsp = "/admin/keyMatch/confirm.jsp";
+
+    /** The path of the JSP: /admin/keyMatch/edit.jsp */
+    String path_AdminKeyMatch_EditJsp = "/admin/keyMatch/edit.jsp";
+
+    /** The path of the JSP: /admin/keyMatch/error.jsp */
+    String path_AdminKeyMatch_ErrorJsp = "/admin/keyMatch/error.jsp";
+
+    /** The path of the JSP: /admin/keyMatch/index.jsp */
+    String path_AdminKeyMatch_IndexJsp = "/admin/keyMatch/index.jsp";
+
+    /** The path of the JSP: /admin/labelType/confirm.jsp */
+    String path_AdminLabelType_ConfirmJsp = "/admin/labelType/confirm.jsp";
+
+    /** The path of the JSP: /admin/labelType/edit.jsp */
+    String path_AdminLabelType_EditJsp = "/admin/labelType/edit.jsp";
+
+    /** The path of the JSP: /admin/labelType/error.jsp */
+    String path_AdminLabelType_ErrorJsp = "/admin/labelType/error.jsp";
+
+    /** The path of the JSP: /admin/labelType/index.jsp */
+    String path_AdminLabelType_IndexJsp = "/admin/labelType/index.jsp";
+
+    /** The path of the JSP: /admin/log/index.jsp */
+    String path_AdminLog_IndexJsp = "/admin/log/index.jsp";
+
+    /** The path of the JSP: /admin/overlappingHost/confirm.jsp */
+    String path_AdminOverlappingHost_ConfirmJsp = "/admin/overlappingHost/confirm.jsp";
+
+    /** The path of the JSP: /admin/overlappingHost/edit.jsp */
+    String path_AdminOverlappingHost_EditJsp = "/admin/overlappingHost/edit.jsp";
+
+    /** The path of the JSP: /admin/overlappingHost/error.jsp */
+    String path_AdminOverlappingHost_ErrorJsp = "/admin/overlappingHost/error.jsp";
+
+    /** The path of the JSP: /admin/overlappingHost/index.jsp */
+    String path_AdminOverlappingHost_IndexJsp = "/admin/overlappingHost/index.jsp";
+
+    /** The path of the JSP: /admin/pathMapping/confirm.jsp */
+    String path_AdminPathMapping_ConfirmJsp = "/admin/pathMapping/confirm.jsp";
+
+    /** The path of the JSP: /admin/pathMapping/edit.jsp */
+    String path_AdminPathMapping_EditJsp = "/admin/pathMapping/edit.jsp";
+
+    /** The path of the JSP: /admin/pathMapping/error.jsp */
+    String path_AdminPathMapping_ErrorJsp = "/admin/pathMapping/error.jsp";
+
+    /** The path of the JSP: /admin/pathMapping/index.jsp */
+    String path_AdminPathMapping_IndexJsp = "/admin/pathMapping/index.jsp";
+
+    /** The path of the JSP: /admin/requestHeader/confirm.jsp */
+    String path_AdminRequestHeader_ConfirmJsp = "/admin/requestHeader/confirm.jsp";
+
+    /** The path of the JSP: /admin/requestHeader/edit.jsp */
+    String path_AdminRequestHeader_EditJsp = "/admin/requestHeader/edit.jsp";
+
+    /** The path of the JSP: /admin/requestHeader/error.jsp */
+    String path_AdminRequestHeader_ErrorJsp = "/admin/requestHeader/error.jsp";
+
+    /** The path of the JSP: /admin/requestHeader/index.jsp */
+    String path_AdminRequestHeader_IndexJsp = "/admin/requestHeader/index.jsp";
+
+    /** The path of the JSP: /admin/roleType/confirm.jsp */
+    String path_AdminRoleType_ConfirmJsp = "/admin/roleType/confirm.jsp";
+
+    /** The path of the JSP: /admin/roleType/edit.jsp */
+    String path_AdminRoleType_EditJsp = "/admin/roleType/edit.jsp";
+
+    /** The path of the JSP: /admin/roleType/error.jsp */
+    String path_AdminRoleType_ErrorJsp = "/admin/roleType/error.jsp";
+
+    /** The path of the JSP: /admin/roleType/index.jsp */
+    String path_AdminRoleType_IndexJsp = "/admin/roleType/index.jsp";
+
+    /** The path of the JSP: /admin/scheduledJob/confirm.jsp */
+    String path_AdminScheduledJob_ConfirmJsp = "/admin/scheduledJob/confirm.jsp";
+
+    /** The path of the JSP: /admin/scheduledJob/edit.jsp */
+    String path_AdminScheduledJob_EditJsp = "/admin/scheduledJob/edit.jsp";
+
+    /** The path of the JSP: /admin/scheduledJob/error.jsp */
+    String path_AdminScheduledJob_ErrorJsp = "/admin/scheduledJob/error.jsp";
+
+    /** The path of the JSP: /admin/scheduledJob/index.jsp */
+    String path_AdminScheduledJob_IndexJsp = "/admin/scheduledJob/index.jsp";
+
+    /** The path of the JSP: /admin/searchList/confirmDelete.jsp */
+    String path_AdminSearchList_ConfirmDeleteJsp = "/admin/searchList/confirmDelete.jsp";
+
+    /** The path of the JSP: /admin/searchList/index.jsp */
+    String path_AdminSearchList_IndexJsp = "/admin/searchList/index.jsp";
+
+    /** The path of the JSP: /admin/searchLog/confirm.jsp */
+    String path_AdminSearchLog_ConfirmJsp = "/admin/searchLog/confirm.jsp";
+
+    /** The path of the JSP: /admin/searchLog/error.jsp */
+    String path_AdminSearchLog_ErrorJsp = "/admin/searchLog/error.jsp";
+
+    /** The path of the JSP: /admin/searchLog/index.jsp */
+    String path_AdminSearchLog_IndexJsp = "/admin/searchLog/index.jsp";
+
+    /** The path of the JSP: /admin/stats/index.jsp */
+    String path_AdminStats_IndexJsp = "/admin/stats/index.jsp";
+
+    /** The path of the JSP: /admin/suggestBadWord/confirm.jsp */
+    String path_AdminSuggestBadWord_ConfirmJsp = "/admin/suggestBadWord/confirm.jsp";
+
+    /** The path of the JSP: /admin/suggestBadWord/download.jsp */
+    String path_AdminSuggestBadWord_DownloadJsp = "/admin/suggestBadWord/download.jsp";
+
+    /** The path of the JSP: /admin/suggestBadWord/edit.jsp */
+    String path_AdminSuggestBadWord_EditJsp = "/admin/suggestBadWord/edit.jsp";
+
+    /** The path of the JSP: /admin/suggestBadWord/error.jsp */
+    String path_AdminSuggestBadWord_ErrorJsp = "/admin/suggestBadWord/error.jsp";
+
+    /** The path of the JSP: /admin/suggestBadWord/index.jsp */
+    String path_AdminSuggestBadWord_IndexJsp = "/admin/suggestBadWord/index.jsp";
+
+    /** The path of the JSP: /admin/suggestBadWord/upload.jsp */
+    String path_AdminSuggestBadWord_UploadJsp = "/admin/suggestBadWord/upload.jsp";
+
+    /** The path of the JSP: /admin/suggestElevateWord/confirm.jsp */
+    String path_AdminSuggestElevateWord_ConfirmJsp = "/admin/suggestElevateWord/confirm.jsp";
+
+    /** The path of the JSP: /admin/suggestElevateWord/download.jsp */
+    String path_AdminSuggestElevateWord_DownloadJsp = "/admin/suggestElevateWord/download.jsp";
+
+    /** The path of the JSP: /admin/suggestElevateWord/edit.jsp */
+    String path_AdminSuggestElevateWord_EditJsp = "/admin/suggestElevateWord/edit.jsp";
+
+    /** The path of the JSP: /admin/suggestElevateWord/error.jsp */
+    String path_AdminSuggestElevateWord_ErrorJsp = "/admin/suggestElevateWord/error.jsp";
+
+    /** The path of the JSP: /admin/suggestElevateWord/index.jsp */
+    String path_AdminSuggestElevateWord_IndexJsp = "/admin/suggestElevateWord/index.jsp";
+
+    /** The path of the JSP: /admin/suggestElevateWord/upload.jsp */
+    String path_AdminSuggestElevateWord_UploadJsp = "/admin/suggestElevateWord/upload.jsp";
+
+    /** The path of the JSP: /admin/system/index.jsp */
+    String path_AdminSystem_IndexJsp = "/admin/system/index.jsp";
+
+    /** The path of the JSP: /admin/systemInfo/index.jsp */
+    String path_AdminSystemInfo_IndexJsp = "/admin/systemInfo/index.jsp";
+
+    /** The path of the JSP: /admin/userInfo/confirm.jsp */
+    String path_AdminUserInfo_ConfirmJsp = "/admin/userInfo/confirm.jsp";
+
+    /** The path of the JSP: /admin/userInfo/error.jsp */
+    String path_AdminUserInfo_ErrorJsp = "/admin/userInfo/error.jsp";
+
+    /** The path of the JSP: /admin/userInfo/index.jsp */
+    String path_AdminUserInfo_IndexJsp = "/admin/userInfo/index.jsp";
+
+    /** The path of the JSP: /admin/webAuthentication/confirm.jsp */
+    String path_AdminWebAuthentication_ConfirmJsp = "/admin/webAuthentication/confirm.jsp";
+
+    /** The path of the JSP: /admin/webAuthentication/edit.jsp */
+    String path_AdminWebAuthentication_EditJsp = "/admin/webAuthentication/edit.jsp";
+
+    /** The path of the JSP: /admin/webAuthentication/error.jsp */
+    String path_AdminWebAuthentication_ErrorJsp = "/admin/webAuthentication/error.jsp";
+
+    /** The path of the JSP: /admin/webAuthentication/index.jsp */
+    String path_AdminWebAuthentication_IndexJsp = "/admin/webAuthentication/index.jsp";
+
+    /** The path of the JSP: /admin/webCrawlingConfig/confirm.jsp */
+    String path_AdminWebCrawlingConfig_ConfirmJsp = "/admin/webCrawlingConfig/confirm.jsp";
+
+    /** The path of the JSP: /admin/webCrawlingConfig/edit.jsp */
+    String path_AdminWebCrawlingConfig_EditJsp = "/admin/webCrawlingConfig/edit.jsp";
+
+    /** The path of the JSP: /admin/webCrawlingConfig/error.jsp */
+    String path_AdminWebCrawlingConfig_ErrorJsp = "/admin/webCrawlingConfig/error.jsp";
+
+    /** The path of the JSP: /admin/webCrawlingConfig/index.jsp */
+    String path_AdminWebCrawlingConfig_IndexJsp = "/admin/webCrawlingConfig/index.jsp";
+
+    /** The path of the JSP: /admin/wizard/crawlingConfig.jsp */
+    String path_AdminWizard_CrawlingConfigJsp = "/admin/wizard/crawlingConfig.jsp";
+
+    /** The path of the JSP: /admin/wizard/error.jsp */
+    String path_AdminWizard_ErrorJsp = "/admin/wizard/error.jsp";
+
+    /** The path of the JSP: /admin/wizard/index.jsp */
+    String path_AdminWizard_IndexJsp = "/admin/wizard/index.jsp";
+
+    /** The path of the JSP: /admin/wizard/startCrawling.jsp */
+    String path_AdminWizard_StartCrawlingJsp = "/admin/wizard/startCrawling.jsp";
+
+    /** The path of the JSP: /applet/launcher.jsp */
+    String path_Applet_LauncherJsp = "/applet/launcher.jsp";
+
+    /** The path of the JSP: /error/badRequest.jsp */
+    String path_Error_BadRequestJsp = "/error/badRequest.jsp";
+
+    /** The path of the JSP: /error/error_message.jsp */
+    String path_Error_ErrorMessageJsp = "/error/error_message.jsp";
+
+    /** The path of the JSP: /error/footer.jsp */
+    String path_Error_FooterJsp = "/error/footer.jsp";
+
+    /** The path of the JSP: /error/header.jsp */
+    String path_Error_HeaderJsp = "/error/header.jsp";
+
+    /** The path of the JSP: /error/notFound.jsp */
+    String path_Error_NotFoundJsp = "/error/notFound.jsp";
+
+    /** The path of the JSP: /error/redirect.jsp */
+    String path_Error_RedirectJsp = "/error/redirect.jsp";
+
+    /** The path of the JSP: /error/system.jsp */
+    String path_Error_SystemJsp = "/error/system.jsp";
+
+    /** The path of the JSP: /error.jsp */
+    String path_ErrorJsp = "/error.jsp";
+
+    /** The path of the JSP: /footer.jsp */
+    String path_FooterJsp = "/footer.jsp";
+
+    /** The path of the JSP: /header.jsp */
+    String path_HeaderJsp = "/header.jsp";
+
+    /** The path of the JSP: /help.jsp */
+    String path_HelpJsp = "/help.jsp";
+
+    /** The path of the JSP: /index.jsp */
+    String path_IndexJsp = "/index.jsp";
+
+    /** The path of the JSP: /login/error.jsp */
+    String path_Login_ErrorJsp = "/login/error.jsp";
+
+    /** The path of the JSP: /login/footer.jsp */
+    String path_Login_FooterJsp = "/login/footer.jsp";
+
+    /** The path of the JSP: /login/header.jsp */
+    String path_Login_HeaderJsp = "/login/header.jsp";
+
+    /** The path of the JSP: /login/index.jsp */
+    String path_Login_IndexJsp = "/login/index.jsp";
+
+    /** The path of the JSP: /login/logout.jsp */
+    String path_Login_LogoutJsp = "/login/logout.jsp";
+
+    /** The path of the JSP: /search.jsp */
+    String path_SearchJsp = "/search.jsp";
+
+    /** The path of the JSP: /searchNoResult.jsp */
+    String path_SearchNoResultJsp = "/searchNoResult.jsp";
+
+    /** The path of the JSP: /searchResults.jsp */
+    String path_SearchResultsJsp = "/searchResults.jsp";
+}

+ 32 - 0
src/main/java/org/codelibs/fess/lasta/web/action/FessLoginRequired.java

@@ -0,0 +1,32 @@
+/*
+ * 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.codelibs.fess.lasta.web.action;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author jflute
+ */
+// needs inheritance when type because action is enhanced
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.METHOD })
+public @interface FessLoginRequired {
+}

+ 721 - 0
src/main/java/org/codelibs/fess/lasta/web/action/FessMessages.java

@@ -0,0 +1,721 @@
+package org.codelibs.fess.lasta.web.action;
+
+import org.apache.struts.action.ActionMessages;
+import org.apache.struts.action.ActionMessage;
+
+/**
+ * The keys for message.
+ * @author FreeGen
+ */
+public class FessMessages extends ActionMessages {
+
+    /** The serial version UID for object serialization. (Default) */
+    private static final long serialVersionUID = 1L;
+
+    /** The key of the message: <font color="red"><ul> */
+    public static final String ERRORS_HEADER = "errors.header";
+
+    /** The key of the message: </ul></font> */
+    public static final String ERRORS_FOOTER = "errors.footer";
+
+    /** The key of the message: <li> */
+    public static final String ERRORS_PREFIX = "errors.prefix";
+
+    /** The key of the message: </li> */
+    public static final String ERRORS_SUFFIX = "errors.suffix";
+
+    /** The key of the message: {0}は必須です。 */
+    public static final String ERRORS_REQUIRED = "errors.required";
+
+    /** The key of the message: {0}の長さが最小値({1})未満です。 */
+    public static final String ERRORS_MINLENGTH = "errors.minlength";
+
+    /** The key of the message: {0}の長さが最大値({1})を超えています。 */
+    public static final String ERRORS_MAXLENGTH = "errors.maxlength";
+
+    /** The key of the message: {0}のバイト長が最小値({1})未満です。 */
+    public static final String ERRORS_MINBYTELENGTH = "errors.minbytelength";
+
+    /** The key of the message: {0}のバイト長が最大値({1})を超えています。 */
+    public static final String ERRORS_MAXBYTELENGTH = "errors.maxbytelength";
+
+    /** The key of the message: {0}が不正です。 */
+    public static final String ERRORS_INVALID = "errors.invalid";
+
+    /** The key of the message: {0}は{1}と{2}の間でなければいけません。 */
+    public static final String ERRORS_RANGE = "errors.range";
+
+    /** The key of the message: {0}はバイトでなければいけません。 */
+    public static final String ERRORS_BYTE = "errors.byte";
+
+    /** The key of the message: {0}は短整数でなければいけません。 */
+    public static final String ERRORS_SHORT = "errors.short";
+
+    /** The key of the message: {0}は整数でなければいけません。 */
+    public static final String ERRORS_INTEGER = "errors.integer";
+
+    /** The key of the message: {0}は長整数でなければいけません。 */
+    public static final String ERRORS_LONG = "errors.long";
+
+    /** The key of the message: {0}は単精度実数でなければいけません。 */
+    public static final String ERRORS_FLOAT = "errors.float";
+
+    /** The key of the message: {0}は倍精度実数でなければいけません。 */
+    public static final String ERRORS_DOUBLE = "errors.double";
+
+    /** The key of the message: {0}は日付でなければいけません */
+    public static final String ERRORS_DATE = "errors.date";
+
+    /** The key of the message: {0}はクレジットカード番号として不正です。 */
+    public static final String ERRORS_CREDITCARD = "errors.creditcard";
+
+    /** The key of the message: {0}はメールアドレスとして不正です。 */
+    public static final String ERRORS_EMAIL = "errors.email";
+
+    /** The key of the message: {0}はURLとして不正です。 */
+    public static final String ERRORS_URL = "errors.url";
+
+    /** The key of the message: {0}は数値を入力してください */
+    public static final String ERRORS_NUMBER = "errors.number";
+
+    /** The key of the message: {0}に同一の項目が選ばれています */
+    public static final String ERRORS_SAME_VALUE = "errors.same.value";
+
+    /** The key of the message: {0}は{1}より大きい数値を入力してください */
+    public static final String ERRORS_GREATER_THAN = "errors.greater.than";
+
+    /** The key of the message: {0}のいずれかを入力してください */
+    public static final String ERRORS_REQUIRED_AT_LEAST_ONE = "errors.required.at.least.one";
+
+    /** The key of the message: {0}と{1}のどちらかを入力してください */
+    public static final String ERRORS_REQUIRED_OR = "errors.required.or";
+
+    /** The key of the message: 上限が{1}バイトなのに実際は{0}バイトだったのでアップロードできませんでした。 */
+    public static final String ERRORS_UPLOAD_SIZE = "errors.upload.size";
+
+    /** The key of the message: メールアドレスまたはパスワードが未入力です */
+    public static final String ERRORS_EMPTY_LOGIN = "errors.empty.login";
+
+    /** The key of the message: メールアドレス又はパスワードが間違っています */
+    public static final String ERRORS_NOT_LOGIN = "errors.not.login";
+
+    /** The key of the message: 既に登録済みのメールアドレスです */
+    public static final String ERRORS_EMAIL_EXISTS = "errors.email.exists";
+
+    /** The key of the message: {0}はすでに登録されている{1}です */
+    public static final String ERRORS_ALREADY_REGISTERED = "errors.already.registered";
+
+    /** The key of the message: 他の人が更新した可能性があります。再度やり直してください */
+    public static final String ERRORS_APP_ALREADY_DELETED = "errors.app.already.deleted";
+
+    /** The key of the message: 他の人が更新した可能性があります。再度やり直してください */
+    public static final String ERRORS_APP_ALREADY_UPDATED = "errors.app.already.updated";
+
+    /** The key of the message: 既に登録されているデータです。再度やり直してください */
+    public static final String ERRORS_APP_ALREADY_EXISTS = "errors.app.already.exists";
+
+    /** The key of the message: 不正なアクセスがされました。再度やり直してください */
+    public static final String ERRORS_APP_ILLEGAL_TRANSITION = "errors.app.illegal.transition";
+
+    /** The key of the message: 検索キーワードを入力 */
+    public static final String MESSAGES_INPUT_NOTE_KEYWORD = "messages.input.note.keyword";
+
+    /** The key of the message: メールアドレスを入力 */
+    public static final String MESSAGES_INPUT_NOTE_EMAIL = "messages.input.note.email";
+
+    /** The key of the message: Pixy って入れてー */
+    public static final String MESSAGES_INPUT_NOTE_EMAIL_OR_ACCOUNT = "messages.input.note.emailOrAccount";
+
+    /** The key of the message: sea って入れてー */
+    public static final String MESSAGES_INPUT_NOTE_PASSWORD = "messages.input.note.password";
+
+    /** The key of the message: 例: 153-0051 */
+    public static final String MESSAGES_INPUT_NOTE_ZIP_CODE = "messages.input.note.zipCode";
+
+    /**
+     * Add the created action message for the key 'errors.header' with parameters.
+     * <pre>
+     * message: <font color="red"><ul>
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addErrorsHeader(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_HEADER, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.footer' with parameters.
+     * <pre>
+     * message: </ul></font>
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addErrorsFooter(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_FOOTER, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.prefix' with parameters.
+     * <pre>
+     * message: <li>
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addErrorsPrefix(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_PREFIX, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.suffix' with parameters.
+     * <pre>
+     * message: </li>
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addErrorsSuffix(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_SUFFIX, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.required' with parameters.
+     * <pre>
+     * message: {0}は必須です。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsRequired(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_REQUIRED, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.minlength' with parameters.
+     * <pre>
+     * message: {0}の長さが最小値({1})未満です。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     * @param arg1 The parameter 1 for message. (NotNull)
+     */
+    public void addErrorsMinlength(String property, String arg0, String arg1) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_MINLENGTH, arg0, arg1));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.maxlength' with parameters.
+     * <pre>
+     * message: {0}の長さが最大値({1})を超えています。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     * @param arg1 The parameter 1 for message. (NotNull)
+     */
+    public void addErrorsMaxlength(String property, String arg0, String arg1) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_MAXLENGTH, arg0, arg1));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.minbytelength' with parameters.
+     * <pre>
+     * message: {0}のバイト長が最小値({1})未満です。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     * @param arg1 The parameter 1 for message. (NotNull)
+     */
+    public void addErrorsMinbytelength(String property, String arg0, String arg1) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_MINBYTELENGTH, arg0, arg1));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.maxbytelength' with parameters.
+     * <pre>
+     * message: {0}のバイト長が最大値({1})を超えています。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     * @param arg1 The parameter 1 for message. (NotNull)
+     */
+    public void addErrorsMaxbytelength(String property, String arg0, String arg1) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_MAXBYTELENGTH, arg0, arg1));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.invalid' with parameters.
+     * <pre>
+     * message: {0}が不正です。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsInvalid(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_INVALID, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.range' with parameters.
+     * <pre>
+     * message: {0}は{1}と{2}の間でなければいけません。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     * @param arg1 The parameter 1 for message. (NotNull)
+     * @param arg2 The parameter 2 for message. (NotNull)
+     */
+    public void addErrorsRange(String property, String arg0, String arg1, String arg2) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_RANGE, arg0, arg1, arg2));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.byte' with parameters.
+     * <pre>
+     * message: {0}はバイトでなければいけません。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsByte(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_BYTE, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.short' with parameters.
+     * <pre>
+     * message: {0}は短整数でなければいけません。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsShort(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_SHORT, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.integer' with parameters.
+     * <pre>
+     * message: {0}は整数でなければいけません。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsInteger(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_INTEGER, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.long' with parameters.
+     * <pre>
+     * message: {0}は長整数でなければいけません。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsLong(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_LONG, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.float' with parameters.
+     * <pre>
+     * message: {0}は単精度実数でなければいけません。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsFloat(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_FLOAT, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.double' with parameters.
+     * <pre>
+     * message: {0}は倍精度実数でなければいけません。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsDouble(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_DOUBLE, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.date' with parameters.
+     * <pre>
+     * message: {0}は日付でなければいけません
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsDate(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_DATE, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.creditcard' with parameters.
+     * <pre>
+     * message: {0}はクレジットカード番号として不正です。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsCreditcard(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_CREDITCARD, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.email' with parameters.
+     * <pre>
+     * message: {0}はメールアドレスとして不正です。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsEmail(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_EMAIL, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.url' with parameters.
+     * <pre>
+     * message: {0}はURLとして不正です。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsUrl(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_URL, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.number' with parameters.
+     * <pre>
+     * message: {0}は数値を入力してください
+     * comment: -------------
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsNumber(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_NUMBER, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.same.value' with parameters.
+     * <pre>
+     * message: {0}に同一の項目が選ばれています
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsSameValue(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_SAME_VALUE, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.greater.than' with parameters.
+     * <pre>
+     * message: {0}は{1}より大きい数値を入力してください
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     * @param arg1 The parameter 1 for message. (NotNull)
+     */
+    public void addErrorsGreaterThan(String property, String arg0, String arg1) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_GREATER_THAN, arg0, arg1));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.required.at.least.one' with parameters.
+     * <pre>
+     * message: {0}のいずれかを入力してください
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsRequiredAtLeastOne(String property, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_REQUIRED_AT_LEAST_ONE, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.required.or' with parameters.
+     * <pre>
+     * message: {0}と{1}のどちらかを入力してください
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     * @param arg1 The parameter 1 for message. (NotNull)
+     */
+    public void addErrorsRequiredOr(String property, String arg0, String arg1) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_REQUIRED_OR, arg0, arg1));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.upload.size' with parameters.
+     * <pre>
+     * message: 上限が{1}バイトなのに実際は{0}バイトだったのでアップロードできませんでした。
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg1 The parameter 1 for message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     */
+    public void addErrorsUploadSize(String property, String arg1, String arg0) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_UPLOAD_SIZE, arg1, arg0));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.empty.login' with parameters.
+     * <pre>
+     * message: メールアドレスまたはパスワードが未入力です
+     * comment: ----------------
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addErrorsEmptyLogin(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_EMPTY_LOGIN, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.not.login' with parameters.
+     * <pre>
+     * message: メールアドレス又はパスワードが間違っています
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addErrorsNotLogin(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_NOT_LOGIN, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.email.exists' with parameters.
+     * <pre>
+     * message: 既に登録済みのメールアドレスです
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addErrorsEmailExists(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_EMAIL_EXISTS, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.already.registered' with parameters.
+     * <pre>
+     * message: {0}はすでに登録されている{1}です
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     * @param arg0 The parameter 0 for message. (NotNull)
+     * @param arg1 The parameter 1 for message. (NotNull)
+     */
+    public void addErrorsAlreadyRegistered(String property, String arg0, String arg1) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_ALREADY_REGISTERED, arg0, arg1));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.app.already.deleted' with parameters.
+     * <pre>
+     * message: 他の人が更新した可能性があります。再度やり直してください
+     * comment: ---------------------
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addErrorsAppAlreadyDeleted(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_APP_ALREADY_DELETED, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.app.already.updated' with parameters.
+     * <pre>
+     * message: 他の人が更新した可能性があります。再度やり直してください
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addErrorsAppAlreadyUpdated(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_APP_ALREADY_UPDATED, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.app.already.exists' with parameters.
+     * <pre>
+     * message: 既に登録されているデータです。再度やり直してください
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addErrorsAppAlreadyExists(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_APP_ALREADY_EXISTS, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'errors.app.illegal.transition' with parameters.
+     * <pre>
+     * message: 不正なアクセスがされました。再度やり直してください
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addErrorsAppIllegalTransition(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(ERRORS_APP_ILLEGAL_TRANSITION, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'messages.input.note.keyword' with parameters.
+     * <pre>
+     * message: 検索キーワードを入力
+     * comment: ----------
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addMessagesInputNoteKeyword(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(MESSAGES_INPUT_NOTE_KEYWORD, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'messages.input.note.email' with parameters.
+     * <pre>
+     * message: メールアドレスを入力
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addMessagesInputNoteEmail(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(MESSAGES_INPUT_NOTE_EMAIL, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'messages.input.note.emailOrAccount' with parameters.
+     * <pre>
+     * message: Pixy って入れてー
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addMessagesInputNoteEmailOrAccount(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(MESSAGES_INPUT_NOTE_EMAIL_OR_ACCOUNT, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'messages.input.note.password' with parameters.
+     * <pre>
+     * message: sea って入れてー
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addMessagesInputNotePassword(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(MESSAGES_INPUT_NOTE_PASSWORD, (Object[])null));
+    }
+
+    /**
+     * Add the created action message for the key 'messages.input.note.zipCode' with parameters.
+     * <pre>
+     * message: 例: 153-0051
+     * </pre>
+     * @param property The property name for the message. (NotNull)
+     */
+    public void addMessagesInputNoteZipCode(String property) {
+        assertPropertyNotNull(property);
+        add(property, new ActionMessage(MESSAGES_INPUT_NOTE_ZIP_CODE, (Object[])null));
+    }
+
+    /**
+     * Assert the property is not null.
+     * @param property The value of the property. (NotNull)
+     */
+    protected void assertPropertyNotNull(String property) {
+        if (property == null) {
+            String msg = "The argument 'property' for message should not be null.";
+            throw new IllegalArgumentException(msg);
+        }
+    }
+
+    /**
+     * The definition of keys for labels.
+     * @author FreeGen
+     */
+    public static interface LabelKey {
+
+        /** The key of the label: 会員 */
+        String LABELS_MEMBER = "labels.member";
+
+        /** The key of the label: 会員ID */
+        String LABELS_MEMBER_ID = "labels.memberId";
+
+        /** The key of the label: 会員名称 */
+        String LABELS_MEMBER_NAME = "labels.memberName";
+
+        /** The key of the label: メールアドレス */
+        String LABELS_EMAIL = "labels.email";
+
+        /** The key of the label: メアドもしくはアカウント */
+        String LABELS_EMAIL_OR_ACCOUNT = "labels.emailOrAccount";
+
+        /** The key of the label: パスワード */
+        String LABELS_PASSWORD = "labels.password";
+
+        /** The key of the label: バージョンNo */
+        String LABELS_VERSION_NO = "labels.versionNo";
+
+        /** The key of the label: 一覧 */
+        String LABELS_LIST = "labels.list";
+
+        /** The key of the label: 追加 */
+        String LABELS_ADD = "labels.add";
+
+        /** The key of the label: 編集 */
+        String LABELS_EDIT = "labels.edit";
+
+        /** The key of the label: 検索 */
+        String LABELS_SEARCH = "labels.search";
+
+        /** The key of the label: 登録 */
+        String LABELS_REGISTER = "labels.register";
+
+        /** The key of the label: 更新 */
+        String LABELS_UPDATE = "labels.update";
+
+        /** The key of the label: @[labels.member]@[labels.list] */
+        String LABELS_MEMBER_LIST = "labels.member.list";
+
+        /** The key of the label: @[labels.member]@[labels.add] */
+        String LABELS_MEMBER_ADD = "labels.member.add";
+
+        /** The key of the label: @[labels.member]@[labels.edit] */
+        String LABELS_MEMBER_EDIT = "labels.member.edit";
+
+        /** The key of the label: お知らせ */
+        String LABELS_HEADER_TITLE_ERROR_MESSAGE = "labels.header.title.error.message";
+    }
+}

+ 29 - 0
src/main/java/org/codelibs/fess/lasta/web/action/FessPerformLogin.java

@@ -0,0 +1,29 @@
+/*
+ * 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.codelibs.fess.lasta.web.action;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author jflute
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(value = ElementType.METHOD)
+public @interface FessPerformLogin {
+}

+ 79 - 0
src/main/java/org/codelibs/fess/lasta/web/action/FessUserBean.java

@@ -0,0 +1,79 @@
+/*
+ * 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.codelibs.fess.lasta.web.action;
+
+import org.dbflute.saflute.web.action.login.TypicalUserBaseBean;
+
+/**
+ * @author jflute
+ */
+public class FessUserBean extends TypicalUserBaseBean {
+
+    // ===================================================================================
+    //                                                                          Definition
+    //                                                                          ==========
+    /** The serial version UID for object serialization. (Default) */
+    private static final long serialVersionUID = 1L;
+
+    /** The user type for Member, e.g. used by access context. */
+    public static final String USER_TYPE = "M";
+
+    /** The application type for Dockside, e.g. used by access context. */
+    public static final String DOMAIN_TYPE = "DCK";
+
+    // ===================================================================================
+    //                                                                           Attribute
+    //                                                                           =========
+    protected final Long memberId;
+    protected final String memberName;
+
+    // ===================================================================================
+    //                                                                         Constructor
+    //                                                                         ===========
+    public FessUserBean() {
+        memberId = null;
+        memberName = null;
+    }
+
+    // ===================================================================================
+    //                                                                      Implementation
+    //                                                                      ==============
+    @Override
+    public Long getUserId() {
+        return memberId;
+    }
+
+    @Override
+    public String getUserType() {
+        return USER_TYPE;
+    }
+
+    @Override
+    public String getDomainType() {
+        return DOMAIN_TYPE;
+    }
+
+    // ===================================================================================
+    //                                                                            Accessor
+    //                                                                            ========
+    public Long getMemberId() {
+        return memberId;
+    }
+
+    public String getMemberName() {
+        return memberName;
+    }
+}

+ 1 - 1
src/main/java/org/codelibs/fess/struts/action/FessRequestProcessor.java

@@ -21,7 +21,7 @@ import java.util.Map;
 import org.codelibs.fess.util.SearchParamMap;
 import org.seasar.struts.action.S2RequestProcessor;
 
-public class FessRequestProcessor extends S2RequestProcessor/*ActionRequestProcessor*/ {
+public class FessRequestProcessor extends S2RequestProcessor/*ActionRequestProcessor*/{
     @Override
     @SuppressWarnings("unchecked")
     protected void setMapProperty(final Map map, final String name,

+ 0 - 2
src/main/resources/app.dicon

@@ -4,9 +4,7 @@
 <components>
 	<include path="convention.dicon"/>
 	<include path="aop.dicon"/>
-	<!-- 
 	<include path="saflute.dicon"/>
-	 -->
 	<include path="dbflute.dicon"/>
 
 	<include path="fess.dicon"/>

+ 3 - 3
src/main/resources/customizer.dicon

@@ -5,7 +5,7 @@
     <include path="default-customizer.dicon"/>
 	
     <component name="actionCustomizer"
-        class="org.seasar.framework.container.customizer.CustomizerChain">
+        class="org.dbflute.saflute.web.action.interceptor.ActionCustomizerChain">
         <initMethod name="addAspectCustomizer">
             <arg>"aop.traceInterceptor"</arg>
         </initMethod>
@@ -24,11 +24,11 @@
     </component>
     
     <component name="formCustomizer"
-        class="org.seasar.framework.container.customizer.CustomizerChain">
+        class="org.dbflute.saflute.core.interceptor.ConcreteDrivenCustomizerChain">
     </component>
 	
     <component name="serviceCustomizer"
-        class="org.seasar.framework.container.customizer.CustomizerChain">
+        class="org.dbflute.saflute.core.interceptor.ConcreteDrivenCustomizerChain">
         <initMethod name="addAspectCustomizer">
             <arg>"aop.traceInterceptor"</arg>
         </initMethod>

+ 27 - 0
src/main/resources/fess_config.properties

@@ -0,0 +1,27 @@
+# _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
+# Fess basic configuration for All Environments
+# _/_/_/_/_/_/_/_/_/_/
+# ========================================================================================
+#                                                                                    Core
+#                                                                                   ======
+#
+# The title of domain the application for logging
+domain.title = Fess
+
+
+# ========================================================================================
+#                                                                                     DB
+#                                                                                   ======
+
+
+# ========================================================================================
+#                                                                                    Web
+#                                                                                  =======
+# The default path of cookie (basically '/' if no context path)
+cookie.default.path = /
+
+# The default expire of cookie in seconds e.g. 31556926: one year, 86400: one day
+cookie.default.expire = 31556926
+
+# The eternal expire of cookie in seconds e.g. 315360000: ten year, 86400: one day
+cookie.eternal.expire = 315360000

+ 45 - 0
src/main/resources/fess_env.properties

@@ -0,0 +1,45 @@
+# _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
+# Fess environment configuration for Local Development
+# _/_/_/_/_/_/_/_/_/_/
+# ========================================================================================
+#                                                                                    Core
+#                                                                                   ======
+# Is development environment here? (used for various purpose, you should set false if unknown)
+development.here = true
+
+# The title of environment (e.g. local or integartion or production)
+environment.title = Local Development
+
+# Does it enable the Framework internal debug? (true only when emergency)
+framework.debug = false
+
+# one day: 86400000, three days: 259200000, five days: 432000000, one week: 604800000, one year: 31556926000
+# special script :: absolute mode: $(2014/07/10), relative mode: addDay(3).addMonth(4)
+# The milliseconds for (relative or absolute) adjust time (set only when test) @LongType *dynamic in development
+time.adjust.time.millis = 0
+
+
+# ========================================================================================
+#                                                                                     Mail
+#                                                                                     ====
+# Does it send mock mail? (true: no send actually, logging only)
+mail.send.mock = true
+
+# SMTP server settings for default: host:port
+mail.smtp.server.default.host.and.port = localhost:25
+
+
+# ========================================================================================
+#                                                                                     JDBC
+#                                                                                     ====
+# The URL of database connection for JDBC
+jdbc.url = jdbc:mysql://localhost:3306/maihamadb
+
+# The user of database connection for JDBC
+jdbc.user = maihamauser
+
+# @Secure The password of database connection for JDBC
+jdbc.password = maihamaword
+
+# The (max) pooling size of Seasar's connection pool
+jdbc.connection.pooling.size = 10

+ 107 - 0
src/main/resources/fess_message.properties

@@ -0,0 +1,107 @@
+
+# ========================================================================================
+#                                                                           Struts Default
+#                                                                           ==============
+errors.header=<font color="red"><ul>
+errors.footer=</ul></font>
+errors.prefix=<li>
+errors.suffix=</li>
+errors.invalid={0} is invalid.
+errors.maxlength={0} can not be greater than {1} characters.
+errors.minlength={0} can not be less than {1} characters.
+errors.maxbytelength={0} can not be greater than {1} bytes.
+errors.minbytelength={0} can not be less than {1} bytes.
+errors.range={0} is not in the range {1} through {2}.
+errors.required={0} is required.
+errors.byte={0} must be an byte.
+errors.date={0} is not a date.
+errors.double={0} must be an double.
+errors.float={0} must be an float.
+errors.integer={0} must be an integer.
+errors.long={0} must be an long.
+errors.short={0} must be an short.
+errors.creditcard={0} is not a valid credit card number.
+errors.email={0} is an invalid e-mail address.
+errors.url={0} is an invalid url (web address).
+
+
+# ========================================================================================
+#                                                                           Project Common
+#                                                                           ==============
+# ----------------------------------------------------------
+#                                              Value Message
+#                                              -------------
+errors.number=input number for {0}
+errors.same.value=same value is selected in {0}
+errors.greater.than=input {0} greater than {1}
+errors.required.at.least.one=input {0} at least one
+errors.required.or=input either {0} or {1}
+errors.upload.size=Uploading failed, because actual size {0} bytes exceeded limit size {1} bytes.
+
+# ----------------------------------------------------------
+#                                           Business Message
+#                                           ----------------
+errors.empty.login=input mail address or password
+errors.not.login=invalid mail address or password
+errors.email.exists=mail address already registered
+errors.already.registered={0} is already-regsitered {1}
+
+# ----------------------------------------------------------
+#                                      Application Exception
+#                                      ---------------------
+errors.app.illegal.transition=retry because of illegal transition
+errors.app.already.deleted=others might be updated, so retry
+errors.app.already.updated=others might be updated, so retry
+errors.app.already.exists=already existing data, so retry
+
+
+# ========================================================================================
+#                                                                                   Labels
+#                                                                                   ======
+# ----------------------------------------------------------
+#                                                     Entity
+#                                                     ------
+labels.member = Member
+
+# ----------------------------------------------------------
+#                                                  Attribute
+#                                                  ---------
+labels.memberId = Member ID
+labels.memberName = Member Name
+labels.email = Mail Address
+labels.emailOrAccount = Mail or Account
+labels.password = Password
+labels.versionNo = Version No
+
+# ----------------------------------------------------------
+#                                                     Screen
+#                                                     ------
+labels.list = List
+labels.edit = Edit
+labels.add = Add
+labels.search = Search
+labels.register = Register
+labels.update = Update
+
+labels.member.list = @[labels.list] of @[labels.member]
+labels.member.add = @[labels.add] @[labels.member]
+labels.member.edit = @[labels.edit] @[labels.member]
+
+# ----------------------------------------------------------
+#                                               Header Title
+#                                               ------------
+labels.header.title.error.message = Notice
+
+
+# ========================================================================================
+#                                                                                 Messages
+#                                                                                 ========
+# ----------------------------------------------------------
+#                                                 Input Note
+#                                                 ----------
+messages.input.note.keyword = Input keyword to search 
+messages.input.note.email = Input your E-mail address
+messages.input.note.emailOrAccount = Input your E-mail or account
+messages.input.note.password = Input your password
+messages.input.note.zipCode = e.g. 153-0051
+

+ 106 - 0
src/main/resources/fess_message_ja.properties

@@ -0,0 +1,106 @@
+
+# ========================================================================================
+#                                                                           Struts Default
+#                                                                           ==============
+errors.header=<font color="red"><ul>
+errors.footer=</ul></font>
+errors.prefix=<li>
+errors.suffix=</li>
+errors.required={0}\u306f\u5fc5\u9808\u3067\u3059\u3002
+errors.minlength={0}\u306e\u9577\u3055\u304c\u6700\u5c0f\u5024({1})\u672a\u6e80\u3067\u3059\u3002
+errors.maxlength={0}\u306e\u9577\u3055\u304c\u6700\u5927\u5024({1})\u3092\u8d85\u3048\u3066\u3044\u307e\u3059\u3002
+errors.minbytelength={0}\u306e\u30d0\u30a4\u30c8\u9577\u304c\u6700\u5c0f\u5024({1})\u672a\u6e80\u3067\u3059\u3002
+errors.maxbytelength={0}\u306e\u30d0\u30a4\u30c8\u9577\u304c\u6700\u5927\u5024({1})\u3092\u8d85\u3048\u3066\u3044\u307e\u3059\u3002
+errors.invalid={0}\u304c\u4e0d\u6b63\u3067\u3059\u3002
+errors.range={0}\u306f{1}\u3068{2}\u306e\u9593\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093\u3002
+errors.byte={0}\u306f\u30d0\u30a4\u30c8\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093\u3002
+errors.short={0}\u306f\u77ed\u6574\u6570\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093\u3002
+errors.integer={0}\u306f\u6574\u6570\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093\u3002
+errors.long={0}\u306f\u9577\u6574\u6570\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093\u3002
+errors.float={0}\u306f\u5358\u7cbe\u5ea6\u5b9f\u6570\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093\u3002
+errors.double={0}\u306f\u500d\u7cbe\u5ea6\u5b9f\u6570\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093\u3002
+errors.date={0}\u306f\u65e5\u4ed8\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+errors.creditcard={0}\u306f\u30af\u30ec\u30b8\u30c3\u30c8\u30ab\u30fc\u30c9\u756a\u53f7\u3068\u3057\u3066\u4e0d\u6b63\u3067\u3059\u3002
+errors.email={0}\u306f\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3068\u3057\u3066\u4e0d\u6b63\u3067\u3059\u3002
+errors.url={0}\u306fURL\u3068\u3057\u3066\u4e0d\u6b63\u3067\u3059\u3002
+
+
+# ========================================================================================
+#                                                                           Project Common
+#                                                                           ==============
+# ----------------------------------------------------------
+#                                              Value Message
+#                                              -------------
+errors.number={0}\u306f\u6570\u5024\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044
+errors.same.value={0}\u306b\u540c\u4e00\u306e\u9805\u76ee\u304c\u9078\u3070\u308c\u3066\u3044\u307e\u3059
+errors.greater.than={0}\u306f{1}\u3088\u308a\u5927\u304d\u3044\u6570\u5024\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044
+errors.required.at.least.one={0}\u306e\u3044\u305a\u308c\u304b\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044
+errors.required.or={0}\u3068{1}\u306e\u3069\u3061\u3089\u304b\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044
+errors.upload.size=\u4e0a\u9650\u304c{1}\u30d0\u30a4\u30c8\u306a\u306e\u306b\u5b9f\u969b\u306f{0}\u30d0\u30a4\u30c8\u3060\u3063\u305f\u306e\u3067\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002
+
+# ----------------------------------------------------------
+#                                           Business Message
+#                                           ----------------
+errors.empty.login=\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u672a\u5165\u529b\u3067\u3059
+errors.not.login=\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u53c8\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u9593\u9055\u3063\u3066\u3044\u307e\u3059
+errors.email.exists=\u65e2\u306b\u767b\u9332\u6e08\u307f\u306e\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3067\u3059
+errors.already.registered={0}\u306f\u3059\u3067\u306b\u767b\u9332\u3055\u308c\u3066\u3044\u308b{1}\u3067\u3059
+
+# ----------------------------------------------------------
+#                                      Application Exception
+#                                      ---------------------
+errors.app.already.deleted=\u4ed6\u306e\u4eba\u304c\u66f4\u65b0\u3057\u305f\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u518d\u5ea6\u3084\u308a\u76f4\u3057\u3066\u304f\u3060\u3055\u3044
+errors.app.already.updated=\u4ed6\u306e\u4eba\u304c\u66f4\u65b0\u3057\u305f\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u518d\u5ea6\u3084\u308a\u76f4\u3057\u3066\u304f\u3060\u3055\u3044
+errors.app.already.exists=\u65e2\u306b\u767b\u9332\u3055\u308c\u3066\u3044\u308b\u30c7\u30fc\u30bf\u3067\u3059\u3002\u518d\u5ea6\u3084\u308a\u76f4\u3057\u3066\u304f\u3060\u3055\u3044
+errors.app.illegal.transition=\u4e0d\u6b63\u306a\u30a2\u30af\u30bb\u30b9\u304c\u3055\u308c\u307e\u3057\u305f\u3002\u518d\u5ea6\u3084\u308a\u76f4\u3057\u3066\u304f\u3060\u3055\u3044
+
+
+# ========================================================================================
+#                                                                                   Labels
+#                                                                                   ======
+# ----------------------------------------------------------
+#                                                     Entity
+#                                                     ------
+labels.member = \u4f1a\u54e1
+
+# ----------------------------------------------------------
+#                                                  Attribute
+#                                                  ---------
+labels.memberId = \u4f1a\u54e1ID
+labels.memberName = \u4f1a\u54e1\u540d\u79f0
+labels.email = \u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9
+labels.emailOrAccount = \u30e1\u30a2\u30c9\u3082\u3057\u304f\u306f\u30a2\u30ab\u30a6\u30f3\u30c8
+labels.password = \u30d1\u30b9\u30ef\u30fc\u30c9
+labels.versionNo = \u30d0\u30fc\u30b8\u30e7\u30f3No
+
+# ----------------------------------------------------------
+#                                                     Screen
+#                                                     ------
+labels.list = \u4e00\u89a7
+labels.add = \u8ffd\u52a0
+labels.edit = \u7de8\u96c6
+labels.search = \u691c\u7d22
+labels.register = \u767b\u9332
+labels.update = \u66f4\u65b0
+
+labels.member.list = @[labels.member]@[labels.list]
+labels.member.add = @[labels.member]@[labels.add]
+labels.member.edit = @[labels.member]@[labels.edit]
+
+# ----------------------------------------------------------
+#                                               Header Title
+#                                               ------------
+labels.header.title.error.message = \u304a\u77e5\u3089\u305b
+
+
+# ========================================================================================
+#                                                                                 Messages
+#                                                                                 ========
+# ----------------------------------------------------------
+#                                                 Input Note
+#                                                 ----------
+messages.input.note.keyword = \u691c\u7d22\u30ad\u30fc\u30ef\u30fc\u30c9\u3092\u5165\u529b
+messages.input.note.email = \u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3092\u5165\u529b
+messages.input.note.emailOrAccount = Pixy \u3063\u3066\u5165\u308c\u3066\u30fc
+messages.input.note.password = sea \u3063\u3066\u5165\u308c\u3066\u30fc
+messages.input.note.zipCode = \u4f8b: 153-0051

+ 6 - 0
src/main/resources/s2container.dicon

@@ -5,4 +5,10 @@
     <include condition="#ENV == 'ut'" path="warmdeploy.dicon"/>
     <include condition="#ENV == 'ct'" path="hotdeploy.dicon"/>
     <include condition="#ENV != 'ut' and #ENV != 'ct'" path="cooldeploy.dicon"/>
+
+    <!-- = = = = = = = = = = -->
+    <!-- for S2ClassBuilder  -->
+    <!-- = = = = = = = = = = -->
+    <component name="dicon" class="org.seasar.cms.classbuilder.impl.RedefinableXmlS2ContainerBuilder" />
+    <component class="org.seasar.cms.classbuilder.impl.RedefinableResourceResolver" />
 </components>

+ 9 - 0
src/main/resources/saflute_assist++.dicon

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"
+	"http://www.seasar.org/dtd/components24.dtd">
+<components>
+	<component name="assistantDirector" class="org.codelibs.fess.lasta.core.direction.FessFwAssistantDirector"/>
+	<component name="docksideConfig" class="org.codelibs.fess.lasta.core.direction.FessConfig$SimpleImpl">
+		<initMethod name="initialize"/>
+	</component>
+</components>

+ 1 - 1
src/main/webapp/WEB-INF/cmd/resources/customizer.dicon

@@ -5,7 +5,7 @@
     <include path="default-customizer.dicon"/>
 	
     <component name="serviceCustomizer"
-        class="org.seasar.framework.container.customizer.CustomizerChain">
+        class="org.dbflute.saflute.core.interceptor.ConcreteDrivenCustomizerChain">
         <initMethod name="addAspectCustomizer">
             <arg>"aop.traceInterceptor"</arg>
         </initMethod>

+ 11 - 0
src/main/webapp/WEB-INF/view/error/error_message.jsp

@@ -0,0 +1,11 @@
+<c:import url="${viewPrefix}/common/header.jsp">
+	<c:param name="title" value="エラー通知"/>
+</c:import>
+<div class="contents">
+	<h2><bean:message key="labels.header.title.error.message"/></h2>
+	<html:errors/>
+	<div class="listback">
+		<s:link href="/member/list/">会員一覧へ</s:link>
+	</div>
+</div>
+<c:import url="${viewPrefix}/common/footer.jsp"/>

+ 16 - 1
src/main/webapp/WEB-INF/web.xml

@@ -82,6 +82,11 @@
     </init-param>
   </filter>
 
+  <filter>
+    <filter-name>requestLoggingFilter</filter-name>
+    <filter-class>org.dbflute.saflute.web.servlet.filter.RequestLoggingFilter</filter-class>
+  </filter>
+
   <!--
   <filter>
     <filter-name>adLoginInfoFilter</filter-name>
@@ -187,6 +192,16 @@
     <dispatcher>INCLUDE</dispatcher>
   </filter-mapping>
 
+  <!--
+   basically you should set original filters after Seasar's basic filters
+   because request and session might be filtered by the filters
+   (e.g. session attributes are wrapped in holder when hot-deploy)
+   -->
+  <filter-mapping>
+    <filter-name>requestLoggingFilter</filter-name>
+    <url-pattern>/*</url-pattern>
+  </filter-mapping>
+
   <filter-mapping>
     <filter-name>authenticationFilter</filter-name>
     <url-pattern>/*</url-pattern>
@@ -251,7 +266,7 @@
 
   <servlet>
     <servlet-name>s2container</servlet-name>
-    <servlet-class>org.seasar.framework.container.servlet.S2ContainerServlet</servlet-class>
+    <servlet-class>org.dbflute.saflute.web.servlet.ContainerManagementServlet</servlet-class>
     <load-on-startup>4</load-on-startup>
   </servlet>
 

+ 35 - 0
src/test/java/org/codelibs/fess/lasta/LastaEnvTest.java

@@ -0,0 +1,35 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project 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.codelibs.fess.lasta;
+
+import javax.annotation.Resource;
+
+import org.codelibs.fess.lasta.core.direction.FessConfig;
+import org.codelibs.fess.unit.UnitFessContainerTestCase;
+
+/**
+ * @author jflute
+ */
+public class LastaEnvTest extends UnitFessContainerTestCase {
+
+    @Resource
+    protected FessConfig fessConfig;
+
+    public void test_boot() throws Exception {
+        assertNotNull(fessConfig);
+    }
+}

+ 94 - 0
src/test/java/org/codelibs/fess/unit/UnitFessContainerTestCase.java

@@ -0,0 +1,94 @@
+/*
+ * Copyright 2009-2015 the CodeLibs Project 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.codelibs.fess.unit;
+
+import java.util.Date;
+
+import javax.annotation.Resource;
+
+import org.dbflute.saflute.core.direction.FwAssistantDirector;
+import org.dbflute.saflute.core.direction.OptionalCoreDirection;
+import org.dbflute.saflute.core.magic.ThreadCacheContext;
+import org.dbflute.saflute.core.magic.TransactionTimeContext;
+import org.dbflute.saflute.core.time.TimeManager;
+import org.dbflute.utflute.seasar.ContainerTestCase;
+
+/**
+ * Use like this:
+ * <pre>
+ * YourTest extends {@link UnitFessContainerTestCase} {
+ * 
+ *     public void test_yourMethod() {
+ *         <span style="color: #3F7E5E">// ## Arrange ##</span>
+ *         YourAction action = new YourAction();
+ *         <span style="color: #FD4747">inject</span>(action);
+ * 
+ *         <span style="color: #3F7E5E">// ## Act ##</span>
+ *         action.submit();
+ * 
+ *         <span style="color: #3F7E5E">// ## Assert ##</span>
+ *         assertTrue(action...);
+ *     }
+ * }
+ * </pre>
+ * @author jflute
+ */
+public abstract class UnitFessContainerTestCase extends ContainerTestCase {
+
+    // ===================================================================================
+    //                                                                           Attribute
+    //                                                                           =========
+    @Resource
+    protected FwAssistantDirector assistantDirector;
+
+    @Resource
+    protected TimeManager timeManager;
+
+    // ===================================================================================
+    //                                                                            Settings
+    //                                                                            ========
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        initializeThreadCacheContext();
+        initializeTransactionTime();
+        initializeAssistantDirector();
+    }
+
+    protected void initializeThreadCacheContext() {
+        ThreadCacheContext.initialize();
+    }
+
+    protected void initializeTransactionTime() {
+        // because of non-UserTransaction transaction in UTFlute
+        final Date transactionTime = timeManager.getFlashDate();
+        TransactionTimeContext.setTransactionTime(transactionTime);
+    }
+
+    protected void initializeAssistantDirector() {
+        OptionalCoreDirection direction = assistantDirector
+                .assistOptionalCoreDirection();
+        direction.assistBootProcessCallback().callback(assistantDirector);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        TransactionTimeContext.clear();
+        ThreadCacheContext.clear();
+        super.tearDown();
+    }
+}

+ 1 - 0
src/test/resources/app_ut.dicon

@@ -4,6 +4,7 @@
 <components>
 	<include path="convention.dicon"/>
 	<include path="aop.dicon"/>
+	<include path="saflute.dicon"/>
 	<include path="dbflute.dicon"/>
 
 	<include path="fess.dicon"/>