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 6aa81ab Swagger UI improvements. 6aa81ab is described below commit 6aa81abfc100bd624921267ba9e7bb54ce0472a8 Author: JamesBognar <jamesbog...@apache.org> AuthorDate: Fri Apr 13 19:19:50 2018 -0400 Swagger UI improvements. --- .../java/org/apache/juneau/html/BasicHtmlTest.java | 2 +- .../org/apache/juneau/utils/StringUtilsTest.java | 12 + .../java/org/apache/juneau/dto/LinkString.java | 43 +-- .../apache/juneau/dto/swagger/OperationMap.java | 31 +- .../org/apache/juneau/html/HtmlParserSession.java | 2 +- .../apache/juneau/html/HtmlSerializerSession.java | 2 +- .../apache/juneau/html/annotation/HtmlLink.java | 4 +- .../org/apache/juneau/internal/StringUtils.java | 17 ++ juneau-doc/src/main/javadoc/overview.html | 23 +- juneau-examples/juneau-examples-rest/examples.cfg | 2 +- .../juneau/examples/rest/DirectoryResource.java | 305 ------------------- .../examples/rest/PredefinedLabelsResource.java | 19 +- .../examples/rest/SystemPropertiesResource.java | 16 +- .../juneau/examples/rest/TempDirResource.java | 13 +- .../examples/rest/TestMultiPartFormPostsTest.java | 2 +- .../microservice/resources/DirectoryResource.java | 327 ++++++++------------- .../my-microservice.cfg | 2 +- .../org/apache/juneau/rest/BasicRestConfig.java | 2 +- .../java/org/apache/juneau/rest/RestRequest.java | 9 + ...ServletRoot.java => RedirectToServletRoot.java} | 6 +- .../apache/juneau/rest/vars/RequestPathVar.java | 2 + 21 files changed, 257 insertions(+), 584 deletions(-) diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/html/BasicHtmlTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/html/BasicHtmlTest.java index 10b9c5f..61d7f0d 100644 --- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/html/BasicHtmlTest.java +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/html/BasicHtmlTest.java @@ -2832,7 +2832,7 @@ public class BasicHtmlTest { } } - @HtmlLink(nameProperty="a",hrefProperty="b") + @HtmlLink(nameProperty="a",uriProperty="b") public static class LinkBean { public String a; public String b; diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java index 1b9f080..44eaa14 100755 --- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java +++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/StringUtilsTest.java @@ -875,4 +875,16 @@ public class StringUtilsTest { assertEquals("1: foo\n", getNumberedLines("foo")); assertEquals("1: foo\n2: bar\n", getNumberedLines("foo\nbar")); } + + //==================================================================================================== + // compare(String,String) + //==================================================================================================== + @Test + public void testCompare() throws Exception { + assertTrue(compare("a","b") < 0); + assertTrue(compare("b","a") > 0); + assertTrue(compare(null,"b") < 0); + assertTrue(compare("b",null) > 0); + assertTrue(compare(null,null) == 0); + } } diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/LinkString.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/LinkString.java index 0bb04d6..a91d85a 100644 --- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/LinkString.java +++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/LinkString.java @@ -16,6 +16,7 @@ import static org.apache.juneau.internal.StringUtils.*; import java.text.*; +import org.apache.juneau.annotation.*; import org.apache.juneau.html.*; import org.apache.juneau.html.annotation.*; import org.apache.juneau.httppart.*; @@ -34,9 +35,11 @@ import org.apache.juneau.utils.*; * When encountered by the {@link HtmlSerializer} class, this object gets converted to a hyperlink. * All other serializers simply convert it to a simple bean. */ -@HtmlLink(nameProperty = "name", hrefProperty = "href") +@HtmlLink +@Bean(fluentSetters=true) public class LinkString implements Comparable<LinkString> { - private String name, href; + private String name; + private java.net.URI uri; /** No-arg constructor. */ public LinkString() {} @@ -45,12 +48,12 @@ public class LinkString implements Comparable<LinkString> { * Constructor. * * @param name Corresponds to the text inside of the <xt><A></xt> element. - * @param href Corresponds to the value of the <xa>href</xa> attribute of the <xt><A></xt> element. - * @param hrefArgs Optional arguments for {@link MessageFormat} style arguments in the href. + * @param uri Corresponds to the value of the <xa>href</xa> attribute of the <xt><A></xt> element. + * @param uriArgs Optional arguments for {@link MessageFormat} style arguments in the href. */ - public LinkString(String name, String href, Object...hrefArgs) { - setName(name); - setHref(href, hrefArgs); + public LinkString(String name, String uri, Object...uriArgs) { + name(name); + uri(uri, uriArgs); } @@ -76,48 +79,48 @@ public class LinkString implements Comparable<LinkString> { * @param name The new value for the <property>name</property> property on this bean. * @return This object (for method chaining). */ - public LinkString setName(String name) { + public LinkString name(String name) { this.name = name; return this; } /** - * Bean property getter: <property>href</property>. + * Bean property getter: <property>uri</property>. * * <p> * Corresponds to the value of the <xa>href</xa> attribute of the <xt><A></xt> element. * * @return The value of the <property>href</property> property on this bean, or <jk>null</jk> if it is not set. */ - public String getHref() { - return href; + public java.net.URI getUri() { + return uri; } /** - * Bean property setter: <property>href</property>. + * Bean property setter: <property>uri</property>. * - * @param href The new value for the <property>href</property> property on this bean. + * @param uri The new value for the <property>href</property> property on this bean. * @return This object (for method chaining). */ - public LinkString setHref(String href) { - setHref(href, new Object[0]); + public LinkString uri(String uri) { + uri(uri, new Object[0]); return this; } /** - * Bean property setter: <property>href</property>. + * Bean property setter: <property>uri</property>. * * <p> - * Same as {@link #setHref(String)} except allows for {@link MessageFormat} style arguments. + * Same as {@link #uri(String)} except allows for {@link MessageFormat} style arguments. * - * @param href The new href. + * @param uri The new href. * @param args Optional {@link MessageFormat}-style arguments. * @return This object (for method chaining). */ - public LinkString setHref(String href, Object...args) { + public LinkString uri(String uri, Object...args) { for (int i = 0; i < args.length; i++) args[i] = SimpleUonPartSerializer.DEFAULT.serialize(HttpPartType.PATH, args[i]); - this.href = format(href, args); + this.uri = java.net.URI.create(format(uri, args)); return this; } diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/OperationMap.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/OperationMap.java index 8221758..017d0c1 100644 --- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/OperationMap.java +++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/OperationMap.java @@ -16,6 +16,7 @@ import static org.apache.juneau.internal.StringUtils.*; import java.util.*; +import org.apache.juneau.internal.*; import org.apache.juneau.utils.*; /** @@ -38,24 +39,24 @@ public class OperationMap extends TreeMap<String,Operation> { private static final long serialVersionUID = 1L; private static final Comparator<String> OP_SORTER = new Comparator<String>() { - private final Map<String,Integer> methods = new AMap<String,Integer>() - .append("get",7) - .append("put",6) - .append("post",5) - .append("delete",4) - .append("options",3) - .append("head",2) - .append("patch",1); + private final Map<String,String> methods = new AMap<String,String>() + .append("get","0") + .append("put","1") + .append("post","2") + .append("delete","3") + .append("options","4") + .append("head","5") + .append("patch","6"); @Override public int compare(String o1, String o2) { - Integer i1 = methods.get(o1); - Integer i2 = methods.get(o2); - if (i1 == null) - i1 = 0; - if (i2 == null) - i2 = 0; - return i2.compareTo(i1); + String s1 = methods.get(o1); + String s2 = methods.get(o2); + if (s1 == null) + s1 = o1; + if (s2 == null) + s2 = o2; + return StringUtils.compare(s1, s2); } }; diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java index 993b603..72b3be3 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlParserSession.java @@ -315,7 +315,7 @@ public final class HtmlParserSession extends XmlParserSession { if (beanClass.isAnnotationPresent(HtmlLink.class)) { HtmlLink h = beanClass.getAnnotation(HtmlLink.class); BeanMap<T> m = newBeanMap(beanClass); - m.put(h.hrefProperty(), href); + m.put(h.uriProperty(), href); m.put(h.nameProperty(), name); return m.getBean(); } diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java index 5bb1124..94871d3 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java @@ -378,7 +378,7 @@ public class HtmlSerializerSession extends XmlSerializerSession { Class<?> c = o.getClass(); if (c.isAnnotationPresent(HtmlLink.class)) { HtmlLink h = o.getClass().getAnnotation(HtmlLink.class); - Object urlProp = m.get(h.hrefProperty()); + Object urlProp = m.get(h.uriProperty()); Object nameProp = m.get(h.nameProperty()); out.oTag("a").attrUri("href", urlProp).append('>').text(nameProp).eTag("a"); cr = CR_MIXED; diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/HtmlLink.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/HtmlLink.java index 735e970..7a4873c 100644 --- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/HtmlLink.java +++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/html/annotation/HtmlLink.java @@ -44,10 +44,10 @@ public @interface HtmlLink { /** * The bean property whose value becomes the name in the hyperlink. */ - String nameProperty() default ""; + String nameProperty() default "name"; /** * The bean property whose value becomes the url in the hyperlink. */ - String hrefProperty() default ""; + String uriProperty() default "uri"; } 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 58af833..694a01b 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 @@ -2149,4 +2149,21 @@ public final class StringUtils { sb.append(String.format("%0"+digits+"d", start++)).append(": ").append(l).append("\n"); return sb.toString(); } + + /** + * Compares two strings, but gracefully handles <jk>nulls</jk>. + * + * @param s1 The first string. + * @param s2 The second string. + * @return The same as {@link String#compareTo(String)}. + */ + public static int compare(String s1, String s2) { + if (s1 == null && s2 == null) + return 0; + if (s1 == null) + return Integer.MIN_VALUE; + if (s2 == null) + return Integer.MAX_VALUE; + return s1.compareTo(s2); + } } diff --git a/juneau-doc/src/main/javadoc/overview.html b/juneau-doc/src/main/javadoc/overview.html index e0f54db..7f3922d 100644 --- a/juneau-doc/src/main/javadoc/overview.html +++ b/juneau-doc/src/main/javadoc/overview.html @@ -15443,14 +15443,23 @@ } </p> <p> - This example renders the following consisting of a list of hyperlinks: + The {@link org.apache.juneau.dto.LinkString Action} bean is a predefined <ja>@HtmlLink</ja> bean provided + to simplify specifying actions. + <br>The following is equivalent to above. + </p> + <p class='bcode w800'> + <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/htmlLinks"</js>) + <jk>public</jk> Action[] htmlLinks() { + <jk>return new</jk> Action[] { + <jk>new</jk> Action(<js>"apache"</js>, <js>"http://apache.org"</js>), + <jk>new</jk> Action(<js>"juneau"</js>, <js>"http://juneau.apache.org"</js>) + }; + } + </p> + <p> + Both examples render the following consisting of a list of hyperlinks: </p> <img class='bordered' src='doc-files/juneau-rest-server.PredefinedLabelBeans.3.png' style='width:92px'/> - - <h5 class='section'>See Also:</h5> - <ul> - <li class='jc'>{@link org.apache.juneau.dto.LinkString} - </ul> </div> <!-- ======================================================================================================== --> @@ -21392,7 +21401,7 @@ <li class='jc'>{@link org.apache.juneau.rest.helper.ReaderResource} <li class='jc'>{@link org.apache.juneau.rest.helper.ReaderResourceBuilder} <li class='jc'>{@link org.apache.juneau.rest.helper.Redirect} - <li class='jc'>{@link org.apache.juneau.rest.helper.RedirectServletRoot} + <li class='jc'>{@link org.apache.juneau.rest.helper.RedirectToServletRoot} <li class='jc'>{@link org.apache.juneau.rest.helper.ResourceDescription} <li class='jc'>{@link org.apache.juneau.rest.helper.StreamResource} <li class='jc'>{@link org.apache.juneau.rest.helper.StreamResourceBuilder} diff --git a/juneau-examples/juneau-examples-rest/examples.cfg b/juneau-examples/juneau-examples-rest/examples.cfg index 5336293..e869a35 100755 --- a/juneau-examples/juneau-examples-rest/examples.cfg +++ b/juneau-examples/juneau-examples-rest/examples.cfg @@ -55,7 +55,7 @@ headerLink = http://juneau.apache.org footerIcon = servlet:/htdocs/images/asf.png footerLink = http://www.apache.org -icon = $C{REST/headerIcon} +favicon = $C{REST/headerIcon} header = <a href='$U{$C{REST/headerLink}}'><img src='$U{$C{REST/headerIcon}}' style='position:absolute;top:5;right:5;background-color:transparent;height:30px'/></a> footer = <a href='$U{$C{REST/footerLink}}'><img style='float:right;padding-right:20px;height:32px' src='$U{$C{REST/footerIcon}}'> diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java deleted file mode 100644 index 069da14..0000000 --- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/DirectoryResource.java +++ /dev/null @@ -1,305 +0,0 @@ -// *************************************************************************************************************************** -// * 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.examples.rest; - -import static java.util.logging.Level.*; -import static org.apache.juneau.html.HtmlSerializer.*; -import static org.apache.juneau.http.HttpMethodName.*; -import static org.apache.juneau.rest.annotation.HookEvent.*; - -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.logging.*; - -import org.apache.juneau.rest.*; -import org.apache.juneau.rest.annotation.*; -import org.apache.juneau.rest.converters.*; -import org.apache.juneau.rest.exception.*; -import org.apache.juneau.rest.helper.*; -import org.apache.juneau.rest.widget.*; -import org.apache.juneau.utils.*; - -/** - * Sample REST resource for exploring local file systems. - */ -@RestResource( - messages="nls/DirectoryResource", - htmldoc=@HtmlDoc( - widgets={ - ContentTypeMenuItem.class, - ThemeMenuItem.class - }, - navlinks={ - "up: request:/..", - "options: servlet:/?method=OPTIONS", - "$W{ContentTypeMenuItem}", - "$W{ThemeMenuItem}", - "source: $C{Source/gitHub}/org/apache/juneau/examples/rest/$R{servletClassSimple}.java" - } - ), - allowedMethodParams="*", - properties={ - @Property(name=HTML_uriAnchorText, value="PROPERTY_NAME"), - @Property(name="rootDir", value="$S{java.io.tmpdir}"), - @Property(name="allowViews", value="false"), - @Property(name="allowDeletes", value="false"), - @Property(name="allowPuts", value="false") - }, - swagger={ - "info: {", - "contact:{name:'Juneau Developer',email:'d...@juneau.apache.org'},", - "license:{name:'Apache 2.0',url:'http://www.apache.org/licenses/LICENSE-2.0.html'},", - "version:'2.0',", - "termsOfService:'You are on your own.'", - "},", - "externalDocs:{description:'Apache Juneau',url:'http://juneau.apache.org'}" - } -) -public class DirectoryResource extends BasicRestServlet { - private static final long serialVersionUID = 1L; - - private File rootDir; // The root directory - - // Settings enabled through servlet init parameters - boolean allowDeletes, allowPuts, allowViews; - - private static Logger logger = Logger.getLogger(DirectoryResource.class.getName()); - - @RestHook(INIT) - public void init(RestContextBuilder builder) throws Exception { - RestContextProperties p = builder.getProperties(); - rootDir = new File(p.getString("rootDir")); - allowViews = p.getBoolean("allowViews", false); - allowDeletes = p.getBoolean("allowDeletes", false); - allowPuts = p.getBoolean("allowPuts", false); - } - - /** Returns the root directory defined by the 'rootDir' init parameter */ - protected File getRootDir() { - if (rootDir == null) { - rootDir = new File(getProperties().getString("rootDir")); - if (! rootDir.exists()) - if (! rootDir.mkdirs()) - throw new RuntimeException("Could not create root dir"); - } - return rootDir; - } - - @RestMethod( - name=GET, - path="/*", - summary="Get file or directory information", - description="Returns information about a file or directory.", - converters={Queryable.class}, - swagger={ - "parameters:[", - Queryable.SWAGGER_PARAMS, - "]" - } - ) - public Object doGet(RestRequest req, RequestProperties properties) throws NotFound, InternalServerError { - - String pathInfo = req.getPathInfo(); - File f = pathInfo == null ? rootDir : new File(rootDir.getAbsolutePath() + pathInfo); - - if (!f.exists()) - throw new NotFound("File not found"); - - properties.put("path", f.getAbsolutePath()); - - try { - if (f.isDirectory()) { - List<FileResource> l = new LinkedList<>(); - File[] lfc = f.listFiles(); - if (lfc != null) { - for (File fc : lfc) { - URL fUrl = new URL(req.getRequestURL().append("/").append(fc.getName()).toString()); - l.add(new FileResource(fc, fUrl)); - } - } - return l; - } - - return new FileResource(f, new URL(req.getRequestURL().toString())); - - } catch (MalformedURLException e) { - throw new InternalServerError(e); - } - } - - @RestMethod( - name=DELETE, - path="/*", - summary="Delete file", - description="Delete a file on the file system.", - guards=AdminGuard.class - ) - public Object doDelete(RestRequest req) throws MethodNotAllowed, Forbidden { - - if (! allowDeletes) - throw new MethodNotAllowed("DELETE not enabled"); - - File f = new File(rootDir.getAbsolutePath() + req.getPathInfo()); - deleteFile(f); - - if (req.getHeader("Accept").contains("text/html")) - return new Redirect(); - - return "File deleted"; - } - - @RestMethod( - name=PUT, - path="/*", - summary="Upload file", - description="Uploads a file to the file system.", - guards=AdminGuard.class - ) - public Object doPut(RestRequest req) throws MethodNotAllowed, InternalServerError, Forbidden { - - if (! allowPuts) - throw new MethodNotAllowed("PUT not enabled"); - - File f = new File(rootDir.getAbsolutePath() + req.getPathInfo()); - String parentSubPath = f.getParentFile().getAbsolutePath().substring(rootDir.getAbsolutePath().length()); - - try (InputStream is = req.getInputStream(); OutputStream os = new BufferedOutputStream(new FileOutputStream(f))) { - IOPipe.create(is, os).run(); - } catch (IOException e) { - throw new InternalServerError(e); - } - - if (req.getContentType().contains("html")) - return new Redirect(parentSubPath); - - return "File added"; - } - - /** VIEW request handler (overloaded GET for viewing file contents) */ - @SuppressWarnings("resource") - @RestMethod( - name="VIEW", - path="/*", - summary="View file", - description="Views the contents of a file as plain text." - ) - public void doView(RestRequest req, RestResponse res) throws MethodNotAllowed, NotFound { - - if (! allowViews) - throw new MethodNotAllowed("VIEW not enabled"); - - File f = new File(rootDir.getAbsolutePath() + req.getPathInfo()); - - if (f.isDirectory()) - throw new MethodNotAllowed("VIEW not available on directories"); - - try { - res.setOutput(new FileReader(f)).setContentType("text/plain"); - } catch (FileNotFoundException e) { - throw new NotFound("File not found"); - } - } - - /** DOWNLOAD request handler (overloaded GET for downloading file contents) */ - @SuppressWarnings("resource") - @RestMethod( - name="DOWNLOAD", - path="/*", - summary="Download file", - description="Download the contents of a file as an octet stream." - ) - public void doDownload(RestRequest req, RestResponse res) throws MethodNotAllowed, NotFound { - - if (! allowViews) - throw new MethodNotAllowed("DOWNLOAD not enabled"); - - File f = new File(rootDir.getAbsolutePath() + req.getPathInfo()); - - if (f.isDirectory()) - throw new MethodNotAllowed("DOWNLOAD not available on directories"); - - try { - res.setOutput(new FileReader(f)).setContentType("application"); - } catch (FileNotFoundException e) { - throw new NotFound("File not found"); - } - } - - /** File POJO */ - public class FileResource { - private File f; - private URL url; - - /** Constructor */ - public FileResource(File f, URL url) { - this.f = f; - this.url = url; - } - - // Bean property getters - - public URL getUrl() { - return url; - } - - public String getType() { - return (f.isDirectory() ? "dir" : "file"); - } - - public String getName() { - return f.getName(); - } - - public long getSize() { - return f.length(); - } - - public Date getLastModified() { - return new Date(f.lastModified()); - } - - public URL getView() throws Exception { - if (allowViews && f.canRead() && ! f.isDirectory()) - return new URL(url + "?method=VIEW"); - return null; - } - - public URL getDownload() throws Exception { - if (allowViews && f.canRead() && ! f.isDirectory()) - return new URL(url + "?method=DOWNLOAD"); - return null; - } - - public URL getDelete() throws Exception { - if (allowDeletes && f.canWrite()) - return new URL(url + "?method=DELETE"); - return null; - } - } - - /** Utility method */ - private void deleteFile(File f) { - try { - if (f.isDirectory()) { - File[] lfc = f.listFiles(); - if (lfc != null) - for (File fc : lfc) - deleteFile(fc); - } - f.delete(); - } catch (Exception e) { - logger.log(WARNING, "Cannot delete file '" + f.getAbsolutePath() + "'", e); - } - } -} diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PredefinedLabelsResource.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PredefinedLabelsResource.java index af80312..da0befb 100644 --- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PredefinedLabelsResource.java +++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/PredefinedLabelsResource.java @@ -14,8 +14,8 @@ package org.apache.juneau.examples.rest; import static org.apache.juneau.http.HttpMethodName.*; +import org.apache.juneau.dto.*; import org.apache.juneau.examples.addressbook.*; -import org.apache.juneau.html.annotation.*; import org.apache.juneau.rest.*; import org.apache.juneau.rest.annotation.*; import org.apache.juneau.rest.helper.*; @@ -68,19 +68,10 @@ public class PredefinedLabelsResource extends BasicRestServlet { } @RestMethod(name=GET, path="/htmlLinks") - public ALink[] htmlLinks() throws Exception { - return new ALink[] { - new ALink("apache", "http://apache.org"), - new ALink("juneau", "http://juneau.apache.org") + public LinkString[] htmlLinks() throws Exception { + return new LinkString[] { + new LinkString("apache", "http://apache.org"), + new LinkString("juneau", "http://juneau.apache.org") }; } - - @HtmlLink(nameProperty="n", hrefProperty="l") - public static class ALink { - public String n, l; - public ALink(String n, String l) { - this.n = n; - this.l = l; - } - } } diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SystemPropertiesResource.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SystemPropertiesResource.java index f381e63..591d042 100644 --- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SystemPropertiesResource.java +++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/SystemPropertiesResource.java @@ -134,13 +134,13 @@ public class SystemPropertiesResource extends BasicRestServlet { description="Sets a new value for the specified system property.", guards=AdminGuard.class ) - public RedirectServletRoot setSystemProperty( + public RedirectToServletRoot setSystemProperty( @Path(description="The system property name") String propertyName, @Body(description="The new system property value") String value ) throws UserNotAdminException, NotAcceptable, UnsupportedMediaType { System.setProperty(propertyName, value); - return RedirectServletRoot.INSTANCE; + return RedirectToServletRoot.INSTANCE; } @RestMethod( @@ -149,12 +149,12 @@ public class SystemPropertiesResource extends BasicRestServlet { description="Takes in a map of key/value pairs and creates a set of new system properties.", guards=AdminGuard.class ) - public RedirectServletRoot setSystemProperties( + public RedirectToServletRoot setSystemProperties( @Body(description="The new system property values", example="{key1:'val1',key2:123}") java.util.Properties newProperties ) throws UserNotAdminException, NotAcceptable, UnsupportedMediaType { System.setProperties(newProperties); - return RedirectServletRoot.INSTANCE; + return RedirectToServletRoot.INSTANCE; } @RestMethod( @@ -163,12 +163,12 @@ public class SystemPropertiesResource extends BasicRestServlet { description="Deletes the specified system property.", guards=AdminGuard.class ) - public RedirectServletRoot deleteSystemProperty( + public RedirectToServletRoot deleteSystemProperty( @Path(description="The system property name", example="PATH") String propertyName ) throws UserNotAdminException, NotAcceptable { System.clearProperty(propertyName); - return RedirectServletRoot.INSTANCE; + return RedirectToServletRoot.INSTANCE; } @RestMethod( @@ -207,13 +207,13 @@ public class SystemPropertiesResource extends BasicRestServlet { description="Accepts a simple form post of a system property name/value pair.", guards=AdminGuard.class ) - public RedirectServletRoot formPagePost( + public RedirectToServletRoot formPagePost( @FormData("name") String name, @FormData("value") String value ) throws UserNotAdminException, NotAcceptable, UnsupportedMediaType { System.setProperty(name, value); - return RedirectServletRoot.INSTANCE; + return RedirectToServletRoot.INSTANCE; } diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/TempDirResource.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/TempDirResource.java index a3a3bf7..79d3fb3 100644 --- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/TempDirResource.java +++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/TempDirResource.java @@ -22,6 +22,7 @@ import org.apache.commons.fileupload.*; import org.apache.commons.fileupload.servlet.*; import org.apache.commons.io.*; import org.apache.juneau.dto.html5.*; +import org.apache.juneau.microservice.resources.*; import org.apache.juneau.rest.*; import org.apache.juneau.rest.annotation.*; import org.apache.juneau.rest.helper.*; @@ -56,10 +57,10 @@ import org.apache.juneau.utils.*; } ), properties={ - @Property(name="rootDir", value="$C{TempDirResource/dir,$S{java.io.tmpdir}}"), - @Property(name="allowViews", value="true"), - @Property(name="allowDeletes", value="true"), - @Property(name="allowPuts", value="false") + @Property(name="DirectoryResource.rootDir", value="$C{TempDirResource/dir,$S{java.io.tmpdir}}"), + @Property(name="DirectoryResource.allowViews", value="true"), + @Property(name="DirectoryResource.allowDeletes", value="true"), + @Property(name="DirectoryResource.allowPuts", value="false") }, swagger={ "info: {", @@ -115,7 +116,7 @@ public class TempDirResource extends DirectoryResource { }, matchers=TempDirResource.MultipartFormDataMatcher.class ) - public Redirect uploadFile(RestRequest req) throws Exception { + public RedirectToServletRoot uploadFile(RestRequest req) throws Exception { ServletFileUpload upload = new ServletFileUpload(); FileItemIterator iter = upload.getItemIterator(req); while (iter.hasNext()) { @@ -127,7 +128,7 @@ public class TempDirResource extends DirectoryResource { } } } - return new Redirect(); // Redirect to the servlet root. + return RedirectToServletRoot.INSTANCE; } /** Causes a 404 if POST isn't multipart/form-data */ diff --git a/juneau-examples/juneau-examples-rest/src/test/java/org/apache/juneau/examples/rest/TestMultiPartFormPostsTest.java b/juneau-examples/juneau-examples-rest/src/test/java/org/apache/juneau/examples/rest/TestMultiPartFormPostsTest.java index 55dc99b..7f43edd 100644 --- a/juneau-examples/juneau-examples-rest/src/test/java/org/apache/juneau/examples/rest/TestMultiPartFormPostsTest.java +++ b/juneau-examples/juneau-examples-rest/src/test/java/org/apache/juneau/examples/rest/TestMultiPartFormPostsTest.java @@ -41,7 +41,7 @@ public class TestMultiPartFormPostsTest extends RestTestcase { HttpEntity entity = MultipartEntityBuilder.create().addBinaryBody(f.getName(), f).build(); client.doPost(URL + "/upload", entity); - String downloaded = client.doGet(URL + '/' + f.getName() + "?method=VIEW").getResponseAsString(); + String downloaded = client.doGet(URL + "/file/" + f.getName() + "?method=VIEW").getResponseAsString(); assertEquals("test!", downloaded); } } \ No newline at end of file diff --git a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java index 679b50d..e5be768 100755 --- a/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java +++ b/juneau-microservice/juneau-microservice-server/src/main/java/org/apache/juneau/microservice/resources/DirectoryResource.java @@ -15,15 +15,16 @@ package org.apache.juneau.microservice.resources; import static java.util.logging.Level.*; import static org.apache.juneau.html.HtmlDocSerializer.*; import static org.apache.juneau.http.HttpMethodName.*; +import static org.apache.juneau.internal.StringUtils.*; +import static org.apache.juneau.rest.annotation.HookEvent.*; import java.io.*; -import java.net.*; +import java.net.URI; import java.util.*; import java.util.logging.*; -import javax.servlet.*; - import org.apache.juneau.annotation.*; +import org.apache.juneau.dto.*; import org.apache.juneau.rest.*; import org.apache.juneau.rest.annotation.*; import org.apache.juneau.rest.converters.*; @@ -60,7 +61,6 @@ import org.apache.juneau.utils.*; */ @RestResource( title="File System Explorer", - description="Contents of $RA{path}", messages="nls/DirectoryResource", htmldoc=@HtmlDoc( navlinks={ @@ -74,6 +74,7 @@ import org.apache.juneau.utils.*; @Property(name="DirectoryResource.rootDir", value="") } ) +@SuppressWarnings("javadoc") public class DirectoryResource extends BasicRestServlet { private static final long serialVersionUID = 1L; @@ -84,9 +85,9 @@ public class DirectoryResource extends BasicRestServlet { private static Logger logger = Logger.getLogger(DirectoryResource.class.getName()); - @Override /* Servlet */ - public void init() throws ServletException { - RestContextProperties p = getProperties(); + @RestHook(INIT) + public void init(RestContextBuilder b) throws Exception { + RestContextProperties p = b.getProperties(); rootDir = new File(p.getString("DirectoryResource.rootDir")); allowViews = p.getBoolean("DirectoryResource.allowViews", false); allowDeletes = p.getBoolean("DirectoryResource.allowDeletes", false); @@ -111,257 +112,189 @@ public class DirectoryResource extends BasicRestServlet { return rootDir; } - /** - * [GET /*] - On directories, returns a directory listing. On files, returns information about the file. - * - * @param req The HTTP request. - * @return Either a FileResource or list of FileResources depending on whether it's a - * file or directory. - * @throws NotFound If file was not found. - * @throws MalformedURLException - */ - @RestMethod(name=GET, path="/*", - description="On directories, returns a directory listing.\nOn files, returns information about the file.", + @RestMethod( + name=GET, + path="/*", + summary="View files on directory", + description="Returns a listing of all files in the specified directory.", converters={Queryable.class} ) - public Object doGet(RestRequest req) throws NotFound, MalformedURLException { - checkAccess(req); - - String pathInfo = req.getPathInfo(); - File f = pathInfo == null ? rootDir : new File(rootDir.getAbsolutePath() + pathInfo); - - if (!f.exists()) - throw new NotFound("File not found"); - - req.setAttribute("path", f.getAbsolutePath()); - - if (f.isDirectory()) { - List<FileResource> l = new LinkedList<>(); - File[] files = f.listFiles(); - if (files != null) { - for (File fc : files) { - URL fUrl = new URL(req.getRequestURL().append("/").append(fc.getName()).toString()); - l.add(new FileResource(fc, fUrl)); - } - } - return l; - } - - return new FileResource(f, new URL(req.getRequestURL().toString())); + public FileListing listFiles(@PathRemainder String path) throws NotFound, Exception { + FileListing l = new FileListing(); + for (File fc : getDir(path).listFiles()) + l.add(new FileResource(fc, (path != null ? (path + '/') : "") + urlEncode(fc.getName()))); + return l; + } + + @RestMethod( + name=GET, + path="/file/*", + summary="View information about file", + description="Returns detailed information about the specified file." + ) + public FileResource getFileInfo(@PathRemainder String path) throws NotFound, Exception { + return new FileResource(getFile(path), path); } - /** - * [DELETE /*] - Delete a file on the file system. - * - * @param req The HTTP request. - * @return The message <js>"File deleted"</js> if successful. - * @throws Exception If file could not be read or access was not granted. - */ - @RestMethod(name=DELETE, path="/*", + @RestMethod( + name=DELETE, + path="/file/*", + summary="Delete file", description="Delete a file on the file system." ) - public Object doDelete(RestRequest req) throws Exception { - checkAccess(req); - - File f = new File(rootDir.getAbsolutePath() + req.getPathInfo()); - deleteFile(f); - - if (req.getHeader("Accept").contains("text/html")) - return new Redirect(); - return "File deleted"; + public RedirectToRoot deleteFile(@PathRemainder String path) throws MethodNotAllowed { + if (! allowDeletes) + throw new MethodNotAllowed("DELETE not enabled"); + deleteFile(getFile(path)); + return new RedirectToRoot(); } - /** - * [PUT /*] - Add or overwrite a file on the file system. - * - * @param req The HTTP request. - * @return The message <js>"File added"</js> if successful. - * @throws InternalServerError If file could not be read or access was not granted. - */ - @RestMethod(name=PUT, path="/*", + @RestMethod( + name=PUT, + path="/file/*", + summary="Add or replace file", description="Add or overwrite a file on the file system." ) - public Object doPut(RestRequest req) throws InternalServerError { - checkAccess(req); + public RedirectToRoot updateFile( + @Body(schema="{type:'string',format:'binary'}") InputStream is, + @PathRemainder String path + ) throws InternalServerError { + + if (! allowPuts) + throw new MethodNotAllowed("PUT not enabled"); - File f = new File(rootDir.getAbsolutePath() + req.getPathInfo()); - String parentSubPath = f.getParentFile().getAbsolutePath().substring(rootDir.getAbsolutePath().length()); - try (InputStream is = req.getInputStream(); OutputStream os = new BufferedOutputStream(new FileOutputStream(f))) { + File f = getFile(path); + + try (OutputStream os = new BufferedOutputStream(new FileOutputStream(f))) { IOPipe.create(is, os).run(); } catch (IOException e) { throw new InternalServerError(e); } - if (req.getContentType().contains("html")) - return new Redirect(parentSubPath); - return "File added"; + + return new RedirectToRoot(); } - /** - * [VIEW /*] - View the contents of a file. - * - * <p> - * Applies to files only. - * - * @param req The HTTP request. - * @param res The HTTP response. - * @return A Reader containing the contents of the file. - * @throws NotFound File not found. - * @throws MethodNotAllowed Method not allowed on directories. - */ - @RestMethod(name="VIEW", path="/*", - description="View the contents of a file.\nApplies to files only." + @RestMethod( + name="VIEW", + path="/file/*", + summary="View contents of file", + description="View the contents of a file." ) - public Reader doView(RestRequest req, RestResponse res) throws NotFound, MethodNotAllowed { - checkAccess(req); - - File f = new File(rootDir.getAbsolutePath() + req.getPathInfo()); - - if (f.isDirectory()) - throw new MethodNotAllowed("VIEW not available on directories"); + public FileContents doView(RestResponse res, @PathRemainder String path) throws NotFound, MethodNotAllowed { + if (! allowViews) + throw new MethodNotAllowed("VIEW not enabled"); res.setContentType("text/plain"); try { - return new FileReader(f); + return new FileContents(getFile(path)); } catch (FileNotFoundException e) { throw new NotFound("File not found"); } } - - /** - * [DOWNLOAD /*] - Download the contents of a file. - * - * <p> - * Applies to files only. - * - * @param req The HTTP request. - * @param res The HTTP response. - * @return A Reader containing the contents of the file. - * @throws NotFound Found could not be found. - * @throws MethodNotAllowed Cannot call on a directory. - */ - @RestMethod(name="DOWNLOAD", path="/*", - description="Download the contents of a file.\nApplies to files only." + + @RestMethod( + name="DOWNLOAD", + path="/file/*", + summary="Download file", + description="Download the contents of a file" ) - public Reader doDownload(RestRequest req, RestResponse res) throws NotFound, MethodNotAllowed { - checkAccess(req); - - File f = new File(rootDir.getAbsolutePath() + req.getPathInfo()); - - if (f.isDirectory()) - throw new MethodNotAllowed("DOWNLOAD not available on directories"); + public FileContents doDownload(RestResponse res, @PathRemainder String path) throws NotFound, MethodNotAllowed { + if (! allowViews) + throw new MethodNotAllowed("DOWNLOAD not enabled"); - res.setContentType("application"); + res.setContentType("application/octet-stream"); try { - return new FileReader(f); + return new FileContents(getFile(path)); } catch (FileNotFoundException e) { throw new NotFound("File not found"); } } - /** - * Verify that the specified request is allowed. - * - * <p> - * Subclasses can override this method to provide customized behavior. - * Method should throw a {@link RestException} if the request should be disallowed. - * - * @param req The HTTP request. - * @throws MethodNotAllowed Thrown if specified method is not allowed. - */ - protected void checkAccess(RestRequest req) throws MethodNotAllowed { - String method = req.getMethod(); - if (method.equals("VIEW") && ! allowViews) - throw new MethodNotAllowed("VIEW not enabled"); - if (method.equals("PUT") && ! allowPuts) - throw new MethodNotAllowed("PUT not enabled"); - if (method.equals("DELETE") && ! allowDeletes) - throw new MethodNotAllowed("DELETE not enabled"); - if (method.equals("DOWNLOAD") && ! allowViews) - throw new MethodNotAllowed("DOWNLOAD not enabled"); + private File getFile(String path) throws NotFound { + File f = new File(rootDir.getAbsolutePath() + '/' + path); + if (f.exists() && f.isFile()) + return f; + throw new NotFound("File not found."); + } + + private File getDir(String path) throws NotFound { + if (path == null) + return rootDir; + File f = new File(rootDir.getAbsolutePath() + '/' + path); + if (f.exists() && f.isDirectory()) + return f; + throw new NotFound("Directory not found."); + } + + //----------------------------------------------------------------------------------------------------------------- + // Helper beans + //----------------------------------------------------------------------------------------------------------------- + + @SuppressWarnings("serial") + @ResponseInfo(description="Directory listing") + static class FileListing extends ArrayList<FileResource> {} + + @ResponseInfo(schema="{schema:{type:'string',format:'binary'}}", description="Contents of file") + static class FileContents extends FileReader { + public FileContents(File file) throws FileNotFoundException { + super(file); + } + } + + @ResponseInfo(description="Redirect to root page on success") + static class RedirectToRoot extends RedirectToServletRoot {} + + @ResponseInfo(description="File action") + public static class Action extends LinkString { + public Action(String name, String uri, Object...uriArgs) { + super(name, uri, uriArgs); + } } - /** File POJO */ + @ResponseInfo(description="File or directory details") public class FileResource { private File f; - private URL url; + private String path; - /** - * Constructor. - * - * @param f The file. - * @param url The URL of the file resource. - */ - public FileResource(File f, URL url) { + public FileResource(File f, String path) { this.f = f; - this.url = url; + this.path = path; } // Bean property getters - /** - * @return The URL of the file resource. - */ - public URL getUrl() { - return url; + public URI getUri() { + if (f.isDirectory()) + return URI.create("servlet:/"+path); + return URI.create("servlet:/file/"+path); } - /** - * @return The file type. - */ public String getType() { return (f.isDirectory() ? "dir" : "file"); } - /** - * @return The file name. - */ public String getName() { return f.getName(); } - /** - * @return The file size. - */ public long getSize() { - return f.length(); + return f.isDirectory() ? f.listFiles().length : f.length(); } - /** - * @return The file last modified timestamp. - */ @Swap(DateSwap.ISO8601DTP.class) public Date getLastModified() { return new Date(f.lastModified()); } - /** - * @return A hyperlink to view the contents of the file. - * @throws Exception If access is not allowed. - */ - public URL getView() throws Exception { - if (allowViews && f.canRead() && ! f.isDirectory()) - return new URL(url + "?method=VIEW"); - return null; - } - - /** - * @return A hyperlink to download the contents of the file. - * @throws Exception If access is not allowed. - */ - public URL getDownload() throws Exception { - if (allowViews && f.canRead() && ! f.isDirectory()) - return new URL(url + "?method=DOWNLOAD"); - return null; - } - - /** - * @return A hyperlink to delete the file. - * @throws Exception If access is not allowed. - */ - public URL getDelete() throws Exception { - if (allowDeletes && f.canWrite()) - return new URL(url + "?method=DELETE"); - return null; + public List<Action> getActions() throws Exception { + List<Action> l = new ArrayList<>(); + if (allowViews && f.canRead() && ! f.isDirectory()) { + l.add(new Action("view", getUri().toString() + "?method=VIEW")); + l.add(new Action("download", getUri().toString() + "?method=DOWNLOAD")); + } + if (allowDeletes && f.canWrite() && ! f.isDirectory()) + l.add(new Action("delete", getUri().toString() + "?method=DELETE")); + return l; } } diff --git a/juneau-microservice/juneau-microservice-template/my-microservice.cfg b/juneau-microservice/juneau-microservice-template/my-microservice.cfg index 3c8aea5..10c9306 100755 --- a/juneau-microservice/juneau-microservice-template/my-microservice.cfg +++ b/juneau-microservice/juneau-microservice-template/my-microservice.cfg @@ -55,7 +55,7 @@ headerLink = http://juneau.apache.org footerIcon = servlet:/htdocs/images/asf.png footerLink = http://www.apache.org -icon = $C{REST/headerIcon} +favicon = $C{REST/headerIcon} header = <a href='$U{$C{REST/headerLink}}'><img src='$U{$C{REST/headerIcon}}' style='position:absolute;top:5;right:5;background-color:transparent;height:30px'/></a> footer = <a href='$U{$C{REST/footerLink}}'><img style='float:right;padding-right:20px;height:32px' src='$U{$C{REST/footerIcon}}'> diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestConfig.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestConfig.java index fdfce10..0828de5 100644 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestConfig.java +++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestConfig.java @@ -75,7 +75,7 @@ import org.apache.juneau.xmlschema.*; }, stylesheet="$C{REST/theme,servlet:/htdocs/themes/devops.css}", head={ - "<link rel='icon' href='$U{$C{REST/icon}}'/>" + "<link rel='icon' href='$U{$C{REST/favicon}}'/>" }, footer="$C{REST/footer}" ), diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java index f992917..ca8e06b 100644 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java +++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java @@ -650,6 +650,15 @@ public final class RestRequest extends HttpServletRequestWrapper { return getPathMatch().get(name); } + /** + * Shortcut for calling <code>getPathMatch().getRemainder()</code>. + * + * @return The path remainder value, or <jk>null</jk> if not found. + */ + public String getPathRemainder() { + return getPathMatch().getRemainder(); + } + //-------------------------------------------------------------------------------- // Body methods //-------------------------------------------------------------------------------- diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/helper/RedirectServletRoot.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/helper/RedirectToServletRoot.java similarity index 91% rename from juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/helper/RedirectServletRoot.java rename to juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/helper/RedirectToServletRoot.java index 71f6f2a..b1acdeb 100644 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/helper/RedirectServletRoot.java +++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/helper/RedirectToServletRoot.java @@ -18,17 +18,17 @@ import org.apache.juneau.rest.annotation.*; * Convenience subclass of {@link Redirect} for redirecting a response to the servlet root. */ @ResponseInfo(description="Redirect to servlet root") -public class RedirectServletRoot extends Redirect { +public class RedirectToServletRoot extends Redirect { /** * Reusable instance. */ - public static final RedirectServletRoot INSTANCE = new RedirectServletRoot(); + public static final RedirectToServletRoot INSTANCE = new RedirectToServletRoot(); /** * Constructor. */ - public RedirectServletRoot() { + public RedirectToServletRoot() { super("servlet:/"); } } \ No newline at end of file diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestPathVar.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestPathVar.java index 508ef22..7c6cb4b 100644 --- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestPathVar.java +++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/vars/RequestPathVar.java @@ -77,6 +77,8 @@ public class RequestPathVar extends MultipartResolvingVar { @Override /* Parameter */ public String resolve(VarResolverSession session, String key) { RestRequest req = session.getSessionObject(RestRequest.class, SESSION_req); + if ("REMAINDER".equals(key)) + return req.getPathRemainder(); return req.getPath(key); } } \ No newline at end of file -- To stop receiving notification emails like this one, please contact jamesbog...@apache.org.