// *************************************************************************************************************************** | |
// * 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.addressbook; | |
import static javax.servlet.http.HttpServletResponse.*; | |
import static org.apache.juneau.html.HtmlDocSerializer.*; | |
import static org.apache.juneau.jena.RdfCommon.*; | |
import static org.apache.juneau.jena.RdfSerializer.*; | |
import static org.apache.juneau.http.HttpMethodName.*; | |
import java.util.*; | |
import org.apache.juneau.*; | |
import org.apache.juneau.dto.LinkString; | |
import org.apache.juneau.dto.cognos.*; | |
import org.apache.juneau.encoders.*; | |
import org.apache.juneau.examples.addressbook.*; | |
import org.apache.juneau.examples.rest.*; | |
import org.apache.juneau.microservice.*; | |
import org.apache.juneau.rest.*; | |
import org.apache.juneau.rest.annotation.*; | |
import org.apache.juneau.rest.converters.*; | |
import org.apache.juneau.rest.widget.*; | |
import org.apache.juneau.transform.*; | |
import org.apache.juneau.utils.*; | |
/** | |
* Proof-of-concept resource that shows off the capabilities of working with POJO resources. | |
* <p> | |
* Consists of an in-memory address book repository wrapped in a REST API. | |
*/ | |
@RestResource( | |
path="/addressBook", | |
messages="nls/AddressBookResource", | |
htmldoc=@HtmlDoc( | |
// Widgets for $W variables. | |
widgets={ | |
PoweredByJuneau.class, | |
ContentTypeMenuItem.class, | |
QueryMenuItem.class, | |
StyleMenuItem.class | |
}, | |
// Links on the HTML rendition page. | |
// "request:/..." URIs are relative to the request URI. | |
// "servlet:/..." URIs are relative to the servlet URI. | |
// "$C{...}" variables are pulled from the config file. | |
navlinks={ | |
"up: request:/..", | |
"options: servlet:/?method=OPTIONS", | |
"$W{ContentTypeMenuItem}", | |
"$W{StyleMenuItem}", | |
"source: $C{Source/gitHub}/org/apache/juneau/examples/rest/addressbook/$R{servletClassSimple}.java" | |
}, | |
// Arbitrary HTML message on the left side of the page. | |
aside={ | |
"<div style='max-width:400px;min-width:200px'>", | |
" <p>Proof-of-concept resource that shows off the capabilities of working with POJO resources.</p>", | |
" <p>Provides examples of: </p>", | |
" <ul>", | |
" <li>XML and RDF namespaces", | |
" <li>Swagger documentation", | |
" <li>Widgets", | |
" </ul>", | |
"</div>" | |
}, | |
// Juneau icon added to footer. | |
footer="$W{PoweredByJuneau}" | |
), | |
// Allow INIT as a method parameter. | |
allowedMethodParams="*", | |
// Properties that get applied to all serializers and parsers. | |
properties={ | |
// Use single quotes. | |
@Property(name=SERIALIZER_quoteChar, value="'"), | |
// Make RDF/XML readable. | |
@Property(name=RDF_rdfxml_tab, value="5"), | |
// Make RDF parsable by adding a root node. | |
@Property(name=RDF_addRootProperty, value="true"), | |
// Make URIs absolute so that we can easily reference them on the client side. | |
@Property(name=SERIALIZER_uriResolution, value="ABSOLUTE"), | |
// Make the anchor text on URLs be just the path relative to the servlet. | |
@Property(name=HTML_uriAnchorText, value="SERVLET_RELATIVE") | |
}, | |
// Support GZIP encoding on Accept-Encoding header. | |
encoders=GzipEncoder.class, | |
// Swagger info. | |
swagger=@ResourceSwagger( | |
contact="{name:'John Smith',email:'john@smith.com'}", | |
license="{name:'Apache 2.0',url:'http://www.apache.org/licenses/LICENSE-2.0.html'}", | |
version="2.0", | |
termsOfService="You're on your own.", | |
tags="[{name:'Java',description:'Java utility',externalDocs:{description:'Home page',url:'http://juneau.apache.org'}}]", | |
externalDocs="{description:'Home page',url:'http://juneau.apache.org'}" | |
) | |
) | |
public class AddressBookResource extends BasicRestServletJena { | |
private static final long serialVersionUID = 1L; | |
// The in-memory address book | |
private AddressBook addressBook; | |
@Override /* Servlet */ | |
public void init() { | |
try { | |
// Create the address book | |
addressBook = new AddressBook(java.net.URI.create("servlet:/")); | |
// Initialize it with some contents. | |
addressBook.init(); | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
} | |
/** | |
* [GET /] | |
* Get root page. | |
*/ | |
@RestMethod(name=GET, path="/") | |
public LinkString[] getRoot() throws Exception { | |
return new LinkString[] { | |
new LinkString("people", "people"), | |
new LinkString("addresses", "addresses") | |
}; | |
} | |
/** | |
* [GET /people/*] | |
* Get all people in the address book. | |
* Traversable filtering enabled to allow nodes in returned POJO tree to be addressed. | |
* Introspectable filtering enabled to allow public methods on the returned object to be invoked. | |
*/ | |
@RestMethod(name=GET, path="/people/*", | |
converters={Traversable.class,Queryable.class,Introspectable.class}, | |
htmldoc=@HtmlDoc( | |
navlinks={ | |
"INHERIT", // Inherit links from class. | |
"[2]:$W{QueryMenuItem}" // Insert QUERY link in position 2. | |
} | |
) | |
) | |
public AddressBook getAllPeople() throws Exception { | |
return addressBook; | |
} | |
/** | |
* [GET /people/{id}/*] | |
* Get a single person by ID. | |
* Traversable filtering enabled to allow nodes in returned POJO tree to be addressed. | |
* Introspectable filtering enabled to allow public methods on the returned object to be invoked. | |
*/ | |
@RestMethod(name=GET, path="/people/{id}/*", | |
converters={Traversable.class,Introspectable.class} | |
) | |
public Person getPerson(@Path int id) throws Exception { | |
return findPerson(id); | |
} | |
/** | |
* [GET /addresses/*] | |
* Get all addresses in the address book. | |
*/ | |
@RestMethod(name=GET, path="/addresses/*", | |
converters={Traversable.class,Queryable.class}, | |
htmldoc=@HtmlDoc( | |
navlinks={ | |
"INHERIT", // Inherit links from class. | |
"[2]:$W{QueryMenuItem}" // Insert QUERY link in position 2. | |
} | |
) | |
) | |
public List<Address> getAllAddresses() throws Exception { | |
return addressBook.getAddresses(); | |
} | |
/** | |
* [GET /addresses/{id}/*] | |
* Get a single address by ID. | |
*/ | |
@RestMethod(name=GET, path="/addresses/{id}/*", | |
converters={Traversable.class} | |
) | |
public Address getAddress(@Path int id) throws Exception { | |
return findAddress(id); | |
} | |
/** | |
* [POST /people] | |
* Create a new Person bean. | |
*/ | |
@RestMethod(name=POST, path="/people", | |
guards=AdminGuard.class | |
) | |
public Redirect createPerson(@Body CreatePerson cp) throws Exception { | |
Person p = addressBook.createPerson(cp); | |
return new Redirect("people/{0}", p.id); | |
} | |
/** | |
* [POST /people/{id}/addresses] | |
* Create a new Address bean. | |
*/ | |
@RestMethod(name=POST, path="/people/{id}/addresses", | |
guards=AdminGuard.class | |
) | |
public Redirect createAddress(@Path int id, @Body CreateAddress ca) throws Exception { | |
Person p = findPerson(id); | |
Address a = p.createAddress(ca); | |
return new Redirect("addresses/{0}", a.id); | |
} | |
/** | |
* [DELETE /people/{id}] | |
* Delete a Person bean. | |
*/ | |
@RestMethod(name=DELETE, path="/people/{id}", | |
guards=AdminGuard.class | |
) | |
public String deletePerson(@Path int id) throws Exception { | |
addressBook.removePerson(id); | |
return "DELETE successful"; | |
} | |
/** | |
* [DELETE /addresses/{id}] | |
* Delete an Address bean. | |
*/ | |
@RestMethod(name=DELETE, path="/addresses/{id}", | |
guards=AdminGuard.class | |
) | |
public String deleteAddress(@Path int addressId) throws Exception { | |
Person p = addressBook.findPersonWithAddress(addressId); | |
if (p == null) | |
throw new RestException(SC_NOT_FOUND, "Person not found"); | |
Address a = findAddress(addressId); | |
p.addresses.remove(a); | |
return "DELETE successful"; | |
} | |
/** | |
* [PUT /people/{id}/*] | |
* Change property on Person bean. | |
*/ | |
@RestMethod(name=PUT, path="/people/{id}/*", | |
guards=AdminGuard.class | |
) | |
public String updatePerson(RequestBody body, @Path int id, @PathRemainder String remainder) throws Exception { | |
try { | |
Person p = findPerson(id); | |
PojoRest r = new PojoRest(p); | |
ClassMeta<?> cm = r.getClassMeta(remainder); | |
Object in = body.asType(cm); | |
r.put(remainder, in); | |
return "PUT successful"; | |
} catch (Exception e) { | |
throw new RestException(SC_BAD_REQUEST, "PUT unsuccessful").initCause(e); | |
} | |
} | |
/** | |
* [PUT /addresses/{id}/*] | |
* Change property on Address bean. | |
*/ | |
@RestMethod(name=PUT, path="/addresses/{id}/*", | |
guards=AdminGuard.class | |
) | |
public String updateAddress(RestRequest req, @Path int id, @PathRemainder String remainder) throws Exception { | |
try { | |
Address a = findAddress(id); | |
PojoRest r = new PojoRest(a); | |
ClassMeta<?> cm = r.getClassMeta(remainder); | |
Object in = req.getBody().asType(cm); | |
r.put(remainder, in); | |
return "PUT successful"; | |
} catch (Exception e) { | |
throw new RestException(SC_BAD_REQUEST, "PUT unsuccessful").initCause(e); | |
} | |
} | |
/** | |
* [INIT /] | |
* Reinitialize this resource. | |
*/ | |
@RestMethod(name="INIT", path="/", | |
guards=AdminGuard.class | |
) | |
public String doInit() throws Exception { | |
init(); | |
return "OK"; | |
} | |
/** | |
* [GET /cognos] | |
* Get data in Cognos/XML format | |
*/ | |
@RestMethod(name=GET, path="/cognos") | |
public DataSet getCognosData() throws Exception { | |
// The Cognos metadata | |
Column[] items = { | |
new Column("name", "xs:String", 255), | |
new Column("age", "xs:int"), | |
new Column("numAddresses", "xs:int") | |
.addPojoSwap( | |
new PojoSwap<Person,Integer>() { | |
@Override /* PojoSwap */ | |
public Integer swap(BeanSession session, Person p) { | |
return p.addresses.size(); | |
} | |
} | |
) | |
}; | |
return new DataSet(items, addressBook, this.getContext().getBeanContext().createSession()); | |
} | |
/** | |
* [PROXY /*] | |
* Return a proxy interface to IAddressBook. | |
*/ | |
@RestMethod(name=PROXY, path="/proxy/*") | |
public IAddressBook getProxy() { | |
return addressBook; | |
} | |
/** Convenience method - Find a person by ID */ | |
private Person findPerson(int id) throws RestException { | |
Person p = addressBook.findPerson(id); | |
if (p == null) | |
throw new RestException(SC_NOT_FOUND, "Person not found"); | |
return p; | |
} | |
/** Convenience method - Find an address by ID */ | |
private Address findAddress(int id) throws RestException { | |
Address a = addressBook.findAddress(id); | |
if (a == null) | |
throw new RestException(SC_NOT_FOUND, "Address not found"); | |
return a; | |
} | |
} | |