Java tutorial
/* * Copyright 2000-2013 Enonic AS * http://www.enonic.com/license */ package com.enonic.cms.web.portal.page; import java.io.IOException; import java.io.OutputStream; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.lang.StringUtils; import org.joda.time.DateTime; import org.joda.time.Interval; import com.google.common.base.Preconditions; import com.enonic.esl.util.DigestUtil; import com.enonic.cms.framework.util.HttpCacheControlSettings; import com.enonic.cms.framework.util.HttpServletUtil; import com.enonic.cms.api.plugin.ext.http.HttpProcessor; import com.enonic.cms.api.plugin.ext.http.HttpResponseFilter; import com.enonic.cms.core.SiteBasePath; import com.enonic.cms.core.SiteBasePathAndSitePath; import com.enonic.cms.core.SiteBasePathAndSitePathToStringBuilder; import com.enonic.cms.core.SiteBasePathResolver; import com.enonic.cms.core.portal.PortalRenderingException; import com.enonic.cms.core.portal.PortalRequest; import com.enonic.cms.core.portal.PortalResponse; import com.enonic.cms.core.portal.RedirectInstruction; import com.enonic.cms.core.portal.livetrace.PortalRequestTrace; import com.enonic.cms.core.structure.SitePath; import com.enonic.cms.web.portal.SiteRedirectAndForwardHelper; import com.enonic.cms.web.portal.instanttrace.InstantTraceId; import com.enonic.cms.web.portal.instanttrace.InstantTraceResponseWriter; import com.enonic.cms.web.portal.instanttrace.InstantTraceSessionInspector; import com.enonic.cms.web.portal.instanttrace.InstantTraceSessionObject; public class PortalResponseProcessor { private final static String EXECUTED_PLUGINS = "EXECUTED_PLUGINS"; private static final int SECOND_IN_MILLIS = 1000; private SiteRedirectAndForwardHelper siteRedirectAndForwardHelper; private List<HttpResponseFilter> responseFilters; private PortalRequest request; private PortalResponse response; private HttpSession httpSession; private HttpServletResponse httpResponse; private HttpServletRequest httpRequest; private boolean inPreview; private boolean renderTraceOn; private boolean cacheHeadersEnabledForSite = false; private boolean forceNoCacheForSite = false; private boolean deviceClassificationEnabled = false; private boolean localizationEnabled = false; private boolean instantTraceEnabled = false; private PortalRequestTrace currentPortalRequestTrace; private boolean encodeRedirectUrl; private String doctypeHandler; public void serveResponse() throws Exception { if (response.hasRedirectInstruction()) { serveRedirect(); } else if (response.isForwardToSitePath()) { serveForwardToSitePathResponse(); } else { servePageResponse(); } } private void servePageResponse() throws IOException { HttpServletUtil.setDateHeader(httpResponse, request.getRequestTime().toDate()); boolean forceNoCache = false; if (inPreview || renderTraceOn) { forceNoCache = true; final DateTime expirationTime = request.getRequestTime(); setHttpCacheHeaders(request.getRequestTime(), expirationTime, forceNoCache); } else if (cacheHeadersEnabledForSite) { final DateTime expirationTime = resolveExpirationTime(request.getRequestTime(), response.getExpirationTime()); setHttpCacheHeaders(request.getRequestTime(), expirationTime, forceNoCache); } // filter response with any response plugins String content = filterResponseWithPlugins(response.getContent(), response.getHttpContentType()); response.setContent(content); boolean isHeadRequest = "HEAD".compareToIgnoreCase(httpRequest.getMethod()) == 0; boolean writeContent = !isHeadRequest; boolean handleEtagLogic = cacheHeadersEnabledForSite && !forceNoCacheForSite && !instantTraceEnabled; if (handleEtagLogic && !StringUtils.isEmpty(content)) // resolveEtag does not like empty strings { // Handling etag logic if cache headers are enabled final String etagFromContent = resolveEtag(content); HttpServletUtil.setEtag(httpResponse, etagFromContent); if (!isContentModified(etagFromContent)) { httpResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED); writeContent = false; } } if (instantTraceEnabled && currentPortalRequestTrace != null) { final InstantTraceSessionObject instantTraceSessionObject = InstantTraceSessionInspector .getInstantTraceSessionObject(httpSession); final InstantTraceId instantTraceId = new InstantTraceId( currentPortalRequestTrace.getCompletedNumber()); instantTraceSessionObject.addTrace(instantTraceId, currentPortalRequestTrace); InstantTraceResponseWriter.applyInstantTraceId(httpResponse, instantTraceId); } httpResponse.setContentType(response.getHttpContentType()); if (isHeadRequest) { httpResponse.setContentLength(response.getContentAsBytes().length); } if (writeContent) { writeContent(response.getContentAsBytes()); } } private String resolveEtag(String content) { Preconditions.checkArgument(StringUtils.isNotEmpty(content)); return "content_" + DigestUtil.generateSHA(content); } private boolean isContentModified(String etagFromContent) { return HttpServletUtil.isContentModifiedAccordingToIfNoneMatchHeader(httpRequest, etagFromContent); } private void writeContent(byte[] content) throws IOException { httpResponse.setContentLength(content.length); OutputStream out = httpResponse.getOutputStream(); out.write(content); } private void setHttpCacheHeaders(final DateTime requestTime, final DateTime expirationTime, final boolean forceNoCache) { final Interval maxAge = new Interval(requestTime, expirationTime); @SuppressWarnings({ "UnnecessaryLocalVariable" }) boolean notCachableByClient = forceNoCache; if (notCachableByClient) { HttpServletUtil.setCacheControlNoCache(httpResponse); } else { HttpCacheControlSettings cacheControlSettings = new HttpCacheControlSettings(); // To eliminate proxy caching of pages (decided by TSI) cacheControlSettings.publicAccess = false; boolean setCacheTimeToZero = dynamicResolversEnabled(); if (setCacheTimeToZero) { cacheControlSettings.maxAgeSecondsToLive = new Long(0); HttpServletUtil.setExpiresHeader(httpResponse, requestTime.toDate()); } else { cacheControlSettings.maxAgeSecondsToLive = maxAge.toDurationMillis() / SECOND_IN_MILLIS; HttpServletUtil.setExpiresHeader(httpResponse, expirationTime.toDate()); } HttpServletUtil.setCacheControl(httpResponse, cacheControlSettings); } } private boolean dynamicResolversEnabled() { return deviceClassificationEnabled || localizationEnabled; } private void serveRedirect() throws IOException { RedirectInstruction redirectInstruction = response.getRedirectInstruction(); int redirectStatus = redirectInstruction.isPermanentRedirect() ? HttpServletResponse.SC_MOVED_PERMANENTLY : HttpServletResponse.SC_MOVED_TEMPORARILY; if (redirectInstruction.hasRedirectSitePath()) { serveRedirectToSitePath(redirectInstruction.getRedirectSitePath(), redirectStatus); } else if (redirectInstruction.hasRedirectUrl()) { serveRedirectResponse(redirectInstruction.getRedirectUrl(), redirectStatus); } else { throw new IllegalStateException("Redirect must have target url or sitepath set"); } } private void serveRedirectToSitePath(final SitePath toSitePath, final int redirectStatus) throws IOException { SiteBasePath siteBasePath = SiteBasePathResolver.resolveSiteBasePath(httpRequest, toSitePath.getSiteKey()); SiteBasePathAndSitePath siteBasePathAndSitePath = new SiteBasePathAndSitePath(siteBasePath, toSitePath); SiteBasePathAndSitePathToStringBuilder siteBasePathAndSitePathToStringBuilder = new SiteBasePathAndSitePathToStringBuilder(); siteBasePathAndSitePathToStringBuilder.setEncoding("UTF-8"); siteBasePathAndSitePathToStringBuilder.setHtmlEscapeParameterAmps(false); siteBasePathAndSitePathToStringBuilder.setIncludeFragment(true); siteBasePathAndSitePathToStringBuilder.setIncludeParamsInPath(true); siteBasePathAndSitePathToStringBuilder.setUrlEncodePath(true); String redirectUrl = siteBasePathAndSitePathToStringBuilder.toString(siteBasePathAndSitePath); sendRedirectResponse(redirectUrl, redirectStatus); } private void sendRedirectResponse(final String redirectUrl, final int redirectStatus) { if (redirectStatus == HttpServletResponse.SC_MOVED_PERMANENTLY) { httpResponse.setStatus(redirectStatus); } else { httpResponse.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); } final String location = this.encodeRedirectUrl ? httpResponse.encodeRedirectURL(redirectUrl) : redirectUrl; httpResponse.setHeader("Location", location); } private void serveForwardToSitePathResponse() throws Exception { siteRedirectAndForwardHelper.forward(httpRequest, httpResponse, response.getForwardToSitePath()); } private void serveRedirectResponse(final String redirectUrl, final int redirectStatus) { sendRedirectResponse(redirectUrl, redirectStatus); } private DateTime resolveExpirationTime(final DateTime requestTime, final DateTime expirationTime) { if (expirationTime == null) { return requestTime; } if (expirationTime.isBefore(requestTime)) { return requestTime; } return expirationTime; } private String filterResponseWithPlugins(String response, final String contentType) { try { // internal filter. needs for doctypeHandler response = new HTML5HttpResponseFilter(doctypeHandler).filterResponse(httpRequest, response, contentType); //noinspection unchecked Set<HttpProcessor> executedPlugins = (Set<HttpProcessor>) httpRequest.getAttribute(EXECUTED_PLUGINS); if (executedPlugins == null) { executedPlugins = new HashSet<HttpProcessor>(); httpRequest.setAttribute(EXECUTED_PLUGINS, executedPlugins); } for (HttpResponseFilter plugin : responseFilters) { if (!executedPlugins.contains(plugin)) { response = plugin.filterResponse(httpRequest, response, contentType); executedPlugins.add(plugin); } } return response; } catch (Exception e) { throw new PortalRenderingException("Response filter plugin failed: " + e.getMessage(), e); } } public void setSiteRedirectAndForwardHelper(final SiteRedirectAndForwardHelper siteRedirectAndForwardHelper) { this.siteRedirectAndForwardHelper = siteRedirectAndForwardHelper; } public void setRequest(final PortalRequest request) { this.request = request; } public void setResponse(final PortalResponse response) { this.response = response; } public void setHttpSession(final HttpSession httpSession) { this.httpSession = httpSession; } public void setHttpResponse(final HttpServletResponse httpResponse) { this.httpResponse = httpResponse; } public void setHttpRequest(final HttpServletRequest httpRequest) { this.httpRequest = httpRequest; } public void setInPreview(final boolean inPreview) { this.inPreview = inPreview; } public void setRenderTraceOn(final boolean renderTraceOn) { this.renderTraceOn = renderTraceOn; } public void setCacheHeadersEnabledForSite(final boolean cacheHeadersEnabledForSite) { this.cacheHeadersEnabledForSite = cacheHeadersEnabledForSite; } public void setForceNoCacheForSite(final boolean forceNoCacheForSite) { this.forceNoCacheForSite = forceNoCacheForSite; } public void setDeviceClassificationEnabled(final boolean deviceClassificationEnabled) { this.deviceClassificationEnabled = deviceClassificationEnabled; } public void setLocalizationEnabled(final boolean localizationEnabled) { this.localizationEnabled = localizationEnabled; } public void setResponseFilters(final List<HttpResponseFilter> responseFilters) { this.responseFilters = responseFilters; } public void setInstantTraceEnabled(final boolean instantTraceEnabled) { this.instantTraceEnabled = instantTraceEnabled; } public void setCurrentPortalRequestTrace(final PortalRequestTrace currentPortalRequestTrace) { this.currentPortalRequestTrace = currentPortalRequestTrace; } public void setEncodeRedirectUrl(final boolean encodeRedirectUrl) { this.encodeRedirectUrl = encodeRedirectUrl; } public void setDoctypeHandler(final String doctypeHandler) { this.doctypeHandler = doctypeHandler; } public String getDoctypeHandler() { return doctypeHandler; } }