View Javadoc
1   /*
2    * Copyright 2012-2025 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess;
17  
18  // DO NOT DEPEND OTHER JARs
19  
20  import java.io.File;
21  import java.util.List;
22  import java.util.Properties;
23  
24  import org.apache.catalina.Container;
25  import org.apache.catalina.Context;
26  import org.apache.catalina.Host;
27  import org.apache.catalina.connector.Connector;
28  import org.apache.catalina.core.StandardHost;
29  import org.apache.catalina.startup.Tomcat;
30  import org.apache.tomcat.util.http.CookieProcessorBase;
31  import org.apache.tomcat.util.http.Rfc6265CookieProcessor;
32  import org.apache.tomcat.util.net.SSLHostConfig;
33  import org.apache.tomcat.util.net.SSLHostConfigCertificate;
34  import org.codelibs.core.lang.StringUtil;
35  import org.codelibs.fess.tomcat.valve.SuppressErrorReportValve;
36  import org.codelibs.fess.tomcat.webresources.FessWebResourceRoot;
37  import org.dbflute.tomcat.TomcatBoot;
38  import org.dbflute.tomcat.logging.BootLogger;
39  import org.dbflute.tomcat.props.BootPropsTranslator;
40  
41  /**
42   * Main boot class for the Fess search engine application.
43   * This class extends TomcatBoot to provide Fess-specific Tomcat server configuration
44   * and initialization, including SSL setup, context path handling, and resource management.
45   *
46   * <p>The class handles system property configuration for paths, ports, and other
47   * Fess-specific settings during application startup.</p>
48   *
49   * @since 1.0
50   */
51  public class FessBoot extends TomcatBoot {
52  
53      /** Configuration file name for logging properties */
54      private static final String LOGGING_PROPERTIES = "logging.properties";
55  
56      /** System property key for Fess context path configuration */
57      private static final String FESS_CONTEXT_PATH = "fess.context.path";
58  
59      /** System property key for Fess port configuration */
60      private static final String FESS_PORT = "fess.port";
61  
62      /** System property key for Fess temporary directory path */
63      private static final String FESS_TEMP_PATH = "fess.temp.path";
64  
65      /** System property key for Fess variable directory path */
66      private static final String FESS_VAR_PATH = "fess.var.path";
67  
68      /** System property key for Fess web application path */
69      private static final String FESS_WEBAPP_PATH = "fess.webapp.path";
70  
71      /** System property key for Java temporary directory */
72      private static final String JAVA_IO_TMPDIR = "java.io.tmpdir";
73  
74      /** System property key for Tomcat configuration path */
75      private static final String TOMCAT_CONFIG_PATH = "tomcat.config.path";
76  
77      /**
78       * Constructs a new FessBoot instance with the specified port and context path.
79       *
80       * @param port the port number for the Tomcat server
81       * @param contextPath the context path for the web application
82       */
83      public FessBoot(final int port, final String contextPath) {
84          super(port, contextPath);
85      }
86  
87      /**
88       * Prepares and returns the web application path.
89       * Checks for the fess.webapp.path system property first, then falls back to the parent implementation.
90       *
91       * @return the web application path
92       */
93      @Override
94      protected String prepareWebappPath() {
95          final String value = System.getProperty(FESS_WEBAPP_PATH);
96          if (value != null) {
97              return value;
98          }
99          return super.prepareWebappPath();
100     }
101 
102     /**
103      * Returns the directory path for temporary mark files.
104      *
105      * @return the absolute path to the fessboot directory in the system temp directory
106      */
107     @Override
108     protected String getMarkDir() {
109         return new File(System.getProperty(JAVA_IO_TMPDIR), "fessboot").getAbsolutePath();
110     }
111 
112     // ===================================================================================
113     //                                                                        main
114     //                                                                        ============
115 
116     /**
117      * Main method to start the Fess application.
118      * Sets up system properties, configures Tomcat, and starts the server.
119      *
120      * @param args command line arguments (not used)
121      */
122     public static void main(final String[] args) {
123         // update java.io.tmpdir
124         final String tempPath = System.getProperty(FESS_TEMP_PATH);
125         if (tempPath != null) {
126             System.setProperty(JAVA_IO_TMPDIR, tempPath);
127         }
128 
129         final TomcatBoot tomcatBoot = new FessBoot(getPort(), getContextPath()) //
130                 .useTldDetect(); // for JSP
131         final String varPath = System.getProperty(FESS_VAR_PATH);
132         if (varPath != null) {
133             tomcatBoot.atBaseDir(new File(varPath, "webapp").getAbsolutePath());
134         } else if (tempPath != null) {
135             tomcatBoot.atBaseDir(new File(tempPath, "webapp").getAbsolutePath());
136         }
137         final String tomcatConfigPath = getTomcatConfigPath();
138         if (tomcatConfigPath != null) {
139             tomcatBoot.configure(tomcatConfigPath); // e.g. URIEncoding
140         }
141         tomcatBoot.logging(LOGGING_PROPERTIES, op -> {
142             op.ignoreNoFile();
143             String fessLogPath = System.getProperty("fess.log.path");
144             if (fessLogPath == null) {
145                 fessLogPath = "../../logs";
146             }
147             op.replace("fess.log.path", fessLogPath.replace("\\", "/"));
148         }).asYouLikeIt(resource -> {
149             final Host host = resource.getHost();
150             if (host instanceof final StandardHost standardHost) {
151                 standardHost.setErrorReportValveClass(SuppressErrorReportValve.class.getName());
152             }
153         }).useTldDetect(jarName -> (jarName.contains("jstl") || jarName.contains("lasta-taglib"))).asDevelopment(isNoneEnv()).bootAwait();
154     }
155 
156     /**
157      * Shuts down the Fess application.
158      *
159      * @param args command line arguments (not used)
160      */
161     public static void shutdown(final String[] args) {
162         System.exit(0);
163     }
164 
165     /**
166      * Checks if the lasta.env system property is not set.
167      *
168      * @return true if lasta.env is not set, false otherwise
169      */
170     private static boolean isNoneEnv() {
171         return System.getProperty("lasta.env") == null;
172     }
173 
174     /**
175      * Gets the port number for the Tomcat server from system properties.
176      *
177      * @return the port number (default 8080 if not specified)
178      */
179     protected static int getPort() {
180         final String value = System.getProperty(FESS_PORT);
181         if (value != null) {
182             return Integer.parseInt(value);
183         }
184         return 8080;
185     }
186 
187     /**
188      * Gets the context path for the web application from system properties.
189      *
190      * @return the context path (empty string if not specified or if set to "/")
191      */
192     protected static String getContextPath() {
193         final String value = System.getProperty(FESS_CONTEXT_PATH);
194         if (value != null && !"/".equals(value)) {
195             return value;
196         }
197         return StringUtil.EMPTY;
198     }
199 
200     /**
201      * Gets the Tomcat configuration path from system properties.
202      *
203      * @return the Tomcat configuration path, or null if not specified
204      */
205     protected static String getTomcatConfigPath() {
206         return System.getProperty(TOMCAT_CONFIG_PATH);
207     }
208 
209     /**
210      * Sets up the web application context with Fess-specific configurations.
211      * Configures the web resource root and cookie processor for the context.
212      */
213     @Override
214     protected void setupWebappContext() {
215         super.setupWebappContext();
216         String contextPath = getContextPath();
217         if (contextPath.length() > 0 && contextPath.endsWith("/")) {
218             contextPath = contextPath.replaceAll("/+$", StringUtil.EMPTY);
219         }
220         final Context context = (Context) server.getHost().findChild(contextPath);
221         if (context != null) {
222             context.setResources(new FessWebResourceRoot(context));
223             context.setCookieProcessor(new Rfc6265CookieProcessor());
224         }
225     }
226 
227     /**
228      * Creates a Fess-specific boot properties translator.
229      *
230      * @return a new FessBootPropsTranslator instance
231      */
232     @Override
233     protected BootPropsTranslator createBootPropsTranslator() {
234         return new FessBootPropsTranslator();
235     }
236 
237     /**
238      * Fess-specific implementation of BootPropsTranslator.
239      * Handles SSL configuration and cookie settings for the Tomcat server.
240      */
241     static class FessBootPropsTranslator extends BootPropsTranslator {
242         /**
243          * Sets up server configuration if needed, including SSL and cookie settings.
244          *
245          * @param logger the boot logger for logging configuration messages
246          * @param server the Tomcat server instance
247          * @param connector the Tomcat connector
248          * @param props the configuration properties
249          * @param readConfigList the list of read configuration items
250          */
251         @Override
252         public void setupServerConfigIfNeeds(final BootLogger logger, final Tomcat server, final Connector connector,
253                 final Properties props, final List<String> readConfigList) {
254             if (props == null) {
255                 return;
256             }
257             super.setupServerConfigIfNeeds(logger, server, connector, props, readConfigList);
258             doSetupServerConfig(logger, props, "SSLEnabled", value -> {
259                 if ("true".equalsIgnoreCase(value)) {
260                     connector.setProperty("SSLEnabled", "true");
261                     final SSLHostConfig sslHostConfig = new SSLHostConfig();
262                     sslHostConfig.setHostName("_default_");
263                     final SSLHostConfigCertificate certificate =
264                             new SSLHostConfigCertificate(sslHostConfig, SSLHostConfigCertificate.Type.UNDEFINED);
265                     doSetupServerConfig(logger, props, "certificateKeystoreFile", v -> certificate.setCertificateKeystoreFile(v));
266                     doSetupServerConfig(logger, props, "certificateKeystorePassword", v -> certificate.setCertificateKeystorePassword(v));
267                     doSetupServerConfig(logger, props, "certificateKeyAlias", v -> certificate.setCertificateKeyAlias(v));
268                     doSetupServerConfig(logger, props, "sslProtocol", v -> sslHostConfig.setSslProtocol(v));
269                     doSetupServerConfig(logger, props, "enabledProtocols", v -> sslHostConfig.setEnabledProtocols(v.trim().split(",")));
270                     sslHostConfig.addCertificate(certificate);
271                     connector.addSslHostConfig(sslHostConfig);
272 
273                 }
274             });
275             doSetupServerConfig(logger, props, "sameSiteCookies", value -> {
276                 for (final Container container : server.getHost().findChildren()) {
277                     if (container instanceof final Context context
278                             && context.getCookieProcessor() instanceof final CookieProcessorBase cookieProcessor) {
279                         cookieProcessor.setSameSiteCookies(value);
280                     }
281                 }
282             });
283         }
284     }
285 }