View Javadoc
1   /*
2    * Copyright 2012-2017 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.helper;
17  
18  import static org.codelibs.core.stream.StreamUtil.stream;
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.io.UnsupportedEncodingException;
23  import java.net.InetAddress;
24  import java.net.URLEncoder;
25  import java.net.UnknownHostException;
26  import java.nio.file.Files;
27  import java.security.SecureRandom;
28  import java.time.LocalDateTime;
29  import java.util.ArrayList;
30  import java.util.Date;
31  import java.util.HashMap;
32  import java.util.LinkedHashMap;
33  import java.util.List;
34  import java.util.Locale;
35  import java.util.Map;
36  import java.util.Random;
37  import java.util.UUID;
38  import java.util.concurrent.ExecutionException;
39  import java.util.concurrent.TimeUnit;
40  import java.util.concurrent.atomic.AtomicBoolean;
41  import java.util.concurrent.atomic.AtomicInteger;
42  
43  import javax.annotation.PostConstruct;
44  import javax.annotation.PreDestroy;
45  import javax.servlet.ServletContext;
46  
47  import org.apache.commons.lang3.LocaleUtils;
48  import org.apache.commons.lang3.RandomStringUtils;
49  import org.apache.commons.lang3.StringUtils;
50  import org.codelibs.core.lang.StringUtil;
51  import org.codelibs.core.misc.Pair;
52  import org.codelibs.fess.Constants;
53  import org.codelibs.fess.crawler.util.CharUtil;
54  import org.codelibs.fess.mylasta.action.FessMessages;
55  import org.codelibs.fess.mylasta.action.FessUserBean;
56  import org.codelibs.fess.mylasta.direction.FessConfig;
57  import org.codelibs.fess.util.ComponentUtil;
58  import org.codelibs.fess.validation.FessActionValidator;
59  import org.lastaflute.core.message.supplier.UserMessagesCreator;
60  import org.lastaflute.web.TypicalAction;
61  import org.lastaflute.web.ruts.process.ActionRuntime;
62  import org.lastaflute.web.servlet.request.RequestManager;
63  import org.lastaflute.web.util.LaServletContextUtil;
64  import org.lastaflute.web.validation.ActionValidator;
65  import org.slf4j.Logger;
66  import org.slf4j.LoggerFactory;
67  
68  import com.google.common.cache.CacheBuilder;
69  import com.google.common.cache.CacheLoader;
70  import com.google.common.cache.LoadingCache;
71  import com.ibm.icu.util.ULocale;
72  
73  public class SystemHelper {
74      private static final Logger logger = LoggerFactory.getLogger(SystemHelper.class);
75  
76      protected final Map<String, String> designJspFileNameMap = new LinkedHashMap<>();
77  
78      protected final AtomicBoolean forceStop = new AtomicBoolean(false);
79  
80      protected LoadingCache<String, List<Map<String, String>>> langItemsCache;
81  
82      protected String filterPathEncoding;
83  
84      protected String[] supportedLanguages;
85  
86      protected List<Runnable> shutdownHookList = new ArrayList<>();
87  
88      protected Random random = new SecureRandom();
89  
90      protected AtomicInteger previousClusterState = new AtomicInteger(0);
91  
92      @PostConstruct
93      public void init() {
94          final FessConfig fessConfig = ComponentUtil.getFessConfig();
95          filterPathEncoding = fessConfig.getPathEncoding();
96          supportedLanguages = fessConfig.getSupportedLanguagesAsArray();
97          langItemsCache =
98                  CacheBuilder.newBuilder().maximumSize(20).expireAfterAccess(1, TimeUnit.HOURS)
99                          .build(new CacheLoader<String, List<Map<String, String>>>() {
100                             @Override
101                             public List<Map<String, String>> load(final String key) throws Exception {
102                                 final ULocale uLocale = new ULocale(key);
103                                 final Locale displayLocale = uLocale.toLocale();
104                                 final List<Map<String, String>> langItems = new ArrayList<>(supportedLanguages.length);
105                                 final String msg = ComponentUtil.getMessageManager().getMessage(displayLocale, "labels.allLanguages");
106                                 final Map<String, String> defaultMap = new HashMap<>(2);
107                                 defaultMap.put(Constants.ITEM_LABEL, msg);
108                                 defaultMap.put(Constants.ITEM_VALUE, "all");
109                                 langItems.add(defaultMap);
110 
111                                 for (final String lang : supportedLanguages) {
112                                     final Locale locale = LocaleUtils.toLocale(lang);
113                                     final String label = locale.getDisplayName(displayLocale);
114                                     final Map<String, String> map = new HashMap<>(2);
115                                     map.put(Constants.ITEM_LABEL, label);
116                                     map.put(Constants.ITEM_VALUE, lang);
117                                     langItems.add(map);
118                                 }
119                                 return langItems;
120                             }
121                         });
122 
123         ComponentUtil.doInitProcesses(p -> p.run());
124     }
125 
126     @PreDestroy
127     public void destroy() {
128         shutdownHookList.forEach(action -> {
129             try {
130                 action.run();
131             } catch (final Exception e) {
132                 logger.warn("Failed to process shutdown task.", e);
133             }
134         });
135     }
136 
137     public String getUsername() {
138         final RequestManager requestManager = ComponentUtil.getRequestManager();
139         return requestManager.findUserBean(FessUserBean.class).map(user -> {
140             return user.getUserId();
141         }).orElse(Constants.GUEST_USER);
142     }
143 
144     public Date getCurrentTime() {
145         return new Date();
146     }
147 
148     public long getCurrentTimeAsLong() {
149         return System.currentTimeMillis();
150     }
151 
152     public LocalDateTime getCurrentTimeAsLocalDateTime() {
153         return LocalDateTime.now();
154     }
155 
156     public String getLogFilePath() {
157         final String value = System.getProperty("fess.log.path");
158         if (value != null) {
159             return value;
160         } else {
161             final String userDir = System.getProperty("user.dir");
162             final File targetDir = new File(userDir, "target");
163             return new File(targetDir, "logs").getAbsolutePath();
164         }
165     }
166 
167     public String encodeUrlFilter(final String path) {
168         if (filterPathEncoding == null || path == null) {
169             return path;
170         }
171 
172         try {
173             final StringBuilder buf = new StringBuilder(path.length() + 100);
174             for (int i = 0; i < path.length(); i++) {
175                 final char c = path.charAt(i);
176                 if (CharUtil.isUrlChar(c) || c == '^' || c == '{' || c == '}' || c == '|' || c == '\\') {
177                     buf.append(c);
178                 } else {
179                     buf.append(URLEncoder.encode(String.valueOf(c), filterPathEncoding));
180                 }
181             }
182             return buf.toString();
183         } catch (final UnsupportedEncodingException e) {
184             return path;
185         }
186     }
187 
188     public String getHelpLink(final String name) {
189         final String url = ComponentUtil.getFessConfig().getOnlineHelpBaseLink() + name + "-guide.html";
190         return getHelpUrl(url);
191     }
192 
193     protected String getHelpUrl(final String url) {
194         final Locale locale = ComponentUtil.getRequestManager().getUserLocale();
195         if (locale != null) {
196             final String lang = locale.getLanguage();
197             if (ComponentUtil.getFessConfig().isOnlineHelpSupportedLang(lang)) {
198                 return url.replaceFirst("\\{lang\\}", lang).replaceFirst("\\{version\\}",
199                         Constants.MAJOR_VERSION + "." + Constants.MINOR_VERSION);
200             }
201         }
202         return getDefaultHelpLink(url);
203     }
204 
205     protected String getDefaultHelpLink(final String url) {
206         return url.replaceFirst("/\\{lang\\}/", "/").replaceFirst("\\{version\\}", Constants.MAJOR_VERSION + "." + Constants.MINOR_VERSION);
207     }
208 
209     public void addDesignJspFileName(final String key, final String value) {
210         designJspFileNameMap.put(key, value);
211     }
212 
213     public String getDesignJspFileName(final String fileName) {
214         return designJspFileNameMap.get(fileName);
215     }
216 
217     @SuppressWarnings("unchecked")
218     public Pair<String, String>[] getDesignJspFileNames() {
219         return designJspFileNameMap.entrySet().stream().map(e -> new Pair<>(e.getKey(), e.getValue())).toArray(n -> new Pair[n]);
220     }
221 
222     public void refreshDesignJspFiles() {
223         final ServletContext servletContext = LaServletContextUtil.getServletContext();
224         stream(ComponentUtil.getFessConfig().getVirtualHostPaths()).of(
225                 stream -> stream.filter(s -> s != null && !s.equals("/")).forEach(
226                         key -> {
227                             designJspFileNameMap
228                                     .entrySet()
229                                     .stream()
230                                     .forEach(
231                                             e -> {
232                                                 final File jspFile =
233                                                         new File(servletContext.getRealPath("/WEB-INF/view" + key + "/" + e.getValue()));
234                                                 if (!jspFile.exists()) {
235                                                     jspFile.getParentFile().mkdirs();
236                                                     final File baseJspFile =
237                                                             new File(servletContext.getRealPath("/WEB-INF/view/" + e.getValue()));
238                                                     try {
239                                                         Files.copy(baseJspFile.toPath(), jspFile.toPath());
240                                                     } catch (final IOException ex) {
241                                                         logger.warn("Could not copy from " + baseJspFile.getAbsolutePath() + " to "
242                                                                 + jspFile.getAbsolutePath(), ex);
243                                                     }
244                                                 }
245                                             });
246                         }));
247     }
248 
249     public boolean isForceStop() {
250         return forceStop.get();
251     }
252 
253     public void setForceStop(final boolean b) {
254         forceStop.set(true);
255     }
256 
257     public String generateDocId(final Map<String, Object> map) {
258         return UUID.randomUUID().toString().replace("-", StringUtil.EMPTY);
259     }
260 
261     public String abbreviateLongText(final String str) {
262         return StringUtils.abbreviate(str, ComponentUtil.getFessConfig().getMaxLogOutputLengthAsInteger().intValue());
263     }
264 
265     public String normalizeLang(final String value) {
266         if (StringUtil.isBlank(value)) {
267             return null;
268         }
269 
270         final String localeName = value.trim().toLowerCase(Locale.ENGLISH).replace("-", "_");
271 
272         for (final String supportedLang : supportedLanguages) {
273             if (localeName.startsWith(supportedLang.toLowerCase(Locale.ENGLISH))) {
274                 return supportedLang;
275             }
276         }
277         return null;
278     }
279 
280     public List<Map<String, String>> getLanguageItems(final Locale locale) {
281         try {
282             final String localeStr = locale.toString();
283             return langItemsCache.get(localeStr);
284         } catch (final ExecutionException e) {
285             final List<Map<String, String>> langItems = new ArrayList<>(supportedLanguages.length);
286             final String msg = ComponentUtil.getMessageManager().getMessage(locale, "labels.allLanguages");
287             final Map<String, String> defaultMap = new HashMap<>(2);
288             defaultMap.put(Constants.ITEM_LABEL, msg);
289             defaultMap.put(Constants.ITEM_VALUE, "all");
290             langItems.add(defaultMap);
291             return langItems;
292         }
293     }
294 
295     public void sleep(final int sec) {
296         try {
297             Thread.sleep(sec * 1000L);
298         } catch (final InterruptedException e) {
299             if (logger.isDebugEnabled()) {
300                 logger.debug("Interrupted.", e);
301             }
302         }
303     }
304 
305     public void addShutdownHook(final Runnable hook) {
306         shutdownHookList.add(hook);
307     }
308 
309     public String getHostname() {
310         final Map<String, String> env = System.getenv();
311         if (env.containsKey("COMPUTERNAME")) {
312             return env.get("COMPUTERNAME");
313         } else if (env.containsKey("HOSTNAME")) {
314             return env.get("HOSTNAME");
315         }
316         try {
317             return InetAddress.getLocalHost().getHostAddress();
318         } catch (final UnknownHostException e) {
319             logger.debug("Unknown hostname.", e);
320         }
321         return "Unknown";
322     }
323 
324     public void setupAdminHtmlData(final TypicalAction action, final ActionRuntime runtime) {
325         runtime.registerData("developmentMode", ComponentUtil.getFessEsClient().isEmbedded());
326         final String url = ComponentUtil.getFessConfig().getOnlineHelpInstallation();
327         runtime.registerData("installationLink", getHelpUrl(url));
328     }
329 
330     public String getSearchRoleByUser(final String name) {
331         return createSearchRole(ComponentUtil.getFessConfig().getRoleSearchUserPrefix(), name);
332     }
333 
334     public String getSearchRoleByGroup(final String name) {
335         return createSearchRole(ComponentUtil.getFessConfig().getRoleSearchGroupPrefix(), name);
336     }
337 
338     public String getSearchRoleByRole(final String name) {
339         return createSearchRole(ComponentUtil.getFessConfig().getRoleSearchRolePrefix(), name);
340     }
341 
342     protected String createSearchRole(final String type, final String name) {
343         return type + name;
344     }
345 
346     public void reloadConfiguration() {
347         ComponentUtil.getFessEsClient().refresh();
348         ComponentUtil.getLabelTypeHelper().init();
349         ComponentUtil.getPathMappingHelper().init();
350         ComponentUtil.getSuggestHelper().init();
351         ComponentUtil.getPopularWordHelper().init();
352         ComponentUtil.getJobManager().reboot();
353         ComponentUtil.getLdapManager().updateConfig();
354         ComponentUtil.getRelatedContentHelper().update();
355         ComponentUtil.getRelatedQueryHelper().update();
356     }
357 
358     public String generateAccessToken() {
359         return RandomStringUtils.random(ComponentUtil.getFessConfig().getApiAccessTokenLengthAsInteger().intValue(), 0, 0, true, true,
360                 null, random);
361     }
362 
363     public void setRandom(final Random random) {
364         this.random = random;
365     }
366 
367     public boolean isChangedClusterState(final int status) {
368         return previousClusterState.getAndSet(status) != status;
369     }
370 
371     public ActionValidator<FessMessages> createValidator(final RequestManager requestManager,
372             final UserMessagesCreator<FessMessages> messagesCreator, final Class<?>[] runtimeGroups) {
373         return new FessActionValidator<>(requestManager, messagesCreator, runtimeGroups);
374     }
375 
376 }