private static HttpResponse addEtag( HttpRequest nettyRequest, HttpResponse httpResponse, File file) { if (Play.mode == Play.Mode.DEV) { httpResponse.setHeader(CACHE_CONTROL, "no-cache"); } else { String maxAge = Play.configuration.getProperty("http.cacheControl", "3600"); if (maxAge.equals("0")) { httpResponse.setHeader(CACHE_CONTROL, "no-cache"); } else { httpResponse.setHeader(CACHE_CONTROL, "max-age=" + maxAge); } } boolean useEtag = Play.configuration.getProperty("http.useETag", "true").equals("true"); long last = file.lastModified(); final String etag = "\"" + last + "-" + file.hashCode() + "\""; if (!isModified(etag, last, nettyRequest)) { if (nettyRequest.getMethod().equals(HttpMethod.GET)) { httpResponse.setStatus(HttpResponseStatus.NOT_MODIFIED); } if (useEtag) { httpResponse.setHeader(ETAG, etag); } } else { httpResponse.setHeader(LAST_MODIFIED, Utils.getHttpDateFormatter().format(new Date(last))); if (useEtag) { httpResponse.setHeader(ETAG, etag); } } return httpResponse; }
public static Object invokeControllerMethod(Method method, Object[] forceArgs) throws Exception { if (Modifier.isStatic(method.getModifiers()) && !method.getDeclaringClass().getName().matches("^controllers\\..*\\$class$")) { return invoke( method, null, forceArgs == null ? getActionMethodArgs(method, null) : forceArgs); } else if (Modifier.isStatic(method.getModifiers())) { Object[] args = getActionMethodArgs(method, null); args[0] = Http.Request.current().controllerClass.getDeclaredField("MODULE$").get(null); return invoke(method, null, args); } else { Object instance = null; try { instance = method.getDeclaringClass().getDeclaredField("MODULE$").get(null); } catch (Exception e) { Annotation[] annotations = method.getDeclaredAnnotations(); String annotation = Utils.getSimpleNames(annotations); if (!StringUtils.isEmpty(annotation)) { throw new UnexpectedException( "Method public static void " + method.getName() + "() annotated with " + annotation + " in class " + method.getDeclaringClass().getName() + " is not static."); } // TODO: Find a better error report throw new ActionNotFoundException(Http.Request.current().action, e); } return invoke( method, instance, forceArgs == null ? getActionMethodArgs(method, instance) : forceArgs); } }
public static boolean isModified(String etag, long last, HttpRequest nettyRequest) { if (nettyRequest.containsHeader(IF_NONE_MATCH)) { final String browserEtag = nettyRequest.getHeader(IF_NONE_MATCH); if (browserEtag.equals(etag)) { return false; } return true; } if (nettyRequest.containsHeader(IF_MODIFIED_SINCE)) { final String ifModifiedSince = nettyRequest.getHeader(IF_MODIFIED_SINCE); if (!StringUtils.isEmpty(ifModifiedSince)) { try { Date browserDate = Utils.getHttpDateFormatter().parse(ifModifiedSince); if (browserDate.getTime() >= last) { return false; } } catch (ParseException ex) { Logger.warn("Can't parse HTTP date", ex); } return true; } } return true; }
private static boolean isValidTimeStamp(long last, String dateString) { try { long browserDate = Utils.getHttpDateFormatter().parse(dateString).getTime(); return browserDate >= last; } catch (ParseException e) { Logger.error("Can't parse date", e); return false; } }
public void serveStatic( HttpServletResponse servletResponse, HttpServletRequest servletRequest, RenderStatic renderStatic) throws IOException { VirtualFile file = Play.getVirtualFile(renderStatic.file); if (file == null || file.isDirectory() || !file.exists()) { serve404( servletRequest, servletResponse, new NotFound("The file " + renderStatic.file + " does not exist")); } else { servletResponse.setContentType(MimeTypes.getContentType(file.getName())); boolean raw = false; for (PlayPlugin plugin : Play.plugins) { if (plugin.serveStatic(file, Request.current(), Response.current())) { raw = true; break; } } if (raw) { copyResponse(Request.current(), Response.current(), servletRequest, servletResponse); } else { if (Play.mode == Play.Mode.DEV) { servletResponse.setHeader("Cache-Control", "no-cache"); servletResponse.setHeader("Content-Length", String.valueOf(file.length())); if (!servletRequest.getMethod().equals("HEAD")) { copyStream(servletResponse, file.inputstream()); } else { copyStream(servletResponse, new ByteArrayInputStream(new byte[0])); } } else { long last = file.lastModified(); String etag = "\"" + last + "-" + file.hashCode() + "\""; if (!isModified(etag, last, servletRequest)) { servletResponse.setHeader("Etag", etag); servletResponse.setStatus(304); } else { servletResponse.setHeader( "Last-Modified", Utils.getHttpDateFormatter().format(new Date(last))); servletResponse.setHeader( "Cache-Control", "max-age=" + Play.configuration.getProperty("http.cacheControl", "3600")); servletResponse.setHeader("Etag", etag); copyStream(servletResponse, file.inputstream()); } } } } }
public static Object[] getActionMethodArgs(Method method, Object o) throws Exception { String[] paramsNames = Java.parameterNames(method); if (paramsNames == null && method.getParameterTypes().length > 0) { throw new UnexpectedException("Parameter names not found for method " + method); } // Check if we have already performed the bind operation Object[] rArgs = CachedBoundActionMethodArgs.current().retrieveActionMethodArgs(method); if (rArgs != null) { // We have already performed the binding-operation for this method // in this request. return rArgs; } rArgs = new Object[method.getParameterTypes().length]; for (int i = 0; i < method.getParameterTypes().length; i++) { Class<?> type = method.getParameterTypes()[i]; Map<String, String[]> params = new HashMap<String, String[]>(); // In case of simple params, we don't want to parse the body. if (type.equals(String.class) || Number.class.isAssignableFrom(type) || type.isPrimitive()) { params.put(paramsNames[i], Scope.Params.current().getAll(paramsNames[i])); } else { params.putAll(Scope.Params.current().all()); } Logger.trace( "getActionMethodArgs name [" + paramsNames[i] + "] annotation [" + Utils.join(method.getParameterAnnotations()[i], " ") + "]"); RootParamNode root = ParamNode.convert(params); rArgs[i] = Binder.bind( root, paramsNames[i], method.getParameterTypes()[i], method.getGenericParameterTypes()[i], method.getParameterAnnotations()[i], new Binder.MethodAndParamInfo(o, method, i + 1)); } CachedBoundActionMethodArgs.current().storeActionMethodArgs(method, rArgs); return rArgs; }
public static boolean isModified(String etag, long last, HttpServletRequest request) { if (!(request.getHeader("If-None-Match") == null && request.getHeaders("If-Modified-Since") == null)) { return true; } else { String browserEtag = request.getHeader("If-None-Match"); if (!browserEtag.equals(etag)) { return true; } else { try { Date browserDate = Utils.getHttpDateFormatter().parse(request.getHeader("If-Modified-Since")); if (browserDate.getTime() >= last) { return false; } } catch (ParseException ex) { Logger.error("Can't parse date", ex); } return true; } } }
/** * Check if the parts of a HTTP request equal this Route. * * @param method GET/POST/etc. * @param path Part after domain and before query-string. Starts with a "/". * @param accept Format, e.g. html. * @param domain The domain (host without port). * @return ??? */ public Map<String, String> matches(String method, String path, String accept, String domain) { // Normalize if (path.equals(Play.ctxPath)) { path = path + "/"; } // If method is HEAD and we have a GET if (method == null || this.method.equals("*") || method.equalsIgnoreCase(this.method) || (method.equalsIgnoreCase("head") && ("get").equalsIgnoreCase(this.method))) { Matcher matcher = pattern.matcher(path); boolean hostMatches = (domain == null); if (domain != null) { Matcher hostMatcher = hostPattern.matcher(domain); hostMatches = hostMatcher.matches(); } // Extract the host variable if (matcher.matches() && contains(accept) && hostMatches) { // 404 if (action.equals("404")) { throw new NotFound(method, path); } // Static dir if (staticDir != null) { String resource = null; if (!staticFile) { resource = matcher.group("resource"); } try { String root = new File(staticDir).getCanonicalPath(); String urlDecodedResource = Utils.urlDecodePath(resource); String childResourceName = staticDir + (staticFile ? "" : "/" + urlDecodedResource); String child = new File(childResourceName).getCanonicalPath(); if (child.startsWith(root)) { throw new RenderStatic(childResourceName); } } catch (IOException e) { } throw new NotFound(resource); } else { Map<String, String> localArgs = new HashMap<String, String>(); for (Arg arg : args) { // FIXME: Careful with the arguments that are not matching as they are part of the // hostname // Defaultvalue indicates it is a one of these urls. This is a trick and should be // changed. if (arg.defaultValue == null) { localArgs.put(arg.name, Utils.urlDecodePath(matcher.group(arg.name))); } } if (hostArg != null && domain != null) { // Parse the hostname and get only the part we are interested in String routeValue = hostArg.defaultValue.replaceAll("\\{.*}", ""); domain = domain.replace(routeValue, ""); localArgs.put(hostArg.name, domain); } localArgs.putAll(staticArgs); return localArgs; } } } return null; }
@SuppressWarnings("unchecked") static Object bindInternal( String name, Class clazz, Type type, Annotation[] annotations, Map<String, String[]> params, String suffix, String[] profiles) { try { Logger.trace("bindInternal: name [" + name + "] suffix [" + suffix + "]"); String[] value = params.get(name + suffix); Logger.trace("bindInternal: value [" + value + "]"); Logger.trace("bindInternal: profile [" + Utils.join(profiles, ",") + "]"); // Let see if we have a BindAs annotation and a separator. If so, we need to split the values // Look up for the BindAs annotation. Extract the profile if there is any. // TODO: Move me somewhere else? if (annotations != null) { for (Annotation annotation : annotations) { if ((clazz.isArray() || Collection.class.isAssignableFrom(clazz)) && value != null && value.length > 0 && annotation.annotationType().equals(As.class)) { As as = ((As) annotation); final String separator = as.value()[0]; value = value[0].split(separator); } if (annotation.annotationType().equals(NoBinding.class)) { NoBinding bind = ((NoBinding) annotation); String[] localUnbindProfiles = bind.value(); Logger.trace( "bindInternal: localUnbindProfiles [" + Utils.join(localUnbindProfiles, ",") + "]"); if (localUnbindProfiles != null && contains(profiles, localUnbindProfiles)) { return NO_BINDING; } } } } // Arrays types // The array condition is not so nice... We should find another way of doing this.... if (clazz.isArray() && (clazz != byte[].class && clazz != byte[][].class && clazz != File[].class && clazz != Upload[].class)) { if (value == null) { value = params.get(name + suffix + "[]"); } if (value == null) { return MISSING; } Object r = Array.newInstance(clazz.getComponentType(), value.length); for (int i = 0; i <= value.length; i++) { try { Array.set(r, i, directBind(name, annotations, value[i], clazz.getComponentType())); } catch (Exception e) { // ?? One item was bad } } return r; } // Enums if (Enum.class.isAssignableFrom(clazz)) { if (value == null || value.length == 0) { return MISSING; } else if (StringUtils.isEmpty(value[0])) { return null; } return Enum.valueOf(clazz, value[0]); } // Map if (Map.class.isAssignableFrom(clazz)) { Class keyClass = String.class; Class valueClass = String.class; if (type instanceof ParameterizedType) { keyClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0]; valueClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[1]; } // Special case Map<String, String> // Multivalues composite params are binded to a Map<String, String> // see http://play.lighthouseapp.com/projects/57987/tickets/443 if (keyClass == String.class && valueClass == String.class && isComposite(name, params)) { Map<String, String> stringMap = Utils.filterParams(params, name); if (stringMap.size() > 0) return stringMap; } // Search for all params Map<Object, Object> r = new HashMap<Object, Object>(); for (String param : params.keySet()) { Pattern p = Pattern.compile("^" + name + suffix + "\\[([^\\]]+)\\](.*)$"); Matcher m = p.matcher(param); if (m.matches()) { String key = m.group(1); value = params.get(param); Map<String, String[]> tP = new HashMap<String, String[]>(); tP.put("key", new String[] {key}); Object oKey = bindInternal("key", keyClass, keyClass, annotations, tP, "", value); if (oKey != MISSING) { if (isComposite(name + suffix + "[" + key + "]", params)) { BeanWrapper beanWrapper = getBeanWrapper(valueClass); Object oValue = beanWrapper.bind( "", type, params, name + suffix + "[" + key + "]", annotations); r.put(oKey, oValue); } else { tP = new HashMap<String, String[]>(); tP.put("value", params.get(name + suffix + "[" + key + "]")); Object oValue = bindInternal("value", valueClass, valueClass, annotations, tP, "", value); if (oValue != MISSING) { r.put(oKey, oValue); } else { r.put(oKey, null); } } } } } return r; } // Collections types if (Collection.class.isAssignableFrom(clazz)) { if (clazz.isInterface()) { if (clazz.equals(List.class)) { clazz = ArrayList.class; } if (clazz.equals(Set.class)) { clazz = HashSet.class; } if (clazz.equals(SortedSet.class)) { clazz = TreeSet.class; } } Collection r = (Collection) clazz.newInstance(); Class componentClass = String.class; if (type instanceof ParameterizedType) { componentClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0]; } // Create a an array of the component class if (value != null) { Object customArray = Array.newInstance(componentClass, value.length); // custom types for (Class<?> c : supportedTypes.keySet()) { if (c.isAssignableFrom(customArray.getClass())) { Object[] ar = (Object[]) supportedTypes .get(c) .bind("value", annotations, name, customArray.getClass(), null); List l = Arrays.asList(ar); if (clazz.equals(HashSet.class)) { return new HashSet(l); } else if (clazz.equals(TreeSet.class)) { return new TreeSet(l); } return l; } } } if (value == null) { value = params.get(name + suffix + "[]"); if (value == null && r instanceof List) { for (String param : params.keySet()) { Pattern p = Pattern.compile("^" + escape(name + suffix) + "\\[([0-9]+)\\](.*)$"); Matcher m = p.matcher(param); if (m.matches()) { int key = Integer.parseInt(m.group(1)); while (((List<?>) r).size() <= key) { ((List<?>) r).add(null); } if (isComposite(name + suffix + "[" + key + "]", params)) { BeanWrapper beanWrapper = getBeanWrapper(componentClass); Object oValue = beanWrapper.bind( "", type, params, name + suffix + "[" + key + "]", annotations); ((List) r).set(key, oValue); } else { Map<String, String[]> tP = new HashMap<String, String[]>(); tP.put("value", params.get(name + suffix + "[" + key + "]")); Object oValue = bindInternal( "value", componentClass, componentClass, annotations, tP, "", value); if (oValue != MISSING) { ((List) r).set(key, oValue); } } } } return r.isEmpty() ? MISSING : r; } } if (value == null) { return MISSING; } for (String v : value) { try { r.add(directBind(name, annotations, v, componentClass)); } catch (Exception e) { // ?? One item was bad Logger.debug(e, "error:"); } } return r; } // Assume a Bean if isComposite Logger.trace( "bindInternal: class [" + clazz + "] name [" + name + "] annotation [" + Utils.join(annotations, " ") + "] isComposite [" + isComposite(name + suffix, params) + "]"); if (isComposite(name + suffix, params)) { BeanWrapper beanWrapper = getBeanWrapper(clazz); return beanWrapper.bind(name, type, params, suffix, annotations); } // Simple types if (value == null || value.length == 0) { return MISSING; } return directBind(name, annotations, value[0], clazz, type); } catch (Exception e) { Validation.addError(name + suffix, "validation.invalid"); return MISSING; } }
@SuppressWarnings("unchecked") public static Object directBind( String name, Annotation[] annotations, String value, Class<?> clazz, Type type) throws Exception { Logger.trace( "directBind: value [" + value + "] annotation [" + Utils.join(annotations, " ") + "] Class [" + clazz + "]"); boolean nullOrEmpty = value == null || value.trim().length() == 0; if (annotations != null) { for (Annotation annotation : annotations) { if (annotation.annotationType().equals(As.class)) { Class<? extends TypeBinder<?>> toInstanciate = ((As) annotation).binder(); if (!(toInstanciate.equals(As.DEFAULT.class))) { // Instantiate the binder TypeBinder<?> myInstance = toInstanciate.newInstance(); return myInstance.bind(name, annotations, value, clazz, type); } } } } // custom types for (Class<?> c : supportedTypes.keySet()) { Logger.trace("directBind: value [" + value + "] c [" + c + "] Class [" + clazz + "]"); if (c.isAssignableFrom(clazz)) { Logger.trace("directBind: isAssignableFrom is true"); return supportedTypes.get(c).bind(name, annotations, value, clazz, type); } } // application custom types for (Class<TypeBinder<?>> c : Play.classloader.getAssignableClasses(TypeBinder.class)) { if (c.isAnnotationPresent(Global.class)) { Class<?> forType = (Class) ((ParameterizedType) c.getGenericInterfaces()[0]).getActualTypeArguments()[0]; if (forType.isAssignableFrom(clazz)) { return c.newInstance().bind(name, annotations, value, clazz, type); } } } // raw String if (clazz.equals(String.class)) { return value; } // Enums if (Enum.class.isAssignableFrom(clazz)) { if (nullOrEmpty) { return null; } return Enum.valueOf((Class<Enum>) clazz, value); } // int or Integer binding if (clazz.getName().equals("int") || clazz.equals(Integer.class)) { if (nullOrEmpty) { return clazz.isPrimitive() ? 0 : null; } return Integer.parseInt(value.contains(".") ? value.substring(0, value.indexOf(".")) : value); } // long or Long binding if (clazz.getName().equals("long") || clazz.equals(Long.class)) { if (nullOrEmpty) { return clazz.isPrimitive() ? 0l : null; } return Long.parseLong(value.contains(".") ? value.substring(0, value.indexOf(".")) : value); } // byte or Byte binding if (clazz.getName().equals("byte") || clazz.equals(Byte.class)) { if (nullOrEmpty) { return clazz.isPrimitive() ? (byte) 0 : null; } return Byte.parseByte(value.contains(".") ? value.substring(0, value.indexOf(".")) : value); } // short or Short binding if (clazz.getName().equals("short") || clazz.equals(Short.class)) { if (nullOrEmpty) { return clazz.isPrimitive() ? (short) 0 : null; } return Short.parseShort(value.contains(".") ? value.substring(0, value.indexOf(".")) : value); } // float or Float binding if (clazz.getName().equals("float") || clazz.equals(Float.class)) { if (nullOrEmpty) { return clazz.isPrimitive() ? 0f : null; } return Float.parseFloat(value); } // double or Double binding if (clazz.getName().equals("double") || clazz.equals(Double.class)) { if (nullOrEmpty) { return clazz.isPrimitive() ? 0d : null; } return Double.parseDouble(value); } // BigDecimal binding if (clazz.equals(BigDecimal.class)) { if (nullOrEmpty) { return null; } return new BigDecimal(value); } // boolean or Boolean binding if (clazz.getName().equals("boolean") || clazz.equals(Boolean.class)) { if (nullOrEmpty) { return clazz.isPrimitive() ? false : null; } if (value.equals("1") || value.toLowerCase().equals("on") || value.toLowerCase().equals("yes")) { return true; } return Boolean.parseBoolean(value); } return null; }
public static Object bind( String name, Class<?> clazz, Type type, Annotation[] annotations, Map<String, String[]> params, Object o, Method method, int parameterIndex) { Logger.trace("bind: name [" + name + "] annotation [" + Utils.join(annotations, " ") + "] "); Object result = null; // Let a chance to plugins to bind this object for (PlayPlugin plugin : Play.plugins) { result = plugin.bind(name, clazz, type, annotations, params); if (result != null) { return result; } } String[] profiles = null; if (annotations != null) { for (Annotation annotation : annotations) { if (annotation.annotationType().equals(As.class)) { As as = ((As) annotation); profiles = as.value(); } if (annotation.annotationType().equals(NoBinding.class)) { NoBinding bind = ((NoBinding) annotation); profiles = bind.value(); } } } result = bindInternal(name, clazz, type, annotations, params, "", profiles); if (result == MISSING) { // Try the scala default if (o != null && parameterIndex > 0) { try { Method defaultMethod = method .getDeclaringClass() .getDeclaredMethod(method.getName() + "$default$" + parameterIndex); return defaultMethod.invoke(o); } catch (NoSuchMethodException e) { // } catch (Exception e) { throw new UnexpectedException(e); } } if (clazz.equals(boolean.class)) { return false; } if (clazz.equals(int.class)) { return 0; } if (clazz.equals(long.class)) { return 0; } if (clazz.equals(double.class)) { return 0; } if (clazz.equals(short.class)) { return 0; } if (clazz.equals(byte.class)) { return 0; } if (clazz.equals(char.class)) { return ' '; } return null; } return result; }