This is a cache of https://fess.codelibs.org/11.3/xref/org/codelibs/fess/helper/ViewHelper.html. It is a snapshot of the page at 2018-04-28T11:34:58.386+0000.
ViewHelper xref
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.BufferedInputStream;
19  import java.io.File;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.UnsupportedEncodingException;
23  import java.net.URLDecoder;
24  import java.net.URLEncoder;
25  import java.util.ArrayList;
26  import java.util.Date;
27  import java.util.HashMap;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Locale;
31  import java.util.Map;
32  import java.util.Set;
33  import java.util.concurrent.ConcurrentHashMap;
34  import java.util.function.Consumer;
35  import java.util.function.Function;
36  import java.util.regex.Matcher;
37  import java.util.regex.Pattern;
38  
39  import javax.annotation.PostConstruct;
40  import javax.annotation.Resource;
41  import javax.servlet.http.HttpServletRequest;
42  
43  import org.apache.catalina.connector.ClientAbortException;
44  import org.apache.commons.io.IOUtils;
45  import org.apache.commons.lang3.StringUtils;
46  import org.apache.commons.text.StringEscapeUtils;
47  import org.codelibs.core.CoreLibConstants;
48  import org.codelibs.core.lang.StringUtil;
49  import org.codelibs.core.misc.Base64Util;
50  import org.codelibs.core.misc.DynamicProperties;
51  import org.codelibs.fess.Constants;
52  import org.codelibs.fess.crawler.builder.RequestDataBuilder;
53  import org.codelibs.fess.crawler.client.CrawlerClient;
54  import org.codelibs.fess.crawler.client.CrawlerClientFactory;
55  import org.codelibs.fess.crawler.entity.ResponseData;
56  import org.codelibs.fess.crawler.util.CharUtil;
57  import org.codelibs.fess.entity.FacetQueryView;
58  import org.codelibs.fess.es.config.exentity.CrawlingConfig;
59  import org.codelibs.fess.exception.FessSystemException;
60  import org.codelibs.fess.helper.UserAgentHelper.UserAgentType;
61  import org.codelibs.fess.mylasta.direction.FessConfig;
62  import org.codelibs.fess.util.ComponentUtil;
63  import org.codelibs.fess.util.DocumentUtil;
64  import org.codelibs.fess.util.ResourceUtil;
65  import org.lastaflute.taglib.function.LaFunctions;
66  import org.lastaflute.web.response.ActionResponse;
67  import org.lastaflute.web.response.StreamResponse;
68  import org.lastaflute.web.ruts.process.ActionRuntime;
69  import org.lastaflute.web.util.LaRequestUtil;
70  import org.lastaflute.web.util.LaResponseUtil;
71  import org.lastaflute.web.util.LaServletContextUtil;
72  import org.slf4j.Logger;
73  import org.slf4j.LoggerFactory;
74  
75  import com.github.jknack.handlebars.Context;
76  import com.github.jknack.handlebars.Handlebars;
77  import com.github.jknack.handlebars.Template;
78  import com.github.jknack.handlebars.io.FileTemplateLoader;
79  import com.ibm.icu.text.SimpleDateFormat;
80  
81  public class ViewHelper {
82  
83      private static final String CONTENT_DISPOSITION = "Content-Disposition";
84  
85      private static final String HL_CACHE = "hl_cache";
86  
87      private static final String QUERIES = "queries";
88  
89      private static final String CACHE_MSG = "cache_msg";
90  
91      private static final Pattern LOCAL_PATH_PATTERN = Pattern.compile("^file:/+[a-zA-Z]:");
92  
93      private static final Pattern SHARED_FOLDER_PATTERN = Pattern.compile("^file:/+[^/]\\.");
94  
95      private static final Logger logger = LoggerFactory.getLogger(ViewHelper.class);
96  
97      @Resource
98      protected DynamicProperties systemProperties;
99  
100     @Resource
101     protected PathMappingHelper pathMappingHelper;
102 
103     @Resource
104     protected UserAgentHelper userAgentHelper;
105 
106     public int titleLength = 50;
107 
108     public int sitePathLength = 50;
109 
110     public boolean encodeUrlLink = false;
111 
112     public String urlLinkEncoding = Constants.UTF_8;
113 
114     public String[] highlightedFields = new String[] { "hl_content", "digest" };
115 
116     public String originalHighlightTagPre = "<em>";
117 
118     public String originalHighlightTagPost = "</em>";
119 
120     public String highlightTagPre = "<strong>";
121 
122     public String highlightTagPost = "</strong>";
123 
124     protected boolean useSession = true;
125 
126     private final Map<String, String> pageCacheMap = new ConcurrentHashMap<>();
127 
128     private final Map<String, String> initFacetParamMap = new HashMap<>();
129 
130     private final Map<String, String> initGeoParamMap = new HashMap<>();
131 
132     private final List<FacetQueryView> facetQueryViewList = new ArrayList<>();
133 
134     public String cacheTemplateName = "cache";
135 
136     private String escapedHighlightPre = null;
137 
138     private String escapedHighlightPost = null;
139 
140     protected ActionHook actionHook = new ActionHook();
141 
142     private final Set<String> inlineMimeTypeSet = new HashSet<>();
143 
144     @PostConstruct
145     public void init() {
146         escapedHighlightPre = LaFunctions.h(originalHighlightTagPre);
147         escapedHighlightPost = LaFunctions.h(originalHighlightTagPost);
148     }
149 
150     public String getContentTitle(final Map<String, Object> document) {
151         final int size = titleLength;
152         final FessConfig fessConfig = ComponentUtil.getFessConfig();
153         String title = DocumentUtil.getValue(document, fessConfig.getIndexFieldTitle(), String.class);
154         if (StringUtil.isBlank(title)) {
155             title = DocumentUtil.getValue(document, fessConfig.getIndexFieldFilename(), String.class);
156             if (StringUtil.isBlank(title)) {
157                 title = DocumentUtil.getValue(document, fessConfig.getIndexFieldUrl(), String.class);
158             }
159         }
160         return StringUtils.abbreviate(title, size);
161     }
162 
163     public String getContentDescription(final Map<String, Object> document) {
164         for (final String field : highlightedFields) {
165             final String text = DocumentUtil.getValue(document, field, String.class);
166             if (StringUtil.isNotBlank(text)) {
167                 return escapeHighlight(text);
168             }
169         }
170 
171         return StringUtil.EMPTY;
172     }
173 
174     protected String escapeHighlight(final String text) {
175         return LaFunctions.h(text).replaceAll(escapedHighlightPre, highlightTagPre).replaceAll(escapedHighlightPost, highlightTagPost);
176     }
177 
178     protected String removeHighlightTag(final String str) {
179         return str.replaceAll(originalHighlightTagPre, StringUtil.EMPTY).replaceAll(originalHighlightTagPost, StringUtil.EMPTY);
180     }
181 
182     public String getUrlLink(final Map<String, Object> document) {
183         final FessConfig fessConfig = ComponentUtil.getFessConfig();
184         String url = DocumentUtil.getValue(document, fessConfig.getIndexFieldUrl(), String.class);
185 
186         if (StringUtil.isBlank(url)) {
187             return "#not-found-" + DocumentUtil.getValue(document, fessConfig.getIndexFieldDocId(), String.class);
188         }
189 
190         final boolean isSmbUrl = url.startsWith("smb:");
191         final boolean isFtpUrl = url.startsWith("ftp:");
192         final boolean isSmbOrFtpUrl = isSmbUrl || isFtpUrl;
193 
194         // replacing url with mapping data
195         if (pathMappingHelper != null) {
196             url = pathMappingHelper.replaceUrl(url);
197         }
198 
199         final boolean isHttpUrl = url.startsWith("http:") || url.startsWith("https:");
200 
201         if (isSmbUrl) {
202             url = url.replace("smb:", "file:");
203         }
204 
205         if (isHttpUrl && isSmbOrFtpUrl) {
206             //  smb/ftp->http
207             // encode
208             final StringBuilder buf = new StringBuilder(url.length() + 100);
209             for (final char c : url.toCharArray()) {
210                 if (CharUtil.isUrlChar(c)) {
211                     buf.append(c);
212                 } else {
213                     try {
214                         buf.append(URLEncoder.encode(String.valueOf(c), urlLinkEncoding));
215                     } catch (final UnsupportedEncodingException e) {
216                         buf.append(c);
217                     }
218                 }
219             }
220             url = buf.toString();
221         } else if (url.startsWith("file:")) {
222             // file, smb/ftp->http
223             url = updateFileProtocol(url);
224 
225             if (encodeUrlLink) {
226                 return appendQueryParameter(document, url);
227             }
228 
229             // decode
230             if (!isSmbOrFtpUrl) {
231                 // file
232                 try {
233                     url = URLDecoder.decode(url.replace("+", "%2B"), urlLinkEncoding);
234                 } catch (Exception e) {
235                     if (logger.isDebugEnabled()) {
236                         logger.warn("Failed to decode " + url, e);
237                     }
238                 }
239             }
240         }
241         // http, ftp
242         // nothing
243 
244         return appendQueryParameter(document, url);
245     }
246 
247     protected String updateFileProtocol(String url) {
248         final int pos = url.indexOf(':', 5);
249         final boolean isLocalFile = pos > 0 && pos < 12;
250 
251         final UserAgentType ua = userAgentHelper.getUserAgentType();
252         switch (ua) {
253         case IE:
254             if (isLocalFile) {
255                 url = url.replaceFirst("file:/+", systemProperties.getProperty("file.protocol.winlocal.ie", "file://"));
256             } else {
257                 url = url.replaceFirst("file:/+", systemProperties.getProperty("file.protocol.ie", "file://"));
258             }
259             break;
260         case FIREFOX:
261             if (isLocalFile) {
262                 url = url.replaceFirst("file:/+", systemProperties.getProperty("file.protocol.winlocal.firefox", "file://"));
263             } else {
264                 url = url.replaceFirst("file:/+", systemProperties.getProperty("file.protocol.firefox", "file://///"));
265             }
266             break;
267         case CHROME:
268             if (isLocalFile) {
269                 url = url.replaceFirst("file:/+", systemProperties.getProperty("file.protocol.winlocal.chrome", "file://"));
270             } else {
271                 url = url.replaceFirst("file:/+", systemProperties.getProperty("file.protocol.chrome", "file://"));
272             }
273             break;
274         case SAFARI:
275             if (isLocalFile) {
276                 url = url.replaceFirst("file:/+", systemProperties.getProperty("file.protocol.winlocal.safari", "file://"));
277             } else {
278                 url = url.replaceFirst("file:/+", systemProperties.getProperty("file.protocol.safari", "file:////"));
279             }
280             break;
281         case OPERA:
282             if (isLocalFile) {
283                 url = url.replaceFirst("file:/+", systemProperties.getProperty("file.protocol.winlocal.opera", "file://"));
284             } else {
285                 url = url.replaceFirst("file:/+", systemProperties.getProperty("file.protocol.opera", "file://"));
286             }
287             break;
288         default:
289             if (isLocalFile) {
290                 url = url.replaceFirst("file:/+", systemProperties.getProperty("file.protocol.winlocal.other", "file://"));
291             } else {
292                 url = url.replaceFirst("file:/+", systemProperties.getProperty("file.protocol.other", "file://"));
293             }
294             break;
295         }
296         return url;
297     }
298 
299     protected String appendQueryParameter(final Map<String, Object> document, final String url) {
300         final FessConfig fessConfig = ComponentUtil.getFessConfig();
301         if (fessConfig.isAppendQueryParameter()) {
302             if (url.indexOf('#') >= 0) {
303                 return url;
304             }
305 
306             final String mimetype = DocumentUtil.getValue(document, fessConfig.getIndexFieldMimetype(), String.class);
307             if (StringUtil.isNotBlank(mimetype)) {
308                 if ("application/pdf".equals(mimetype)) {
309                     return appendPDFSearchWord(url);
310                 } else {
311                     // TODO others..
312                     return url;
313                 }
314             }
315         }
316         return url;
317     }
318 
319     protected String appendPDFSearchWord(final String url) {
320         final String queries = (String) LaRequestUtil.getRequest().getAttribute(Constants.REQUEST_QUERIES);
321         if (queries != null) {
322             try {
323                 final StringBuilder buf = new StringBuilder(url.length() + 100);
324                 buf.append(url).append("#search=%22");
325                 buf.append(URLEncoder.encode(queries.trim(), Constants.UTF_8));
326                 buf.append("%22");
327                 return buf.toString();
328             } catch (final UnsupportedEncodingException e) {
329                 logger.warn("Unsupported encoding.", e);
330             }
331         }
332         return url;
333     }
334 
335     public String getPagePath(final String page) {
336         final Locale locale = ComponentUtil.getRequestManager().getUserLocale();
337         final String lang = locale.getLanguage();
338         final String country = locale.getCountry();
339 
340         final String pathLC = getLocalizedPagePath(page, lang, country);
341         final String pLC = pageCacheMap.get(pathLC);
342         if (pLC != null) {
343             return pLC;
344         }
345         if (existsPage(pathLC)) {
346             pageCacheMap.put(pathLC, pathLC);
347             return pathLC;
348         }
349 
350         final String pathL = getLocalizedPagePath(page, lang, null);
351         final String pL = pageCacheMap.get(pathL);
352         if (pL != null) {
353             return pL;
354         }
355         if (existsPage(pathL)) {
356             pageCacheMap.put(pathLC, pathL);
357             return pathL;
358         }
359 
360         final String path = getLocalizedPagePath(page, null, null);
361         final String p = pageCacheMap.get(path);
362         if (p != null) {
363             return p;
364         }
365         if (existsPage(path)) {
366             pageCacheMap.put(pathLC, path);
367             return path;
368         }
369 
370         return "index.jsp";
371     }
372 
373     private String getLocalizedPagePath(final String page, final String lang, final String country) {
374         final StringBuilder buf = new StringBuilder(100);
375         buf.append("/WEB-INF/view/").append(page);
376         if (StringUtil.isNotBlank(lang)) {
377             buf.append('_').append(lang);
378             if (StringUtil.isNotBlank(country)) {
379                 buf.append('_').append(country);
380             }
381         }
382         buf.append(".jsp");
383         return buf.toString();
384     }
385 
386     private boolean existsPage(final String path) {
387         final String realPath = LaServletContextUtil.getServletContext().getRealPath(path);
388         final File file = new File(realPath);
389         return file.isFile();
390     }
391 
392     public String createCacheContent(final Map<String, Object> doc, final String[] queries) {
393         final FessConfig fessConfig = ComponentUtil.getFessConfig();
394         final FileTemplateLoader loader = new FileTemplateLoader(ResourceUtil.getViewTemplatePath().toFile());
395         final Handlebars handlebars = new Handlebars(loader);
396 
397         Locale locale = ComponentUtil.getRequestManager().getUserLocale();
398         if (locale == null) {
399             locale = Locale.ENGLISH;
400         }
401         String url = DocumentUtil.getValue(doc, fessConfig.getIndexFieldUrl(), String.class);
402         if (url == null) {
403             url = ComponentUtil.getMessageManager().getMessage(locale, "labels.search_unknown");
404         }
405         doc.put(fessConfig.getResponseFieldUrlLink(), getUrlLink(doc));
406         String createdStr;
407         final Date created = DocumentUtil.getValue(doc, fessConfig.getIndexFieldCreated(), Date.class);
408         if (created != null) {
409             final SimpleDateFormat sdf = new SimpleDateFormat(CoreLibConstants.DATE_FORMAT_ISO_8601_EXTEND);
410             createdStr = sdf.format(created);
411         } else {
412             createdStr = ComponentUtil.getMessageManager().getMessage(locale, "labels.search_unknown");
413         }
414         doc.put(CACHE_MSG, ComponentUtil.getMessageManager()
415                 .getMessage(locale, "labels.search_cache_msg", new Object[] { url, createdStr }));
416 
417         doc.put(QUERIES, queries);
418 
419         String cache = DocumentUtil.getValue(doc, fessConfig.getIndexFieldCache(), String.class);
420         if (cache != null) {
421             final String mimetype = DocumentUtil.getValue(doc, fessConfig.getIndexFieldMimetype(), String.class);
422             if (!ComponentUtil.getFessConfig().isHtmlMimetypeForCache(mimetype)) {
423                 cache = StringEscapeUtils.escapeHtml4(cache);
424             }
425             cache = pathMappingHelper.replaceUrls(cache);
426             if (queries != null && queries.length > 0) {
427                 doc.put(HL_CACHE, replaceHighlightQueries(cache, queries));
428             } else {
429                 doc.put(HL_CACHE, cache);
430             }
431         } else {
432             doc.put(fessConfig.getIndexFieldCache(), StringUtil.EMPTY);
433             doc.put(HL_CACHE, StringUtil.EMPTY);
434         }
435 
436         try {
437             final Template template = handlebars.compile(cacheTemplateName);
438             final Context hbsContext = Context.newContext(doc);
439             return template.apply(hbsContext);
440         } catch (final Exception e) {
441             logger.warn("Failed to create a cache response.", e);
442         }
443 
444         return null;
445     }
446 
447     protected String replaceHighlightQueries(final String cache, final String[] queries) {
448         final StringBuffer buf = new StringBuffer(cache.length() + 100);
449         final StringBuffer segBuf = new StringBuffer(1000);
450         final Pattern p = Pattern.compile("<[^>]+>");
451         final Matcher m = p.matcher(cache);
452         final String[] regexQueries = new String[queries.length];
453         final String[] hlQueries = new String[queries.length];
454         for (int i = 0; i < queries.length; i++) {
455             regexQueries[i] = Pattern.quote(queries[i]);
456             hlQueries[i] = highlightTagPre + queries[i] + highlightTagPost;
457         }
458         while (m.find()) {
459             segBuf.setLength(0);
460             m.appendReplacement(segBuf, StringUtil.EMPTY);
461             String segment = segBuf.toString();
462             for (int i = 0; i < queries.length; i++) {
463                 segment = Pattern.compile(regexQueries[i], Pattern.CASE_INSENSITIVE).matcher(segment).replaceAll(hlQueries[i]);
464             }
465             buf.append(segment);
466             buf.append(m.group(0));
467         }
468         segBuf.setLength(0);
469         m.appendTail(segBuf);
470         String segment = segBuf.toString();
471         for (int i = 0; i < queries.length; i++) {
472             segment = Pattern.compile(regexQueries[i], Pattern.CASE_INSENSITIVE).matcher(segment).replaceAll(hlQueries[i]);
473         }
474         buf.append(segment);
475         return buf.toString();
476     }
477 
478     public Object getSitePath(final Map<String, Object> docMap) {
479         final FessConfig fessConfig = ComponentUtil.getFessConfig();
480         final Object urlLink = docMap.get(fessConfig.getResponseFieldUrlLink());
481         if (urlLink != null) {
482             final String returnUrl;
483             final String url = urlLink.toString();
484             if (LOCAL_PATH_PATTERN.matcher(url).find() || SHARED_FOLDER_PATTERN.matcher(url).find()) {
485                 returnUrl = url.replaceFirst("^file:/+", "");
486             } else if (url.startsWith("file:")) {
487                 returnUrl = url.replaceFirst("^file:/+", "/");
488             } else {
489                 returnUrl = url.replaceFirst("^[a-zA-Z0-9]*:/+", "");
490             }
491             return StringUtils.abbreviate(returnUrl, sitePathLength);
492         }
493         return null;
494     }
495 
496     public StreamResponse asContentResponse(final Map<String, Object> doc) {
497         if (logger.isDebugEnabled()) {
498             logger.debug("writing the content of: " + doc);
499         }
500         final FessConfig fessConfig = ComponentUtil.getFessConfig();
501         final CrawlingConfigHelper crawlingConfigHelper = ComponentUtil.getCrawlingConfigHelper();
502         final String configId = DocumentUtil.getValue(doc, fessConfig.getIndexFieldConfigId(), String.class);
503         if (configId == null) {
504             throw new FessSystemException("configId is null.");
505         }
506         if (configId.length() < 2) {
507             throw new FessSystemException("Invalid configId: " + configId);
508         }
509         final CrawlingConfig config = crawlingConfigHelper.getCrawlingConfig(configId);
510         if (config == null) {
511             throw new FessSystemException("No crawlingConfig: " + configId);
512         }
513         final String url = DocumentUtil.getValue(doc, fessConfig.getIndexFieldUrl(), String.class);
514         final CrawlerClientFactory crawlerClientFactory = ComponentUtil.getComponent(CrawlerClientFactory.class);
515         config.initializeClientFactory(crawlerClientFactory);
516         final CrawlerClient client = crawlerClientFactory.getClient(url);
517         if (client == null) {
518             throw new FessSystemException("No CrawlerClient: " + configId + ", url: " + url);
519         }
520         return writeContent(configId, url, client);
521     }
522 
523     protected StreamResponse writeContent(final String configId, final String url, final CrawlerClient client) {
524         final StreamResponse response = new StreamResponse(StringUtil.EMPTY);
525         final ResponseData responseData = client.execute(RequestDataBuilder.newRequestData().get().url(url).build());
526         if (responseData.getHttpStatusCode() == 404) {
527             response.httpStatus(responseData.getHttpStatusCode());
528             IOUtils.closeQuietly(responseData);
529             return response;
530         }
531         writeFileName(response, responseData);
532         writeContentType(response, responseData);
533         writeNoCache(response, responseData);
534         response.stream(out -> {
535             try (final InputStream is = new BufferedInputStream(responseData.getResponseBody())) {
536                 out.write(is);
537             } catch (final IOException e) {
538                 if (!(e.getCause() instanceof ClientAbortException)) {
539                     throw new FessSystemException("Failed to write a content. configId: " + configId + ", url: " + url, e);
540                 }
541             } finally {
542                 IOUtils.closeQuietly(responseData);
543             }
544             if (logger.isDebugEnabled()) {
545                 logger.debug("Finished to write " + url);
546             }
547         });
548         return response;
549     }
550 
551     protected void writeNoCache(final StreamResponse response, final ResponseData responseData) {
552         response.header("Pragma", "no-cache");
553         response.header("Cache-Control", "no-cache");
554         response.header("Expires", "Thu, 01 Dec 1994 16:00:00 GMT");
555     }
556 
557     protected void writeFileName(final StreamResponse response, final ResponseData responseData) {
558         final UserAgentHelper userAgentHelper = ComponentUtil.getUserAgentHelper();
559         final UserAgentType userAgentType = userAgentHelper.getUserAgentType();
560         String charset = responseData.getCharSet();
561         if (charset == null) {
562             charset = Constants.UTF_8;
563         }
564         final String name;
565         final String url = responseData.getUrl();
566         final int pos = url.lastIndexOf('/');
567         try {
568             if (pos >= 0 && pos + 1 < url.length()) {
569                 name = URLDecoder.decode(url.substring(pos + 1), charset);
570             } else {
571                 name = URLDecoder.decode(url, charset);
572             }
573 
574             if (logger.isDebugEnabled()) {
575                 logger.debug("userAgentType: " + userAgentType + ", charset: " + charset + ", name: " + name);
576             }
577 
578             final String contentDispositionType;
579             if (inlineMimeTypeSet.contains(responseData.getMimeType())) {
580                 contentDispositionType = "inline";
581             } else {
582                 contentDispositionType = "attachment";
583             }
584 
585             switch (userAgentType) {
586             case IE:
587                 response.header(CONTENT_DISPOSITION, contentDispositionType + "; filename=\"" + URLEncoder.encode(name, Constants.UTF_8)
588                         + "\"");
589                 break;
590             case OPERA:
591                 response.header(CONTENT_DISPOSITION,
592                         contentDispositionType + "; filename*=utf-8'ja'" + URLEncoder.encode(name, Constants.UTF_8));
593                 break;
594             case SAFARI:
595                 response.header(CONTENT_DISPOSITION, contentDispositionType + "; filename=\"" + name + "\"");
596                 break;
597             case CHROME:
598             case FIREFOX:
599             case OTHER:
600             default:
601                 response.header(CONTENT_DISPOSITION,
602                         contentDispositionType + "; filename=\"=?utf-8?B?" + Base64Util.encode(name.getBytes(Constants.UTF_8)) + "?=\"");
603                 break;
604             }
605         } catch (final Exception e) {
606             logger.warn("Failed to write a filename: " + responseData, e);
607         }
608     }
609 
610     protected void writeContentType(final StreamResponse response, final ResponseData responseData) {
611         final String mimeType = responseData.getMimeType();
612         if (logger.isDebugEnabled()) {
613             logger.debug("mimeType: " + mimeType);
614         }
615         if (mimeType == null) {
616             response.contentTypeOctetStream();
617             return;
618         }
619         if (mimeType.startsWith("text/")) {
620             final String charset = LaResponseUtil.getResponse().getCharacterEncoding();
621             if (charset != null) {
622                 response.contentType(mimeType + "; charset=" + charset);
623                 return;
624             }
625         }
626         response.contentType(mimeType);
627     }
628 
629     public String getClientIp(final HttpServletRequest request) {
630         final String value = request.getHeader("x-forwarded-for");
631         if (StringUtil.isNotBlank(value)) {
632             return value;
633         } else {
634             return request.getRemoteAddr();
635         }
636     }
637 
638     public boolean isUseSession() {
639         return useSession;
640     }
641 
642     public void setUseSession(final boolean useSession) {
643         this.useSession = useSession;
644     }
645 
646     public void addInitFacetParam(final String key, final String value) {
647         initFacetParamMap.put(value, key);
648     }
649 
650     public Map<String, String> getInitFacetParamMap() {
651         return initFacetParamMap;
652     }
653 
654     public void addInitGeoParam(final String key, final String value) {
655         initGeoParamMap.put(value, key);
656     }
657 
658     public Map<String, String> getInitGeoParamMap() {
659         return initGeoParamMap;
660     }
661 
662     public void addFacetQueryView(final FacetQueryView facetQueryView) {
663         facetQueryViewList.add(facetQueryView);
664     }
665 
666     public List<FacetQueryView> getFacetQueryViewList() {
667         return facetQueryViewList;
668     }
669 
670     public void addInlineMimeType(final String mimeType) {
671         inlineMimeTypeSet.add(mimeType);
672     }
673 
674     public ActionHook getActionHook() {
675         return actionHook;
676     }
677 
678     public void setActionHook(final ActionHook actionHook) {
679         this.actionHook = actionHook;
680     }
681 
682     public static class ActionHook {
683 
684         public ActionResponse godHandPrologue(final ActionRuntime runtime, final Function<ActionRuntime, ActionResponse> func) {
685             return func.apply(runtime);
686         }
687 
688         public ActionResponse godHandMonologue(final ActionRuntime runtime, final Function<ActionRuntime, ActionResponse> func) {
689             return func.apply(runtime);
690         }
691 
692         public void godHandEpilogue(final ActionRuntime runtime, final Consumer<ActionRuntime> consumer) {
693             consumer.accept(runtime);
694         }
695 
696         public ActionResponse hookBefore(final ActionRuntime runtime, final Function<ActionRuntime, ActionResponse> func) {
697             return func.apply(runtime);
698         }
699 
700         public void hookFinally(final ActionRuntime runtime, final Consumer<ActionRuntime> consumer) {
701             consumer.accept(runtime);
702         }
703     }
704 }