http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServiceProperties.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServiceProperties.java b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServiceProperties.java new file mode 100644 index 0000000..45104f6 --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServiceProperties.java @@ -0,0 +1,37 @@ +// *************************************************************************************************************************** +// * 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.remoteable; + +import org.apache.juneau.annotation.*; +import org.apache.juneau.rest.*; +import org.apache.juneau.rest.annotation.*; + +/** + * Configurable properties for the {@link RemoteableServlet} class. + * <p> + * Properties can be set on the {@link RestServlet} class using the {@link RestResource#properties} or {@link RestMethod#properties} annotations. + * <p> + * These properties can also be passed in as servlet init parameters. + * <p> + * These properties are only valid at the class level, not the method level. Setting them on {@link RestMethod#properties()} has no effect. + */ +public final class RemoteableServiceProperties { + + /** + * Only expose interfaces and methods annotated with {@link Remoteable @Remoteable} ({@link Boolean}, default=<jk>false</jk>). + * <p> + * When enabled, the {@link RemoteableServlet} class will only work with annotated remoteable interfaces and methods. + * Otherwise, all public methods can be executed through the service. + */ + public static final String REMOTEABLE_includeOnlyRemotableMethods = "RemoteableService.includeOnlyRemoteableMethods"; +}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java new file mode 100644 index 0000000..bdd9409 --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/RemoteableServlet.java @@ -0,0 +1,152 @@ +// *************************************************************************************************************************** +// * 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.remoteable; + +import static javax.servlet.http.HttpServletResponse.*; + +import java.util.*; +import java.util.concurrent.*; + +import org.apache.juneau.*; +import org.apache.juneau.dto.*; +import org.apache.juneau.parser.*; +import org.apache.juneau.rest.*; +import org.apache.juneau.rest.annotation.*; + +/** + * Abstract class for defining Remoteable services. + * <p> + * Remoteable services are POJOs whose methods can be invoked remotely through proxy interfaces. + * <p> + * To implement a remoteable service, developers must simply subclass from this class and implement the {@link #getServiceMap()} method that + * maps java interfaces to POJO instances. + * + * See {@link org.apache.juneau.rest.remoteable} for details. + */ +@SuppressWarnings("serial") +public abstract class RemoteableServlet extends RestServletDefault { + + private Map<String,Class<?>> classNameMap = new ConcurrentHashMap<String,Class<?>>(); + + //-------------------------------------------------------------------------------- + // Abstract methods + //-------------------------------------------------------------------------------- + + /** + * Returns the list of interfaces to their implementation objects. + * <p> + * This class is called often and not cached, so any caching should occur in the subclass if necessary. + * + * @return The service map. + * @throws Exception + */ + protected abstract Map<Class<?>,Object> getServiceMap() throws Exception; + + //-------------------------------------------------------------------------------- + // REST methods + //-------------------------------------------------------------------------------- + + /** + * [GET /] - Get the list of all remote interfaces. + * + * @param req The HTTP servlet request. + * @return The list of links to the remote interfaces. + * @throws Exception + */ + @RestMethod(name="GET", path="/") + public List<Link> getInterfaces(RestRequest req) throws Exception { + List<Link> l = new LinkedList<Link>(); + boolean useAll = ! useOnlyAnnotated(); + for (Class<?> c : getServiceMap().keySet()) { + if (useAll || getBeanContext().getClassMeta(c).isRemoteable()) + l.add(new Link(c.getName(), "{0}/{1}", req.getRequestURI(), c.getName())); //$NON-NLS-1$ + } + return l; + } + + /** + * [GET /{javaInterface] - Get the list of all remoteable methods on the specified interface name. + * + * @param javaInterface The Java interface name. + * @return The methods defined on the interface. + * @throws Exception + */ + @RestMethod(name="GET", path="/{javaInterface}") + public Collection<String> listMethods(@Path String javaInterface) throws Exception { + return getMethods(javaInterface).keySet(); + } + + /** + * [POST /{javaInterface}/{javaMethod}] - Invoke the specified service method. + * + * @param req The HTTP request. + * @param javaInterface The Java interface name. + * @param javaMethod The Java method name or signature. + * @return The results from invoking the specified Java method. + * @throws Exception + */ + @RestMethod(name="POST", path="/{javaInterface}/{javaMethod}") + public Object invoke(RestRequest req, @Path String javaInterface, @Path String javaMethod) throws Exception { + + // Find the parser. + ReaderParser p = req.getReaderParser(); + if (p == null) + throw new RestException(SC_UNSUPPORTED_MEDIA_TYPE, "Could not find parser for media type ''{0}''", req.getMediaType()); //$NON-NLS-1$ + Class<?> c = getInterfaceClass(javaInterface); + + // Find the service. + Object service = getServiceMap().get(c); + if (service == null) + throw new RestException(SC_NOT_FOUND, "Service not found"); //$NON-NLS-1$ + + // Find the method. + java.lang.reflect.Method m = getMethods(javaInterface).get(javaMethod); + if (m == null) + throw new RestException(SC_NOT_FOUND, "Method not found"); //$NON-NLS-1$ + + // Parse the args and invoke the method. + ClassMeta<?>[] argTypes = req.getBeanSession().getClassMetas(m.getParameterTypes()); + Object[] params = p.parseArgs(req.getReader(), argTypes); + return m.invoke(service, params); + } + + //-------------------------------------------------------------------------------- + // Other methods + //-------------------------------------------------------------------------------- + + private boolean useOnlyAnnotated() { + return getProperties().getBoolean(RemoteableServiceProperties.REMOTEABLE_includeOnlyRemotableMethods, false); + } + + private Map<String,java.lang.reflect.Method> getMethods(String javaInterface) throws Exception { + Class<?> c = getInterfaceClass(javaInterface); + ClassMeta<?> cm = getBeanContext().getClassMeta(c); + return (useOnlyAnnotated() ? cm.getRemoteableMethods() : cm.getPublicMethods()); + } + + /** + * Return the <code>Class</code> given it's name if it exists in the services map. + */ + private Class<?> getInterfaceClass(String javaInterface) throws Exception { + Class<?> c = classNameMap.get(javaInterface); + if (c == null) { + for (Class<?> c2 : getServiceMap().keySet()) + if (c2.getName().equals(javaInterface)) { + classNameMap.put(javaInterface, c2); + return c2; + } + throw new RestException(SC_NOT_FOUND, "Interface class not found"); + } + return c; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/1.png ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/1.png b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/1.png new file mode 100644 index 0000000..9cd4b2f Binary files /dev/null and b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/1.png differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/2.png ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/2.png b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/2.png new file mode 100644 index 0000000..de2074d Binary files /dev/null and b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/2.png differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/3.png ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/3.png b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/3.png new file mode 100644 index 0000000..8307c13 Binary files /dev/null and b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/3.png differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/4.png ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/4.png b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/4.png new file mode 100644 index 0000000..896f579 Binary files /dev/null and b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/4.png differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/5.png ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/5.png b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/5.png new file mode 100644 index 0000000..2c255d3 Binary files /dev/null and b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/5.png differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/6.png ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/6.png b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/6.png new file mode 100644 index 0000000..f45a3d2 Binary files /dev/null and b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/doc-files/6.png differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/package.html ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/package.html b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/package.html new file mode 100644 index 0000000..fbc7c41 --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/remoteable/package.html @@ -0,0 +1,356 @@ +<!DOCTYPE HTML> +<!-- +/*************************************************************************************************************************** + * 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. + * + ***************************************************************************************************************************/ + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../../../../../javadoc.css"); + + /* For viewing in REST interface */ + @IMPORT url("../htdocs/javadoc.css"); + body { + margin: 20px; + } + </style> + <script> + /* Replace all @code and @link tags. */ + window.onload = function() { + document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>'); + document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>'); + } + </script> +</head> +<body> +<p>Remoteable service API</p> + +<script> + function toggle(x) { + var div = x.nextSibling; + while (div != null && div.nodeType != 1) + div = div.nextSibling; + if (div != null) { + var d = div.style.display; + if (d == 'block' || d == '') { + div.style.display = 'none'; + x.className += " closed"; + } else { + div.style.display = 'block'; + x.className = x.className.replace(/(?:^|\s)closed(?!\S)/g , '' ); + } + } + } +</script> + +<p> + Defines an API for remote proxy interfaces (e.g. Remoteable Services). +</p> + +<a id='TOC'></a><h5 class='toc'>Table of Contents</h5> +<ol class='toc'> + <li><p><a class='doclink' href='#Intro'>Remoteable Services</a></p> + <li><p><a class='doclink' href='#Client'>Client Side</a></p> + <li><p><a class='doclink' href='#Server'>Server Side</a></p> + <li><p><a class='doclink' href='#RemoteableAnnotation'>@Remoteable Annotation</a></p> +</ol> + +<!-- ======================================================================================================== --> +<a id="Intro"></a> +<h2 class='topic' onclick='toggle(this)'>1 - Remoteable Services</h2> +<div class='topic'> + <p> + The Remoteable Service API allows for client side code to use interface proxies for + calling methods on POJOs on the server side. + </p> + <p> + Proxy interfaces are retrieved using the {@link org.apache.juneau.client.RestClient#getRemoteableProxy(Class)} method. + The {@link org.apache.juneau.client.RestClient#setRemoteableServletUri(String)} method is used to specify the location + of the remoteable services servlet running on the server. + The remoteable servlet is a specialized subclass of {@link org.apache.juneau.rest.RestServlet} that provides a full-blown + REST interface for calling remoteable services (e.g. POJOs) remotely. + </p> + <p> + The following simplified example shows how a method on a POJO on a server can be called through an interface + on a client... + <p class='bcode'> + <jk>public interface</jk> IAddressBook { + Person createPerson(CreatePerson cp) <jk>throws</jk> Exception; + Person findPerson(<jk>int</jk> id); + Address findAddress(<jk>int</jk> id); + Person findPersonWithAddress(<jk>int</jk> id); + } + </p> + <p> + The client side code for invoking this method is shown below... + </p> + <p class='bcode'> + <jc>// Create a RestClient using JSON for serialization, and point to the server-side remoteable servlet.</jc> + RestClient client = <jk>new</jk> RestClient(JsonSerializer.<jk>class</jk>,JsonParser.<jk>class</jk>) + .setRemoteableServletUri(<js>"https://localhost:9080/juneau/sample/remoteable"</js>); + + <jc>// Create a proxy interface.</jc> + IAddressBook ab = client.getRemoteableProxy(IAddressBook.<jk>class</jk>); + + <jc>// Invoke a method on the server side and get the returned result.</jc> + Person p = ab.createPerson( + <jk>new</jk> CreatePerson(<js>"Test Person"</js>, + AddressBook.<jsm>toCalendar</jsm>(<js>"Aug 1, 1999"</js>), + <jk>new</jk> CreateAddress(<js>"Test street"</js>, <js>"Test city"</js>, <js>"Test state"</js>, 12345, <jk>true</jk>)) + ); + </p> + <p> + The requirements for a method to be callable through the remoteable service are: + </p> + <ul class='spaced-list'> + <li>The method must be public. + <li>The parameter and return types must be <a href='../../../../../overview-summary.html#Core.PojoCategories'>serializable and parsable</a>. + </ul> +</div> + +<!-- ======================================================================================================== --> +<a id="Client"></a> +<h2 class='topic' onclick='toggle(this)'>2 - Client Side</h2> +<div class='topic'> + <p> + Remoteable interface proxies are retrieved through the existing {@link org.apache.juneau.client.RestClient} class. + </p> + <p> + It may seem that the client-side code would need to be complex. + In reality, it builds upon existing serializing, parsing, and REST capabilities in Juneau resulting + in very little additional code. + The entire code for the <code>RestClient.getRemoteableProxy(Class)</code> method is shown below: + </p> + <p class='bcode'> + <jk>public</jk> <T> T getRemoteableProxy(<jk>final</jk> Class<T> interfaceClass) { + <jk>return</jk> (T)Proxy.newProxyInstance( + interfaceClass.getClassLoader(), + <jk>new</jk> Class[] { interfaceClass }, + <jk>new</jk> InvocationHandler() { + <ja>@Override</ja> + <jk>public</jk> Object invoke(Object proxy, Method method, Object[] args) { + <jk>try</jk> { + String uri = <jf>remoteableServletUri</jf> + '/' + interfaceClass.getName() + '/' + ClassUtils.<jsm>getMethodSignature</jsm>(method); + <jk>return</jk> doPost(uri, args).getResponse(method.getReturnType()); + } <jk>catch</jk> (Exception e) { + <jk>throw new</jk> RuntimeException(e); + } + } + }); + } + </p> + <p> + Since we build upon the existing <code>RestClient</code> API, we inherit all of it's features. + For example, convenience methods for setting POJO filters and properties to customize + the behavior of the serializers and parsers, and the ability to provide your own + customized Apache <code>HttpClient</code> for handling various scenarios involving + authentication and internet proxies. + </p> +</div> + +<!-- ======================================================================================================== --> +<a id="Server"></a> +<h2 class='topic' onclick='toggle(this)'>3 - Server Side</h2> +<div class='topic'> + <p> + The server side is only slightly more complex, but boasts useful debugging and discovery capabilities. + </p> + <p> + The {@link org.apache.juneau.rest.remoteable.RemoteableServlet} class is an implementation of {@link org.apache.juneau.rest.RestServlet} + that provides a REST interface for invoking calls on POJOs. + The <code>RemoteableServlet</code> class is abstract and must implement a single method for providing + the set of POJOs to expose as remote interfaces. + </p> + <p> + The samples bundle includes a sample implementation of a remotable service that can be used to interact with the + address book POJO also included in the bundle. + The method that must be implemented is {@link org.apache.juneau.rest.remoteable.RemoteableServlet#getServiceMap()} + that simply returns a mapping of Java interfaces (or classes) to POJO instances. + </p> + <p class='bcode'> + <ja>@RestResource</ja>( + path=<js>"/remoteable"</js> + ) + <jk>public class</jk> SampleRemoteableServlet <jk>extends</jk> RemoteableServlet { + + <jc>// The POJO being manipulated (i.e. the remoteable service)</jc> + AddressBook <jf>addressBook</jf> = <jk>new</jk> AddressBook(); + + <ja>@Override</ja> <jc>/* RemoteableServlet */</jc> + <jk>protected</jk> Map<Class<?>,Object> getServiceMap() <jk>throws</jk> Exception { + Map<Class<?>,Object> m = <jk>new</jk> LinkedHashMap<Class<?>,Object>(); + + <jc>// In this simplified example, we expose the same POJO service under two different interfaces. + // One is IAddressBook which only exposes methods defined on that interface, and + // the other is AddressBook itself which exposes all public methods defined on the class itself.</jc> + m.put(IAddressBook.<jk>class</jk>, addressBook); + m.put(AddressBook.<jk>class</jk>, addressBook); + <jk>return</jk> m; + } + } + </p> + <p> + Since this class is a servlet, and can be deployed as such. + In the sample code, it's listed as a child resource to <code>org.apache.juneau.rest.samples.RootResources</code> + which makes it available under the URL <code>/juneau/sample/remoteable</code>. + </p> + <p> + If you point your browser to that URL, you get a list of available interfaces: + </p> + <img class='bordered' src="doc-files/1.png"> + <p> + Clicking the hyperlinks on each shows you the list of methods that can be invoked on that service. + Note that the <code>IAddressBook</code> link shows that you can only invoke methods defined on that + interface, whereas the <code>AddressBook</code> link shows ALL public methods defined on that class. + Since <code>AddressBook</code> extends from <code>LinkedList</code>, you may notice familiar collections + framework methods listed. + </p> + <img class='bordered' src="doc-files/2.png"> + <img class='bordered' src="doc-files/3.png"> + <p> + Let's see how we can interact with this interface through nothing more than REST calls + to get a better idea on how this works. + We'll use the same method call as in the introduction. + First, we need to create the serialized form of the arguments: + </p> + <p class='bcode'> + Object[] args = <jk>new</jk> Object[] { + <jk>new</jk> CreatePerson(<js>"Test Person"</js>, + AddressBook.<jsm>toCalendar</jsm>(<js>"Aug 1, 1999"</js>), + <jk>new</jk> CreateAddress(<js>"Test street"</js>, <js>"Test city"</js>, <js>"Test state"</js>, 12345, <jk>true</jk>)) + }; + String asJson = JsonSerializer.<jsf>DEFAULT_LAX_READABLE</jsf>.toString(args); + System.<jsf>err</jsf>.println(asJson); + </p> + <p> + That produces the following JSON output: + </p> + <p class='bcode'> + [ + { + name: <js>'Test Person'</js>, + birthDate: <js>'Aug 1, 1999'</js>, + addresses: [ + { + street: <js>'Test street'</js>, + city: <js>'Test city'</js>, + state: <js>'Test state'</js>, + zip: 12345, + isCurrent: <jk>true</jk> + } + ] + } + ] + </p> + <p> + Note that in this example we're using JSON. + However, various other content types can also be used such as XML, URL-Encoding, UON, or HTML. + In practice however, JSON will preferred since it is often the most efficient. + </p> + <p> + Next, we can use a tool such as Poster to make the REST call. + Methods are invoked by POSTing the serialized object array to the URI of the interface method. + In this case, we want to POST our JSON to <code>/juneau/sample/remoteable/org.apache.juneau.samples.addressbook.IAddressBook/createPerson(org.apache.juneau.samples.addressbook.CreatePerson)</code>. + Make sure that we specify the <code>Content-Type</code> of the body as <code>text/json</code>. + We also want the results to be returned as JSON, so we set the <code>Accept</code> header to <code>text/json</code> as well. + </p> + <img class='bordered' src="doc-files/4.png"> + <p> + When we execute the POST, we should see the following successful response whose body contains the returned <code>Person</code> bean + serialized to JSON: + </p> + <img class='bordered' src="doc-files/5.png"> + <p> + From there, we could use the following code snippet to reconstruct the response object from JSON: + </p> + <p class='bcode'> + String response = <js>"<i>output from above</i>"</js>; + Person p = JsonParser.<jsf>DEFAULT</jsf>.parse(response, Person.<jk>class</jk>); + </p> + <p> + If we alter our servlet to allow overloaded GET requests, we can invoke methods using nothing more than a browser... + </p> + <p class='bcode'> + <ja>@RestResource</ja>( + path=<js>"/remoteable"</js>, + properties={ + <jc>// Allow us to use method=POST from a browser.</jc> + <ja>@Property</ja>(name=<jsf>REST_allowMethodParam</jsf>, value=<js>"*"</js>) + } + ) + <jk>public class</jk> SampleRemoteableServlet <jk>extends</jk> RemoteableServlet { + </p> + <p> + For example, here we call the <code>findPerson(<jk>int</jk>)</code> method to retrieve a person and get the returned POJO + (in this case as HTML since that's what's in the <code>Accept</code> header when calling from a browser): + </p> + <img class='bordered' src="doc-files/6.png"> + <p> + When specifying the POST body as a <code>&content</code> parameter, the method arguments should be in UON notation. + See {@link org.apache.juneau.urlencoding.UonSerializer} for more information about this encoding. + Usually you can also pass in JSON if you specify <code>&Content-Type=text/json</code> in the URL parameters + but passing in unencoded JSON in a URL may not work in all browsers. Therefore, UON is preferred. + </p> +</div> + +<!-- ======================================================================================================== --> +<a id="RemoteableAnnotation"></a> +<h2 class='topic' onclick='toggle(this)'>4 - @Remoteable Annotation</h2> +<div class='topic'> + <p> + What if you want fine-tuned control over which methods are exposed in an interface instead of just all public methods? + For this, the {@link org.apache.juneau.annotation.Remoteable @Remoteable} annotation is provided. + It can be applied to individual interface methods to only expose those methods through the remoteable servlet. + </p> + <p> + For example, to expose only the first 2 methods in our <code>IAddressBook</code> interface... + </p> + <p class='bcode'> + <jk>public interface</jk> IAddressBook { + <ja>@Remoteable</ja> Person createPerson(CreatePerson cp) <jk>throws</jk> Exception; + <ja>@Remoteable</ja> Person findPerson(<jk>int</jk> id); + Address findAddress(<jk>int</jk> id); + Person findPersonWithAddress(<jk>int</jk> id); + } + </p> + <p> + On the server side, the option to restrict access to only annotated methods is defined through a property: + </p> + <p class='bcode'> + <ja>@RestResource</ja>( + path=<js>"/remoteable"</js>, + properties={ + <jc>// Only expose methods annotated with @Remoteable.</jc> + <ja>@Property</ja>(name=<jsf>REMOTEABLE_includeOnlyRemotableMethods</jsf>, value=<js>"true"</js>) + } + ) + <jk>public class</jk> SampleRemoteableServlet <jk>extends</jk> RemoteableServlet { + </p> + <p> + The <ja>@Remoteable</ja> annotation can also be applied to the interface class to expose all public methods defined on that interface. + </p> + <p class='bcode'> + <ja>@Remoteable</ja> + <jk>public interface</jk> IAddressBook { + Person createPerson(CreatePerson cp) <jk>throws</jk> Exception; + Person findPerson(<jk>int</jk> id); + Address findAddress(<jk>int</jk> id); + Person findPersonWithAddress(<jk>int</jk> id); + } + </p> +</div> +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java new file mode 100644 index 0000000..7110014 --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/DefaultHandler.java @@ -0,0 +1,88 @@ +// *************************************************************************************************************************** +// * 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.response; + +import static javax.servlet.http.HttpServletResponse.*; + +import java.io.*; + +import org.apache.juneau.*; +import org.apache.juneau.annotation.*; +import org.apache.juneau.rest.*; +import org.apache.juneau.serializer.*; + +/** + * Response handler for POJOs not handled by other handlers. + * <p> + * This uses the serializers defined on the response to serialize the POJO. + * <p> + * The {@link Serializer} used is based on the <code>Accept</code> header on the request. + * <p> + * The <code>Content-Type</code> header is set to the mime-type defined on the selected + * serializer based on the {@link Produces#contentType() @Produces.contentType} annotation. + * <p> + * This handler is registered by default on {@link RestServlet RestServlets} via the + * default implementation of the {@link RestServlet#createResponseHandlers} method. + */ +public class DefaultHandler implements ResponseHandler { + + @Override /* ResponseHandler */ + public boolean handle(RestRequest req, RestResponse res, Object output) throws IOException, RestException { + SerializerGroup g = res.getSerializerGroup(); + String accept = req.getHeader("Accept", ""); + String matchingAccept = g.findMatch(accept); + if (matchingAccept != null) { + Serializer s = g.getSerializer(matchingAccept); + String contentType = s.getResponseContentType(); + if (contentType == null) + contentType = res.getContentType(); + if (contentType == null) + contentType = matchingAccept; + res.setContentType(contentType); + ObjectMap headers = s.getResponseHeaders(res.getProperties()); + if (headers != null) + for (String key : headers.keySet()) + res.setHeader(key, headers.getString(key)); + + try { + ObjectMap p = res.getProperties(); + if (req.isPlainText()) { + p.put(SerializerContext.SERIALIZER_useIndentation, true); + res.setContentType("text/plain"); + } + p.append("mediaType", matchingAccept).append("characterEncoding", res.getCharacterEncoding()); + if (! s.isWriterSerializer()) { + OutputStreamSerializer s2 = (OutputStreamSerializer)s; + OutputStream os = res.getNegotiatedOutputStream(); + SerializerSession session = s.createSession(os, p, req.getJavaMethod(), req.getLocale(), req.getTimeZone()); + s2.serialize(session, output); + os.close(); + } else { + WriterSerializer s2 = (WriterSerializer)s; + Writer w = res.getNegotiatedWriter(); + SerializerSession session = s.createSession(w, p, req.getJavaMethod(), req.getLocale(), req.getTimeZone()); + s2.serialize(session, output); + w.close(); + } + } catch (SerializeException e) { + throw new RestException(SC_INTERNAL_SERVER_ERROR, e); + } + } else { + throw new RestException(SC_NOT_ACCEPTABLE, + "Unsupported media-type in request header ''Accept'': ''{0}''\n\tSupported media-types: {1}", + req.getHeader("Accept", ""), g.getSupportedMediaTypes() + ); + } + return true; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/response/InputStreamHandler.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/InputStreamHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/InputStreamHandler.java new file mode 100644 index 0000000..1eebcfb --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/InputStreamHandler.java @@ -0,0 +1,42 @@ +// *************************************************************************************************************************** +// * 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.response; + +import java.io.*; + +import org.apache.juneau.rest.*; +import org.apache.juneau.utils.*; + +/** + * Response handler for {@link InputStream} objects. + * <p> + * Simply pipes the contents of the {@link InputStream} to {@link RestResponse#getNegotiatedOutputStream()}. + * <p> + * Sets the <code>Content-Type</code> response header to whatever was set via {@link RestResponse#setContentType(String)}. + * <p> + * This handler is registered by default on {@link RestServlet RestServlets} via the + * default implementation of the {@link RestServlet#createResponseHandlers} method. + */ +public final class InputStreamHandler implements ResponseHandler { + + @Override /* ResponseHandler */ + public boolean handle(RestRequest req, RestResponse res, Object output) throws IOException, RestException { + if (output instanceof InputStream) { + res.setHeader("Content-Type", res.getContentType()); + OutputStream os = res.getNegotiatedOutputStream(); + IOPipe.create(output, os).closeOut().run(); + return true; + } + return false; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/response/ReaderHandler.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/ReaderHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/ReaderHandler.java new file mode 100644 index 0000000..6f5b6ef --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/ReaderHandler.java @@ -0,0 +1,40 @@ +// *************************************************************************************************************************** +// * 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.response; + +import java.io.*; + +import org.apache.juneau.rest.*; +import org.apache.juneau.utils.*; + +/** + * Response handler for {@link Reader} objects. + * <p> + * Simply pipes the contents of the {@link Reader} to {@link RestResponse#getNegotiatedWriter()}. + * <p> + * This handler is registered by default on {@link RestServlet RestServlets} via the + * default implementation of the {@link RestServlet#createResponseHandlers} method. + */ +public final class ReaderHandler implements ResponseHandler { + + @Override /* ResponseHandler */ + public boolean handle(RestRequest req, RestResponse res, Object output) throws IOException, RestException { + if (output instanceof Reader) { + Writer w = res.getNegotiatedWriter(); + IOPipe.create(output, w).closeOut().run(); + return true; + } + return false; + } +} + http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/response/RedirectHandler.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/RedirectHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/RedirectHandler.java new file mode 100644 index 0000000..b2054ce --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/RedirectHandler.java @@ -0,0 +1,48 @@ +// *************************************************************************************************************************** +// * 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.response; + +import java.io.*; + +import org.apache.juneau.internal.*; +import org.apache.juneau.rest.*; + +/** + * Response handler for {@link Redirect} objects. + * <p> + * This handler is registered by default on {@link RestServlet RestServlets} via the + * default implementation of the {@link RestServlet#createResponseHandlers} method. + */ +public final class RedirectHandler implements ResponseHandler { + + @Override /* ResponseHandler */ + public boolean handle(RestRequest req, RestResponse res, Object output) throws IOException, RestException { + if (output instanceof Redirect) { + Redirect r = (Redirect)output; + String uri = r.toUrl(res.getUrlEncodingSerializer()); + if (StringUtils.isEmpty(uri)) + uri = req.getServletURI(); + else { + char c = (uri.length() > 0 ? uri.charAt(0) : 0); + if (c != '/' && uri.indexOf("://") == -1) + uri = req.getServletURIBuilder().append('/').append(uri).toString(); + } + int rc = r.getHttpResponseCode(); + if (rc != 0) + res.setStatus(rc); // TODO - This may get ignored by the call below. + res.sendRedirect(uri); + return true; + } + return false; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/response/StreamableHandler.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/StreamableHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/StreamableHandler.java new file mode 100644 index 0000000..724584e --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/StreamableHandler.java @@ -0,0 +1,51 @@ +// *************************************************************************************************************************** +// * 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.response; + +import java.io.*; +import java.util.*; + +import org.apache.juneau.*; +import org.apache.juneau.rest.*; + +/** + * Response handler for {@link Writable} and {@link ReaderResource} objects. + * <p> + * Uses the {@link Writable#writeTo(Writer)} method to send the contents to the {@link RestResponse#getNegotiatedWriter()} writer. + * <p> + * This handler is registered by default on {@link RestServlet RestServlets} via the + * default implementation of the {@link RestServlet#createResponseHandlers} method. + */ +public final class StreamableHandler implements ResponseHandler { + + @Override /* ResponseHandler */ + public boolean handle(RestRequest req, RestResponse res, Object output) throws IOException, RestException { + if (output instanceof Streamable) { + if (output instanceof StreamResource) { + StreamResource r = (StreamResource)output; + String mediaType = r.getMediaType(); + if (mediaType != null) + res.setContentType(mediaType); + for (Map.Entry<String,String> h : r.getHeaders().entrySet()) + res.setHeader(h.getKey(), h.getValue()); + } + OutputStream os = res.getOutputStream(); + ((Streamable)output).streamTo(os); + os.flush(); + os.close(); + return true; + } + return false; + } +} + http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/response/WritableHandler.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/WritableHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/WritableHandler.java new file mode 100644 index 0000000..c6a9598 --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/WritableHandler.java @@ -0,0 +1,51 @@ +// *************************************************************************************************************************** +// * 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.response; + +import java.io.*; +import java.util.*; + +import org.apache.juneau.*; +import org.apache.juneau.rest.*; + +/** + * Response handler for {@link Writable} and {@link ReaderResource} objects. + * <p> + * Uses the {@link Writable#writeTo(Writer)} method to send the contents to the {@link RestResponse#getNegotiatedWriter()} writer. + * <p> + * This handler is registered by default on {@link RestServlet RestServlets} via the + * default implementation of the {@link RestServlet#createResponseHandlers} method. + */ +public final class WritableHandler implements ResponseHandler { + + @Override /* ResponseHandler */ + public boolean handle(RestRequest req, RestResponse res, Object output) throws IOException, RestException { + if (output instanceof Writable) { + if (output instanceof ReaderResource) { + ReaderResource r = (ReaderResource)output; + String mediaType = r.getMediaType(); + if (mediaType != null) + res.setContentType(mediaType); + for (Map.Entry<String,String> h : r.getHeaders().entrySet()) + res.setHeader(h.getKey(), h.getValue()); + } + Writer w = res.getNegotiatedWriter(); + ((Writable)output).writeTo(w); + w.flush(); + w.close(); + return true; + } + return false; + } +} + http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/response/ZipFileListResponseHandler.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/ZipFileListResponseHandler.java b/juneau-rest/src/main/java/org/apache/juneau/rest/response/ZipFileListResponseHandler.java new file mode 100644 index 0000000..32c8742 --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/ZipFileListResponseHandler.java @@ -0,0 +1,62 @@ +// *************************************************************************************************************************** +// * 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.response; + +import java.io.*; +import java.util.zip.*; + +import org.apache.juneau.rest.*; +import org.apache.juneau.rest.annotation.*; +import org.apache.juneau.utils.*; +import org.apache.juneau.utils.ZipFileList.*; + +/** + * Response handler for ZipFileList objects. + * <p> + * Can be associated with a REST resource using the {@link RestResource#responseHandlers} annotation. + * <p> + * Sets the following headers: + * <ul class='spaced-list'> + * <li><code>Content-Type</code> - <code>application/zip</code> + * <li><code>Content-Disposition=attachment;filename=X</code> - Sets X to the file name passed in through + * the constructor {@link ZipFileList#ZipFileList(String)}. + * </ul> + */ +public class ZipFileListResponseHandler implements ResponseHandler { + + @Override /* ResponseHandler */ + public boolean handle(RestRequest req, RestResponse res, Object output) throws IOException, RestException { + if (output.getClass() == ZipFileList.class) { + ZipFileList m = (ZipFileList)output; + res.setContentType("application/zip"); //$NON-NLS-1$ + res.setHeader("Content-Disposition", "attachment;filename=" + m.fileName); //$NON-NLS-1$ //$NON-NLS-2$ + OutputStream os = res.getOutputStream(); + try { + ZipOutputStream zos = new ZipOutputStream(os); + try { + for (ZipFileEntry e : m) + e.write(zos); + } catch (Exception e) { + e.printStackTrace(); + } finally { + zos.flush(); + zos.close(); + } + } finally { + os.flush(); + } + return true; + } + return false; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/response/package.html ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/response/package.html b/juneau-rest/src/main/java/org/apache/juneau/rest/response/package.html new file mode 100644 index 0000000..6e89506 --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/response/package.html @@ -0,0 +1,41 @@ +<!DOCTYPE HTML> +<!-- +/*************************************************************************************************************************** + * 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. + * + ***************************************************************************************************************************/ + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../../../../javadoc.css"); + + /* For viewing in REST interface */ + @IMPORT url("../htdocs/javadoc.css"); + body { + margin: 20px; + } + </style> + <script> + /* Replace all @code and @link tags. */ + window.onload = function() { + document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>'); + document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>'); + } + </script> +</head> +<body> +<p>HTTP Response handlers</p> +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/vars/LocalizationVar.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/vars/LocalizationVar.java b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/LocalizationVar.java new file mode 100644 index 0000000..10161cd --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/LocalizationVar.java @@ -0,0 +1,54 @@ +// *************************************************************************************************************************** +// * 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.vars; + +import java.util.*; + +import org.apache.juneau.rest.*; +import org.apache.juneau.svl.*; + +/** + * Localized string variable resolver. + * <p> + * The format for this var is <js>"$L{key}"</js> or <js>"$L{key,args...}"</js>. + * <p> + * This variable resolver requires that a {@link RestRequest} object be set as a context object on the resolver or a + * session object on the resolver session. + * <p> + * Values are pulled from the {@link RestRequest#getMessage(String,Object[])} method. + * These in turn are pulled from the resource bundle associated with the servlet class where the request was made. + * <p> + * Since this is a {@link SimpleVar}, any variables contained in the result will be recursively resolved. + * Likewise, if the arguments contain any variables, those will be resolved before they are passed to this var. + * + * @see org.apache.juneau.svl + */ +public class LocalizationVar extends MultipartVar { + + /** + * Constructor. + */ + public LocalizationVar() { + super("L"); + } + + @Override /* Parameter */ + public String resolve(VarResolverSession session, String[] args) { + if (args.length > 0) { + String key = args[0]; + String[] a = (args.length > 1) ? Arrays.copyOfRange(args, 1, args.length) : new String[0]; + return session.getSessionObject(RestRequest.class, RequestVar.SESSION_req).getMessage(key, (Object[])a); + } + return ""; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/vars/RequestVar.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/vars/RequestVar.java b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/RequestVar.java new file mode 100644 index 0000000..5d3c31d --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/RequestVar.java @@ -0,0 +1,123 @@ +// *************************************************************************************************************************** +// * 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.vars; + +import org.apache.juneau.internal.*; +import org.apache.juneau.rest.*; +import org.apache.juneau.svl.*; + +/** + * Request attribute variable resolver. + * <p> + * The format for this var is <js>"$R{key}"</js>. + * The possible values are: + * <ul> + * <li><code>$R{contextPath}</code> - Value returned by {@link RestRequest#getContextPath()}. + * <li><code>$R{method}</code> - Value returned by {@link RestRequest#getMethod()}. + * <li><code>$R{methodDescription}</code> - Value returned by {@link RestRequest#getMethodDescription()}. + * <li><code>$R{pathInfo}</code> - Value returned by {@link RestRequest#getPathInfo()}. + * <li><code>$R{requestParentURI}</code> - Value returned by {@link RestRequest#getRequestParentURI()}. + * <li><code>$R{requestURI}</code> - Value returned by {@link RestRequest#getRequestURI()}. + * <li><code>$R{servletDescription}</code> - Value returned by {@link RestRequest#getServletDescription()}. + * <li><code>$R{servletTitle}</code> - Value returned by {@link RestRequest#getServletTitle()}. + * <li><code>$R{servletParentURI}</code> - Value returned by {@link RestRequest#getServletParentURI()}. + * <li><code>$R{servletPath}</code> - Value returned by {@link RestRequest#getServletPath()}. + * <li><code>$R{servletURI}</code> - Value returned by {@link RestRequest#getServletURI()}. + * <li><code>$R{trimmedRequestURI}</code> - Value returned by {@link RestRequest#getTrimmedRequestURI()}. + * </ul> + * <p> + * This variable resolver requires that a {@link RestRequest} object be set as a context object on the resolver or a + * session object on the resolver session. + * <p> + * Since this is a {@link SimpleVar}, any variables contained in the result will be recursively resolved. + * Likewise, if the arguments contain any variables, those will be resolved before they are passed to this var. + * + * @see org.apache.juneau.svl + */ +public class RequestVar extends SimpleVar { + + /** + * The name of the session or context object that identifies the {@link RestRequest} object. + */ + public static final String SESSION_req = "req"; + + /** + * Constructor. + */ + public RequestVar() { + super("R"); + } + + @Override /* Parameter */ + public String resolve(VarResolverSession session, String key) { + RestRequest req = session.getSessionObject(RestRequest.class, SESSION_req); + if (key.length() > 0) { + String k = key.toString(); + if (k.indexOf('.') != -1) { + String prefix = k.substring(0, k.indexOf('.')); + String remainder = k.substring(k.indexOf('.')+1); + if ("path".equals(prefix)) + return req.getPathParameter(remainder); + if ("query".equals(prefix)) + return req.getQueryParameter(remainder); + if ("formData".equals(prefix)) + return req.getFormDataParameter(remainder); + if ("header".equals(prefix)) + return req.getHeader(remainder); + if ("attribute".equals(prefix)) + return StringUtils.toString(req.getAttribute(remainder)); + } + char c = key.charAt(0); + if (c == 'c') { + if (key.equals("contextPath")) + return req.getContextPath(); + } else if (c == 'm') { + if (key.equals("method")) + return req.getMethod(); + if (key.equals("methodSummary")) + return req.getMethodSummary(); + if (key.equals("methodDescription")) + return req.getMethodDescription(); + } else if (c == 'p') { + if (key.equals("pathInfo")) + return req.getPathInfo(); + } else if (c == 'r') { + if (key.equals("requestURI")) + return req.getRequestURI(); + if (key.equals("relativeServletURI")) + return req.getRelativeServletURI(); + if (key.equals("requestParentURI")) + return req.getRequestParentURI(); + } else if (c == 's') { + if (key.equals("servletPath")) + return req.getServletPath(); + if (key.equals("servletURI")) + return req.getServletURI(); + if (key.equals("servletParentURI")) + return req.getServletParentURI(); + if (key.equals("servletTitle")) + return req.getServletTitle(); + if (key.equals("servletDescription")) + return req.getServletDescription(); + } else if (c == 't') { + if (key.equals("trimmedRequestURI")) + return req.getTrimmedRequestURI(); + } + Object o = req.getProperties().get(key); + if (o != null) + return o.toString(); + return req.getPathParameter(key); + } + return null; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/vars/SerializedRequestAttrVar.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/vars/SerializedRequestAttrVar.java b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/SerializedRequestAttrVar.java new file mode 100644 index 0000000..f1b750d --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/SerializedRequestAttrVar.java @@ -0,0 +1,64 @@ +// *************************************************************************************************************************** +// * 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.vars; + +import java.io.*; + +import org.apache.juneau.internal.*; +import org.apache.juneau.rest.*; +import org.apache.juneau.serializer.*; +import org.apache.juneau.svl.*; + +/** + * Serialized request attribute variable resolver. + * <p> + * The format for this var is <js>"$SA{contentType,key}"</js> or <js>"$SA{contentType,key,defaultValue}"</js>. + * <p> + * This variable resolver requires that a {@link RestRequest} object be set as a context object on the resolver or a + * session object on the resolver session. + * <p> + * Since this is a {@link SimpleVar}, any variables contained in the result will be recursively resolved. + * Likewise, if the arguments contain any variables, those will be resolved before they are passed to this var. + * + * @see org.apache.juneau.svl + */ +public class SerializedRequestAttrVar extends StreamedVar { + + /** + * Constructor. + */ + public SerializedRequestAttrVar() { + super("SA"); + } + + @Override /* Parameter */ + public void resolveTo(VarResolverSession session, Writer w, String key) { + try { + int i = key.indexOf(','); + if (i == -1) + throw new RuntimeException("Invalid format for $SA var. Must be of the format $SA{contentType,key[,defaultValue]}"); + String[] s2 = StringUtils.split(key, ','); + RestRequest req = session.getSessionObject(RestRequest.class, RequestVar.SESSION_req); + if (req != null) { + Object o = req.getAttribute(key); + if (o == null) + o = key; + Serializer s = req.getSerializerGroup().getSerializer(s2[0]); + if (s != null) + s.serialize(w, o); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/vars/ServletInitParamVar.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/vars/ServletInitParamVar.java b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/ServletInitParamVar.java new file mode 100644 index 0000000..f24cd98 --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/ServletInitParamVar.java @@ -0,0 +1,46 @@ +// *************************************************************************************************************************** +// * 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.vars; + +import org.apache.juneau.rest.*; +import org.apache.juneau.svl.*; + +/** + * Servlet init parameter variable resolver. + * <p> + * The format for this var is <js>"$I{key}"</js> or <js>"$I{key,defaultValue}"</js>. + * <p> + * This variable resolver requires that a {@link RestRequest} object be set as a context object on the resolver or a + * session object on the resolver session. + * <p> + * Values are pulled from the {@link RestServlet#getInitParameter(String)} method. + * <p> + * Since this is a {@link SimpleVar}, any variables contained in the result will be recursively resolved. + * Likewise, if the arguments contain any variables, those will be resolved before they are passed to this var. + * + * @see org.apache.juneau.svl + */ +public class ServletInitParamVar extends DefaultingVar { + + /** + * Constructor. + */ + public ServletInitParamVar() { + super("I"); + } + + @Override /* Parameter */ + public String resolve(VarResolverSession session, String key) { + return session.getSessionObject(RestRequest.class, RequestVar.SESSION_req).getServlet().getServletConfig().getInitParameter(key); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/vars/UrlEncodeVar.java ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/vars/UrlEncodeVar.java b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/UrlEncodeVar.java new file mode 100644 index 0000000..d3e85e2 --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/UrlEncodeVar.java @@ -0,0 +1,43 @@ +// *************************************************************************************************************************** +// * 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.vars; + +import org.apache.juneau.rest.*; +import org.apache.juneau.svl.*; + +/** + * URL-encoding variable resolver. + * <p> + * The format for this var is <js>"$UE{innerValue}"</js>. + * <p> + * This variable takes the contents inside the variable and replaces it with a value returned by calling {@link RestUtils#encode(String)}). + * <p> + * Since this is a {@link SimpleVar}, any variables contained in the result will be recursively resolved. + * Likewise, if the arguments contain any variables, those will be resolved before they are passed to this var. + * + * @see org.apache.juneau.svl + */ +public class UrlEncodeVar extends SimpleVar { + + /** + * Constructor. + */ + public UrlEncodeVar() { + super("UE"); + } + + @Override /* Parameter */ + public String resolve(VarResolverSession session, String key) { + return RestUtils.encode(key); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/java/org/apache/juneau/rest/vars/package.html ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/vars/package.html b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/package.html new file mode 100644 index 0000000..ac4588d --- /dev/null +++ b/juneau-rest/src/main/java/org/apache/juneau/rest/vars/package.html @@ -0,0 +1,41 @@ +<!DOCTYPE HTML> +<!-- +/*************************************************************************************************************************** + * 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. + * + ***************************************************************************************************************************/ + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../../../../javadoc.css"); + + /* For viewing in REST interface */ + @IMPORT url("../htdocs/javadoc.css"); + body { + margin: 20px; + } + </style> + <script> + /* Replace all @code and @link tags. */ + window.onload = function() { + document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>'); + document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>'); + } + </script> +</head> +<body> +<p>Predefined SVL variables</p> +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/91a388d0/juneau-rest/src/main/resources/org/apache/juneau/rest/htdocs/MethodExampleResource1.png ---------------------------------------------------------------------- diff --git a/juneau-rest/src/main/resources/org/apache/juneau/rest/htdocs/MethodExampleResource1.png b/juneau-rest/src/main/resources/org/apache/juneau/rest/htdocs/MethodExampleResource1.png new file mode 100644 index 0000000..1ca74fe Binary files /dev/null and b/juneau-rest/src/main/resources/org/apache/juneau/rest/htdocs/MethodExampleResource1.png differ
