| <!-- |
| /*************************************************************************************************************************** |
| * 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. |
| ***************************************************************************************************************************/ |
| --> |
| |
| PhotosResource |
| |
| <p> |
| The <l>PhotosResource</l> class shows examples of the following: |
| </p> |
| <ul class='spaced-list'> |
| <li> |
| How to define custom serializers and parsers at the method level. |
| In this case, you define a serializer and parser to handle images. |
| </ul> |
| <p> |
| The resource consists of a simple registry of images with integer IDs. |
| </p> |
| <p class='bpcode w800'> |
| http://localhost:10000/photos |
| </p> |
| <img class='bordered w800' src='doc-files/juneau-examples-rest.PhotosResource.1.png'> |
| <p> |
| It is initialized with a single entry, which can be accessed through a GET request. |
| </p> |
| <p class='bpcode w800'> |
| http://localhost:10000/photos/cat |
| </p> |
| <img class='bordered w800' src='doc-files/juneau-examples-rest.PhotosResource.2.png'> |
| |
| <h5 class='figure'>PhotosResource.java</h5> |
| <p class='bpcode w800'> |
| <jd>/** |
| * Sample resource that allows images to be uploaded and retrieved. |
| */</jd> |
| <ja>@RestResource</ja>( |
| path=<js>"/photos"</js>, |
| messages=<js>"nls/PhotosResource"</js>, |
| title=<js>"Photo REST service"</js>, |
| description=<js>"Sample resource that allows images to be uploaded and retrieved."</js>, |
| htmldoc=<ja>@HtmlDoc</ja>( |
| navlinks={ |
| <js>"up: request:/.."</js>, |
| <js>"options: servlet:/?method=OPTIONS"</js>, |
| <js>"source: $C{Source/gitHub}/org/apache/juneau/examples/rest/$R{servletClassSimple}.java"</js> |
| }, |
| aside={ |
| <js>"<div style='max-width:400px;min-width:200px' class='text'>"</js>, |
| <js>" <p>Shows an example of using custom serializers and parsers to create REST interfaces over binary resources.</p>"</js>, |
| <js>" <p>In this case, our resources are marshalled jpeg and png binary streams and are stored in an in-memory 'database' (also known as a <code>TreeMap</code>).</p>"</js>, |
| <js>"</div>"</js> |
| } |
| ), |
| properties={ |
| <jc>// Make the anchor text on URLs be just the path relative to the servlet.</jc> |
| <ja>@Property</ja>(name=<jsf>HTML_uriAnchorText</jsf>, value=<js>"SERVLET_RELATIVE"</js>) |
| } |
| ) |
| <jk>public class</jk> PhotosResource <jk>extends</jk> BasicRestServlet { |
| |
| <jc>// Our cache of photos</jc> |
| <jk>private</jk> Map<String,Photo> <jf>photos</jf> = <jk>new</jk> TreeMap<>(); |
| |
| <ja>@Override</ja> <jc>/* Servlet */</jc> |
| <jk>public void</jk> init() { |
| <jk>try</jk> (InputStream is = getClass().getResourceAsStream(<js>"averycutecat.jpg"</js>)) { |
| BufferedImage image = ImageIO.<jsm>read</jsm>(is); |
| Photo photo = <jk>new</jk> Photo(<js>"cat"</js>, image); |
| <jf>photos</jf>.put(photo.<jf>id</jf>, photo); |
| } <jk>catch</jk> (IOException e) { |
| <jk>throw new</jk> RuntimeException(e); |
| } |
| } |
| |
| <jd>/** Bean class for storing photos */</jd> |
| <jk>public static class</jk> Photo { |
| <jk>private</jk> String <jf>id</jf>; |
| BufferedImage <jf>image</jf>; |
| |
| Photo(String id, BufferedImage image) { |
| <jk>this</jk>.<jf>id</jf> = id; |
| <jk>this</jk>.<jf>image</jf> = image; |
| } |
| |
| <jk>public</jk> URI getURI() <jk>throws</jk> URISyntaxException { |
| <jk>return new</jk> URI(<js>"servlet:/"</js> + <jf>id</jf>); |
| } |
| } |
| |
| <jd>/** GET request handler for list of all photos */</jd> |
| <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/"</js>, summary=<js>"Show the list of all currently loaded photos"</js>) |
| <jk>public</jk> Collection<Photo> getAllPhotos() <jk>throws</jk> Exception { |
| <jk>return</jk> <jf>photos</jf>.values(); |
| } |
| |
| <jd>/** GET request handler for single photo */</jd> |
| <ja>@RestMethod</ja>(name=<jsf>GET</jsf>, path=<js>"/{id}"</js>, serializers=ImageSerializer.<jk>class</jk>, summary=<js>"Get a photo by ID"</js>) |
| <jk>public</jk> BufferedImage getPhoto(<ja>@Path</ja>(<js>"id"</js>) String id) <jk>throws</jk> Exception { |
| Photo p = <jf>photos</jf>.get(id); |
| <jk>if</jk> (p == <jk>null</jk>) |
| <jk>throw new</jk> RestException(<jsf>SC_NOT_FOUND</jsf>, <js>"Photo not found"</js>); |
| <jk>return</jk> p.<jf>image</jf>; |
| } |
| |
| <jd>/** PUT request handler */</jd> |
| <ja>@RestMethod</ja>(name=<jsf>PUT</jsf>, path=<js>"/{id}"</js>, parsers=ImageParser.<jk>class</jk>, summary=<js>"Add or overwrite a photo"</js>) |
| <jk>public</jk> String addPhoto(<ja>@Path</ja>(<js>"id"</js>) String id, <ja>@Body</ja> BufferedImage image) <jk>throws</jk> Exception { |
| <jf>photos</jf>.put(id, <jk>new</jk> Photo(id, image)); |
| <jk>return</jk> <js>"OK"</js>; |
| } |
| |
| <jd>/** POST request handler */</jd> |
| <ja>@RestMethod</ja>(name=<jsf>POST</jsf>, path=<js>"/"</js>, parsers=ImageParser.<jk>class</jk>, summary=<js>"Add a photo"</js>) |
| <jk>public</jk> Photo setPhoto(<ja>@Body</ja> BufferedImage image) <jk>throws</jk> Exception { |
| Photo p = <jk>new</jk> Photo(UUID.<jsm>randomUUID</jsm>().toString(), image); |
| <jf>photos</jf>.put(p.<jf>id</jf>, p); |
| <jk>return</jk> p; |
| } |
| |
| <jd>/** DELETE request handler */</jd> |
| <ja>@RestMethod</ja>(name=<jsf>DELETE</jsf>, path=<js>"/{id}"</js>, summary=<js>"Delete a photo by ID"</js>) |
| <jk>public</jk> String deletePhoto(<ja>@Path</ja>(<js>"id"</js>) String id) <jk>throws</jk> Exception { |
| Photo p = <jf>photos</jf>.remove(id); |
| <jk>if</jk> (p == <jk>null</jk>) |
| <jk>throw new</jk> RestException(<jsf>SC_NOT_FOUND</jsf>, <js>"Photo not found"</js>); |
| <jk>return</jk> <js>"OK"</js>; |
| } |
| |
| <jd>/** Serializer for converting images to byte streams */</jd> |
| <jk>public static class</jk> ImageSerializer <jk>extends</jk> OutputStreamSerializer { |
| |
| <jd>/** |
| * Constructor. |
| * <ja>@param</ja> ps The property store containing all the settings for this object. |
| */</jd> |
| <jk>public</jk> ImageSerializer(PropertyStore ps) { |
| <jk>super</jk>(ps, <jk>null</jk>, <js>"image/png"</js>, <js>"image/jpeg"</js>); |
| } |
| |
| <ja>@Override</ja> <jc>/* Serializer */</jc> |
| <jk>public</jk> OutputStreamSerializerSession createSession(SerializerSessionArgs args) { |
| <jk>return new</jk> OutputStreamSerializerSession(args) { |
| |
| <ja>@Override</ja> <jc>/* SerializerSession */</jc> |
| <jk>protected void</jk> doSerialize(SerializerPipe out, Object o) <jk>throws</jk> Exception { |
| RenderedImage image = (RenderedImage)o; |
| String mediaType = getProperty(<js>"mediaType"</js>, String.<jk>class</jk>, (String)<jk>null</jk>); |
| ImageIO.<jsm>write</jsm>(image, mediaType.substring(mediaType.indexOf(<js>'/'</js>)+1), out.getOutputStream()); |
| } |
| }; |
| } |
| } |
| |
| <jd>/** Parser for converting byte streams to images */</jd> |
| <jk>public static class</jk> ImageParser <jk>extends</jk> InputStreamParser { |
| |
| <jd>/** |
| * Constructor. |
| * <ja>@param</ja> ps The property store containing all the settings for this object. |
| */</jd> |
| <jk>public</jk> ImageParser(PropertyStore ps) { |
| <jk>super</jk>(ps, <js>"image/png"</js>, <js>"image/jpeg"</js>); |
| } |
| |
| <ja>@Override</ja> <jc>/* Parser */</jc> |
| <jk>public</jk> InputStreamParserSession createSession(<jk>final</jk> ParserSessionArgs args) { |
| <jk>return new</jk> InputStreamParserSession(args) { |
| |
| <ja>@Override</ja> <jc>/* ParserSession */</jc> |
| <ja>@SuppressWarnings</ja>(<js>"unchecked"</js>) |
| <jk>protected</jk> <T> T doParse(ParserPipe pipe, ClassMeta<T> type) <jk>throws</jk> Exception { |
| <jk>return</jk> (T)ImageIO.<jsm>read</jsm>(pipe.getInputStream()); |
| } |
| }; |
| } |
| } |
| } |
| </p> |