blob: 8e0f468e99381a266d80742081dd35730056f324 [file] [log] [blame]
<!--
/***************************************************************************************************************************
* 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.
***************************************************************************************************************************/
-->
Dual-purpose (end-to-end) interfaces
<p>
A common coding practice is to use the same Java interface to define both your server and client side REST interfaces.
The advantage to this approach is that changes that you make to your REST interface can be reflected in both places
at the same time, reducing the chances for compatibility mistakes.
</p>
<p>
What makes this possible is that method-level annotations such as <ja>@RestMethod</ja> and parameter-level annotations
such as <ja>@Query</ja> are inherited from parent classes.
This normally isn't possible, but the framework will spider up the parent hierarchy of classes to find method and parameter level
annotations defined on overridden methods.
</p>
<p>
The general approach is to define your {@link oajr.client.remote.RemoteResource @RemoteResource}-annotated interface first.
The following example is pulled from the PetStore app:
</p>
<p class='bpcode w800'>
<ja>@RemoteResource</ja>(path=<js>"/petstore"</js>)
<jk>public interface</jk> PetStore {
<ja>@RemoteMethod</ja>(method=<jsf>GET</jsf>, path=<js>"/pet"</js>)
<jk>public</jk> Collection&lt;Pet&gt; getPets() <jk>throws</jk> NotAcceptable;
<ja>@RemoteMethod</ja>(method=<jsf>DELETE</jsf>, path=<js>"/pet/{petId}"</js>)
<jk>public</jk> Ok deletePet(
<ja>@Header</ja>(
name=<js>"api_key"</js>,
description=<js>"Security API key"</js>,
required=<jk>true</jk>,
example=<js>"foobar"</js>
)
String apiKey,
<ja>@Path</ja>(
name=<js>"petId"</js>,
description=<js>"Pet id to delete"</js>,
example=<js>"123"</js>
)
<jk>long</jk> petId
) <jk>throws</jk> IdNotFound, NotAcceptable;
...
</p>
<p>
Next you define the implementation of your interface as a normal Juneau REST resource:
</p>
<p class='bpcode w800'>
<ja>@Rest</ja>(
path=<js>"/petstore"</js>,
title=<js>"Petstore application"</js>,
...
)
<jk>public class</jk> PetStoreResource <jk>extends</jk> BasicRestJena <jk>implements</jk> PetStore {
...
<ja>@Override</ja> <jc>/* PetStore */</jc>
<ja>@RestMethod</ja>(
name=<jsm>GET</jsm>,
path=<js>"/pet"</js>,
summary=<js>"All pets in the store"</js>,
...
)
<jk>public</jk> Collection&lt;Pet&gt; getPets() <jk>throws</jk> NotAcceptable {
<jk>return</jk> <jsf>store</jsf>.getPets();
}
<ja>@Override</ja> <jc>/* PetStore */</jc>
<ja>@RestMethod</ja>(
name=<jsf>DELETE</jsf>,
path=<js>"/pet/{petId}"</js>,
summary=<js>"Deletes a pet"</js>,
...
)
<jk>public</jk> Ok deletePet(String apiKey, long petId) <jk>throws</jk> IdNotFound, NotAcceptable {
<jsf>store</jsf>.removePet(petId);
<jk>return</jk> <jsf>OK</jsf>;
}
</p>
<p>
Then use the interface as a remote resource like so:
</p>
<p class='bpcode w800'>
<jk>try</jk> (RestClient rc = RestClient.<jsm>create</jsm>().json().rootUrl(<js>"http://localhost:10000"</js>).build()) {
PetStore ps = rc.getRemoteResource(PetStore.<jk>class</jk>);
<jk>for</jk> (Pet x : ps.getPets()) {
ps.deletePet(<js>"my-special-key"</js>, x.getId());
System.<jsf>err</jsf>.println(<js>"Deleted pet: id="</js> + x.getId());
}
}
</p>
<p>
In the example above, we chose to add the <ja>@RestMethod</ja> annotation to the implementation class.
However, they could have been added to the interface instead.
</p>
<p>
Note how we didn't need to use the <ja>@Header</ja> and <ja>@Path</ja> annotations in our implementation since
the annotations were inherited from the interface.
</p>