View Javadoc
1   /*
2    * Copyright 2012-2021 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 java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  import java.util.function.BiFunction;
23  import java.util.regex.Matcher;
24  
25  import javax.annotation.PostConstruct;
26  
27  import org.apache.logging.log4j.LogManager;
28  import org.apache.logging.log4j.Logger;
29  import org.codelibs.core.lang.StringUtil;
30  import org.codelibs.fess.Constants;
31  import org.codelibs.fess.es.config.exbhv.PathMappingBhv;
32  import org.codelibs.fess.es.config.exentity.PathMapping;
33  import org.codelibs.fess.util.ComponentUtil;
34  import org.codelibs.fess.util.DocumentUtil;
35  import org.lastaflute.di.core.exception.ComponentNotFoundException;
36  import org.lastaflute.di.core.factory.SingletonLaContainerFactory;
37  import org.lastaflute.web.util.LaRequestUtil;
38  
39  public class PathMappingHelper extends AbstractConfigHelper {
40  
41      private static final Logger logger = LogManager.getLogger(PathMappingHelper.class);
42  
43      protected static final String FUNCTION_ENCODEURL_MATCHER = "function:encodeUrl";
44  
45      protected static final String GROOVY_MATCHER = "groovy:";
46  
47      protected final Map<String, List<PathMapping>> pathMappingMap = new HashMap<>();
48  
49      protected volatile List<PathMapping> cachedPathMappingList = null;
50  
51      @PostConstruct
52      public void init() {
53          if (logger.isDebugEnabled()) {
54              logger.debug("Initialize {}", this.getClass().getSimpleName());
55          }
56          load();
57      }
58  
59      @Override
60      public int load() {
61          final List<String> ptList = getProcessTypeList();
62  
63          try {
64              final PathMappingBhv pathMappingBhv = ComponentUtil.getComponent(PathMappingBhv.class);
65              cachedPathMappingList = pathMappingBhv.selectList(cb -> {
66                  cb.query().addOrderBy_SortOrder_Asc();
67                  cb.query().setProcessType_InScope(ptList);
68                  cb.fetchFirst(ComponentUtil.getFessConfig().getPagePathMappingMaxFetchSizeAsInteger());
69              });
70              return cachedPathMappingList.size();
71          } catch (final ComponentNotFoundException e) {
72              if (logger.isDebugEnabled()) {
73                  logger.debug("Failed to load path mappings.", e);
74              }
75              cachedPathMappingList = new ArrayList<>();
76          } catch (final Exception e) {
77              logger.warn("Failed to load path mappings.", e);
78          }
79          return 0;
80      }
81  
82      protected List<String> getProcessTypeList() {
83          final List<String> ptList = new ArrayList<>();
84          final String executeType = System.getProperty("lasta.env");
85          if (Constants.EXECUTE_TYPE_CRAWLER.equalsIgnoreCase(executeType)) {
86              ptList.add(Constants.PROCESS_TYPE_REPLACE);
87          } else {
88              ptList.add(Constants.PROCESS_TYPE_DISPLAYING);
89              ptList.add(Constants.PROCESS_TYPE_BOTH);
90          }
91          return ptList;
92      }
93  
94      public void setPathMappingList(final String sessionId, final List<PathMapping> pathMappingList) {
95          if (sessionId != null) {
96              if (pathMappingList != null) {
97                  pathMappingMap.put(sessionId, pathMappingList);
98              } else {
99                  removePathMappingList(sessionId);
100             }
101         }
102     }
103 
104     public void removePathMappingList(final String sessionId) {
105         pathMappingMap.remove(sessionId);
106     }
107 
108     public List<PathMapping> getPathMappingList(final String sessionId) {
109         if (sessionId == null) {
110             return null;
111         }
112         return pathMappingMap.get(sessionId);
113     }
114 
115     public String replaceUrl(final String sessionId, final String url) { // for crawling
116         final List<PathMapping> pathMappingList = getPathMappingList(sessionId);
117         if (pathMappingList == null) {
118             return url;
119         }
120         return replaceUrl(pathMappingList, url);
121     }
122 
123     public String replaceUrls(final String text) {
124         if (cachedPathMappingList == null) {
125             synchronized (this) {
126                 if (cachedPathMappingList == null) {
127                     init();
128                 }
129             }
130         }
131         String result = text;
132         for (final PathMapping pathMapping : cachedPathMappingList) {
133             if (matchUserAgent(pathMapping)) {
134                 String replacement = pathMapping.getReplacement();
135                 if (replacement == null) {
136                     replacement = StringUtil.EMPTY;
137                 }
138                 result = result.replaceAll("(\"[^\"]*)" + pathMapping.getRegex() + "([^\"]*\")", "$1" + replacement + "$2");
139             }
140         }
141         return result;
142     }
143 
144     public String replaceUrl(final String url) { // for display or url converer
145         if (cachedPathMappingList == null) {
146             synchronized (this) {
147                 if (cachedPathMappingList == null) {
148                     init();
149                 }
150             }
151         }
152         return replaceUrl(cachedPathMappingList, url);
153     }
154 
155     public BiFunction<String, Matcher, String> createPathMatcher(final Matcher matcher, final String replacement) { // for PathMapping
156         if (FUNCTION_ENCODEURL_MATCHER.equals(replacement)) {
157             return (u, m) -> DocumentUtil.encodeUrl(u);
158         }
159         if (!replacement.startsWith(GROOVY_MATCHER)) {
160             return (u, m) -> m.replaceAll(replacement);
161         }
162         final String template = replacement.substring(GROOVY_MATCHER.length());
163         return (u, m) -> {
164             final Map<String, Object> paramMap = new HashMap<>();
165             paramMap.put("url", u);
166             paramMap.put("matcher", m);
167             final Object value =
168                     ComponentUtil.getScriptEngineFactory().getScriptEngine(Constants.DEFAULT_SCRIPT).evaluate(template, paramMap);
169             if (value == null) {
170                 return u;
171             }
172             return value.toString();
173         };
174     }
175 
176     protected String replaceUrl(final List<PathMapping> pathMappingList, final String url) {
177         String newUrl = url;
178         for (final PathMapping pathMapping : pathMappingList) {
179             if (matchUserAgent(pathMapping)) {
180                 newUrl = pathMapping.process(this, newUrl);
181             }
182         }
183         return newUrl;
184     }
185 
186     protected boolean matchUserAgent(final PathMapping pathMapping) {
187         if (!pathMapping.hasUAMathcer()) {
188             return true;
189         }
190 
191         if (SingletonLaContainerFactory.getExternalContext().getRequest() != null) {
192             return LaRequestUtil.getOptionalRequest().map(request -> {
193                 final String userAgent = request.getHeader("user-agent");
194                 if (StringUtil.isBlank(userAgent)) {
195                     return false;
196                 }
197 
198                 return pathMapping.getUAMatcher(userAgent).find();
199             }).orElse(false);
200         }
201         return false;
202     }
203 }