1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 org.apache.logging.log4j.LogManager;
26 import org.apache.logging.log4j.Logger;
27 import org.codelibs.core.lang.StringUtil;
28 import org.codelibs.fess.Constants;
29 import org.codelibs.fess.opensearch.config.exbhv.PathMappingBhv;
30 import org.codelibs.fess.opensearch.config.exentity.PathMapping;
31 import org.codelibs.fess.util.ComponentUtil;
32 import org.codelibs.fess.util.DocumentUtil;
33 import org.lastaflute.di.core.exception.ComponentNotFoundException;
34 import org.lastaflute.di.core.factory.SingletonLaContainerFactory;
35 import org.lastaflute.web.util.LaRequestUtil;
36
37 import jakarta.annotation.PostConstruct;
38
39
40
41
42 public class PathMappingHelper extends AbstractConfigHelper {
43
44
45
46
47 public PathMappingHelper() {
48 super();
49 }
50
51 private static final Logger logger = LogManager.getLogger(PathMappingHelper.class);
52
53
54 protected static final String FUNCTION_ENCODEURL_MATCHER = "function:encodeUrl";
55
56
57 protected static final String GROOVY_MATCHER = "groovy:";
58
59
60 protected final Map<String, List<PathMapping>> pathMappingMap = new HashMap<>();
61
62
63 protected volatile List<PathMapping> cachedPathMappingList = null;
64
65
66
67
68 @PostConstruct
69 public void init() {
70 if (logger.isDebugEnabled()) {
71 logger.debug("Initializing {}", this.getClass().getSimpleName());
72 }
73 load();
74 }
75
76 @Override
77 public int load() {
78 final List<String> ptList = getProcessTypeList();
79
80 try {
81 final PathMappingBhv pathMappingBhv = ComponentUtil.getComponent(PathMappingBhv.class);
82 cachedPathMappingList = pathMappingBhv.selectList(cb -> {
83 cb.query().addOrderBy_SortOrder_Asc();
84 cb.query().setProcessType_InScope(ptList);
85 cb.fetchFirst(ComponentUtil.getFessConfig().getPagePathMappingMaxFetchSizeAsInteger());
86 });
87 if (logger.isDebugEnabled()) {
88 cachedPathMappingList.forEach(e -> {
89 logger.debug("path mapping: {}: {} -> {}", e.getId(), e.getRegex(), e.getReplacement());
90 });
91 }
92 return cachedPathMappingList.size();
93 } catch (final ComponentNotFoundException e) {
94 if (logger.isDebugEnabled()) {
95 logger.debug("Failed to load path mappings.", e);
96 }
97 cachedPathMappingList = new ArrayList<>();
98 } catch (final Exception e) {
99 logger.warn("Failed to load path mappings.", e);
100 }
101 return 0;
102 }
103
104
105
106
107
108
109 protected List<String> getProcessTypeList() {
110 final List<String> ptList = new ArrayList<>();
111 final String executeType = System.getProperty("lasta.env");
112 if (Constants.EXECUTE_TYPE_CRAWLER.equalsIgnoreCase(executeType)) {
113 ptList.add(Constants.PROCESS_TYPE_REPLACE);
114 } else {
115 ptList.add(Constants.PROCESS_TYPE_DISPLAYING);
116 ptList.add(Constants.PROCESS_TYPE_BOTH);
117 }
118 return ptList;
119 }
120
121
122
123
124
125
126
127 public void setPathMappingList(final String sessionId, final List<PathMapping> pathMappingList) {
128 if (sessionId != null) {
129 if (pathMappingList != null) {
130 pathMappingMap.put(sessionId, pathMappingList);
131 } else {
132 removePathMappingList(sessionId);
133 }
134 }
135 }
136
137
138
139
140
141
142 public void removePathMappingList(final String sessionId) {
143 pathMappingMap.remove(sessionId);
144 }
145
146
147
148
149
150
151
152 public List<PathMapping> getPathMappingList(final String sessionId) {
153 if (sessionId == null) {
154 return null;
155 }
156 return pathMappingMap.get(sessionId);
157 }
158
159
160
161
162
163
164
165
166 public String replaceUrl(final String sessionId, final String url) {
167 final List<PathMapping> pathMappingList = getPathMappingList(sessionId);
168 if (pathMappingList == null) {
169 return url;
170 }
171 return replaceUrl(pathMappingList, url);
172 }
173
174
175
176
177
178
179
180 public String replaceUrls(final String text) {
181 if (cachedPathMappingList == null) {
182 synchronized (this) {
183 if (cachedPathMappingList == null) {
184 init();
185 }
186 }
187 }
188 String result = text;
189 for (final PathMapping pathMapping : cachedPathMappingList) {
190 if (matchUserAgent(pathMapping)) {
191 String replacement = pathMapping.getReplacement();
192 if (replacement == null) {
193 replacement = StringUtil.EMPTY;
194 }
195 result = result.replaceAll("(\"[^\"]*)" + pathMapping.getRegex() + "([^\"]*\")", "$1" + replacement + "$2");
196 }
197 }
198 return result;
199 }
200
201
202
203
204
205
206
207 public String replaceUrl(final String url) {
208 if (cachedPathMappingList == null) {
209 synchronized (this) {
210 if (cachedPathMappingList == null) {
211 init();
212 }
213 }
214 }
215 return replaceUrl(cachedPathMappingList, url);
216 }
217
218
219
220
221
222
223
224
225 public BiFunction<String, Matcher, String> createPathMatcher(final Matcher matcher, final String replacement) {
226 if (FUNCTION_ENCODEURL_MATCHER.equals(replacement)) {
227 return (u, m) -> DocumentUtil.encodeUrl(u);
228 }
229 if (!replacement.startsWith(GROOVY_MATCHER)) {
230 return (u, m) -> m.replaceAll(replacement);
231 }
232 final String template = replacement.substring(GROOVY_MATCHER.length());
233 return (u, m) -> {
234 final Map<String, Object> paramMap = new HashMap<>();
235 paramMap.put("url", u);
236 paramMap.put("matcher", m);
237 final Object value =
238 ComponentUtil.getScriptEngineFactory().getScriptEngine(Constants.DEFAULT_SCRIPT).evaluate(template, paramMap);
239 if (value == null) {
240 return u;
241 }
242 return value.toString();
243 };
244 }
245
246
247
248
249
250
251
252
253 protected String replaceUrl(final List<PathMapping> pathMappingList, final String url) {
254 String newUrl = url;
255 for (final PathMapping pathMapping : pathMappingList) {
256 if (matchUserAgent(pathMapping)) {
257 newUrl = pathMapping.process(this, newUrl);
258 }
259 }
260 if (logger.isDebugEnabled() && !StringUtil.equals(url, newUrl)) {
261 logger.debug("replace: {} -> {}", url, newUrl);
262 }
263 return newUrl;
264 }
265
266
267
268
269
270
271
272 protected boolean matchUserAgent(final PathMapping pathMapping) {
273 if (!pathMapping.hasUAMathcer()) {
274 return true;
275 }
276
277 if (SingletonLaContainerFactory.getExternalContext().getRequest() != null) {
278 return LaRequestUtil.getOptionalRequest().map(request -> {
279 final String userAgent = request.getHeader("user-agent");
280 if (StringUtil.isBlank(userAgent)) {
281 return false;
282 }
283
284 return pathMapping.getUAMatcher(userAgent).find();
285 }).orElse(false);
286 }
287 return false;
288 }
289 }