Changeset 3639

Show
Ignore:
Timestamp:
25/02/08 22:04:13 (4 years ago)
Author:
michael
Message:

Annotation based EntityResource. EntityFinder uses reflection on the EntityResource so that only the resource needs subclassing. Refs #6, spent 16

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/build.properties

    r3613 r3639  
    11project.name=restlet-jpa 
    2 project.rev=0.3 
    3 project.status=release 
     2project.rev=0.4 
     3project.status=integration 
  • trunk/src/main/java/org/sarugo/restlet/jpa/EntityFinder.java

    r3613 r3639  
    2121import java.lang.reflect.Method; 
    2222import java.util.Collections; 
    23 import java.util.List; 
    2423import java.util.Map; 
    2524import java.util.logging.Level; 
     
    2726import javax.persistence.Id; 
    2827 
    29 import org.restlet.Finder; 
    30 import org.restlet.Route; 
     28import org.restlet.Handler; 
    3129import org.restlet.data.Reference; 
    3230import org.restlet.data.Request; 
    3331import org.restlet.data.Response; 
     32import org.restlet.resource.Resource; 
     33import org.sarugo.restlet.jpa.resource.EntityInstance; 
    3434 
    3535/** 
    36  * An entity finder maps a request to an {@link EntityResource}. 
     36 * An entity finder maps a request to an {@link EntityResource}. Provides 
     37 * methods to hook into the resource instance to retrieve entity id attributes 
     38 * for mapping entity instances to URLs and other support functions. 
    3739 *  
    3840 * @author Michael Terrington 
    3941 *  
    40  * @param <E> 
    41  *            The entity type. 
    4242 */ 
    43 public class EntityFinder<E> extends Finder { 
    44  
    45     public static final String REQUEST_ATTRIBUTE = EntityFinder.class.getName(); 
    46  
    47     private final EntityRouter router; 
    48  
    49     private final EntityRouter children; 
    50  
    51     private final Class<E> entityClass; 
     43public class EntityFinder extends ContextInjectingFinder { 
     44 
     45    public final static String REQUEST_ATTRIBUTE = EntityFinder.class.getName(); 
     46 
     47    private final Class<? extends Object> entityClass; 
    5248 
    5349    private final Class<? extends Object> entityIdClass; 
    5450 
     51    private final Method getEntityMethod; 
     52 
     53    private final Method getParentEntityMethod; 
     54 
     55    private final Method getIdAttributesMethod; 
     56 
    5557    private String idAttribute; 
    56  
    57     private Route route; 
    5858 
    5959    /** 
     
    6868     */ 
    6969    @SuppressWarnings("unchecked") 
    70     public EntityFinder(EntityRouter router, Class<E> entityClass, 
    71             Class<? extends EntityResource> resource) { 
    72         super(router.getContext(), resource); 
    73         this.router = router; 
    74         this.children = new EntityRouter(this); 
     70    public EntityFinder(EntityRouter router, Class<? extends Resource> resource) { 
     71        super(router, resource); 
    7572        this.idAttribute = "id"; 
    76         this.entityClass = entityClass; 
     73        EntityResource erAnnotation = resource 
     74                .getAnnotation(EntityResource.class); 
     75        this.entityClass = erAnnotation.entity(); 
    7776        this.entityIdClass = EntityHelper.findIdType(entityClass); 
    78     } 
    79  
    80     /** 
    81      * Passes the request to children if possible or handles directly if no 
    82      * children map to the URL. 
    83      */ 
     77        Method m = null; 
     78        try { 
     79            m = resource.getMethod("getEntity"); 
     80        } catch (Exception e) { 
     81            // ignore - leave as null 
     82        } 
     83        this.getEntityMethod = m; 
     84        m = null; 
     85        try { 
     86            m = resource.getMethod("getParentEntity", entityClass); 
     87        } catch (Exception e) { 
     88            // ignore - leave as null 
     89        } 
     90        this.getParentEntityMethod = m; 
     91        m = null; 
     92        try { 
     93            m = resource.getMethod("getIdAttributes", entityClass); 
     94        } catch (Exception e) { 
     95            // ignore - leave as null 
     96        } 
     97        this.getIdAttributesMethod = m; 
     98    } 
     99 
    84100    @Override 
    85     public void handle(Request request, Response response) { 
    86         if (!children.handleIfPossible(request, response)) { 
    87             super.handle(request, response); 
    88         } 
    89     } 
    90  
    91     @Override 
    92     protected EntityResource findTarget(Request request, Response response) { 
     101    protected Handler findTarget(Request request, Response response) { 
    93102        request.getAttributes().put(REQUEST_ATTRIBUTE, this); 
    94         return (EntityResource) super.findTarget(request, response); 
     103        return super.findTarget(request, response); 
     104    } 
     105 
     106    /** 
     107     * Set the URI template variable used to map URI's to entities and back. For 
     108     * entities with complex keys, provide a custom {@link EntityInstance} and 
     109     * override {@link #getIdAttributes(Object)}. 
     110     */ 
     111    public EntityFinder setIdAttribute(String idAttribute) { 
     112        this.idAttribute = idAttribute; 
     113        return this; 
    95114    } 
    96115 
     
    98117     * Convert a URL in the request to an entity instance. Works by first 
    99118     * checking if any children match the requested URL. If no children match 
    100      * then the {@link EntityResource} is constructed and 
    101      * {@link EntityResource#getEntity()} returned. 
     119     * then the {@link EntityInstance} is constructed and 
     120     * {@link EntityInstance#getEntity()} returned. 
    102121     */ 
    103122    public Object getEntity(Request request) { 
    104123        Object entity = null; 
    105         String remainingPart = request.getResourceRef().getRemainingPart(); 
    106         int matchedLength = getRoute().getTemplate().parse(remainingPart, 
    107                 request); 
    108         if (matchedLength != -1) { 
    109             // Update the remaining part 
    110             String matchedPart = remainingPart.substring(0, matchedLength); 
    111             Reference baseRef = request.getResourceRef().getBaseRef(); 
    112  
    113             if (baseRef == null) { 
    114                 baseRef = new Reference(matchedPart); 
    115             } else { 
    116                 baseRef = new Reference(baseRef.toString(false, false) 
    117                         + matchedPart); 
    118             } 
    119  
    120             request.getResourceRef().setBaseRef(baseRef); 
    121  
    122             entity = children.getEntity(request); 
    123             if (entity == null) { 
    124                 @SuppressWarnings("unchecked") 
    125                 EntityResource<E> resource = (EntityResource<E>) findTarget( 
    126                         request, new Response(request)); 
    127                 if (resource != null) { 
    128                     entity = resource.getEntity(); 
    129                 } 
     124        if (getEntityMethod != null) { 
     125            Handler handler = findTarget(request, new Response(request)); 
     126            try { 
     127                entity = getEntityMethod.invoke(handler); 
     128            } catch (Exception e) { 
     129                getLogger() 
     130                        .log( 
     131                                Level.WARNING, 
     132                                "Exception while attempting to invoke getEntity method on EntityResource.", 
     133                                e); 
    130134            } 
    131135        } 
    132136        return entity; 
    133     } 
    134  
    135     public EntityRouter getRouter() { 
    136         return router; 
    137     } 
    138  
    139     /** 
    140      * Set the URI template variable used to map URI's to entities and back. For 
    141      * entities with complex keys, provide a custom {@link EntityResource} and 
    142      * override {@link #getIdAttributes(Object)}. 
    143      */ 
    144     public EntityFinder<E> setIdAttribute(String idAttribute) { 
    145         this.idAttribute = idAttribute; 
    146         return this; 
    147     } 
    148  
    149     public Route getRoute() { 
    150         return route; 
    151     } 
    152  
    153     /** 
    154      * Set the Route for this finder. Also sets the 
    155      * {@link #setIdAttribute(String) id attribute} to the first variable in the 
    156      * route's URI template. 
    157      */ 
    158     public EntityFinder<E> setRoute(Route route) { 
    159         this.route = route; 
    160         List<String> vars = route.getTemplate().getVariableNames(); 
    161         if (!vars.isEmpty()) { 
    162             this.idAttribute = vars.get(0); 
    163         } 
    164         return this; 
    165     } 
    166  
    167     /** The router child entities should be attached to. */ 
    168     public EntityRouter getChildRouter() { 
    169         return children; 
    170     } 
    171  
    172     /** True if this finder is a child. */ 
    173     public boolean isChild() { 
    174         return router.isChild(); 
    175     } 
    176  
    177     /** Gets the parent finder (if this is a child). */ 
    178     public EntityFinder getParentFinder() { 
    179         return router.getParent(); 
    180     } 
    181  
    182     /** Gets the parent resource for the given request (if this is a child). */ 
    183     public EntityResource getParentResource(Request request, Response response) { 
    184         EntityResource resource = null; 
    185         if (isChild()) { 
    186             resource = getParentFinder().findTarget(request, response); 
    187         } 
    188         return resource; 
    189137    } 
    190138 
     
    200148     * @return The parent entity instance or null if unknown / unavaiable. 
    201149     */ 
    202     public Object getParentEntity(E entity) { 
    203         return null; 
     150    public Object getParentEntity(Object entity) { 
     151        Object parent = null; 
     152        if (getParentEntityMethod != null) { 
     153            try { 
     154                parent = getParentEntityMethod.invoke(null, entity); 
     155            } catch (Exception e) { 
     156                getLogger() 
     157                        .log( 
     158                                Level.WARNING, 
     159                                "Exception while attempting to invoke getParentEntity method on EntityResource.", 
     160                                e); 
     161            } 
     162        } 
     163        return parent; 
    204164    } 
    205165 
     
    215175     *         {@link Reference#encode(String) URL encoded}. 
    216176     */ 
    217     public Map<String, Object> getIdAttributes(E entity) { 
     177    @SuppressWarnings("unchecked") 
     178    public Map<String, Object> getIdAttributes(Object entity) { 
     179        // TODO pre-scan for the field/method 
    218180        Map<String, Object> attributes = Collections.emptyMap(); 
    219         String idValue = null; 
    220         if (entity != null) { 
    221             for (Field f : entity.getClass().getDeclaredFields()) { 
    222                 if (f.isAnnotationPresent(Id.class)) { 
    223                     f.setAccessible(true); 
    224                     try { 
    225                         idValue = String.valueOf(f.get(entity)); 
    226                         break; 
    227                     } catch (IllegalArgumentException e) { 
    228                         getLogger().log(Level.WARNING, 
    229                                 "Unable to get Id for object", e); 
    230                     } catch (IllegalAccessException e) { 
    231                         getLogger().log(Level.WARNING, 
    232                                 "Unable to get Id for object", e); 
    233                     } 
    234                 } 
    235             } 
    236             if (idValue == null) { 
    237                 for (Method m : entity.getClass().getDeclaredMethods()) { 
    238                     if (m.isAnnotationPresent(Id.class)) { 
    239                         m.setAccessible(true); 
     181        if (getIdAttributesMethod != null) { 
     182            try { 
     183                attributes = (Map<String, Object>) getIdAttributesMethod 
     184                        .invoke(null, entity); 
     185            } catch (Exception e) { 
     186                getLogger() 
     187                        .log( 
     188                                Level.WARNING, 
     189                                "Exception while attempting to invoke getIdAttributes method on EntityResource.", 
     190                                e); 
     191            } 
     192        } else { 
     193            // TODO pre-scan for the field/method 
     194            String idValue = null; 
     195            if (entity != null) { 
     196                for (Field f : entity.getClass().getDeclaredFields()) { 
     197                    if (f.isAnnotationPresent(Id.class)) { 
     198                        f.setAccessible(true); 
    240199                        try { 
    241                             idValue = String.valueOf(m.invoke(entity)); 
     200                            idValue = String.valueOf(f.get(entity)); 
    242201                            break; 
    243202                        } catch (IllegalArgumentException e) { 
     
    247206                            getLogger().log(Level.WARNING, 
    248207                                    "Unable to get Id for object", e); 
    249                         } catch (InvocationTargetException e) { 
    250                             getLogger().log(Level.WARNING, 
    251                                     "Unable to get Id for object", e); 
    252208                        } 
    253209                    } 
    254210                } 
    255             } 
    256         } 
    257         if (idValue != null) { 
    258             attributes = Collections.singletonMap(idAttribute, 
    259                     (Object) Reference.encode(idValue)); 
    260         } 
     211                if (idValue == null) { 
     212                    for (Method m : entity.getClass().getDeclaredMethods()) { 
     213                        if (m.isAnnotationPresent(Id.class)) { 
     214                            m.setAccessible(true); 
     215                            try { 
     216                                idValue = String.valueOf(m.invoke(entity)); 
     217                                break; 
     218                            } catch (IllegalArgumentException e) { 
     219                                getLogger().log(Level.WARNING, 
     220                                        "Unable to get Id for object", e); 
     221                            } catch (IllegalAccessException e) { 
     222                                getLogger().log(Level.WARNING, 
     223                                        "Unable to get Id for object", e); 
     224                            } catch (InvocationTargetException e) { 
     225                                getLogger().log(Level.WARNING, 
     226                                        "Unable to get Id for object", e); 
     227                            } 
     228                        } 
     229                    } 
     230                } 
     231            } 
     232            if (idValue != null) { 
     233                attributes = Collections.singletonMap(idAttribute, 
     234                        (Object) Reference.encode(idValue)); 
     235            } 
     236        } 
     237 
    261238        return attributes; 
    262239    } 
    263240 
    264     /** Used by the default {@link EntityResource} implementation. */ 
    265     public Class<E> getEntityClass() { 
     241    /** Used by the default {@link EntityInstance} implementation. */ 
     242    public Class<? extends Object> getEntityClass() { 
    266243        return entityClass; 
    267244    } 
    268245 
    269     /** Used by the default {@link EntityResource} implementation. */ 
     246    /** Used by the default {@link EntityInstance} implementation. */ 
    270247    public Class<? extends Object> getEntityIdClass() { 
    271248        return entityIdClass; 
    272249    } 
    273250 
    274     /** Used by the default {@link EntityResource} implementation. */ 
     251    /** Used by the default {@link EntityInstance} implementation. */ 
    275252    public String getIdAttribute() { 
    276253        return idAttribute; 
  • trunk/src/main/java/org/sarugo/restlet/jpa/EntityResource.java

    r3613 r3639  
    1 /* 
    2  * Copyright 2007, 2008 Sarugo Pty Ltd  
    3  *  
    4  * Licensed under the Common Development and Distribution License, 
    5  * you may not use this file except in compliance with the License. 
    6  * You may obtain a copy of the License at 
    7  *  
    8  *   http://www.sun.com/cddl/ 
    9  *    
    10  * Unless required by applicable law or agreed to in writing, software 
    11  * distributed under the License is distributed on an "AS IS" BASIS, 
    12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or  
    13  * implied. See the License for the specific language governing 
    14  * permissions and limitations under the License. 
    15  */ 
    16  
    171package org.sarugo.restlet.jpa; 
    182 
    19 import java.util.Map; 
     3import java.lang.annotation.Documented; 
     4import java.lang.annotation.ElementType; 
     5import java.lang.annotation.Retention; 
     6import java.lang.annotation.RetentionPolicy; 
     7import java.lang.annotation.Target; 
    208 
    21 import javax.persistence.EntityManager; 
    22  
    23 import org.restlet.Context; 
    24 import org.restlet.data.Request; 
    25 import org.restlet.data.Response; 
    26 import org.restlet.resource.Resource; 
    27  
    28 /** 
    29  * A resource which maps to a JPA entity. 
    30  *  
    31  * @author Michael Terrington 
    32  */ 
    33 public class EntityResource<E> extends Resource { 
    34  
    35     private EntityRouter router; 
    36  
    37     private EntityFinder<E> finder; 
    38  
    39     private EntityResource parentResource; 
    40  
    41     protected E entity; 
    42  
    43     /** 
    44      * Initialise this resource from a request. Initialises router and parent 
    45      * resource (if applicable) then calls 
    46      * {@link #initEntity(Request, Response)}. 
    47      */ 
    48     @SuppressWarnings("unchecked") 
    49     public final void init(Context context, Request request, Response response) { 
    50         super.init(context, request, response); 
    51         this.router = (EntityRouter) request.getAttributes().get( 
    52                 EntityRouter.REQUEST_ATTRIBUTE); 
    53         this.finder = (EntityFinder<E>) request.getAttributes().get( 
    54                 EntityFinder.REQUEST_ATTRIBUTE); 
    55         this.parentResource = finder.getParentResource(request, response); 
    56         initEntity(request, response); 
    57     } 
    58  
    59     /** 
    60      * Get the entity identified by the request URL. By default this invokes 
    61      * {@link EntityManager#find(Class, Object)} with the conversion of the 
    62      * request attributes using {@link #getId(Map)}. 
    63      *  
    64      * When to override: 
    65      * <ul> 
    66      * <li>If you need to load the entity from a parent rather than by primary 
    67      * key.</li> 
    68      * </ul> 
    69      */ 
    70     protected void initEntity(Request request, Response response) { 
    71         Object id = getId(request.getAttributes()); 
    72         if (id != null) { 
    73             entity = router.getEntityManager() 
    74                     .find(finder.getEntityClass(), id); 
    75         } 
    76     } 
    77  
    78     /** 
    79      * Convert the id attributes to an object suitable for passing to 
    80      * {@link EntityManager#find(Class, Object)}. For simple entity id's the 
    81      * default implementation should suffice. 
    82      *  
    83      * When to override: 
    84      * <ul> 
    85      * <li>If the entity has a compound primary key.</li> 
    86      * <li>If the primary key is contained in multiple request attributes.</li> 
    87      * </ul> 
    88      *  
    89      * @param attributes 
    90      *            The request attributes. 
    91      * @return The identity object. 
    92      */ 
    93     @SuppressWarnings("unchecked") 
    94     protected Object getId(Map<String, Object> attributes) { 
    95         Object id = null; 
    96         String value = (String) attributes.get(finder.getIdAttribute()); 
    97         if (value != null) { 
    98             id = EntityHelper.convertToType(finder.getEntityIdClass(), value, 
    99                     null); 
    100         } 
    101         return id; 
    102     } 
    103  
    104     /** Gets the parent resource of this resource (if this is a child resource). */ 
    105     public final EntityResource getParent() { 
    106         return parentResource; 
    107     } 
    108  
    109     /** 
    110      * Get the parent entity of the entity this resource maps to (if this is a 
    111      * child resource). Calls {@link #getEntity()} on the result of 
    112      * {@link #getParent()}. 
    113      */ 
    114     public final Object getParentEntity() { 
    115         Object parent = null; 
    116         if (parentResource != null) { 
    117             parent = parentResource.getEntity(); 
    118         } 
    119         return parent; 
    120     } 
    121  
    122     /** The root {@link EntityRouter} that created this resource. */ 
    123     protected final EntityRouter getRouter() { 
    124         return router; 
    125     } 
    126  
    127     /** 
    128      * Helper that calls {@link EntityRouter#getEntityManager()} on 
    129      * {@link #getRouter()}. 
    130      */ 
    131     public final EntityManager getEntityManager() { 
    132         return getRouter().getEntityManager(); 
    133     } 
    134  
    135     /** The entity this resource maps to. */ 
    136     public final E getEntity() { 
    137         return entity; 
    138     } 
    139  
    140     /** True if the entity is available (not null). */ 
    141     @Override 
    142     public boolean isAvailable() { 
    143         return entity != null; 
    144     } 
    145  
     9@Documented 
     10@Target( { ElementType.TYPE }) 
     11@Retention(RetentionPolicy.RUNTIME) 
     12public @interface EntityResource { 
     13    Class<? extends Object> entity(); 
    14614} 
  • trunk/src/main/java/org/sarugo/restlet/jpa/EntityRouter.java

    r3613 r3639  
    2424 
    2525import org.restlet.Context; 
     26import org.restlet.Filter; 
    2627import org.restlet.Restlet; 
    2728import org.restlet.Route; 
     
    3132import org.restlet.data.Request; 
    3233import org.restlet.data.Response; 
     34import org.restlet.resource.Resource; 
    3335 
    3436/** 
     
    4547public class EntityRouter extends Router { 
    4648 
    47     public static final String REQUEST_ATTRIBUTE = EntityRouter.class.getName(); 
    48  
    4949    private final EntityManagerFactory entityManagerFactory; 
    5050 
    5151    private final ThreadLocal<EntityManager> entityManager; 
    5252 
    53     private final Map<Class, EntityFinder> entities; 
    54  
    55     private final EntityFinder<? extends Object> parent; 
     53    private final Map<Class, EntityRoute> entities; 
     54 
     55    private final EntityRoute parent; 
    5656 
    5757    private Reference baseRef; 
     
    7373        this.entityManagerFactory = entityManagerFactory; 
    7474        this.entityManager = new ThreadLocal<EntityManager>(); 
    75         this.entities = new HashMap<Class, EntityFinder>(); 
     75        this.entities = new HashMap<Class, EntityRoute>(); 
    7676        this.parent = null; 
    7777        this.baseRef = null; 
    7878    } 
    7979 
    80     public EntityRouter(EntityFinder<? extends Object> parent) { 
     80    public EntityRouter(EntityRoute parent) { 
    8181        super(parent.getContext()); 
    8282        this.parent = parent; 
    8383        this.entityManagerFactory = parent.getRouter().entityManagerFactory; 
    8484        this.entityManager = parent.getRouter().entityManager; 
    85         this.entities = new HashMap<Class, EntityFinder>(); 
     85        this.entities = new HashMap<Class, EntityRoute>(); 
    8686        this.baseRef = null; 
    8787    } 
     
    128128        String url = null; 
    129129        if (entity != null) { 
    130             EntityFinder finder = entities.get(entity.getClass()); 
    131             if (finder != null) { 
     130            EntityRoute route = entities.get(entity.getClass()); 
     131            if (route != null) { 
    132132                // this router holds the entity finder 
    133133                @SuppressWarnings("unchecked") 
    134                 Map<String, Object> attributes = finder.getIdAttributes(entity); 
    135                 String entityUrl = finder.getRoute().getTemplate().format( 
    136                         attributes); 
    137                 if (finder.isChild()) { 
     134                Map<String, Object> attributes = route.getFinder() 
     135                        .getIdAttributes(entity); 
     136                String entityUrl = route.getTemplate().format(attributes); 
     137                if (isChild()) { 
    138138                    @SuppressWarnings("unchecked") 
    139                     Object parentEntity = finder.getParentEntity(entity); 
     139                    Object parentEntity = route.getFinder().getParentEntity( 
     140                            entity); 
    140141                    if (parentEntity != null) { 
    141142                        String parentUrl = getParent().getRouter() 
     
    150151            } else { 
    151152                // otherwise check if our children can map the entity 
    152                 for (EntityFinder parent : entities.values()) { 
     153                for (EntityRoute parent : entities.values()) { 
    153154                    url = parent.getChildRouter().getEntityURL(entity); 
    154155                    if (url != null) { 
     
    187188        Object entity = null; 
    188189        if (!isChild()) { 
    189             request.getAttributes().put(REQUEST_ATTRIBUTE, this); 
    190190            if (baseRef != null 
    191191                    && request.getResourceRef().toString(true, false) 
     
    195195        } 
    196196        Route next = (Route) getNext(request, new Response(request)); 
    197         if (next != null) { 
    198             EntityFinder finder = null; 
    199             for (EntityFinder f : entities.values()) { 
    200                 if (next.equals(f.getRoute())) { 
    201                     finder = f; 
    202                     break; 
    203                 } 
    204             } 
    205             if (finder != null) { 
    206                 entity = finder.getEntity(request); 
     197        if (next != null && next instanceof EntityRoute) { 
     198            EntityRoute route = ((EntityRoute) next); 
     199            String remainingPart = request.getResourceRef().getRemainingPart(); 
     200            int matchedLength = route.getTemplate().parse(remainingPart, 
     201                    request); 
     202            if (matchedLength != -1) { 
     203                // Update the remaining part 
     204                String matchedPart = remainingPart.substring(0, matchedLength); 
     205                Reference baseRef = request.getResourceRef().getBaseRef(); 
     206 
     207                if (baseRef == null) { 
     208                    baseRef = new Reference(matchedPart); 
     209                } else { 
     210                    baseRef = new Reference(baseRef.toString(false, false) 
     211                            + matchedPart); 
     212                } 
     213 
     214                request.getResourceRef().setBaseRef(baseRef); 
     215 
     216                entity = route.getChildRouter().getEntity(request); 
     217                if (entity == null) { 
     218                    entity = route.getFinder().getEntity(request); 
     219                } 
    207220            } 
    208221        } 
     
    226239     */ 
    227240    public boolean handleIfPossible(Request request, Response response) { 
    228         if (!isChild()) { 
    229             request.getAttributes().put(REQUEST_ATTRIBUTE, this); 
    230         } 
    231241        Restlet next = getNext(request, new Response(request)); 
    232242        if (next != null) { 
     
    236246    } 
    237247 
    238     /** 
    239      * Attach an {@link EntityFinder} for handling {@link EntityInstance}s. 
    240      * That is, a handler for entity instances, not the entity collection. 
    241      * Invokes {@link #attachEntity(EntityFinder)} with a new 
    242      * {@link EntityFinder}. 
    243      *  
    244      * @param entityClass 
    245      *            The entity class to attach. 
    246      * @return The finder for the entity. 
    247      * @see #attachCollection(Class) for attaching an {@link EntityFinder} for 
    248      *      the entity collection. 
    249      */ 
    250     public <E extends Object> EntityFinder<E> attachEntity(Class<E> entityClass) { 
    251         return attachEntity(new EntityFinder<E>(this, entityClass, 
    252                 EntityResource.class)); 
    253     } 
    254  
    255     /** 
    256      * Attach an {@link EntityFinder} for handling {@link EntityInstance}s. 
    257      * That is, a handler for entity instances, not the entity collection. 
    258      * Invokes {@link #attachEntity(String, EntityFinder)} with the given 
    259      * {@link EntityFinder} and the URL "/{entityName}/{id}" where 
    260      * "{entityName}" is formed by calling 
    261      * {@link EntityURLHelper#getEntityName(Class)}. 
    262      *  
    263      * @param finder 
    264      *            The entity finder to attach. 
    265      * @return The finder for the entity. 
    266      * @throws IllegalStateException 
    267      *             If the finder has already been attached. 
    268      * @see #attachCollection(EntityFinder) for attaching an 
    269      *      {@link EntityFinder} for the entity collection. 
    270      */ 
    271     public <E extends Object> EntityFinder<E> attachEntity( 
    272             EntityFinder<E> finder) { 
    273         return attachEntity( 
    274                 "/" + EntityHelper.getEntityName(finder.getEntityClass()) 
    275                         + "/{id}", finder); 
    276     } 
    277  
    278     /** 
    279      * Attach an {@link EntityFinder} for handling {@link EntityInstance}s. 
    280      * That is, a handler for entity instances, not the entity collection. 
    281      * Invokes {@link #attachEntity(String, EntityFinder)} with a new 
    282      * {@link EntityFinder}. 
    283      *  
    284      * @param uriPattern 
    285      *            The URL pattern to attach the {@link EntityFinder} at. By 
    286      *            default, {@link EntityFinder#getEntity(Request, Response)} 
    287      *            expects the URL to contain "{id}" to define the entity id. 
    288      * @param entityClass 
    289      *            The entity class to attach. 
    290      * @return The finder for the entity. 
    291      * @see #attachCollection(String, Class)) for attaching an 
    292      *      {@link EntityFinder} for the entity collection. 
    293      */ 
    294     public <E extends Object> EntityFinder<E> attachEntity(String uriPattern, 
    295             Class<E> entityClass) { 
    296         return attachEntity(uriPattern, new EntityFinder<E>(this, entityClass, 
    297                 EntityResource.class)); 
    298     } 
    299  
    300     /** 
    301      * Attach an {@link EntityFinder} for handling {@link EntityInstance}s. 
    302      * That is, a handler for entity instances, not the entity collection. 
    303      *  
    304      * @param uriPattern 
    305      *            The URL pattern to attach the {@link EntityFinder} at. By 
    306      *            default, {@link EntityFinder#getEntity(Request, Response)} 
    307      *            expects the URL to contain "{id}" to define the entity id. 
    308      * @param finder 
    309      *            The entity finder to attach. 
    310      * @return The finder for the entity. 
    311      * @throws IllegalStateException 
    312      *             If the finder has already been attached. 
    313      * @see #attachCollection(String, EntityFinder)) for attaching an 
    314      *      {@link EntityFinder} for the entity collection. 
    315      */ 
    316     public <E extends Object> EntityFinder<E> attachEntity(String uriPattern, 
    317             EntityFinder<E> finder) { 
    318         if (entities.containsKey(finder.getEntityClass())) { 
    319             throw new IllegalStateException("Entity class is already mapped."); 
    320         } 
    321         if (finder.getRoute() != null) { 
    322             throw new IllegalStateException( 
    323                     "Finder is already attached to another router"); 
    324         } 
    325         Route route = attach(uriPattern, finder); 
    326         finder.setRoute(route); 
    327         entities.put(finder.getEntityClass(), finder); 
    328         return finder; 
    329     } 
    330  
    331     public EntityFinder<? extends Object> getParent() { 
     248    @Override 
     249    public Route attach(String uriPattern, Class<? extends Resource> targetClass) { 
     250        if (targetClass.isAnnotationPresent(EntityResource.class)) { 
     251            return attach(uriPattern, new EntityFinder(this, targetClass)); 
     252        } else { 
     253            ContextInjectingFinder finder = new ContextInjectingFinder(this, 
     254                    targetClass); 
     255            if (finder.hasContextFields()) { 
     256                return attach(uriPattern, finder); 
     257            } else { 
     258                return super.attach(uriPattern, targetClass); 
     259            } 
     260        } 
     261    } 
     262 
     263    @Override 
     264    public Route attach(String uriPattern, Restlet target) { 
     265        Restlet next = target; 
     266        while (next != null) { 
     267            if (next instanceof EntityFinder) { 
     268                EntityFinder finder = (EntityFinder) next; 
     269                EntityRoute route = new EntityRoute(this, uriPattern, target); 
     270                if (!entities.containsKey(finder.getEntityClass())) { 
     271                    entities.put(finder.getEntityClass(), route); 
     272                    finder.setIdAttribute(route.getTemplate() 
     273                            .getVariableNames().get(0)); 
     274                } 
     275                getRoutes().add(route); 
     276                return route; 
     277            } else if (next instanceof Filter) { 
     278                next = ((Filter) next).getNext(); 
     279            } else { 
     280                next = null; 
     281            } 
     282        } 
     283        return super.attach(uriPattern, target); 
     284    } 
     285 
     286    public EntityRoute getParent() { 
    332287        return parent; 
    333288    } 
  • trunk/src/test/java/org/sarugo/restlet/jpa/URLMappingTests.java

    r3613 r3639  
    66 
    77import org.restlet.Context; 
     8import org.sarugo.restlet.jpa.resource.EntityInstance; 
    89 
    910public class URLMappingTests extends TestCase { 
     
    1516    BarEntity bar; 
    1617 
    17     private static class BarFinder extends EntityFinder<BarEntity> { 
     18    @EntityResource(entity = FooEntity.class) 
     19    public static class FooResource extends EntityInstance<FooEntity> { 
    1820 
    19         public BarFinder(EntityRouter router) { 
    20             super(router, BarEntity.class, EntityResource.class); 
    21         } 
     21    } 
    2222 
    23         @Override 
    24         public Object getParentEntity(BarEntity bar) { 
     23    @EntityResource(entity = BarEntity.class) 
     24    public static class BarResource extends EntityInstance<BarEntity> { 
     25 
     26        public static Object getParentEntity(BarEntity bar) { 
    2527            return bar.foo; 
    2628        } 
     
    3133        router = new EntityRouter(new Context(), Persistence 
    3234                .createEntityManagerFactory("test")); 
    33         EntityRouter fooRouter = router.attachEntity(FooEntity.class) 
    34                 .getChildRouter(); 
    35         fooRouter.attachEntity("/bar/{barId}", new BarFinder(fooRouter)); 
     35        EntityRoute fooRoute = (EntityRoute) router.attach("/foo/{fooId}", 
     36                FooResource.class); 
     37        fooRoute.getChildRouter().attach("/bar/{barId}", BarResource.class); 
    3638        foo = new FooEntity(); 
    3739        foo.name = "This is the foo"; 
     
    5153        assertEquals("/foo/" + foo.id + "/bar/" + bar.id, router 
    5254                .getEntityURL(bar)); 
    53         EntityFinder<BarEntity> finder = router.attachEntity(BarEntity.class); 
     55        EntityRoute barRoute = (EntityRoute) router.attach("/bar/{barId}", 
     56                BarResource.class); 
    5457        assertEquals("/bar/" + bar.id, router.getEntityURL(bar)); 
    55         router.attach("/baz/{barId}", finder); 
     58        router.attach("/baz/{barId}", barRoute.getNext()); 
    5659        assertEquals("/bar/" + bar.id, router.getEntityURL(bar)); 
    5760        router.setBaseRef("http://server/path"); 
     
    7073                + "/bar/" + bar.id); 
    7174        assertEquals(bar.id, foundBar.id); 
    72         EntityFinder<BarEntity> finder = router.attachEntity(BarEntity.class); 
     75        EntityRoute barRoute = (EntityRoute) router.attach("/bar/{barId}", 
     76                BarResource.class); 
    7377        foundBar = (BarEntity) router.getEntity("/bar/" + bar.id); 
    7478        assertEquals(bar.id, foundBar.id); 
    75         router.attach("/baz/{barId}", finder); 
     79        router.attach("/baz/{barId}", barRoute.getNext()); 
    7680        foundBar = (BarEntity) router.getEntity("/baz/" + bar.id); 
    77         assertNull(foundBar); 
     81        assertEquals(bar.id, foundBar.id); 
    7882        router.setBaseRef("http://server/path"); 
    7983        foundBar = (BarEntity) router.getEntity("http://server/path/bar/"