Java tutorial
/** * Copyright (C) 2012-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package ninja.servlet; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.URI; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import ninja.ContentTypes; import ninja.Context; import ninja.Cookie; import ninja.Result; import ninja.Route; import ninja.bodyparser.BodyParserEngine; import ninja.bodyparser.BodyParserEngineManager; import ninja.servlet.async.AsyncStrategy; import ninja.servlet.async.AsyncStrategyFactoryHolder; import ninja.session.FlashScope; import ninja.session.Session; import ninja.utils.HttpHeaderUtils; import ninja.utils.NinjaConstant; import ninja.utils.NinjaProperties; import ninja.utils.ResponseStreams; import ninja.utils.ResultHandler; import ninja.utils.SwissKnife; import ninja.validation.Validation; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import com.google.inject.Inject; import org.slf4j.LoggerFactory; public class ContextImpl implements Context.Impl { private ServletContext servletContext; private HttpServletRequest httpServletRequest; private HttpServletResponse httpServletResponse; private Route route; private AsyncStrategy asyncStrategy; private final Object asyncLock = new Object(); private final BodyParserEngineManager bodyParserEngineManager; private final FlashScope flashScope; private final NinjaProperties ninjaProperties; private final Session session; private final ResultHandler resultHandler; private final Validation validation; // In Async mode, these values will be set to null, so save them private String requestPath; private String contextPath; private Logger logger = LoggerFactory.getLogger(ContextImpl.class); @Inject public ContextImpl(BodyParserEngineManager bodyParserEngineManager, FlashScope flashCookie, NinjaProperties ninjaProperties, ResultHandler resultHandler, Session sessionCookie, Validation validation) { this.bodyParserEngineManager = bodyParserEngineManager; this.flashScope = flashCookie; this.ninjaProperties = ninjaProperties; this.session = sessionCookie; this.resultHandler = resultHandler; this.validation = validation; } public void init(ServletContext servletContext, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { this.servletContext = servletContext; this.httpServletRequest = httpServletRequest; this.httpServletResponse = httpServletResponse; enforceCorrectEncodingOfRequest(); // init flash scope: flashScope.init(this); // init session scope: session.init(this); contextPath = httpServletRequest.getContextPath(); requestPath = performGetRequestPath(); } @Override public void setRoute(Route route) { this.route = route; } @Override public String getPathParameter(String key) { String encodedParameter = route.getPathParametersEncoded(getRequestPath()).get(key); if (encodedParameter == null) { return null; } else { return URI.create(encodedParameter).getPath(); } } @Override public String getPathParameterEncoded(String key) { return route.getPathParametersEncoded(getRequestPath()).get(key); } @Override public Integer getPathParameterAsInteger(String key) { String parameter = getPathParameter(key); try { return Integer.parseInt(parameter); } catch (NumberFormatException e) { return null; } } @Override public String getParameter(String key) { return httpServletRequest.getParameter(key); } @Override public List<String> getParameterValues(String name) { String[] params = httpServletRequest.getParameterValues(name); if (params == null) { return Collections.emptyList(); } return Arrays.asList(params); } @Override public String getParameter(String key, String defaultValue) { String parameter = getParameter(key); if (parameter == null) { parameter = defaultValue; } return parameter; } @Override public Integer getParameterAsInteger(String key) { return getParameterAsInteger(key, null); } @Override public Integer getParameterAsInteger(String key, Integer defaultValue) { String parameter = getParameter(key); try { return Integer.parseInt(parameter); } catch (NumberFormatException e) { return defaultValue; } } @Override public <T> T getParameterAs(String key, Class<T> clazz) { return getParameterAs(key, clazz, null); } @Override public <T> T getParameterAs(String key, Class<T> clazz, T defaultValue) { String parameter = getParameter(key); try { return SwissKnife.convert(parameter, clazz); } catch (Exception e) { return defaultValue; } } @Override public Map<String, String[]> getParameters() { return httpServletRequest.getParameterMap(); } @Override public String getHeader(String name) { return httpServletRequest.getHeader(name); } @Override public List<String> getHeaders(String name) { return Collections.list(httpServletRequest.getHeaders(name)); } @Override public Map<String, List<String>> getHeaders() { Map<String, List<String>> headers = new HashMap<String, List<String>>(); Enumeration<String> names = httpServletRequest.getHeaderNames(); while (names.hasMoreElements()) { String name = names.nextElement(); headers.put(name, Collections.list(httpServletRequest.getHeaders(name))); } return headers; } @Override public String getCookieValue(String name) { return CookieHelper.getCookieValue(name, httpServletRequest.getCookies()); } @Override public <T> T parseBody(Class<T> classOfT) { String rawContentType = getRequestContentType(); // If the Content-type: xxx header is not set we return null. // we cannot parse that request. if (rawContentType == null) { logger.debug("Not able to parse body because request did not send content type header at: {}", getRequestPath()); return null; } // If Content-type is application/json; charset=utf-8 we split away the charset // application/json String contentTypeOnly = HttpHeaderUtils.getContentTypeFromContentTypeAndCharacterSetting(rawContentType); BodyParserEngine bodyParserEngine = bodyParserEngineManager .getBodyParserEngineForContentType(contentTypeOnly); if (bodyParserEngine == null) { logger.debug("No BodyParserEngine found for Content-Type {} at route {}", CONTENT_TYPE, getRequestPath()); return null; } return bodyParserEngine.invoke(this, classOfT); } @Override public FlashScope getFlashCookie() { return flashScope; } @Override public Session getSessionCookie() { return session; } @Override public FlashScope getFlashScope() { return flashScope; } @Override public Session getSession() { return session; } @Override public Cookie getCookie(String cookieName) { javax.servlet.http.Cookie[] cookies = httpServletRequest.getCookies(); javax.servlet.http.Cookie servletCookie = CookieHelper.getCookie(cookieName, cookies); if (servletCookie == null) { return null; } else { return CookieHelper.convertServletCookieToNinjaCookie(servletCookie); } } @Override public boolean hasCookie(String cookieName) { return CookieHelper.getCookie(cookieName, httpServletRequest.getCookies()) != null; } @Override public List<Cookie> getCookies() { javax.servlet.http.Cookie[] servletCookies = httpServletRequest.getCookies(); List<Cookie> ninjaCookies = new ArrayList<>(servletCookies.length); for (javax.servlet.http.Cookie cookie : servletCookies) { Cookie ninjaCookie = CookieHelper.convertServletCookieToNinjaCookie(cookie); ninjaCookies.add(ninjaCookie); } return ninjaCookies; } @Deprecated @Override public String getRequestUri() { return httpServletRequest.getRequestURI(); } @Override public String getHostname() { return httpServletRequest.getHeader("host"); } @Override public String getRemoteAddr() { boolean isUsageOfXForwardedHeaderEnabled = ninjaProperties .getBooleanWithDefault(Context.NINJA_PROPERTIES_X_FORWARDED_FOR, false); String remoteAddr; if (!isUsageOfXForwardedHeaderEnabled) { remoteAddr = httpServletRequest.getRemoteAddr(); } else { remoteAddr = calculateRemoteAddrAndTakeIntoAccountXForwardHeader(); } return remoteAddr; } private String calculateRemoteAddrAndTakeIntoAccountXForwardHeader() { String remoteAddr = getHeader(X_FORWARD_HEADER); if (remoteAddr != null) { if (remoteAddr.contains(",")) { // sometimes the header is of form client ip,proxy 1 ip,proxy 2 ip,...,proxy n ip, // we just want the client remoteAddr = StringUtils.split(remoteAddr, ',')[0].trim(); } try { // If ip4/6 address string handed over, simply does pattern validation. InetAddress.getByName(remoteAddr); } catch (UnknownHostException e) { remoteAddr = httpServletRequest.getRemoteAddr(); } } else { remoteAddr = httpServletRequest.getRemoteAddr(); } return remoteAddr; } @Override public boolean isAsync() { return asyncStrategy != null; } @Override public void handleAsync() { synchronized (asyncLock) { if (asyncStrategy == null) { asyncStrategy = AsyncStrategyFactoryHolder.getInstance(httpServletRequest) .createStrategy(httpServletRequest, resultHandler); asyncStrategy.handleAsync(); } } } @Override public void returnResultAsync(Result result) { synchronized (asyncLock) { handleAsync(); asyncStrategy.returnResultAsync(result, this); } } @Override public void asyncRequestComplete() { returnResultAsync(null); } /** * Used to indicate that the controller has finished executing */ @Override public Result controllerReturned() { if (asyncStrategy != null) { return asyncStrategy.controllerReturned(); } return null; } @Override public InputStream getInputStream() throws IOException { return httpServletRequest.getInputStream(); } @Override public BufferedReader getReader() throws IOException { return httpServletRequest.getReader(); } private ResponseStreams finalizeHeaders(Result result, Boolean handleFlashAndSessionCookie) { httpServletResponse.setStatus(result.getStatusCode()); // copy headers for (Entry<String, String> header : result.getHeaders().entrySet()) { httpServletResponse.addHeader(header.getKey(), header.getValue()); } // copy ninja cookies / flash and session if (handleFlashAndSessionCookie) { flashScope.save(this, result); session.save(this, result); } // copy cookies for (ninja.Cookie cookie : result.getCookies()) { httpServletResponse.addCookie(CookieHelper.convertNinjaCookieToServletCookie(cookie)); } // set content type if (result.getContentType() != null) { httpServletResponse.setContentType(result.getContentType()); } // Set charset => use utf-8 if not set // Sets correct encoding for Content-Type. But also for the output // writers. if (result.getCharset() != null) { httpServletResponse.setCharacterEncoding(result.getCharset()); } else { httpServletResponse.setCharacterEncoding(NinjaConstant.UTF_8); } // possibly ResponseStreamsServlet responseStreamsServlet = new ResponseStreamsServlet(); responseStreamsServlet.init(httpServletResponse); return responseStreamsServlet; } @Override public ResponseStreams finalizeHeadersWithoutFlashAndSessionCookie(Result result) { return finalizeHeaders(result, false); } @Override public ResponseStreams finalizeHeaders(Result result) { return finalizeHeaders(result, true); } @Override public String getRequestContentType() { return httpServletRequest.getContentType(); } @Override public String getAcceptContentType() { String contentType = httpServletRequest.getHeader("accept"); if (contentType == null) { return Result.TEXT_HTML; } if (contentType.indexOf("application/xhtml") != -1 || contentType.indexOf("text/html") != -1 || contentType.startsWith("*/*")) { return Result.TEXT_HTML; } if (contentType.indexOf("application/xml") != -1 || contentType.indexOf("text/xml") != -1) { return Result.APPLICATION_XML; } if (contentType.indexOf("application/json") != -1 || contentType.indexOf("text/javascript") != -1) { return Result.APPLICATON_JSON; } if (contentType.indexOf("text/plain") != -1) { return Result.TEXT_PLAIN; } if (contentType.indexOf("application/octet-stream") != -1) { return Result.APPLICATION_OCTET_STREAM; } if (contentType.endsWith("*/*")) { return Result.TEXT_HTML; } return Result.TEXT_HTML; } @Override public String getAcceptEncoding() { return httpServletRequest.getHeader("accept-encoding"); } @Override public String getAcceptLanguage() { return httpServletRequest.getHeader("accept-language"); } @Override public String getAcceptCharset() { return httpServletRequest.getHeader("accept-charset"); } @Override public Route getRoute() { return route; } @Override public boolean isMultipart() { return ServletFileUpload.isMultipartContent(httpServletRequest); } @Override public FileItemIterator getFileItemIterator() { ServletFileUpload upload = new ServletFileUpload(); FileItemIterator fileItemIterator = null; try { fileItemIterator = upload.getItemIterator(httpServletRequest); } catch (FileUploadException | IOException e) { logger.error("Error while trying to process mulitpart file upload", e); } return fileItemIterator; } @Override public String getRequestPath() { return requestPath; } private String performGetRequestPath() { // http://stackoverflow.com/questions/966077/java-reading-undecoded-url-from-servlet // this one is unencoded: String unencodedContextPath = httpServletRequest.getContextPath(); // this one is unencoded, too, but may containt the context: String fullUnencodedUri = httpServletRequest.getRequestURI(); String result = fullUnencodedUri.substring(unencodedContextPath.length()); return result; } @Override public Validation getValidation() { return validation; } @Override public String getMethod() { return httpServletRequest.getMethod(); } @Override public Object getAttribute(String name) { return httpServletRequest.getAttribute(name); } @Override public <T> T getAttribute(String name, Class<T> clazz) { return clazz.cast(getAttribute(name)); } @Override public void setAttribute(String name, Object value) { httpServletRequest.setAttribute(name, value); } /** * When a servlet engine gets a content type like: "application/json" it * assumes a default encoding of iso-xxxxx. * * That is not what Ninja does (and is not consistent with default encodings * of application/json and application/xml). * * Therefore we'll set utf-8 as request encoding if it is not set. */ private void enforceCorrectEncodingOfRequest() { String charset = NinjaConstant.UTF_8; String contentType = getHeader(CONTENT_TYPE); if (contentType != null) { charset = HttpHeaderUtils.getCharsetOfContentTypeOrUtf8(contentType); } try { httpServletRequest.setCharacterEncoding(charset); } catch (UnsupportedEncodingException e) { logger.error("Server does not support charset of content type: " + contentType); } } @Override public String getContextPath() { return contextPath; } /** * Convenience method to access ServletContext of this context. * * @return ServletContext of this Context */ public ServletContext getServletContext() { return servletContext; } /** * Convenience method to access HttpServletRequest of this context. * * @return HttpServletRequest of this Context */ public HttpServletRequest getHttpServletRequest() { return httpServletRequest; } /** * Convenience method to access HttpServletResponse of this context. * * Usually you don't want to do that. * * @return HttpServletResponse of this Context. */ public HttpServletResponse getHttpServletResponse() { return httpServletResponse; } @Override public boolean isRequestJson() { String contentType = getRequestContentType(); if (contentType == null || contentType.isEmpty()) { return false; } return contentType.startsWith(ContentTypes.APPLICATION_JSON); } @Override public boolean isRequestXml() { String contentType = getRequestContentType(); if (contentType == null || contentType.isEmpty()) { return false; } return contentType.startsWith(ContentTypes.APPLICATION_XML); } }