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.io.BufferedReader;
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.IOException;
22 import java.io.InputStreamReader;
23 import java.io.Reader;
24 import java.io.StringReader;
25 import java.util.Base64;
26 import java.util.HashSet;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.zip.GZIPInputStream;
30 import java.util.zip.GZIPOutputStream;
31
32 import org.apache.commons.lang3.StringUtils;
33 import org.codelibs.core.io.ReaderUtil;
34 import org.codelibs.core.io.SerializeUtil;
35 import org.codelibs.core.lang.StringUtil;
36 import org.codelibs.fess.Constants;
37 import org.codelibs.fess.crawler.builder.RequestDataBuilder;
38 import org.codelibs.fess.crawler.client.CrawlerClient;
39 import org.codelibs.fess.crawler.client.CrawlerClientFactory;
40 import org.codelibs.fess.crawler.entity.RequestData;
41 import org.codelibs.fess.crawler.entity.ResponseData;
42 import org.codelibs.fess.crawler.entity.ResultData;
43 import org.codelibs.fess.crawler.exception.ChildUrlsException;
44 import org.codelibs.fess.crawler.exception.CrawlerSystemException;
45 import org.codelibs.fess.crawler.exception.CrawlingAccessException;
46 import org.codelibs.fess.crawler.processor.ResponseProcessor;
47 import org.codelibs.fess.crawler.processor.impl.DefaultResponseProcessor;
48 import org.codelibs.fess.crawler.rule.Rule;
49 import org.codelibs.fess.crawler.rule.RuleManager;
50 import org.codelibs.fess.crawler.transformer.Transformer;
51 import org.codelibs.fess.crawler.util.TextUtil;
52 import org.codelibs.fess.es.config.exentity.CrawlingConfig;
53 import org.codelibs.fess.mylasta.direction.FessConfig;
54 import org.codelibs.fess.util.ComponentUtil;
55 import org.lastaflute.di.core.SingletonLaContainer;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 public class DocumentHelper {
60 private static final Logger logger = LoggerFactory.getLogger(DocumentHelper.class);
61
62 private static final String SIMILAR_DOC_HASH_PREFIX = "$";
63
64 public String getContent(final ResponseData responseData, final String content, final Map<String, Object> dataMap) {
65 if (content == null) {
66 return StringUtil.EMPTY;
67 }
68
69 final int maxAlphanumTermSize = getMaxAlphanumTermSize();
70 final int maxSymbolTermSize = getMaxSymbolTermSize();
71 final boolean duplicateTermRemoved = isDuplicateTermRemoved();
72 final int[] spaceChars = getSpaceChars();
73 try (final Reader reader = new StringReader(content)) {
74 return TextUtil.normalizeText(reader).initialCapacity(content.length()).maxAlphanumTermSize(maxAlphanumTermSize)
75 .maxSymbolTermSize(maxSymbolTermSize).duplicateTermRemoved(duplicateTermRemoved).spaceChars(spaceChars).execute();
76 } catch (final IOException e) {
77 return StringUtil.EMPTY;
78 }
79 }
80
81 protected int getMaxAlphanumTermSize() {
82 final FessConfig fessConfig = ComponentUtil.getFessConfig();
83 return fessConfig.getCrawlerDocumentMaxAlphanumTermSizeAsInteger().intValue();
84 }
85
86 protected int getMaxSymbolTermSize() {
87 final FessConfig fessConfig = ComponentUtil.getFessConfig();
88 return fessConfig.getCrawlerDocumentMaxSymbolTermSizeAsInteger().intValue();
89 }
90
91 protected boolean isDuplicateTermRemoved() {
92 final FessConfig fessConfig = ComponentUtil.getFessConfig();
93 return fessConfig.isCrawlerDocumentDuplicateTermRemoved();
94 }
95
96 protected int[] getSpaceChars() {
97 final FessConfig fessConfig = ComponentUtil.getFessConfig();
98 return fessConfig.getCrawlerDocumentSpaceCharsAsArray();
99 }
100
101 public String getDigest(final ResponseData responseData, final String content, final Map<String, Object> dataMap, final int maxWidth) {
102 if (content == null) {
103 return StringUtil.EMPTY;
104 }
105
106 String subContent;
107 if (content.length() < maxWidth * 2) {
108 subContent = content;
109 } else {
110 subContent = content.substring(0, maxWidth * 2);
111 }
112
113 final int[] spaceChars = getSpaceChars();
114 try (final Reader reader = new StringReader(subContent)) {
115 final String originalStr = TextUtil.normalizeText(reader).initialCapacity(content.length()).spaceChars(spaceChars).execute();
116 return StringUtils.abbreviate(originalStr, maxWidth);
117 } catch (final IOException e) {
118 return StringUtil.EMPTY;
119 }
120 }
121
122 public Map<String, Object> processRequest(final CrawlingConfig crawlingConfig, final String crawlingInfoId, final String url) {
123 if (StringUtil.isBlank(crawlingInfoId)) {
124 throw new CrawlingAccessException("sessionId is null.");
125 }
126
127 final CrawlerClientFactory crawlerClientFactory = ComponentUtil.getCrawlerClientFactory();
128 crawlingConfig.initializeClientFactory(crawlerClientFactory);
129 final CrawlerClient client = crawlerClientFactory.getClient(url);
130 if (client == null) {
131 throw new CrawlingAccessException("CrawlerClient is null for " + url);
132 }
133
134 final long startTime = System.currentTimeMillis();
135 try (final ResponseData responseData = client.execute(RequestDataBuilder.newRequestData().get().url(url).build())) {
136 if (responseData.getRedirectLocation() != null) {
137 final Set<RequestData> childUrlList = new HashSet<>();
138 childUrlList.add(RequestDataBuilder.newRequestData().get().url(responseData.getRedirectLocation()).build());
139 throw new ChildUrlsException(childUrlList, "Redirected from " + url);
140 }
141 responseData.setExecutionTime(System.currentTimeMillis() - startTime);
142 responseData.setSessionId(crawlingInfoId);
143
144 final RuleManager ruleManager = SingletonLaContainer.getComponent(RuleManager.class);
145 final Rule rule = ruleManager.getRule(responseData);
146 if (rule == null) {
147 throw new CrawlingAccessException("No url rule for " + url);
148 } else {
149 responseData.setRuleId(rule.getRuleId());
150 final ResponseProcessor responseProcessor = rule.getResponseProcessor();
151 if (responseProcessor instanceof DefaultResponseProcessor) {
152 final Transformer transformer = ((DefaultResponseProcessor) responseProcessor).getTransformer();
153 final ResultData resultData = transformer.transform(responseData);
154 final byte[] data = resultData.getData();
155 if (data != null) {
156 try {
157 @SuppressWarnings("unchecked")
158 final Map<String, Object> result = (Map<String, Object>) SerializeUtil.fromBinaryToObject(data);
159 return result;
160 } catch (final Exception e) {
161 throw new CrawlerSystemException("Could not create an instance from bytes.", e);
162 }
163 }
164 } else {
165 throw new CrawlingAccessException("The response processor is not DefaultResponseProcessor. responseProcessor: "
166 + responseProcessor + ", url: " + url);
167 }
168 }
169 return null;
170 } catch (final Exception e) {
171 throw new CrawlingAccessException("Failed to parse " + url, e);
172 }
173 }
174
175 public String decodeSimilarDocHash(final String hash) {
176 if (hash != null && hash.startsWith(SIMILAR_DOC_HASH_PREFIX) && hash.length() > SIMILAR_DOC_HASH_PREFIX.length()) {
177 final byte[] decode = Base64.getUrlDecoder().decode(hash.substring(SIMILAR_DOC_HASH_PREFIX.length()));
178 try (BufferedReader reader =
179 new BufferedReader(new InputStreamReader(new GZIPInputStream(new ByteArrayInputStream(decode)), Constants.UTF_8))) {
180 return ReaderUtil.readText(reader);
181 } catch (final IOException e) {
182 logger.warn("Failed to decode " + hash, e);
183 }
184 }
185 return hash;
186 }
187
188 public String encodeSimilarDocHash(final String hash) {
189 if (hash != null && !hash.startsWith(SIMILAR_DOC_HASH_PREFIX)) {
190 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
191 try (GZIPOutputStream gos = new GZIPOutputStream(baos)) {
192 gos.write(hash.getBytes(Constants.UTF_8));
193 }
194 return SIMILAR_DOC_HASH_PREFIX + Base64.getUrlEncoder().withoutPadding().encodeToString(baos.toByteArray());
195 } catch (final IOException e) {
196 logger.warn("Failed to encode " + hash, e);
197 }
198 }
199 return hash;
200 }
201
202 }