You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2019/05/29 19:32:09 UTC

[juneau] branch master updated: Clean up RestContextBuilder class.

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 837e707  Clean up RestContextBuilder class.
837e707 is described below

commit 837e7070581205f9bc334589704151ba8b9685a1
Author: JamesBognar <ja...@apache.org>
AuthorDate: Wed May 29 15:31:45 2019 -0400

    Clean up RestContextBuilder class.
---
 .../java/org/apache/juneau/ContextBuilder.java     |  10 +
 .../org/apache/juneau/internal/StringUtils.java    |   9 +-
 .../org/apache/juneau/reflect/AnnotationInfo.java  |  11 +
 .../java/org/apache/juneau/reflect/MethodInfo.java |  18 ++
 .../juneau/serializer/SerializerSession.java       |   2 +
 .../java/org/apache/juneau/rest/RestContext.java   |   4 +-
 .../org/apache/juneau/rest/RestContextBuilder.java |  90 +------
 .../org/apache/juneau/rest/RestMethodContext.java  |  10 +-
 .../rest/annotation/RestResourceConfigApply.java   | 295 +++++++++++++--------
 9 files changed, 252 insertions(+), 197 deletions(-)

diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ContextBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ContextBuilder.java
index 900ae29..07e75ce 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ContextBuilder.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ContextBuilder.java
@@ -239,6 +239,16 @@ public abstract class ContextBuilder {
 	}
 
 	/**
+	 * Peeks at a configuration property on this object.
+	 *
+	 * @param name The property name.
+	 * @return This object (for method chaining).
+	 */
+	public Object peek(String name) {
+		return psb.peek(name);
+	}
+
+	/**
 	 * Sets multiple configuration properties on this object.
 	 *
 	 * @param properties The properties to set on this class.
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
index 4926149..3305f16 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
@@ -2244,16 +2244,19 @@ public final class StringUtils {
 	public static String format(String pattern, Object...args) {
 		if (args == null || args.length == 0)
 			return pattern;
+		Object[] args2 = new Object[args.length];
 		for (int i = 0; i < args.length; i++)
-			args[i] = convertToReadable(args[i]);
-		return MessageFormat.format(pattern, args);
+			args2[i] = convertToReadable(args[i]);
+		return MessageFormat.format(pattern, args2);
 	}
 
-	private static Object convertToReadable(Object o) {
+	private static String convertToReadable(Object o) {
 		if (o == null)
 			return null;
 		if (o instanceof ClassMeta)
 			return ((ClassMeta<?>)o).getFullName();
+		if (o instanceof Class)
+			return ((Class<?>)o).getName();
 		if (BeanContext.DEFAULT == null)
 			return o.toString();
 		ClassMeta<?> cm = BeanContext.DEFAULT.getClassMetaForObject(o);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/AnnotationInfo.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/AnnotationInfo.java
index 898c99e..7bd23d4 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/AnnotationInfo.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/AnnotationInfo.java
@@ -184,6 +184,17 @@ public class AnnotationInfo<T extends Annotation> {
 		return null;
 	}
 
+	/**
+	 * Returns <jk>true</jk> if this annotation is the specified type.
+	 *
+	 * @param a The type to test against.
+	 * @return <jk>true</jk> if this annotation is the specified type.
+	 */
+	public boolean isType(Class<? extends Annotation> a) {
+		Class<? extends Annotation> at = this.a.annotationType();
+		return at == a;
+	}
+
 	@Override
 	public String toString() {
 		return SimpleJson.DEFAULT_READABLE.toString(toObjectMap());
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/MethodInfo.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/MethodInfo.java
index cfb655b..1152723 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/MethodInfo.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/MethodInfo.java
@@ -294,6 +294,15 @@ public final class MethodInfo extends ExecutableInfo implements Comparable<Metho
 	}
 
 	/**
+	 * Same as {@link #getConfigAnnotationListParentFirst()} except only returns annotations defined on methods.
+	 *
+	 * @return A new {@link AnnotationList} object on every call.
+	 */
+	public AnnotationList getConfigAnnotationListMethodOnlyParentFirst() {
+		return appendAnnotationListMethodOnlyParentFirst(new ConfigAnnotationList());
+	}
+
+	/**
 	 * Returns <jk>true</jk> if this method or parent methods have any annotations annotated with {@link PropertyStoreApply}.
 	 *
 	 * @return <jk>true</jk> if this method or parent methods have any annotations annotated with {@link PropertyStoreApply}.
@@ -344,6 +353,15 @@ public final class MethodInfo extends ExecutableInfo implements Comparable<Metho
 		return al;
 	}
 
+	AnnotationList appendAnnotationListMethodOnlyParentFirst(AnnotationList al) {
+		ClassInfo c = this.declaringClass;
+		for (ClassInfo ci : c.getInterfacesParentFirst()) 
+			appendMethodAnnotations(al, ci);
+		for (ClassInfo ci : c.getParentsParentFirst()) 
+			appendMethodAnnotations(al, ci);
+		return al;
+	}
+
 	void appendAnnotations(AnnotationList al, Package p) {
 		if (p != null)
 			for (Annotation a : p.getDeclaredAnnotations())
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java
index 6a6ff95..a0c0826 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java
@@ -513,6 +513,8 @@ public abstract class SerializerSession extends BeanTraverseSession {
 			return null;
 		if (o.getClass() == Class.class)
 			return ClassInfo.of((Class<?>)o).getFullName();
+		if (o.getClass() == ClassInfo.class)
+			return ((ClassInfo)o).getFullName();
 		if (o.getClass().isEnum())
 			return getClassMetaForObject(o).toString(o);
 		String s = o.toString();
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 6029c5a..09a87a9 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -3356,7 +3356,7 @@ public final class RestContext extends BeanContext {
 					msgs.addSearchPath(mbl[i] != null ? mbl[i].baseClass : resourceClass, mbl[i].bundlePath);
 			}
 
-			fullPath = (builder.parentContext == null ? "" : (builder.parentContext.fullPath + '/')) + (builder.path == null ? "_" : builder.path);
+			fullPath = (builder.parentContext == null ? "" : (builder.parentContext.fullPath + '/')) + builder.getPath();
 
 			this.childResources = Collections.synchronizedMap(new LinkedHashMap<String,RestContext>());  // Not unmodifiable on purpose so that children can be replaced.
 
@@ -3600,7 +3600,7 @@ public final class RestContext extends BeanContext {
 				RestContext rc2 = childBuilder.build();
 				if (r instanceof RestServlet)
 					((RestServlet)r).setContext(rc2);
-				path = childBuilder.path;
+				path = childBuilder.getPath();
 				childResources.put(path, rc2);
 			}
 
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
index b5a3213..c32407c 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContextBuilder.java
@@ -12,13 +12,10 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.rest;
 
-import static org.apache.juneau.BeanContext.*;
-import static org.apache.juneau.internal.ArrayUtils.*;
 import static org.apache.juneau.internal.ClassUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
 import static org.apache.juneau.parser.Parser.*;
 import static org.apache.juneau.rest.RestContext.*;
-import static org.apache.juneau.rest.util.RestUtils.*;
 import static org.apache.juneau.serializer.Serializer.*;
 
 import java.nio.charset.*;
@@ -109,7 +106,6 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	RestContextProperties properties;
 	Config config;
 	VarResolverBuilder varResolverBuilder;
-	String path;
 	@SuppressWarnings("deprecation")
 	HtmlDocBuilder htmlDocBuilder;
 
@@ -172,79 +168,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 				}
 			}
 
+			applyAnnotations(rci.getConfigAnnotationListParentFirst(), vr.createSession());
+
 			// Load stuff from parent-to-child order.
 			// This allows child settings to overwrite parent settings.
 			for (AnnotationInfo<RestResource> e : restResourceAnnotationsParentFirst) {
-				ClassInfo c = e.getClassOn();
 				RestResource r = e.getAnnotation();
 				for (Property p : r.properties())
 					set(vr.resolve(p.name()), vr.resolve(p.value()));
 				for (String p : r.flags())
 					set(p, true);
-				serializers(merge(ObjectUtils.toType(psb.peek(REST_serializers), Object[].class), r.serializers()));
-				parsers(merge(ObjectUtils.toType(psb.peek(REST_parsers), Object[].class), r.parsers()));
-				partSerializer(r.partSerializer());
-				partParser(r.partParser());
-				encoders(r.encoders());
-				if (r.produces().length > 0)
-					producesReplace(resolveVars(vr, r.produces()));
-				if (r.consumes().length > 0)
-					consumesReplace(resolveVars(vr, r.consumes()));
-				defaultRequestHeaders(resolveVars(vr, r.defaultRequestHeaders()));
-				defaultAccept(vr.resolve(r.defaultAccept()));
-				defaultContentType(vr.resolve(r.defaultContentType()));
-				defaultResponseHeaders(resolveVars(vr, r.defaultResponseHeaders()));
-				responseHandlers(r.responseHandlers());
-				converters(r.converters());
-				guards(reverse(r.guards()));
-				children(r.children());
-				beanFilters(merge(ObjectUtils.toType(psb.peek(BEAN_beanFilters), Object[].class), r.beanFilters()));
-				pojoSwaps(merge(ObjectUtils.toType(psb.peek(BEAN_pojoSwaps), Object[].class), r.pojoSwaps()));
-				paramResolvers(r.paramResolvers());
-				serializerListener(r.serializerListener());
-				parserListener(r.parserListener());
-				uriContext(vr.resolve(r.uriContext()));
-				uriAuthority(vr.resolve(r.uriAuthority()));
-				uriRelativity(vr.resolve(r.uriRelativity()));
-				uriResolution(vr.resolve(r.uriResolution()));
-				for (String mapping : r.staticFiles())
-					staticFiles(c.inner(), vr.resolve(mapping));
-				if (! r.messages().isEmpty())
-					messages(c.inner(), vr.resolve(r.messages()));
-				staticFileResponseHeaders(resolveVars(vr, r.staticFileResponseHeaders()));
-				if (! r.useClasspathResourceCaching().isEmpty())
-					useClasspathResourceCaching(Boolean.valueOf(vr.resolve(r.useClasspathResourceCaching())));
-				if (r.classpathResourceFinder() != ClasspathResourceFinder.Null.class)
-					classpathResourceFinder(r.classpathResourceFinder());
-				if (! r.path().isEmpty())
-					path(vr.resolve(r.path()));
-				if (! r.clientVersionHeader().isEmpty())
-					clientVersionHeader(vr.resolve(r.clientVersionHeader()));
-				if (r.resourceResolver() != RestResourceResolver.Null.class)
-					resourceResolver(r.resourceResolver());
-				if (r.logger() != RestLogger.Null.class)
-					logger(r.logger());
-				if (r.callHandler() != RestCallHandler.Null.class)
-					callHandler(r.callHandler());
-				if (r.infoProvider() != RestInfoProvider.Null.class)
-					infoProvider(r.infoProvider());
-				if (! r.allowHeaderParams().isEmpty())
-					allowHeaderParams(Boolean.valueOf(vr.resolve(r.allowHeaderParams())));
-				if (! r.allowedMethodParams().isEmpty())
-					allowedMethodParams(vr.resolve(r.allowedMethodParams()));
-				if (! r.allowBodyParam().isEmpty())
-					allowBodyParam(Boolean.valueOf(vr.resolve(r.allowBodyParam())));
-				if (! r.renderResponseStackTraces().isEmpty())
-					renderResponseStackTraces(Boolean.valueOf(vr.resolve(r.renderResponseStackTraces())));
-				if (! r.useStackTraceHashes().isEmpty())
-					useStackTraceHashes(Boolean.valueOf(vr.resolve(r.useStackTraceHashes())));
-				if (! r.defaultCharset().isEmpty())
-					defaultCharset(vr.resolve(r.defaultCharset()));
-				if (! r.maxInput().isEmpty())
-					maxInput(vr.resolve(r.maxInput()));
-				if (! r.debug().isEmpty())
-					debug(Boolean.valueOf(vr.resolve(r.debug())));
-				mimeTypes(resolveVars(vr, r.mimeTypes()));
 
 				HtmlDoc hd = r.htmldoc();
 				widgets(hd.widgets());
@@ -273,13 +206,6 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 		}
 	}
 
-	private static String[] resolveVars(VarResolver vr, String[] in) {
-		String[] out = new String[in.length];
-		for (int i = 0; i < in.length; i++)
-			out[i] = vr.resolve(in[i]);
-		return out;
-	}
-
 	/*
 	 * Calls all @RestHook(INIT) methods on the specified resource object.
 	 */
@@ -491,6 +417,16 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 		return varResolverBuilder;
 	}
 
+	/**
+	 * Returns the REST path defined on this builder.
+	 *
+	 * @return The REST path defined on this builder.
+	 */
+	public String getPath() {
+		Object p = peek(REST_path);
+		return p == null ? "_" : p.toString();
+	}
+
 	//----------------------------------------------------------------------------------------------------
 	// Properties
 	//----------------------------------------------------------------------------------------------------
@@ -1420,7 +1356,7 @@ public class RestContextBuilder extends BeanContextBuilder implements ServletCon
 	public RestContextBuilder path(String value) {
 		if (startsWith(value, '/'))
 			value = value.substring(1);
-		this.path = value;
+		set(REST_path, value);
 		return this;
 	}
 
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java
index 33cc6bd..8d0e495 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestMethodContext.java
@@ -160,7 +160,15 @@ public class RestMethodContext implements Comparable<RestMethodContext>  {
 					throw new RestServletException("@RestMethod annotation not found on method ''{0}''", sig);
 
 				VarResolver vr = context.getVarResolver();
-				boolean hasConfigAnnotations = mi.hasConfigAnnotations();
+				
+				// If this method doesn't have any config annotations (e.g. @BeanConfig), then we want to 
+				// reuse the serializers/parsers on the class.
+				boolean hasConfigAnnotations = false;
+				for (AnnotationInfo<?> ai : mi.getConfigAnnotationListMethodOnlyParentFirst()) {
+					hasConfigAnnotations = ! ai.isType(RestMethod.class);
+					if (hasConfigAnnotations)
+						break;
+				}
 
 				serializers = context.getSerializers();
 				parsers = context.getParsers();
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResourceConfigApply.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResourceConfigApply.java
index dd9e731..c470a59 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResourceConfigApply.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestResourceConfigApply.java
@@ -13,10 +13,22 @@
 package org.apache.juneau.rest.annotation;
 
 import static org.apache.juneau.rest.RestContext.*;
+import static org.apache.juneau.rest.util.RestUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.internal.ArrayUtils.*;
+import static org.apache.juneau.serializer.Serializer.*;
+import static org.apache.juneau.parser.Parser.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.httppart.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.parser.*;
 import org.apache.juneau.reflect.*;
+import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.util.*;
+import org.apache.juneau.serializer.*;
 import org.apache.juneau.svl.*;
+import org.apache.juneau.utils.*;
 
 /**
  * Applies {@link RestResource} annotations to a {@link PropertyStoreBuilder}.
@@ -33,126 +45,181 @@ public class RestResourceConfigApply extends ConfigApply<RestResource> {
 		super(c, r);
 	}
 
+	@SuppressWarnings("deprecation")
 	@Override
 	public void apply(AnnotationInfo<RestResource> ai, PropertyStoreBuilder psb) {
 		RestResource a = ai.getAnnotation();
-//		String s = null;
-//
-//		ClassInfo c = ai.getClassOn();
-//		for (Property p : a.properties())
-//			psb.set(string(p.name()), string(p.value()));
-//		for (String p : a.flags())
-//			psb.set(p, true);
-//		psb.set(REST_serializers, merge(ObjectUtils.toType(psb.peek(REST_serializers), Object[].class), a.serializers()));
-//		psb.set(REST_parsers, merge(ObjectUtils.toType(psb.peek(REST_parsers), Object[].class), a.parsers()));
-//		if (a.partSerializer() != HttpPartSerializer.Null.class)
-//			psb.set(REST_partSerializer, a.partSerializer());
-//		if (a.partParser() != HttpPartParser.Null.class)
-//			psb.set(REST_partParser, a.partParser());
-//
-//		psb.addTo(REST_encoders, a.encoders());
-//		if (a.produces().length > 0)
-//			psb.set(REST_produces, strings(a.produces()));
-//		if (a.consumes().length > 0)
-//			psb.set(REST_consumes, strings(a.consumes()));
-//
-//		for (String header : strings(a.defaultRequestHeaders())) {
-//			String[] h = RestUtils.parseHeader(header);
-//			if (h == null)
-//				throw new FormattedRuntimeException("Invalid default request header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", header);
-//			if (isNotEmpty(h[1]))
-//				psb.addTo(REST_defaultRequestHeaders, h[0], h[1]);
-//		}
-//
-//		if (a.defaultAccept().length() > 0) {
-//			s = string(a.defaultAccept());
-//			if (isNotEmpty(s))
-//				psb.addTo(REST_defaultRequestHeaders, "Accept", s);
-//		}
-//		if (a.defaultContentType().length() > 0) {
-//			s = string(a.defaultContentType());
-//			if (isNotEmpty(s))
-//				psb.addTo(REST_defaultRequestHeaders, "Content-Type", s);
-//
-//		}
-//		for (String header : strings(a.defaultResponseHeaders())) {
-//			String[] h = RestUtils.parseHeader(header);
-//			if (h == null)
-//				throw new FormattedRuntimeException("Invalid default response header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", header);
-//			if (isNotEmpty(h[1]))
-//				psb.addTo(REST_defaultResponseHeaders, h[0], h[1]);
-//		}
-//		psb.addTo(REST_responseHandlers, a.responseHandlers());
-//		psb.addTo(REST_converters, a.converters());
-//		psb.addTo(REST_guards, reverse(a.guards()));
-//		psb.addTo(REST_children, a.children());
-//		psb.addTo(BEAN_beanFilters, merge(ObjectUtils.toType(psb.peek(BEAN_beanFilters), Object[].class), a.beanFilters()));
-//		psb.addTo(BEAN_pojoSwaps, merge(ObjectUtils.toType(psb.peek(BEAN_pojoSwaps), Object[].class), a.pojoSwaps()));
-//		psb.addTo(REST_paramResolvers, a.paramResolvers());
-//		if (a.serializerListener() != SerializerListener.Null.class)
-//			psb.set(SERIALIZER_listener, a.serializerListener());
-//		if (a.parserListener() != ParserListener.Null.class)
-//			psb.set(PARSER_listener, a.parserListener());
-//		s = string(a.uriContext());
-//		if (isNotEmpty(s))
-//			psb.set(REST_uriContext, s);
-//		s = string(a.uriAuthority());
-//		if (isNotEmpty(s))
-//			psb.set(REST_uriAuthority, s);
-//		s = string(a.uriRelativity());
-//		if (isNotEmpty(s))
-//			psb.set(REST_uriRelativity, s);
-//		s = string(a.uriResolution());
-//		if (isNotEmpty(s))
-//			psb.set(REST_uriResolution, s);
-//		for (String mapping : a.staticFiles())
-//			if (isNotEmpty(mapping))
-//				psb.addTo(REST_staticFiles, new StaticFileMapping(c.inner(), mapping));
-//		if (! a.messages().isEmpty())
-//			psb.addTo(REST_messages, new MessageBundleLocation(c.inner(), string(a.messages())));
-//		for (String header : strings(a.staticFileResponseHeaders())) {
-//			String[] h = RestUtils.parseHeader(header);
-//			if (h == null)
-//				throw new FormattedRuntimeException("Invalid static file response header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", header);
-//			if (isNotEmpty(h[1]))
-//				psb.addTo(REST_staticFileResponseHeaders, h[0], h[1]);
-//		}
-//		if (! a.useClasspathResourceCaching().isEmpty())
-//			psb.set(REST_useClasspathResourceCaching, bool(a.useClasspathResourceCaching()));
-//		if (a.classpathResourceFinder() != ClasspathResourceFinder.Null.class)
-//			psb.set(REST_classpathResourceFinder, a.classpathResourceFinder());
-//		if (! a.clientVersionHeader().isEmpty())
-//			psb.set(REST_clientVersionHeader, string(a.clientVersionHeader()));
-//		if (a.resourceResolver() != RestResourceResolver.Null.class)
-//			psb.set(REST_resourceResolver, a.resourceResolver());
-//		if (a.logger() != RestLogger.Null.class)
-//			psb.set(REST_logger, a.logger());
-//		if (a.callHandler() != RestCallHandler.Null.class)
-//			psb.set(REST_callHandler, a.callHandler());
-//		if (a.infoProvider() != RestInfoProvider.Null.class)
-//			psb.set(REST_infoProvider, a.infoProvider());
-//		if (! a.allowHeaderParams().isEmpty())
-//			psb.set(REST_allowHeaderParams, bool(a.allowHeaderParams()));
-//		if (! a.allowedMethodParams().isEmpty())
-//			psb.set(REST_allowedMethodParams, string(a.allowedMethodParams()));
-//		if (! a.allowBodyParam().isEmpty())
-//			psb.set(REST_allowBodyParam, bool(a.allowBodyParam()));
-//		if (! a.renderResponseStackTraces().isEmpty())
-//			psb.set(REST_renderResponseStackTraces, bool(a.renderResponseStackTraces()));
-//		if (! a.useStackTraceHashes().isEmpty())
-//			psb.set(REST_useStackTraceHashes, bool(a.useStackTraceHashes()));
-//		if (! a.defaultCharset().isEmpty())
-//			psb.set(REST_defaultCharset, string(a.defaultCharset()));
-//		if (! a.maxInput().isEmpty())
-//			psb.set(REST_maxInput, string(a.maxInput()));
-//		if (! a.debug().isEmpty()) {
-//			psb.set(REST_debug, bool(a.debug()));
-//			psb.set(BEAN_debug, bool(a.debug()));
-//		}
-//		psb.addTo(REST_mimeTypes, strings(a.mimeTypes()));
+		String s = null;
+		ClassInfo c = ai.getClassOn();
+
+		for (Property p : a.properties())
+			psb.set(string(p.name()), string(p.value()));
+
+		for (String p : a.flags())
+			psb.set(p, true);
+
+		if (a.serializers().length > 0)
+			psb.set(REST_serializers, merge(ObjectUtils.toType(psb.peek(REST_serializers), Object[].class), a.serializers()));
+
+		if (a.parsers().length > 0)
+			psb.set(REST_parsers, merge(ObjectUtils.toType(psb.peek(REST_parsers), Object[].class), a.parsers()));
+
+		if (a.partSerializer() != HttpPartSerializer.Null.class)
+			psb.set(REST_partSerializer, a.partSerializer());
+
+		if (a.partParser() != HttpPartParser.Null.class)
+			psb.set(REST_partParser, a.partParser());
+
+		psb.addTo(REST_encoders, a.encoders());
+
+		if (a.produces().length > 0)
+			psb.set(REST_produces, strings(a.produces()));
+
+		if (a.consumes().length > 0)
+			psb.set(REST_consumes, strings(a.consumes()));
+
+		for (String header : strings(a.defaultRequestHeaders())) {
+			String[] h = RestUtils.parseHeader(header);
+			if (h == null)
+				throw new FormattedRuntimeException("Invalid default request header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", header);
+			if (isNotEmpty(h[1]))
+				psb.addTo(REST_defaultRequestHeaders, h[0], h[1]);
+		}
+
+		if (a.defaultAccept().length() > 0) {
+			s = string(a.defaultAccept());
+			if (isNotEmpty(s))
+				psb.addTo(REST_defaultRequestHeaders, "Accept", s);
+		}
+
+		if (a.defaultContentType().length() > 0) {
+			s = string(a.defaultContentType());
+			if (isNotEmpty(s))
+				psb.addTo(REST_defaultRequestHeaders, "Content-Type", s);
+
+		}
+
+		for (String header : strings(a.defaultResponseHeaders())) {
+			String[] h = parseHeader(header);
+			if (h == null)
+				throw new FormattedRuntimeException("Invalid default response header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", header);
+			if (isNotEmpty(h[1]))
+				psb.addTo(REST_defaultResponseHeaders, h[0], h[1]);
+		}
+
+		psb.addTo(REST_responseHandlers, a.responseHandlers());
+
+		psb.addTo(REST_converters, a.converters());
+
+		psb.addTo(REST_guards, reverse(a.guards()));
+
+		psb.addTo(REST_children, a.children());
+
+		psb.set(BEAN_beanFilters, merge(ObjectUtils.toType(psb.peek(BEAN_beanFilters), Object[].class), a.beanFilters()));
+
+		psb.set(BEAN_pojoSwaps, merge(ObjectUtils.toType(psb.peek(BEAN_pojoSwaps), Object[].class), a.pojoSwaps()));
+
+		psb.addTo(REST_paramResolvers, a.paramResolvers());
+
+		if (a.serializerListener() != SerializerListener.Null.class)
+			psb.set(SERIALIZER_listener, a.serializerListener());
+
+		if (a.parserListener() != ParserListener.Null.class)
+			psb.set(PARSER_listener, a.parserListener());
+
+		s = string(a.uriContext());
+		if (isNotEmpty(s))
+			psb.set(REST_uriContext, s);
+
+		s = string(a.uriAuthority());
+		if (isNotEmpty(s))
+			psb.set(REST_uriAuthority, s);
+
+		s = string(a.uriRelativity());
+		if (isNotEmpty(s))
+			psb.set(REST_uriRelativity, s);
+
+		s = string(a.uriResolution());
+		if (isNotEmpty(s))
+			psb.set(REST_uriResolution, s);
+
+		for (String mapping : a.staticFiles())
+			if (isNotEmpty(mapping))
+				psb.addTo(REST_staticFiles, new StaticFileMapping(c.inner(), string(mapping)));
+
+		if (! a.messages().isEmpty())
+			psb.addTo(REST_messages, new MessageBundleLocation(c.inner(), string(a.messages())));
+
+		for (String header : strings(a.staticFileResponseHeaders())) {
+			String[] h = RestUtils.parseHeader(header);
+			if (h == null)
+				throw new FormattedRuntimeException("Invalid static file response header specified: ''{0}''.  Must be in the format: ''Header-Name: header-value''", header);
+			if (isNotEmpty(h[1]))
+				psb.addTo(REST_staticFileResponseHeaders, h[0], h[1]);
+		}
+
+		if (! a.useClasspathResourceCaching().isEmpty())
+			psb.set(REST_useClasspathResourceCaching, bool(a.useClasspathResourceCaching()));
+
+		if (a.classpathResourceFinder() != ClasspathResourceFinder.Null.class)
+			psb.set(REST_classpathResourceFinder, a.classpathResourceFinder());
+
+		if (! a.path().isEmpty())
+			psb.set(REST_path, trimLeadingSlash(string(a.path())));
+
+		if (! a.clientVersionHeader().isEmpty())
+			psb.set(REST_clientVersionHeader, string(a.clientVersionHeader()));
+
+		if (a.resourceResolver() != RestResourceResolver.Null.class)
+			psb.set(REST_resourceResolver, a.resourceResolver());
+
+		if (a.logger() != RestLogger.Null.class)
+			psb.set(REST_logger, a.logger());
+
+		if (a.callHandler() != RestCallHandler.Null.class)
+			psb.set(REST_callHandler, a.callHandler());
+
+		if (a.infoProvider() != RestInfoProvider.Null.class)
+			psb.set(REST_infoProvider, a.infoProvider());
+
+		if (! a.allowHeaderParams().isEmpty())
+			psb.set(REST_allowHeaderParams, bool(a.allowHeaderParams()));
+
+		if (! a.allowedMethodParams().isEmpty())
+			psb.set(REST_allowedMethodParams, string(a.allowedMethodParams()));
+
+		if (! a.allowBodyParam().isEmpty())
+			psb.set(REST_allowBodyParam, bool(a.allowBodyParam()));
+
+		if (! a.renderResponseStackTraces().isEmpty())
+			psb.set(REST_renderResponseStackTraces, bool(a.renderResponseStackTraces()));
+
+		if (! a.useStackTraceHashes().isEmpty())
+			psb.set(REST_useStackTraceHashes, bool(a.useStackTraceHashes()));
+
+		if (! a.defaultCharset().isEmpty())
+			psb.set(REST_defaultCharset, string(a.defaultCharset()));
+
+		if (! a.maxInput().isEmpty())
+			psb.set(REST_maxInput, string(a.maxInput()));
+
+		if (! a.debug().isEmpty()) {
+			psb.set(REST_debug, bool(a.debug()));
+			psb.set(BEAN_debug, bool(a.debug()));
+		}
+
+		psb.addTo(REST_mimeTypes, strings(a.mimeTypes()));
+
 		if (! a.rolesDeclared().isEmpty())
 			psb.set(REST_rolesDeclared, string(a.rolesDeclared()));
+
 		if (! a.roleGuard().isEmpty())
 			psb.set(REST_roleGuard, string(a.roleGuard()));
 	}
+
+	private String trimLeadingSlash(String value) {
+		if (startsWith(value, '/'))
+			return value.substring(1);
+		return value;
+	}
 }