| <?xml version="1.0"?> |
| <!-- |
| |
| Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved. |
| |
| This program and the accompanying materials are made available under the |
| terms of the Eclipse Public License v. 2.0, which is available at |
| http://www.eclipse.org/legal/epl-2.0. |
| |
| This Source Code may also be made available under the following Secondary |
| Licenses when the conditions for such availability set forth in the |
| Eclipse Public License v. 2.0 are satisfied: GNU General Public License, |
| version 2 with the GNU Classpath Exception, which is available at |
| https://www.gnu.org/software/classpath/license.html. |
| |
| SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 |
| |
| --> |
| |
| <!DOCTYPE chapter [ |
| <!ENTITY % ents SYSTEM "jersey.ent" > %ents; |
| <!ENTITY jersey.github.ef.example.link "<link xlink:href='&jersey.github.examples.uri;/entity-filtering'>Entity Filtering example</link>"> |
| <!ENTITY jersey.github.ef.security.example.link "<link xlink:href='&jersey.github.examples.uri;/entity-filtering-security'>Entity Filtering example (with security annotations)</link>"> |
| <!ENTITY jersey.github.ef.selectable.example.link "<link xlink:href='&jersey.github.examples.uri;/entity-filtering-selectable'>Entity Filtering example (based on dynamic and configurable query parameters)</link>"> |
| ]> |
| |
| <chapter xmlns="http://docbook.org/ns/docbook" |
| version="5.0" |
| xml:lang="en" |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| xmlns:xi="http://www.w3.org/2001/XInclude" |
| xmlns:xlink="http://www.w3.org/1999/xlink" |
| xsi:schemaLocation="http://docbook.org/ns/docbook http://docbook.org/xml/5.0/xsd/docbook.xsd |
| http://www.w3.org/1999/xlink http://www.w3.org/1999/xlink.xsd" |
| xml:id="entity-filtering"> |
| |
| <title>Entity Data Filtering</title> |
| |
| <para> |
| Support for Entity Filtering in Jersey introduces a convenient facility for reducing the amount of data exchanged over |
| the wire between client and server without a need to create specialized data view components. The main idea behind this |
| feature is to give you APIs that will let you to selectively filter out any non-relevant data from the marshalled object |
| model before sending the data to the other party based on the context of the particular message exchange. |
| This way, only the necessary or relevant portion of the data is transferred over the network with each client request |
| or server response, without a need to create special facade models for transferring these limited subsets of the model |
| data. |
| </para> |
| <para> |
| Entity filtering feature allows you to define your own entity-filtering rules for your entity classes based on the |
| current context (e.g. matched resource method) and keep these rules in one place (directly in your domain model). |
| With Jersey entity filtering facility it is also possible to assign security access rules to entity classes properties |
| and property accessors. |
| </para> |
| <para> |
| We will first explain the main concepts and then we will explore the entity filtering feature topics from a perspective |
| of basic use-cases, |
| |
| <itemizedlist> |
| <listitem> |
| <para><xref linkend="ef.annotations"/></para> |
| </listitem> |
| <listitem> |
| <para><xref linkend="ef.security.annotations"/></para> |
| </listitem> |
| <listitem> |
| <para><xref linkend="ef.selectable.annotations"/></para> |
| </listitem> |
| </itemizedlist> |
| |
| as well as some more complex ones. |
| |
| <itemizedlist> |
| <listitem> |
| <para><xref linkend="ef.custom.annotations"/></para> |
| </listitem> |
| </itemizedlist> |
| </para> |
| <note> |
| <para> |
| Jersey entity filtering feature is supported via Jersey extension modules listed in <xref linkend="ef.modules"/>. |
| </para> |
| </note> |
| |
| <section> |
| <title>Enabling and configuring Entity Filtering in your application</title> |
| |
| <para> |
| Entity Filtering support in Jersey is provided as an extension module and needs to be mentioned explicitly in your |
| &lit.pom.xml; file (in case of using Maven): |
| |
| <programlisting language="xml" linenumbering="unnumbered"><dependency> |
| <groupId>org.glassfish.jersey.ext</groupId> |
| <artifactId>jersey-entity-filtering</artifactId> |
| <version>&version;</version> |
| </dependency></programlisting> |
| |
| <note> |
| <para> |
| If you're not using Maven make sure to have also all the transitive dependencies |
| (see &jersey.ext.entity-filtering.deps.link;) on the classpath. |
| </para> |
| </note> |
| </para> |
| <para> |
| The entity-filtering extension module provides three &lit.jaxrs.core.Feature;s which you can register into server/client |
| runtime in prior to use Entity Filtering in an application: |
| |
| <itemizedlist> |
| <listitem> |
| <para>&jersey.message.filtering.EntityFilteringFeature;</para> |
| <para> |
| Filtering based on entity-filtering annotations (or i.e. external configuration file) created using |
| &jersey.message.filtering.EntityFiltering; meta-annotation. |
| </para> |
| </listitem> |
| <listitem> |
| <para>&jersey.message.filtering.SecurityEntityFilteringFeature;</para> |
| <para>Filtering based on security (<literal>javax.annotation.security</literal>) and entity-filtering |
| annotations.</para> |
| </listitem> |
| <listitem> |
| <para>&jersey.message.filtering.SelectableEntityFilteringFeature;</para> |
| <para>Filtering based on dynamic and configurable query parameters.</para> |
| </listitem> |
| </itemizedlist> |
| |
| If you want to use both entity-filtering annotations and security annotations for entity data filtering it is enough |
| to register &lit.jersey.message.filtering.SecurityEntityFilteringFeature; as this feature registers also |
| &lit.jersey.message.filtering.EntityFilteringFeature;. |
| </para> |
| <para> |
| Entity-filtering currently recognizes one property that can be passed into the &jaxrs.core.Configuration; instance |
| (client/server): |
| |
| <itemizedlist> |
| <listitem> |
| <para>&jersey.message.filtering.EntityFilteringFeature.ENTITY_FILTERING_SCOPE; - "<literal>jersey.config.entityFiltering.scope</literal>"</para> |
| <para> |
| Defines one or more annotations that should be used as entity-filtering scope when reading/writing an |
| entity. |
| </para> |
| </listitem> |
| </itemizedlist> |
| |
| <note> |
| <para> |
| Processing of entity-filtering annotations to create an entity-filtering scope is defined by |
| following: "<literal>Request/Resource entity annotations</literal>" > |
| "<literal>Configuration</literal>" > "<literal>Resource method/class annotations</literal>" |
| (on server). |
| </para> |
| </note> |
| </para> |
| <para> |
| You can configure entity-filtering on server (basic + security examples) as follows: |
| |
| <example xml:id="ef.example.server.registration"> |
| <title>Registering and configuring entity-filtering feature on server.</title> |
| |
| <programlisting language="java" linenumbering="numbered">new ResourceConfig() |
| // Set entity-filtering scope via configuration. |
| .property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, new Annotation[] {ProjectDetailedView.Factory.get()}) |
| // Register the EntityFilteringFeature. |
| .register(EntityFilteringFeature.class) |
| // Further configuration of ResourceConfig. |
| .register( ... );</programlisting> |
| </example> |
| |
| <example xml:id="ef.example.server.security.registration"> |
| <title>Registering and configuring entity-filtering feature with security annotations on server.</title> |
| |
| <programlisting language="java" linenumbering="numbered">new ResourceConfig() |
| // Set entity-filtering scope via configuration. |
| .property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, new Annotation[] {SecurityAnnotations.rolesAllowed("manager")}) |
| // Register the SecurityEntityFilteringFeature. |
| .register(SecurityEntityFilteringFeature.class) |
| // Further configuration of ResourceConfig. |
| .register( ... );</programlisting> |
| </example> |
| |
| <example xml:id="ef.example.server.selectable.registration"> |
| <title>Registering and configuring entity-filtering feature based on dynamic and configurable query parameters.</title> |
| |
| <programlisting language="java" linenumbering="numbered">new ResourceConfig() |
| // Set query parameter name for dynamic filtering |
| .property(SelectableEntityFilteringFeature.QUERY_PARAM_NAME, "select") |
| // Register the SelectableEntityFilteringFeature. |
| .register(SelectableEntityFilteringFeature.class) |
| // Further configuration of ResourceConfig. |
| .register( ... );</programlisting> |
| </example> |
| </para> |
| <para> |
| Use similar steps to register entity-filtering on client: |
| |
| <example xml:id="ef.example.client.registration"> |
| <title>Registering and configuring entity-filtering feature on client.</title> |
| |
| <programlisting language="java" linenumbering="numbered">final ClientConfig config = new ClientConfig() |
| // Set entity-filtering scope via configuration. |
| .property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, new Annotation[] {ProjectDetailedView.Factory.get()}) |
| // Register the EntityFilteringFeature. |
| .register(EntityFilteringFeature.class) |
| // Further configuration of ClientConfig. |
| .register( ... ); |
| |
| // Create new client. |
| final Client client = ClientClientBuilder.newClient(config); |
| |
| // Use the client.</programlisting> |
| </example> |
| </para> |
| </section> |
| |
| <section> |
| <title>Components used to describe Entity Filtering concepts</title> |
| |
| <para> |
| In the next section the entity-filtering features will be illustrated on a project-tracking application that |
| contains three classes in its domain model and few resources (only <literal>Project</literal> resource will be |
| shown in this chapter). The full source code for the example application can be found in Jersey |
| &jersey.github.ef.example.link;. |
| </para> |
| |
| <para> |
| Suppose there are three domain model classes (or entities) in our model: |
| <literal>Project</literal>, <literal>User</literal> and <literal>Task</literal> (getters/setter are omitted for |
| brevity). |
| |
| <example> |
| <title>Project</title> |
| |
| <programlisting language="java" linenumbering="numbered">public class Project { |
| |
| private Long id; |
| |
| private String name; |
| |
| private String description; |
| |
| private List<Task> tasks; |
| |
| private List<User> users; |
| |
| // getters and setters |
| }</programlisting> |
| </example> |
| |
| <example> |
| <title>User</title> |
| |
| <programlisting language="java" linenumbering="numbered">public class User { |
| |
| private Long id; |
| |
| private String name; |
| |
| private String email; |
| |
| private List<Project> projects; |
| |
| private List<Task> tasks; |
| |
| // getters and setters |
| }</programlisting> |
| </example> |
| |
| <example> |
| <title>Task</title> |
| |
| <programlisting language="java" linenumbering="numbered">public class Task { |
| |
| private Long id; |
| |
| private String name; |
| |
| private String description; |
| |
| private Project project; |
| |
| private User user; |
| |
| // getters and setters |
| }</programlisting> |
| </example> |
| </para> |
| |
| <para> |
| To retrieve the entities from server to client, we have created also a couple of JAX-RS resources from whose the |
| <literal>ProjectsResource</literal> is shown as example. |
| |
| <example> |
| <title>ProjectsResource</title> |
| |
| <programlisting language="java" linenumbering="numbered">@Path("projects") |
| @Produces("application/json") |
| public class ProjectsResource { |
| |
| @GET |
| @Path("{id}") |
| public Project getProject(@PathParam("id") final Long id) { |
| return getDetailedProject(id); |
| } |
| |
| @GET |
| public List<Project> getProjects() { |
| return getDetailedProjects(); |
| } |
| }</programlisting> |
| </example> |
| </para> |
| </section> |
| |
| <section xml:id="ef.annotations"> |
| <title>Using custom annotations to filter entities</title> |
| |
| <para> |
| Entity filtering via annotations is based on an &jersey.message.filtering.EntityFiltering; meta-annotation. |
| This meta-annotation is used to identify entity-filtering annotations that can be then attached to |
| |
| <itemizedlist> |
| <listitem> |
| <para>domain model classes (supported on both, server and client sides), and</para> |
| </listitem> |
| <listitem> |
| <para>resource methods / resource classes (only on server side)</para> |
| </listitem> |
| </itemizedlist> |
| |
| An example of entity-filtering annotation applicable to a class, field or method can be seen in |
| <xref linkend="ef.annotations.sample.annotation"/> below. |
| |
| <example xml:id="ef.annotations.sample.annotation"> |
| <title>ProjectDetailedView</title> |
| |
| <programlisting language="java" linenumbering="numbered">@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) |
| @Retention(RetentionPolicy.RUNTIME) |
| @Documented |
| @EntityFiltering |
| public @interface ProjectDetailedView { |
| |
| /** |
| * Factory class for creating instances of {@code ProjectDetailedView} annotation. |
| */ |
| public static class Factory |
| extends AnnotationLiteral<ProjectDetailedView> |
| implements ProjectDetailedView { |
| |
| private Factory() { |
| } |
| |
| public static ProjectDetailedView get() { |
| return new Factory(); |
| } |
| } |
| }</programlisting> |
| </example> |
| |
| </para> |
| <para> |
| Since creating annotation instances directly in Java code is not trivial, it is a good practice to provide an inner |
| annotation <literal>Factory</literal> class in each custom filtering annotation, through which new instances of |
| the annotation can be directly created. The annotation factory class can be created by extending the HK2 |
| <literal>AnnotationLiteral</literal> class and implementing the annotation interface itself. It should also provide |
| a static factory method that will create and return a new instance of the <literal>Factory</literal> class when |
| invoked. Such annotation instances can be then passed to the client and server run-times to define or override |
| entity-filtering scopes. |
| </para> |
| <para> |
| By placing an entity-filtering annotation on an entity (class, fields, getters or setters) we define a so-called |
| <emphasis>entity-filtering scope</emphasis> for the entity. The purpose of entity-filtering scope is to identify |
| parts of the domain model that should be processed when the model is to be sent over the wire in a particular |
| entity-filtering scope. We distinguish between: |
| |
| <itemizedlist> |
| <listitem> |
| <para> |
| global entity-filtering scope (defined by placing filtering annotation on a class itself), and |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| local entity-filtering scope (defined by placing filtering annotation on a field, getter or setter) |
| </para> |
| </listitem> |
| </itemizedlist> |
| |
| Unannotated members of a domain model class are automatically added to all existing global entity-filtering scopes. |
| If there is no explicit global entity-filtering scope defined on a class a default scope is created for this class |
| to group these members. |
| </para> |
| <para> |
| Creating entity-filtering scopes using custom entity-filtering annotations in domain model classes is illustrated |
| in the following examples. |
| |
| <example> |
| <title>Annotated Project</title> |
| |
| <programlisting language="java" linenumbering="numbered">public class Project { |
| |
| private Long id; |
| |
| private String name; |
| |
| private String description; |
| |
| @ProjectDetailedView |
| private List<Task> tasks; |
| |
| @ProjectDetailedView |
| private List<User> users; |
| |
| // getters and setters |
| }</programlisting> |
| </example> |
| |
| <example> |
| <title>Annotated User</title> |
| |
| <programlisting language="java" linenumbering="numbered">public class User { |
| |
| private Long id; |
| |
| private String name; |
| |
| private String email; |
| |
| @UserDetailedView |
| private List<Project> projects; |
| |
| @UserDetailedView |
| private List<Task> tasks; |
| |
| // getters and setters |
| }</programlisting> |
| </example> |
| |
| <example> |
| <title>Annotated Task</title> |
| |
| <programlisting language="java" linenumbering="numbered">public class Task { |
| |
| private Long id; |
| |
| private String name; |
| |
| private String description; |
| |
| @TaskDetailedView |
| private Project project; |
| |
| @TaskDetailedView |
| private User user; |
| |
| // getters and setters |
| }</programlisting> |
| </example> |
| |
| As you can see in the examples above, we have defined 3 separate scopes using <literal>@ProjectDetailedView</literal>, |
| <literal>@UserDetailedView</literal> and <literal>@TaskDetailedView</literal> annotations and we have applied |
| these scopes selectively to certain fields in the domain model classes. |
| </para> |
| <para> |
| Once the entity-filtering scopes are applied to the parts of a domain model, the entity filtering facility (when |
| enabled) will check the active scopes when the model is being sent over the wire, and filter out all parts from |
| the model for which there is no active scope set in the given context. Therefore, we need a way how to control |
| the scopes active in any given context in order to process the model data in a certain way (e.g. expose the detailed |
| view). We need to tell the server/client runtime which entity-filtering scopes we want to apply. There are 2 ways |
| how to do this for client-side and 3 ways for server-side: |
| |
| <itemizedlist> |
| <listitem> |
| <para>Out-bound client request or server response programmatically created with entity-filtering annotations |
| that identify the scopes to be applied (available on both, client and server)</para> |
| </listitem> |
| <listitem> |
| <para>Property identifying the applied scopes passed through &jaxrs.core.Configuration; |
| (available on both, client and server)</para> |
| </listitem> |
| <listitem> |
| <para>Entity-filtering annotations identifying the applied scopes attached to a resource method or class |
| (server-side only)</para> |
| </listitem> |
| </itemizedlist> |
| |
| When the multiple approaches are combined, the priorities of calculating the applied scopes are as follows: |
| <literal>Entity annotations in request or response</literal> > |
| <literal>Property passed through Configuration</literal> > |
| <literal>Annotations applied to a resource method or class</literal>. |
| </para> |
| <para> |
| In a graph of domain model objects, the entity-filtering scopes are applied to the root node as well as transitively |
| to all the child nodes. Fields and child nodes that do not match at least a single active scope are filtered out. |
| When the scope matching is performed, annotations applied to the domain model classes and fields |
| are used to compute the scope for each particular component of the model. If there are no annotations on the class |
| or its fields, the default scope is assumed. During the filtering, first, the annotations on root model class |
| and its fields are considered. For all composite fields that have not been filtered out, the annotations on the |
| referenced child class and its fields are considered next, and so on. |
| </para> |
| |
| <section> |
| <title>Server-side Entity Filtering</title> |
| |
| <para> |
| To pass entity-filtering annotations via &jaxrs.core.Response; returned from a resource method you can leverage the |
| <link xlink:href='&jaxrs.javadoc.uri;/core/Response.ResponseBuilder.html#entity(java.lang.Object, java.lang.annotation.Annotation[])'>Response.ResponseBuilder#entity(Object, Annotation[])</link> |
| method. The next example illustrates this approach. You will also see why every custom entity-filtering |
| annotation should contain a factory for creating instances of the annotation. |
| |
| <example> |
| <title>ProjectsResource - Response entity-filtering annotations</title> |
| |
| <programlisting language="java" linenumbering="numbered">@Path("projects") |
| @Produces("application/json") |
| public class ProjectsResource { |
| |
| @GET |
| public Response getProjects(@QueryParam("detailed") final boolean isDetailed) { |
| return Response |
| .ok() |
| .entity(new GenericEntity<List<Project>>(EntityStore.getProjects()) {}, |
| isDetailed ? new Annotation[]{ProjectDetailedView.Factory.get()} : new Annotation[0]) |
| .build(); |
| } |
| }</programlisting> |
| </example> |
| |
| Annotating a resource method / class is typically easier although it is less flexible and may require more |
| resource methods to be created to cover all the alternative use case scenarios. For example: |
| |
| <example> |
| <title>ProjectsResource - Entity-filtering annotations on methods</title> |
| |
| <programlisting language="java" linenumbering="numbered">@Path("projects") |
| @Produces("application/json") |
| public class ProjectsResource { |
| |
| @GET |
| public List<Project> getProjects() { |
| return getDetailedProjects(); |
| } |
| |
| @GET |
| @Path("detailed") |
| @ProjectDetailedView |
| public List<Project> getDetailedProjects() { |
| return EntityStore.getProjects(); |
| } |
| }</programlisting> |
| </example> |
| |
| To see how entity-filtering scopes can be applied using a &lit.jaxrs.core.Configuration; property, |
| see the <xref linkend="ef.example.server.registration"/> example. |
| </para> |
| <para> |
| When a <literal>Project</literal> model from the example above is requested in a scope represented by |
| <literal>@ProjectDetailedView</literal> entity-filtering annotation, the <literal>Project</literal> model |
| data sent over the wire would contain: |
| |
| <itemizedlist> |
| <listitem> |
| <para> |
| <literal>Project</literal> - <literal>id</literal>, <literal>name</literal>, |
| <literal>description</literal>, <literal>tasks</literal>, <literal>users</literal> |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| <literal>Task</literal> - <literal>id</literal>, <literal>name</literal>, |
| <literal>description</literal> |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| <literal>User</literal> - <literal>id</literal>, <literal>name</literal>, <literal>email</literal> |
| </para> |
| </listitem> |
| </itemizedlist> |
| |
| Or, to illustrate this in JSON format: |
| |
| <programlisting language="xml" linenumbering="numbered">{ |
| "description" : "Jersey is the open source (under dual EPL+GPL license) JAX-RS 2.1 (JSR 370) production quality Reference Implementation for building RESTful Web services.", |
| "id" : 1, |
| "name" : "Jersey", |
| "tasks" : [ { |
| "description" : "Entity Data Filtering", |
| "id" : 1, |
| "name" : "ENT_FLT" |
| }, { |
| "description" : "OAuth 1 + 2", |
| "id" : 2, |
| "name" : "OAUTH" |
| } ], |
| "users" : [ { |
| "email" : "very@secret.com", |
| "id" : 1, |
| "name" : "Jersey Robot" |
| } ] |
| }</programlisting> |
| |
| For the <emphasis>default entity-filtering scope</emphasis> the filtered model would look like: |
| |
| <itemizedlist> |
| <listitem> |
| <para> |
| <literal>Project</literal> - <literal>id</literal>, <literal>name</literal>, <literal>description</literal> |
| </para> |
| </listitem> |
| </itemizedlist> |
| |
| Or in JSON format: |
| |
| <programlisting language="xml" linenumbering="numbered">{ |
| "description" : "Jersey is the open source (under dual EPL+GPL license) JAX-RS 2.1 (JSR 370) production quality Reference Implementation for building RESTful Web services.", |
| "id" : 1, |
| "name" : "Jersey" |
| }</programlisting> |
| </para> |
| |
| </section> |
| <section> |
| <title>Client-side Entity Filtering</title> |
| |
| <para> |
| As mentioned above you can define applied entity-filtering scopes using a property set either in the client |
| run-time &lit.jaxrs.core.Configuration; (see <xref linkend="ef.example.client.registration"/>) or by |
| passing the entity-filtering annotations during a creation of an individual request to be sent to the server. |
| |
| <example> |
| <title>Client - Request entity-filtering annotations</title> |
| |
| <programlisting language="java" linenumbering="numbered">ClientBuilder.newClient(config) |
| .target(uri) |
| .request() |
| .post(Entity.entity(project, new Annotation[] {ProjectDetailedView.Factory.get()}));</programlisting> |
| </example> |
| |
| You can use the mentioned method with client injected into a resource as well. |
| |
| <example> |
| <title>Client - Request entity-filtering annotations</title> |
| |
| <programlisting language="java" linenumbering="numbered">@Path("clients") |
| @Produces("application/json") |
| public class ClientsResource { |
| |
| @Uri("projects") |
| private WebTarget target; |
| |
| @GET |
| public List<Project> getProjects() { |
| return target.request() |
| .post(Entity.entity(project, new Annotation[] {ProjectDetailedView.Factory.get()})); |
| } |
| }</programlisting> |
| </example> |
| </para> |
| </section> |
| </section> |
| |
| <section xml:id="ef.security.annotations"> |
| <title>Role-based Entity Filtering using (<literal>javax.annotation.security</literal>) annotations</title> |
| |
| <para> |
| Filtering the content sent to the client (or server) based on the authorized security roles is a commonly |
| required use case. By registering &jersey.message.filtering.SecurityEntityFilteringFeature; you can |
| leverage the Jersey Entity Filtering facility in connection with standard |
| <literal>javax.annotation.security</literal> annotations exactly the same way as you would with custom |
| entity-filtering annotations described in previous chapters. Supported security annotations are: |
| |
| <itemizedlist> |
| <listitem> |
| <para>&jee6.annotations.PermitAll;,</para> |
| </listitem> |
| <listitem> |
| <para>&jee6.annotations.RolesAllowed;, and</para> |
| </listitem> |
| <listitem> |
| <para>&jee6.annotations.DenyAll;</para> |
| </listitem> |
| </itemizedlist> |
| </para> |
| <para> |
| Although the mechanics of the Entity Data Filtering feature used for the security annotation-based filtering is |
| the same as with the entity-filtering annotations, the processing of security annotations differs in a few important |
| aspects: |
| |
| <itemizedlist> |
| <listitem> |
| <para> |
| Custom &jaxrs.core.SecurityContext; should be set by a container request filter in order to use |
| &lit.jee6.annotations.RolesAllowed; for role-based filtering of domain model data (server-side) |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| There is no need to provide entity-filtering (or security) annotations on resource methods in order to |
| define entity-filtering scopes for &lit.jee6.annotations.RolesAllowed; that is applied to the domain model |
| components, as all the available roles for the current user are automatically determined using |
| the information from the provided &lit.jaxrs.core.SecurityContext; (server-side only). |
| </para> |
| </listitem> |
| </itemizedlist> |
| |
| <note> |
| <para> |
| Instances of security annotations (to be used for programmatically defined scopes either on client or server) |
| can be created using one of the methods in the &jersey.message.filtering.SecurityAnnotations; factory class |
| that is part of the Jersey Entity Filtering API. |
| </para> |
| </note> |
| </para> |
| </section> |
| |
| <section xml:id="ef.selectable.annotations"> |
| <title>Entity Filtering based on dynamic and configurable query parameters</title> |
| <para> |
| Filtering the content sent to the client (or server) dynamically based on query parameters is another commonly |
| required use case. By registering &jersey.message.filtering.SelectableEntityFilteringFeature; you can |
| leverage the Jersey Entity Filtering facility in connection with query parameters exactly the same way as you would with custom |
| entity-filtering annotations described in previous chapters. |
| </para> |
| |
| <para> |
| <example> |
| <title>Sever - Query Parameter driven entity-filtering</title> |
| |
| <programlisting language="java" linenumbering="numbered">@XmlRootElement |
| public class Address { |
| |
| private String streetAddress; |
| |
| private String region; |
| |
| private PhoneNumber phoneNumber; |
| }</programlisting> |
| </example> |
| </para> |
| <para> |
| Query parameters are supported in comma delimited "dot notation" style similar to <literal>BeanInfo</literal> objects |
| and Spring path expressions. As an example, the following URL: |
| <literal>http://jersey.example.com/addresses/51234?select=region,streetAddress</literal> may render only the address's |
| region and street address properties as in the following example: |
| </para> |
| |
| <example> |
| <programlisting language="xml" linenumbering="numbered">{ |
| "region" : "CA", |
| "streetAddress" : "1234 Fake St." |
| }</programlisting> |
| </example> |
| |
| </section> |
| |
| <section xml:id="ef.custom.annotations"> |
| <title>Defining custom handling for entity-filtering annotations</title> |
| |
| <para> |
| To create a custom entity-filtering annotation with special handling, i.e. an field aggregator annotation used |
| to annotate classes like the one in <xref linkend="ef.custom.annotations.annotation"/> it is, in most cases, |
| sufficient to implement and register the following SPI contracts: |
| |
| <itemizedlist> |
| <listitem> |
| <para>&jersey.message.filtering.EntityProcessor;</para> |
| <para>Implementations of this SPI are invoked to process entity class and its members. Custom |
| implementations can extend from &jersey.message.filtering.AbstractEntityProcessor;.</para> |
| </listitem> |
| <listitem> |
| <para>&jersey.message.filtering.ScopeResolver;</para> |
| <para>Implementations of this SPI are invoked to retrieve entity-filtering scopes from an array of |
| provided annotations.</para> |
| </listitem> |
| </itemizedlist> |
| |
| <example xml:id="ef.custom.annotations.annotation"> |
| <title>Entity-filtering annotation with custom meaning</title> |
| |
| <programlisting language="java" linenumbering="numbered">@Target({ElementType.TYPE}) |
| @Retention(RetentionPolicy.RUNTIME) |
| @EntityFiltering |
| public @interface FilteringAggregator { |
| |
| /** |
| * Entity-filtering scope to add given fields to. |
| */ |
| Annotation filteringScope(); |
| |
| /** |
| * Fields to be a part of the entity-filtering scope. |
| */ |
| String[] fields(); |
| }</programlisting> |
| </example> |
| </para> |
| </section> |
| |
| <section xml:id="ef.custom.providers"> |
| <title>Supporting Entity Data Filtering in custom entity providers or frameworks</title> |
| |
| <para> |
| To support Entity Data Filtering in custom entity providers (e.g. as in <xref linkend="ef.modules.custom"/>), |
| it is sufficient in most of the cases to implement and register the following SPI contracts: |
| |
| <itemizedlist> |
| <listitem> |
| <para>&jersey.message.filtering.ObjectProvider;</para> |
| <para> |
| To be able to obtain an instance of a filtering object model your provider understands and can act on. |
| The implementations can extend &jersey.message.filtering.AbstractObjectProvider;. |
| </para> |
| </listitem> |
| <listitem> |
| <para>&jersey.message.filtering.ObjectGraphTransformer;</para> |
| <para> |
| To transform a read-only generic representation of a domain object model graph to be processed into |
| an entity-filtering object model your provider understands and can act on. The implementations can |
| extend &jersey.message.filtering.AbstractObjectProvider;. |
| </para> |
| </listitem> |
| </itemizedlist> |
| |
| <example xml:id="ef.modules.custom"> |
| <title>Entity Data Filtering support in MOXy JSON binding provider</title> |
| |
| <programlisting language="java" linenumbering="numbered">@Singleton |
| public class FilteringMoxyJsonProvider extends ConfigurableMoxyJsonProvider { |
| |
| @Inject |
| private Provider<ObjectProvider<ObjectGraph>> provider; |
| |
| @Override |
| protected void preWriteTo(final Object object, final Class<?> type, final Type genericType, final Annotation[] annotations, |
| final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders, |
| final Marshaller marshaller) throws JAXBException { |
| super.preWriteTo(object, type, genericType, annotations, mediaType, httpHeaders, marshaller); |
| |
| // Entity Filtering. |
| if (marshaller.getProperty(MarshallerProperties.OBJECT_GRAPH) == null) { |
| final Object objectGraph = provider.get().getFilteringObject(genericType, true, annotations); |
| |
| if (objectGraph != null) { |
| marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, objectGraph); |
| } |
| } |
| } |
| |
| @Override |
| protected void preReadFrom(final Class<Object> type, final Type genericType, final Annotation[] annotations, |
| final MediaType mediaType, final MultivaluedMap<String, String> httpHeaders, |
| final Unmarshaller unmarshaller) throws JAXBException { |
| super.preReadFrom(type, genericType, annotations, mediaType, httpHeaders, unmarshaller); |
| |
| // Entity Filtering. |
| if (unmarshaller.getProperty(MarshallerProperties.OBJECT_GRAPH) == null) { |
| final Object objectGraph = provider.get().getFilteringObject(genericType, false, annotations); |
| |
| if (objectGraph != null) { |
| unmarshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, objectGraph); |
| } |
| } |
| } |
| }</programlisting> |
| </example> |
| </para> |
| </section> |
| |
| <section xml:id="ef.modules"> |
| <title>Modules with support for Entity Data Filtering</title> |
| |
| <para> |
| List of modules from Jersey workspace that support Entity Filtering: |
| |
| <itemizedlist> |
| <listitem> |
| <para> |
| <link linkend='json.moxy'>MOXy</link> |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| <link linkend='json.jackson'>Jackson (2.x)</link> |
| </para> |
| </listitem> |
| </itemizedlist> |
| </para> |
| <para> |
| In order to use Entity Filtering in mentioned modules you need to explicitly register either |
| &jersey.message.filtering.EntityFilteringFeature;, &jersey.message.filtering.SecurityEntityFilteringFeature; |
| or &jersey.message.filtering.SelectableEntityFilteringFeature; to activate Entity Filtering for particular module. |
| </para> |
| </section> |
| |
| <section> |
| <title>Examples</title> |
| |
| <para> |
| To see a complete working examples of entity-filtering feature refer to the: |
| |
| <itemizedlist> |
| <listitem> |
| <para>&jersey.github.ef.example.link;</para> |
| </listitem> |
| <listitem> |
| <para>&jersey.github.ef.security.example.link;</para> |
| </listitem> |
| <listitem> |
| <para>&jersey.github.ef.selectable.example.link;</para> |
| </listitem> |
| </itemizedlist> |
| </para> |
| </section> |
| </chapter> |