http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestLogger.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestLogger.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestLogger.java new file mode 100644 index 0000000..2b55b47 --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestLogger.java @@ -0,0 +1,233 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you 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 org.apache.juneau.rest; + +import static javax.servlet.http.HttpServletResponse.*; + +import java.text.*; +import java.util.logging.*; + +import javax.servlet.http.*; + +import org.apache.juneau.internal.*; +import org.apache.juneau.json.*; +import org.apache.juneau.rest.annotation.*; + +/** + * Logging utility class. + * <p> + * Subclasses can override these methods to tailor logging of HTTP requests. + * Subclasses MUST implement a no-arg public constructor. + * <p> + * RestLoggers are associated with servlets/resources in one of the following ways: + * <ul> + * <li>The {@link RestResource#logger @RestResource.logger()} annotation. + * <li>The {@link RestConfig#setLogger(Class)}/{@link RestConfig#setLogger(RestLogger)} methods. + * </ul> + */ +public abstract class RestLogger { + + /** + * Returns the Java logger used for logging. + * <p> + * Subclasses can provide their own logger. + * The default implementation returns the logger created using <code>Logger.getLogger(getClass())</code>. + * + * @return The logger used for logging. + */ + protected abstract Logger getLogger(); + + /** + * Log a message to the logger. + * <p> + * Subclasses can override this method if they wish to log messages using a library other than + * Java Logging (e.g. Apache Commons Logging). + * + * @param level The log level. + * @param cause The cause. + * @param msg The message to log. + * @param args Optional {@link MessageFormat}-style arguments. + */ + protected abstract void log(Level level, Throwable cause, String msg, Object...args); + + /** + * Log a message. + * <p> + * Equivalent to calling <code>log(level, <jk>null</jk>, msg, args);</code> + * + * @param level The log level. + * @param msg The message to log. + * @param args Optional {@link MessageFormat}-style arguments. + */ + protected void log(Level level, String msg, Object...args) { + log(level, null, msg, args); + } + + /** + * Same as {@link #log(Level, String, Object...)} excepts runs the arguments through {@link JsonSerializer#DEFAULT_LAX_READABLE}. + * <p> + * Serialization of arguments do not occur if message is not logged, so it's safe to use this method from within debug log statements. + * + * <h5 class='section'>Example:</h5> + * <p class='bcode'> + * logObjects(<jsf>DEBUG</jsf>, <js>"Pojo contents:\n{0}"</js>, myPojo); + * </p> + * + * @param level The log level. + * @param msg The message to log. + * @param args Optional {@link MessageFormat}-style arguments. + */ + protected void logObjects(Level level, String msg, Object...args) { + for (int i = 0; i < args.length; i++) + args[i] = JsonSerializer.DEFAULT_LAX_READABLE.toStringObject(args[i]); + log(level, null, msg, args); + } + + /** + * Callback method for logging errors during HTTP requests. + * <p> + * Typically, subclasses will override this method and log errors themselves. + * <p> + * The default implementation simply logs errors to the <code>RestServlet</code> logger. + * <p> + * Here's a typical implementation showing how stack trace hashing can be used to reduce log file sizes... + * </p> + * <p class='bcode'> + * <jk>protected void</jk> onError(HttpServletRequest req, HttpServletResponse res, RestException e, <jk>boolean</jk> noTrace) { + * String qs = req.getQueryString(); + * String msg = <js>"HTTP "</js> + req.getMethod() + <js>" "</js> + e.getStatus() + <js>" "</js> + req.getRequestURI() + (qs == <jk>null</jk> ? <js>""</js> : <js>"?"</js> + qs); + * <jk>int</jk> c = e.getOccurrence(); + * + * <jc>// REST_useStackTraceHashes is disabled, so we have to log the exception every time.</jc> + * <jk>if</jk> (c == 0) + * myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%s] %s"</js>, e.getStatus(), msg), e); + * + * <jc>// This is the first time we've countered this error, so log a stack trace + * // unless ?noTrace was passed in as a URL parameter.</jc> + * <jk>else if</jk> (c == 1 && ! noTrace) + * myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%h.%s.%s] %s"</js>, e.hashCode(), e.getStatus(), c, msg), e); + * + * <jc>// This error occurred before. + * // Only log the message, not the stack trace.</jc> + * <jk>else</jk> + * myLogger.log(Level.<jsf>WARNING</jsf>, <jsm>format</jsm>(<js>"[%h.%s.%s] %s, %s"</js>, e.hashCode(), e.getStatus(), c, msg, e.getLocalizedMessage())); + * } + * </p> + * + * @param req The servlet request object. + * @param res The servlet response object. + * @param e Exception indicating what error occurred. + */ + protected void onError(HttpServletRequest req, HttpServletResponse res, RestException e) { + if (shouldLog(req, res, e)) { + String qs = req.getQueryString(); + String msg = "HTTP " + req.getMethod() + " " + e.getStatus() + " " + req.getRequestURI() + (qs == null ? "" : "?" + qs); + int c = e.getOccurrence(); + if (shouldLogStackTrace(req, res, e)) { + msg = '[' + Integer.toHexString(e.hashCode()) + '.' + e.getStatus() + '.' + c + "] " + msg; + log(Level.WARNING, e, msg); + } else { + msg = '[' + Integer.toHexString(e.hashCode()) + '.' + e.getStatus() + '.' + c + "] " + msg + ", " + e.getLocalizedMessage(); + log(Level.WARNING, msg); + } + } + } + + /** + * Returns <jk>true</jk> if the specified exception should be logged. + * <p> + * Subclasses can override this method to provide their own logic for determining when exceptions are logged. + * <p> + * The default implementation will return <jk>false</jk> if <js>"noTrace=true"</js> is passed in the query string. + * + * @param req The HTTP request. + * @param res The HTTP response. + * @param e The exception. + * @return <jk>true</jk> if exception should be logged. + */ + protected boolean shouldLog(HttpServletRequest req, HttpServletResponse res, RestException e) { + String q = req.getQueryString(); + return (q == null ? true : q.indexOf("noTrace=true") == -1); + } + + /** + * Returns <jk>true</jk> if a stack trace should be logged for this exception. + * <p> + * Subclasses can override this method to provide their own logic for determining when stack traces are logged. + * <p> + * The default implementation will only log a stack trace if {@link RestException#getOccurrence()} returns <code>1</code> + * and the exception is not one of the following: + * </p> + * <ul> + * <li>{@link HttpServletResponse#SC_UNAUTHORIZED} + * <li>{@link HttpServletResponse#SC_FORBIDDEN} + * <li>{@link HttpServletResponse#SC_NOT_FOUND} + * </ul> + * + * @param req The HTTP request. + * @param res The HTTP response. + * @param e The exception. + * @return <jk>true</jk> if stack trace should be logged. + */ + protected boolean shouldLogStackTrace(HttpServletRequest req, HttpServletResponse res, RestException e) { + if (e.getOccurrence() == 1) { + switch (e.getStatus()) { + case SC_UNAUTHORIZED: + case SC_FORBIDDEN: + case SC_NOT_FOUND: return false; + default: return true; + } + } + return false; + } + + /** + * NO-OP logger. + * <p> + * Disables all logging. + * + * @author James Bognar ([email protected]) + */ + public static class NoOp extends RestLogger { + + @Override /* RestLogger */ + protected Logger getLogger() { + return null; + } + + @Override /* RestLogger */ + protected void log(Level level, Throwable cause, String msg, Object...args) {} + } + + /** + * Default logger. + * <p> + * Logs all messages to the logger returned by <code>Logger.<jsm>getLogger</jsm>(getClass().getName())</code> + */ + public static class Normal extends RestLogger { + + private final JuneauLogger logger = JuneauLogger.getLogger(getClass()); + + @Override /* RestLogger */ + protected Logger getLogger() { + return logger; + } + + @Override /* RestLogger */ + protected void log(Level level, Throwable cause, String msg, Object...args) { + if (args.length > 0) + msg = MessageFormat.format(msg, args); + getLogger().log(level, msg, cause); + } + } +}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java index 4a2619d..2a1cc16 100644 --- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestMatcherReflecting.java @@ -15,7 +15,7 @@ package org.apache.juneau.rest; import java.lang.reflect.*; /** - * Subclass of {@link RestMatcher} that gives access to the servlet and Java method it's applied to. + * Subclass of {@link RestMatcher} that gives access to the servlet/resource and Java method it's applied to. * <p> * Essentially the same as {@link RestMatcher} except has a constructor where the * Java method is passed in so that you can access annotations defined on it to tailor @@ -26,8 +26,8 @@ public abstract class RestMatcherReflecting extends RestMatcher { /** * Constructor. * - * @param servlet The REST servlet. + * @param resource The REST servlet. * @param javaMethod The Java method that this rest matcher is defined on. */ - protected RestMatcherReflecting(RestServlet servlet, Method javaMethod) {} + protected RestMatcherReflecting(Object resource, Method javaMethod) {} } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java index cc60feb..98aba93 100644 --- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java @@ -19,6 +19,7 @@ import static javax.servlet.http.HttpServletResponse.*; import java.io.*; import java.lang.reflect.*; import java.net.*; +import java.nio.charset.*; import java.text.*; import java.util.*; import java.util.logging.*; @@ -63,13 +64,15 @@ import org.apache.juneau.utils.*; @SuppressWarnings("unchecked") public final class RestRequest extends HttpServletRequestWrapper { - private final RestServlet servlet; + private final RestContext context; + private final String method; private String pathRemainder, body; private Method javaMethod; private ObjectMap properties; private SerializerGroup serializerGroup; private ParserGroup parserGroup; + private EncoderGroup encoders; private Encoder encoder; private int contentLength; private final boolean debug; @@ -89,17 +92,17 @@ public final class RestRequest extends HttpServletRequestWrapper { /** * Constructor. */ - RestRequest(RestServlet servlet, HttpServletRequest req) throws ServletException { + RestRequest(RestContext context, HttpServletRequest req) throws ServletException { super(req); + this.context = context; try { - this.servlet = servlet; isPost = req.getMethod().equalsIgnoreCase("POST"); // If this is a POST, we want to parse the query parameters ourselves to prevent // the servlet code from processing the HTTP body as URL-Encoded parameters. if (isPost) - queryParams = servlet.getUrlEncodingParser().parseIntoSimpleMap(getQueryString()); + queryParams = context.getUrlEncodingParser().parseIntoSimpleMap(getQueryString()); else { queryParams = req.getParameterMap(); } @@ -109,23 +112,23 @@ public final class RestRequest extends HttpServletRequestWrapper { String _method = super.getMethod(); String m = getQueryParameter("method"); - if (! StringUtils.isEmpty(m) && (servlet.context.allowMethodParams.contains(m) || servlet.context.allowMethodParams.contains("*"))) + if (context.allowMethodParam(m)) _method = m; method = _method; - if (servlet.context.allowBodyParam) { + if (context.isAllowBodyParam()) { body = getQueryParameter("body"); if (body != null) setHeader("Content-Type", UonSerializer.DEFAULT.getResponseContentType()); } - defaultServletHeaders = servlet.getDefaultRequestHeaders(); + defaultServletHeaders = context.getDefaultRequestHeaders(); debug = "true".equals(getQueryParameter("debug", "false")); if (debug) { - servlet.log(Level.INFO, toString()); + context.getLogger().log(Level.INFO, toString()); } } catch (RestException e) { @@ -139,7 +142,7 @@ public final class RestRequest extends HttpServletRequestWrapper { * Called from RestServlet after a match has been made but before the guard or method invocation. */ @SuppressWarnings("hiding") - final void init(Method javaMethod, String pathRemainder, ObjectMap properties, Map<String,String> mDefaultRequestHeaders, String defaultCharset, SerializerGroup mSerializers, ParserGroup mParsers, UrlEncodingParser mUrlEncodingParser) { + final void init(Method javaMethod, String pathRemainder, ObjectMap properties, Map<String,String> mDefaultRequestHeaders, String defaultCharset, SerializerGroup mSerializers, ParserGroup mParsers, UrlEncodingParser mUrlEncodingParser, EncoderGroup encoders) { this.javaMethod = javaMethod; this.pathRemainder = pathRemainder; this.properties = properties; @@ -149,6 +152,7 @@ public final class RestRequest extends HttpServletRequestWrapper { this.urlEncodingParser = mUrlEncodingParser; this.beanSession = urlEncodingParser.getBeanContext().createSession(); this.defaultCharset = defaultCharset; + this.encoders = encoders; } /** @@ -403,7 +407,7 @@ public final class RestRequest extends HttpServletRequestWrapper { } if (charset == null) charset = defaultCharset; - if (! RestServlet.availableCharsets.containsKey(charset)) + if (! Charset.isSupported(charset)) throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported charset in header ''Content-Type'': ''{0}''", h); } return charset; @@ -1299,11 +1303,11 @@ public final class RestRequest extends HttpServletRequestWrapper { properties.append("mediaType", mediaType).append("characterEncoding", getCharacterEncoding()); if (! p.isReaderParser()) { InputStreamParser p2 = (InputStreamParser)p; - ParserSession session = p2.createSession(getInputStream(), properties, getJavaMethod(), getServlet(), locale, timeZone, mediaType); + ParserSession session = p2.createSession(getInputStream(), properties, getJavaMethod(), context.getResource(), locale, timeZone, mediaType); return p2.parseSession(session, cm); } ReaderParser p2 = (ReaderParser)p; - ParserSession session = p2.createSession(getUnbufferedReader(), properties, getJavaMethod(), getServlet(), locale, timeZone, mediaType); + ParserSession session = p2.createSession(getUnbufferedReader(), properties, getJavaMethod(), context.getResource(), locale, timeZone, mediaType); return p2.parseSession(session, cm); } catch (ParseException e) { throw new RestException(SC_BAD_REQUEST, @@ -1576,45 +1580,45 @@ public final class RestRequest extends HttpServletRequestWrapper { /** * Returns the localized servlet title. * <p> - * Equivalent to calling {@link RestServlet#getTitle(RestRequest)} with this object. + * Equivalent to calling {@link RestInfoProvider#getTitle(RestRequest)} with this object. * * @return The localized servlet label. */ public String getServletTitle() { - return servlet.getTitle(this); + return context.getInfoProvider().getTitle(this); } /** * Returns the localized servlet description. * <p> - * Equivalent to calling {@link RestServlet#getDescription(RestRequest)} with this object. + * Equivalent to calling {@link RestInfoProvider#getDescription(RestRequest)} with this object. * * @return The localized servlet description. */ public String getServletDescription() { - return servlet.getDescription(this); + return context.getInfoProvider().getDescription(this); } /** * Returns the localized method summary. * <p> - * Equivalent to calling {@link RestServlet#getMethodSummary(String, RestRequest)} with this object. + * Equivalent to calling {@link RestInfoProvider#getMethodSummary(String, RestRequest)} with this object. * * @return The localized method description. */ public String getMethodSummary() { - return servlet.getMethodSummary(javaMethod.getName(), this); + return context.getInfoProvider().getMethodSummary(javaMethod.getName(), this); } /** * Returns the localized method description. * <p> - * Equivalent to calling {@link RestServlet#getMethodDescription(String, RestRequest)} with this object. + * Equivalent to calling {@link RestInfoProvider#getMethodDescription(String, RestRequest)} with this object. * * @return The localized method description. */ public String getMethodDescription() { - return servlet.getMethodDescription(javaMethod.getName(), this); + return context.getInfoProvider().getMethodDescription(javaMethod.getName(), this); } @@ -1713,14 +1717,14 @@ public final class RestRequest extends HttpServletRequestWrapper { } /** - * Shortcut method for calling {@link RestServlet#getMessage(Locale, String, Object...)} based on the request locale. + * Shortcut method for calling {@link MessageBundle#getString(Locale, String, Object...)} based on the request locale. * * @param key The message key. * @param args Optional {@link MessageFormat}-style arguments. * @return The localized message. */ public String getMessage(String key, Object...args) { - return servlet.getMessage(getLocale(), key, args); + return context.getMessages().getString(getLocale(), key, args); } /** @@ -1729,7 +1733,7 @@ public final class RestRequest extends HttpServletRequestWrapper { * @return The resource bundle. Never <jk>null</jk>. */ public MessageBundle getResourceBundle() { - return servlet.getMessages(getLocale()); + return context.getMessages().getBundle(getLocale()); } /** @@ -1740,8 +1744,8 @@ public final class RestRequest extends HttpServletRequestWrapper { * * @return The servlet handling the request. */ - public RestServlet getServlet() { - return servlet; + public RestContext getContext() { + return context; } /** @@ -1772,13 +1776,13 @@ public final class RestRequest extends HttpServletRequestWrapper { } /** - * Returns the variable resolver session for this request using session objects created by {@link RestServlet#getSessionObjects(RestRequest)}. + * Returns the variable resolver session for this request using session objects created by {@link RestCallHandler#getSessionObjects(RestRequest)}. * * @return The variable resolver for this request. */ public VarResolverSession getVarResolverSession() { if (varSession == null) - varSession = servlet.getVarResolver().createSession(servlet.getSessionObjects(this)); + varSession = context.getVarResolver().createSession(context.getCallHandler().getSessionObjects(this)); return varSession; } @@ -1803,7 +1807,7 @@ public final class RestRequest extends HttpServletRequestWrapper { * @throws IOException */ public ReaderResource getReaderResource(String name, boolean resolveVars, MediaType mediaType) throws IOException { - String s = servlet.getResourceAsString(name, getLocale()); + String s = context.getResourceAsString(name, getLocale()); if (s == null) return null; ReaderResource.Builder b = new ReaderResource.Builder().mediaType(mediaType).contents(s); @@ -1813,8 +1817,8 @@ public final class RestRequest extends HttpServletRequestWrapper { } /** - * Same as {@link #getReaderResource(String, boolean, MediaType)} except uses {@link RestServlet#getMimetypesFileTypeMap()} - * to determine the media type. + * Same as {@link #getReaderResource(String, boolean, MediaType)} except uses the resource mime-type map + * constructed using {@link RestConfig#addMimeTypes(String...)} to determine the media type. * * @param name The name of the resource (i.e. the value normally passed to {@link Class#getResourceAsStream(String)}. * @param resolveVars If <jk>true</jk>, any {@link org.apache.juneau.rest.annotation.Parameter} variables will be resolved by the variable resolver returned @@ -1823,7 +1827,7 @@ public final class RestRequest extends HttpServletRequestWrapper { * @throws IOException */ public ReaderResource getReaderResource(String name, boolean resolveVars) throws IOException { - return getReaderResource(name, resolveVars, MediaType.forString(servlet.getMimetypesFileTypeMap().getContentType(name))); + return getReaderResource(name, resolveVars, MediaType.forString(context.getMediaTypeForName(name))); } /** @@ -1834,7 +1838,7 @@ public final class RestRequest extends HttpServletRequestWrapper { * @throws IOException */ public ReaderResource getReaderResource(String name) throws IOException { - return getReaderResource(name, false, MediaType.forString(servlet.getMimetypesFileTypeMap().getContentType(name))); + return getReaderResource(name, false, MediaType.forString(context.getMediaTypeForName(name))); } /** @@ -1842,9 +1846,9 @@ public final class RestRequest extends HttpServletRequestWrapper { * * @return The config file associated with the servlet, or <jk>null</jk> if servlet does not have a config file associated with it. */ - public ConfigFile getConfig() { + public ConfigFile getConfigFile() { if (cf == null) - cf = servlet.getConfig().getResolving(getVarResolverSession()); + cf = context.getConfigFile().getResolving(getVarResolverSession()); return cf; } @@ -1855,7 +1859,7 @@ public final class RestRequest extends HttpServletRequestWrapper { */ public Swagger getSwagger() { if (swagger == null) - swagger = servlet.getSwagger(this); + swagger = context.getInfoProvider().getSwagger(this); return swagger; } @@ -1871,7 +1875,7 @@ public final class RestRequest extends HttpServletRequestWrapper { */ protected Swagger getSwaggerFromFile() { if (fileSwagger == null) - fileSwagger = servlet.getSwaggerFromFile(this.getLocale()); + fileSwagger = context.getInfoProvider().getSwaggerFromFile(this.getLocale()); if (fileSwagger == null) fileSwagger = Swagger.NULL; return fileSwagger == Swagger.NULL ? null : fileSwagger; @@ -1895,7 +1899,7 @@ public final class RestRequest extends HttpServletRequestWrapper { sb.append(getBodyAsString()).append("\n"); } catch (Exception e1) { sb.append(e1.getLocalizedMessage()); - servlet.log(WARNING, e1, "Error occurred while trying to read debug input."); + context.getLogger().log(WARNING, e1, "Error occurred while trying to read debug input."); } } return sb.toString(); @@ -1942,11 +1946,11 @@ public final class RestRequest extends HttpServletRequestWrapper { String ce = getHeader("content-encoding"); if (! (ce == null || ce.isEmpty())) { ce = ce.trim(); - encoder = servlet.getEncoders().getEncoder(ce); + encoder = encoders.getEncoder(ce); if (encoder == null) throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported encoding in request header ''Content-Encoding'': ''{0}''\n\tSupported codings: {1}", - getHeader("content-encoding"), servlet.getEncoders().getSupportedEncodings() + getHeader("content-encoding"), encoders.getSupportedEncodings() ); } @@ -1966,7 +1970,7 @@ public final class RestRequest extends HttpServletRequestWrapper { */ private String getOverriddenHeader(String name) { String h = null; - if (servlet.context.allowHeaderParams) + if (context.isAllowHeaderParams()) h = getQueryParameter(name); if (h != null) return h; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestResourceResolver.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestResourceResolver.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestResourceResolver.java new file mode 100644 index 0000000..7989eb9 --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestResourceResolver.java @@ -0,0 +1,80 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you 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 org.apache.juneau.rest; + +import java.lang.reflect.*; + +import org.apache.juneau.internal.*; +import org.apache.juneau.rest.annotation.*; + +/** + * Class used to resolve {@link Class} objects to instances. + * <p> + * Used to convert classes defined via {@link RestResource#children() @RestResource.children()} into child instances. + * <p> + * Subclasses can be created to provide customized resource resolution. + * These can be associated with REST resources in one of the following ways: + * <ul> + * <li>{@link RestResource#resourceResolver() @RestResource.resourceResolver()} annotation. + * <li>{@link RestConfig#setResourceResolver(Class)}/{@link RestConfig#setResourceResolver(RestResourceResolver)} methods. + * </ul> + * <p> + * The default implementation simply instantiates the class using one of the following constructors: + * <ul> + * <li><code><jk>public</jk> T(RestConfig)</code> + * <li><code><jk>public</jk> T()</code> + * </ul> + * The former constructor can be used to get access to the {@link RestConfig} object to get access to the + * config file and initialization information or make programmatic modifications to the resource before + * full initialization. + * <p> + * Non-<code>RestServlet</code> classes can also add the following two methods to get access to the + * {@link RestConfig} and {@link RestContext} objects: + * <ul> + * <li><jk>public void</jk> init(RestConfig);</code> + * <li><jk>public void</jk> init(RestContext);</code> + * </ul> + */ +public class RestResourceResolver { + + /** + * Denotes the default resolver. + */ + public static final class DEFAULT extends RestResourceResolver {} + + /** + * Resolves the specified class to a resource object. + * <p> + * Subclasses can override this method to provide their own custom resolution. + * <p> + * The default implementation simply creates a new class instance using {@link Class#newInstance()}. + * + * @param c The class to resolve. + * @param config The initialization configuration for the resource. + * @return The instance of that class. + * @throws RestServletException If class could not be resolved. + */ + public Object resolve(Class<?> c, RestConfig config) throws RestServletException { + try { + Constructor<?> c1 = ClassUtils.findPublicConstructor(c, RestConfig.class); + if (c1 != null) + return c1.newInstance(config); + c1 = ClassUtils.findPublicConstructor(c); + if (c1 != null) + return c1.newInstance(); + } catch (Exception e) { + throw new RestServletException("Could not instantiate resource class ''{0}''", c.getName()).initCause(e); + } + throw new RestServletException("Could not find public constructor for class ''{0}''.", c); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/07843d64/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java index 3fa5398..637dc7e 100644 --- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestResponse.java @@ -13,6 +13,7 @@ package org.apache.juneau.rest; import java.io.*; +import java.nio.charset.*; import java.util.*; import javax.servlet.*; @@ -55,24 +56,22 @@ public final class RestResponse extends HttpServletResponseWrapper { SerializerGroup serializerGroup; UrlEncodingSerializer urlEncodingSerializer; // The serializer used to convert arguments passed into Redirect objects. private EncoderGroup encoders; - private final RestServlet servlet; private ServletOutputStream os; /** * Constructor. */ - RestResponse(RestServlet servlet, RestRequest req, HttpServletResponse res) { + RestResponse(RestContext context, RestRequest req, HttpServletResponse res) { super(res); this.request = req; - this.servlet = servlet; - for (Map.Entry<String,Object> e : servlet.getDefaultResponseHeaders().entrySet()) + for (Map.Entry<String,Object> e : context.getDefaultResponseHeaders().entrySet()) setHeader(e.getKey(), e.getValue().toString()); try { String passThroughHeaders = req.getHeader("x-response-headers"); if (passThroughHeaders != null) { - ObjectMap m = servlet.getUrlEncodingParser().parseParameter(passThroughHeaders, ObjectMap.class); + ObjectMap m = context.getUrlEncodingParser().parseParameter(passThroughHeaders, ObjectMap.class); for (Map.Entry<String,Object> e : m.entrySet()) setHeader(e.getKey(), e.getValue().toString()); } @@ -101,7 +100,7 @@ public final class RestResponse extends HttpServletResponseWrapper { MediaType mt = r.getMediaType(); if (mt.getType().equals("*")) charset = defaultCharset; - else if (RestServlet.availableCharsets.containsKey(mt.getType())) + else if (Charset.isSupported(mt.getType())) charset = mt.getType(); if (charset != null) break; @@ -138,7 +137,7 @@ public final class RestResponse extends HttpServletResponseWrapper { * @throws RestServletException */ public List<String> getSupportedEncodings() throws RestServletException { - return servlet.getEncoders().getSupportedEncodings(); + return encoders.getSupportedEncodings(); } /**
