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 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; // 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; // 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; // 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; // 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 }