Changeset 3613

Show
Ignore:
Timestamp:
12/02/08 17:16:29 (4 years ago)
Author:
michael
Message:

Working support for child entities. Supports full URL<->entity mapping. Fixes #4, spent 8

Files:

Legend:

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

    r3603 r3613  
    11project.name=restlet-jpa 
    22project.rev=0.3 
    3 project.status=integration 
     3project.status=release 
  • trunk/src/main/java/org/sarugo/restlet/jpa/EntityHelper.java

    r3606 r3613  
    1717package org.sarugo.restlet.jpa; 
    1818 
     19import java.beans.PropertyEditor; 
     20import java.beans.PropertyEditorManager; 
     21import java.lang.reflect.Constructor; 
    1922import java.lang.reflect.Field; 
    2023import java.lang.reflect.Method; 
     
    6568     * or classes without any id fields/methods the type is set to 
    6669     * {@link String}. This makes the default 
    67      * {@link EntityInstanceFinder#convertIdString(String)} a no-op for compound or 
     70     * {@link EntityFinder#convertIdString(String)} a no-op for compound or 
    6871     * undefined keys. 
    6972     *  
     
    101104    } 
    102105 
     106    public static <T extends Object> T convertToType(Class<T> type, 
     107            String value, EntityRouter router) { 
     108        // Short cut for String types 
     109        if (type.isAssignableFrom(String.class)) { 
     110            return type.cast(value); 
     111        } 
     112 
     113        // Short cut for null 
     114        if (value == null) { 
     115            return null; 
     116        } 
     117 
     118        // Otherwise, check if the type is an enum 
     119        if (type.isEnum()) { 
     120            for (T constant : type.getEnumConstants()) { 
     121                if (((Enum) constant).name().equals(value)) { 
     122                    return constant; 
     123                } 
     124            } 
     125            return null; 
     126        } 
     127 
     128        // Otherwise, use JavaBean coercion 
     129        try { 
     130            PropertyEditor e = PropertyEditorManager.findEditor(type); 
     131            e.setAsText(value); 
     132            return type.cast(e.getValue()); 
     133        } catch (Exception e) { 
     134            // Ignore 
     135        } 
     136 
     137        // Otherwise check if the type is an entity type 
     138        if (router != null) { 
     139            @SuppressWarnings("unchecked") 
     140            T entity = (T) router.getEntity(value); 
     141            if (entity != null) { 
     142                return entity; 
     143            } 
     144        } 
     145 
     146        // Otherwise, try a String constructor 
     147        try { 
     148            Constructor<T> c = type.getConstructor(String.class); 
     149            return c.newInstance(value); 
     150        } catch (Exception e) { 
     151            // Ignore 
     152        } 
     153 
     154        // If all else failed, just return null 
     155        return null; 
     156    } 
     157 
    103158} 
  • trunk/src/main/java/org/sarugo/restlet/jpa/EntityResource.java

    r3606 r3613  
    1515 */ 
    1616 
    17 package org.sarugo.restlet.jpa.resource
     17package org.sarugo.restlet.jpa
    1818 
     19import java.util.Map; 
     20 
     21import javax.persistence.EntityManager; 
     22 
     23import org.restlet.Context; 
    1924import org.restlet.data.Request; 
    2025import org.restlet.data.Response; 
    21 import org.restlet.resource.Representation; 
    2226import org.restlet.resource.Resource; 
    23 import org.restlet.resource.ResourceException; 
    24 import org.restlet.resource.Variant; 
    25 import org.sarugo.restlet.jpa.EntityFinder; 
    26 import org.sarugo.restlet.jpa.EntityInstanceFinder; 
    27 import org.sarugo.restlet.jpa.converter.OutputConverter; 
    2827 
    2928/** 
    30  * A resource which maps to a JPA entity. The behaviour of this class is largely 
    31  * controlled by the {@link EntityInstanceFinder} which creates it. 
     29 * A resource which maps to a JPA entity. 
    3230 *  
    3331 * @author Michael Terrington 
    3432 */ 
    35 public abstract class EntityResource<E> extends Resource { 
     33public class EntityResource<E> extends Resource { 
    3634 
    37     private final EntityFinder<E> finder; 
     35    private EntityRouter router; 
     36 
     37    private EntityFinder<E> finder; 
     38 
     39    private EntityResource parentResource; 
     40 
     41    protected E entity; 
    3842 
    3943    /** 
    40      * Initialise this entity resource, linking it to the given finder. The set 
    41      * of variants this resource produces is initialised to the variants 
    42      * returned from the output converters in the finder. 
     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)}. 
    4363     *  
    44      * @param finder 
    45      *            The finder associated with this resource. 
     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> 
    4669     */ 
    47     public EntityResource(EntityFinder<E> finder) { 
    48         this.finder = finder; 
    49         for (OutputConverter<E> converter : finder.getOutputConverters()) { 
    50             getVariants().addAll(converter.getVariants()); 
     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); 
    5175        } 
    5276    } 
    5377 
    54     /** Lookup the entity/entity list for the given request. */ 
    55     public abstract void init(Request request, Response response); 
    56  
    57     /** The finder this resource is initialised from. */ 
    58     public final EntityFinder<E> getFinder() { 
    59         return finder; 
     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; 
    60102    } 
    61103 
    62     /** True if the finder provides input converters. */ 
    63     @Override 
    64     public boolean isModifiable() { 
    65         return !getFinder().getInputConverters().isEmpty(); 
    66     } 
    67  
    68     /** True if the finder provides output converters. */ 
    69     @Override 
    70     public boolean isReadable() { 
    71         return !getFinder().getOutputConverters().isEmpty(); 
     104    /** Gets the parent resource of this resource (if this is a child resource). */ 
     105    public final EntityResource getParent() { 
     106        return parentResource; 
    72107    } 
    73108 
    74109    /** 
    75      * Represent this resource using the first matching output converter from 
    76      * the finder. 
     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()}. 
    77113     */ 
     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). */ 
    78141    @Override 
    79     public Representation represent(Variant variant) throws ResourceException { 
    80         Representation representation = null; 
    81         for (OutputConverter<E> converter : getFinder().getOutputConverters()) { 
    82             representation = converter.represent(this, variant); 
    83             if (representation != null) { 
    84                 break; 
    85             } 
    86         } 
    87         return representation; 
     142    public boolean isAvailable() { 
     143        return entity != null; 
    88144    } 
    89145 
  • trunk/src/main/java/org/sarugo/restlet/jpa/EntityRouter.java

    r3607 r3613  
    1919import java.util.HashMap; 
    2020import java.util.Map; 
    21 import java.util.Set; 
    22 import java.util.concurrent.CopyOnWriteArraySet; 
    2321 
    2422import javax.persistence.EntityManager; 
     
    3331import org.restlet.data.Request; 
    3432import org.restlet.data.Response; 
    35 import org.restlet.data.Status; 
    36 import org.sarugo.restlet.jpa.resource.EntityCollection; 
    37 import org.sarugo.restlet.jpa.resource.EntityInstance; 
    3833 
    3934/** 
     
    4136 * should share a common router, this enables link resolution. 
    4237 *  
    43  * {@link EntityInstanceFinder}s are attached to handle entity instances or 
     38 * {@link EntityFinder}s are attached to handle entity instances or 
    4439 * collections. Instances are attached independently from collections. An 
    45  * {@link EntityInstanceFinder} can only be attached to one URL, however it may 
    46  * have several {@link #alias(String, EntityInstanceFinder) aliases}. 
     40 * {@link EntityFinder} can only be attached to one URL, however it may have 
     41 * several {@link #alias(String, EntityFinder) aliases}. 
    4742 *  
    4843 * @author Michael Terrington 
     
    5045public class EntityRouter extends Router { 
    5146 
     47    public static final String REQUEST_ATTRIBUTE = EntityRouter.class.getName(); 
     48 
    5249    private final EntityManagerFactory entityManagerFactory; 
    5350 
    5451    private final ThreadLocal<EntityManager> entityManager; 
    5552 
    56     private final Map<Class, EntityInstanceFinder> entities; 
    57  
    58     private final Map<Class, Set<EntityInstanceFinder>> childEntities; 
    59  
    60     private final EntityInstanceFinder<? extends Object> parent; 
     53    private final Map<Class, EntityFinder> entities; 
     54 
     55    private final EntityFinder<? extends Object> parent; 
    6156 
    6257    private Reference baseRef; 
     
    7873        this.entityManagerFactory = entityManagerFactory; 
    7974        this.entityManager = new ThreadLocal<EntityManager>(); 
    80         this.entities = new HashMap<Class, EntityInstanceFinder>(); 
    81         this.childEntities = new HashMap<Class, Set<EntityInstanceFinder>>(); 
     75        this.entities = new HashMap<Class, EntityFinder>(); 
    8276        this.parent = null; 
    8377        this.baseRef = null; 
    8478    } 
    8579 
    86     public EntityRouter(EntityInstanceFinder<? extends Object> parent) { 
     80    public EntityRouter(EntityFinder<? extends Object> parent) { 
    8781        super(parent.getContext()); 
    8882        this.parent = parent; 
    89         this.entityManagerFactory = parent.getRootRouter().entityManagerFactory; 
    90         this.entityManager = parent.getRootRouter().entityManager; 
    91         this.entities = new HashMap<Class, EntityInstanceFinder>(); 
    92         this.childEntities = null; 
     83        this.entityManagerFactory = parent.getRouter().entityManagerFactory; 
     84        this.entityManager = parent.getRouter().entityManager; 
     85        this.entities = new HashMap<Class, EntityFinder>(); 
    9386        this.baseRef = null; 
    9487    } 
     
    10497     * Get a thread-local {@link EntityManager}. Typically during request 
    10598     * processing an {@link EntityManager} is created and a transaction begun 
    106      * before passing control to an {@link EntityInstanceFinder}. During 
    107      * processing (which is always in the same thread), the 
    108      * {@link EntityInstanceFinder} can call this method to get the 
    109      * {@link EntityManager} associated with the request. After request 
    110      * processing is complete, the transaction is completed and the 
    111      * {@link EntityManager} closed and cleared from the thread-local. 
     99     * before passing control to an {@link EntityFinder}. During processing 
     100     * (which is always in the same thread), the {@link EntityFinder} can call 
     101     * this method to get the {@link EntityManager} associated with the request. 
     102     * After request processing is complete, the transaction is completed and 
     103     * the {@link EntityManager} closed and cleared from the thread-local. 
    112104     */ 
    113105    public EntityManager getEntityManager() { 
     
    126118 
    127119    /** 
    128      * Convert an entity to a URL based on an attached 
    129      * {@link EntityInstanceFinder}. 
     120     * Convert an entity to a URL based on an attached {@link EntityFinder}. 
    130121     *  
    131122     * @param entity 
    132123     *            The entity to get a URL for. 
    133124     * @return The entity's URL relative to this router. 
    134      * @see #attachInstance(String, EntityInstanceFinder) 
    135      */ 
    136     @SuppressWarnings("unchecked") 
     125     * @see #attachEntity(String, EntityFinder) 
     126     */ 
    137127    public String getEntityURL(Object entity) { 
    138         EntityInstanceFinder finder = entities.get(entity.getClass()); 
    139128        String url = null; 
    140         if (finder != null) { 
    141             // this router holds the entity instance finder 
    142             url = finder.getEntityURL(entity); 
    143         } else if (!isChild()) { 
    144             // else - as the top ancestor, see if there's a child 
    145             // finder that maps 
    146             Set<EntityInstanceFinder> parents = childEntities.get(entity 
    147                     .getClass()); 
    148             if (parents != null) { 
    149                 for (EntityInstanceFinder parent : parents) { 
    150                     url = parent.getEntityURL(entity); 
     129        if (entity != null) { 
     130            EntityFinder finder = entities.get(entity.getClass()); 
     131            if (finder != null) { 
     132                // this router holds the entity finder 
     133                @SuppressWarnings("unchecked") 
     134                Map<String, Object> attributes = finder.getIdAttributes(entity); 
     135                String entityUrl = finder.getRoute().getTemplate().format( 
     136                        attributes); 
     137                if (finder.isChild()) { 
     138                    @SuppressWarnings("unchecked") 
     139                    Object parentEntity = finder.getParentEntity(entity); 
     140                    if (parentEntity != null) { 
     141                        String parentUrl = getParent().getRouter() 
     142                                .getEntityURL(parentEntity); 
     143                        if (parentUrl != null) { 
     144                            url = parentUrl + entityUrl; 
     145                        } 
     146                    } 
     147                } else { 
     148                    url = entityUrl; 
     149                } 
     150            } else { 
     151                // otherwise check if our children can map the entity 
     152                for (EntityFinder parent : entities.values()) { 
     153                    url = parent.getChildRouter().getEntityURL(entity); 
    151154                    if (url != null) { 
    152155                        break; 
     
    154157                } 
    155158            } 
    156         } 
    157         if (url != null && !isChild() && baseRef != null) { 
    158             url = baseRef + url; 
     159            if (url != null && !isChild() && baseRef != null) { 
     160                url = baseRef.toString(false, false) + url; 
     161            } 
    159162        } 
    160163        return url; 
     
    183186    public Object getEntity(Request request) { 
    184187        Object entity = null; 
     188        if (!isChild()) { 
     189            request.getAttributes().put(REQUEST_ATTRIBUTE, this); 
     190            if (baseRef != null 
     191                    && request.getResourceRef().toString(true, false) 
     192                            .startsWith(baseRef.toString(true, false))) { 
     193                request.getResourceRef().setBaseRef(baseRef); 
     194            } 
     195        } 
    185196        Route next = (Route) getNext(request, new Response(request)); 
    186         if (next != null && next.getNext() instanceof EntityInstanceFinder) { 
    187             EntityInstanceFinder finder = (EntityInstanceFinder) next.getNext(); 
    188             entity = finder.getEntity(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); 
     207            } 
    189208        } 
    190209        return entity; 
     
    204223     * mappings. For transation support see {@link TransactionFilter}. 
    205224     *  
    206      * @return true if the request was handled. Response status will also be set 
    207      *         to {@link Status#CLIENT_ERROR_NOT_FOUND} if request was not 
    208      *         handled. 
     225     * @return true if the request was handled. 
    209226     */ 
    210227    public boolean handleIfPossible(Request request, Response response) { 
    211         Restlet next = getNext(request, response); 
     228        if (!isChild()) { 
     229            request.getAttributes().put(REQUEST_ATTRIBUTE, this); 
     230        } 
     231        Restlet next = getNext(request, new Response(request)); 
    212232        if (next != null) { 
    213233            next.handle(request, response); 
     
    217237 
    218238    /** 
    219      * Attach an {@link EntityInstanceFinder} for handling 
    220      * {@link EntityInstance}s. That is, a handler for entity instances, not 
    221      * the entity collection. Invokes 
    222      * {@link #attachInstance(EntityInstanceFinder)} with a new 
    223      * {@link EntityInstanceFinder}. 
     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}. 
    224243     *  
    225244     * @param entityClass 
    226245     *            The entity class to attach. 
    227246     * @return The finder for the entity. 
    228      * @see #attachCollection(Class) for attaching an 
    229      *      {@link EntityInstanceFinder} for the entity collection. 
    230      */ 
    231     public <E extends Object> EntityInstanceFinder<E> attachInstance( 
    232             Class<E> entityClass) { 
    233         return attachInstance(new EntityInstanceFinder<E>(this, entityClass, 
    234                 EntityInstance.class)); 
    235     } 
    236  
    237     /** 
    238      * Attach an {@link EntityInstanceFinder} for handling 
    239      * {@link EntityInstance}s. That is, a handler for entity instances, not 
    240      * the entity collection. Invokes 
    241      * {@link #attachInstance(String, EntityInstanceFinder)} with the given 
    242      * {@link EntityInstanceFinder} and the URL "/{entityName}/{id}" where 
     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 
    243260     * "{entityName}" is formed by calling 
    244261     * {@link EntityURLHelper#getEntityName(Class)}. 
     
    249266     * @throws IllegalStateException 
    250267     *             If the finder has already been attached. 
    251      * @see #attachCollection(EntityInstanceFinder) for attaching an 
    252      *      {@link EntityInstanceFinder} for the entity collection. 
    253      */ 
    254     public <E extends Object> EntityInstanceFinder<E> attachInstance
    255             EntityInstanceFinder<E> finder) { 
    256         return attachInstance
     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
    257274                "/" + EntityHelper.getEntityName(finder.getEntityClass()) 
    258275                        + "/{id}", finder); 
     
    260277 
    261278    /** 
    262      * Attach an {@link EntityInstanceFinder} for handling 
    263      * {@link EntityInstance}s. That is, a handler for entity instances, not 
    264      * the entity collection. Invokes 
    265      * {@link #attachInstance(String, EntityInstanceFinder)} with a new 
    266      * {@link EntityInstanceFinder}. 
     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}. 
    267283     *  
    268284     * @param uriPattern 
    269      *            The URL pattern to attach the {@link EntityInstanceFinder} at. 
    270      *            By default, 
    271      *            {@link EntityInstanceFinder#getEntity(Request, Response)} 
     285     *            The URL pattern to attach the {@link EntityFinder} at. By 
     286     *            default, {@link EntityFinder#getEntity(Request, Response)} 
    272287     *            expects the URL to contain "{id}" to define the entity id. 
    273288     * @param entityClass 
     
    275290     * @return The finder for the entity. 
    276291     * @see #attachCollection(String, Class)) for attaching an 
    277      *      {@link EntityInstanceFinder} for the entity collection. 
    278      */ 
    279     public <E extends Object> EntityInstanceFinder<E> attachInstance( 
    280             String uriPattern, Class<E> entityClass) { 
    281         return attachInstance(uriPattern, new EntityInstanceFinder<E>(this, 
    282                 entityClass, EntityInstance.class)); 
    283     } 
    284  
    285     /** 
    286      * Attach an {@link EntityInstanceFinder} for handling 
    287      * {@link EntityInstance}s. That is, a handler for entity instances, not 
    288      * the entity collection. 
     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. 
    289303     *  
    290304     * @param uriPattern 
    291      *            The URL pattern to attach the {@link EntityInstanceFinder} at. 
    292      *            By default, 
    293      *            {@link EntityInstanceFinder#getEntity(Request, Response)} 
     305     *            The URL pattern to attach the {@link EntityFinder} at. By 
     306     *            default, {@link EntityFinder#getEntity(Request, Response)} 
    294307     *            expects the URL to contain "{id}" to define the entity id. 
    295308     * @param finder 
     
    299312     *             If the finder has already been attached. 
    300313     * @see #attachCollection(String, EntityFinder)) for attaching an 
    301      *      {@link EntityInstanceFinder} for the entity collection. 
    302      */ 
    303     public <E extends Object> EntityInstanceFinder<E> attachInstance( 
    304             String uriPattern, EntityInstanceFinder<E> finder) { 
     314     *      {@link EntityFinder} for the entity collection. 
     315     */ 
     316    public <E extends Object> EntityFinder<E> attachEntity(String uriPattern, 
     317            EntityFinder<E> finder) { 
    305318        if (entities.containsKey(finder.getEntityClass())) { 
    306319            throw new IllegalStateException("Entity class is already mapped."); 
     
    316329    } 
    317330 
    318     /** 
    319      * Attach an {@link EntityInstanceFinder} for handling 
    320      * {@link EntityCollection}s. That is, a handler for an entity collection, 
    321      * not entity instances. Invokes 
    322      * {@link #attachCollection(EntityInstanceFinder)} with a new 
    323      * {@link EntityInstanceFinder}. 
    324      *  
    325      * @param entityClass 
    326      *            The entity class to attach. 
    327      * @return The finder for the entity. 
    328      */ 
    329     public <E extends Object> EntityCollectionFinder<E> attachCollection( 
    330             Class<E> entityClass) { 
    331         return attachCollection(new EntityCollectionFinder<E>(this, 
    332                 entityClass, EntityCollection.class)); 
    333     } 
    334  
    335     /** 
    336      * Attach an {@link EntityInstanceFinder} for handling 
    337      * {@link EntityCollection}s. That is, a handler for an entity collection, 
    338      * not entity instances. Invokes 
    339      * {@link #attachCollection(String, EntityInstanceFinder)} with the given 
    340      * {@link EntityInstanceFinder} and the URL "/{entityName}" where 
    341      * "{entityName}" is formed by calling 
    342      * {@link EntityURLHelper#getEntityName(Class)}. 
    343      *  
    344      * @param finder 
    345      *            The entity finder to attach. 
    346      * @return The finder for the entity. 
    347      * @throws IllegalStateException 
    348      *             If the finder has already been attached. 
    349      */ 
    350     public <E extends Object> EntityCollectionFinder<E> attachCollection( 
    351             EntityCollectionFinder<E> finder) { 
    352         return attachCollection("/" 
    353                 + EntityHelper.getEntityName(finder.getEntityClass()), finder); 
    354     } 
    355  
    356     /** 
    357      * Attach an {@link EntityInstanceFinder} for handling 
    358      * {@link EntityCollection}s. That is, a handler for an entity collection, 
    359      * not entity instances. Invokes 
    360      * {@link #attachCollection(String, EntityInstanceFinder)} with a new 
    361      * {@link EntityInstanceFinder}. 
    362      *  
    363      * @param uriPattern 
    364      *            The URL pattern to attach the {@link EntityInstanceFinder} at. 
    365      * @param entityClass 
    366      *            The entity class to attach. 
    367      * @return The finder for the entity. 
    368      */ 
    369     public <E extends Object> EntityCollectionFinder<E> attachCollection( 
    370             String uriPattern, Class<E> entityClass) { 
    371         return attachCollection(uriPattern, new EntityCollectionFinder<E>(this, 
    372                 entityClass, EntityCollection.class)); 
    373     } 
    374  
    375     /** 
    376      * Attach an {@link EntityInstanceFinder} for handling 
    377      * {@link EntityCollection}s. That is, a handler for an entity collection, 
    378      * not entity instances. 
    379      *  
    380      * @param uriPattern 
    381      *            The URL pattern to attach the {@link EntityInstanceFinder} at. 
    382      * @param finder 
    383      *            The entity finder to attach. 
    384      * @return The finder for the entity. 
    385      * @throws IllegalStateException 
    386      *             If the finder has already been attached. 
    387      */ 
    388     public <E extends Object> EntityCollectionFinder<E> attachCollection( 
    389             String uriPattern, EntityCollectionFinder<E> finder) { 
    390         if (finder.getRoute() != null) { 
    391             throw new IllegalStateException( 
    392                     "Finder is already attached to another router"); 
    393         } 
    394         Route route = attach(uriPattern, finder); 
    395         finder.setRoute(route); 
    396         return finder; 
    397     } 
    398  
    399     /** 
    400      * Associate an {@link EntityFinder} with an extra URL. For 
    401      * {@link EntityInstanceFinder}s, the URL must contain the template 
    402      * variables required to find the instance (by default {id}). 
    403      *  
    404      * @param uriPattern 
    405      *            The URL pattern to alias the {@link EntityFinder} to. 
    406      * @param finder 
    407      *            The entity finder to alias. 
    408      * @return The {@link Route} for the alias. 
    409      */ 
    410     public <E extends Object> Route alias(String uriPattern, 
    411             EntityFinder<E> finder) { 
    412         Route route = attach(uriPattern, finder); 
    413         finder.getAliases().add(route); 
    414         return route; 
    415     } 
    416  
    417     public int size() { 
    418         return entities.size(); 
    419     } 
    420  
    421     public EntityInstanceFinder<? extends Object> getParent() { 
     331    public EntityFinder<? extends Object> getParent() { 
    422332        return parent; 
    423333    } 
     
    425335    public boolean isChild() { 
    426336        return parent != null; 
    427     } 
    428  
    429     public void addChildFinder(EntityInstanceFinder child) { 
    430         if (isChild()) { 
    431             throw new IllegalStateException("Child routers can't add finders."); 
    432         } 
    433         Set<EntityInstanceFinder> parents = childEntities.get(child 
    434                 .getEntityClass()); 
    435         if (parents == null) { 
    436             parents = new CopyOnWriteArraySet<EntityInstanceFinder>(); 
    437             childEntities.put(child.getEntityClass(), parents); 
    438         } 
    439         parents.add(child); 
    440337    } 
    441338 
  • trunk/src/test/java/org/sarugo/restlet/jpa/URLMappingTests.java

    r3609 r3613  
    66 
    77import org.restlet.Context; 
    8 import org.sarugo.restlet.jpa.resource.EntityInstance; 
    98 
    109public class URLMappingTests extends TestCase { 
     
    1615    BarEntity bar; 
    1716 
    18     private static class BarResource extends EntityInstance<BarEntity> { 
     17    private static class BarFinder extends EntityFinder<BarEntity> { 
    1918 
    20         public BarResource(EntityInstanceFinder<BarEntity> finder) { 
    21             super(finder); 
     19        public BarFinder(EntityRouter router) { 
     20            super(router, BarEntity.class, EntityResource.class); 
    2221        } 
    2322 
    2423        @Override 
    25         public Object getParentEntity() { 
    26             return getEntity().foo; 
     24        public Object getParentEntity(BarEntity bar) { 
     25            return bar.foo; 
    2726        } 
    2827 
     
    3231        router = new EntityRouter(new Context(), Persistence 
    3332                .createEntityManagerFactory("test")); 
    34         router.attachInstance(FooEntity.class).attachChild("/bar/{id}", 
    35                 BarEntity.class, BarResource.class); 
     33        EntityRouter fooRouter = router.attachEntity(FooEntity.class) 
     34                .getChildRouter(); 
     35        fooRouter.attachEntity("/bar/{barId}", new BarFinder(fooRouter)); 
    3636        foo = new FooEntity(); 
    3737        foo.name = "This is the foo"; 
     
    5151        assertEquals("/foo/" + foo.id + "/bar/" + bar.id, router 
    5252                .getEntityURL(bar)); 
    53         EntityInstanceFinder<BarEntity> finder = router 
    54                 .attachInstance(BarEntity.class); 
     53        EntityFinder<BarEntity> finder = router.attachEntity(BarEntity.class); 
    5554        assertEquals("/bar/" + bar.id, router.getEntityURL(bar)); 
    56         router.alias("/baz/{id}", finder); 
     55        router.attach("/baz/{barId}", finder); 
     56        assertEquals("/bar/" + bar.id, router.getEntityURL(bar)); 
     57        router.setBaseRef("http://server/path"); 
     58        assertEquals("http://server/path/bar/" + bar.id, router 
     59                .getEntityURL(bar)); 
    5760    } 
    5861 
     
    6770                + "/bar/" + bar.id); 
    6871        assertEquals(bar.id, foundBar.id); 
    69         EntityInstanceFinder<BarEntity> finder = router 
    70                 .attachInstance(BarEntity.class); 
     72        EntityFinder<BarEntity> finder = router.attachEntity(BarEntity.class); 
    7173        foundBar = (BarEntity) router.getEntity("/bar/" + bar.id); 
    7274        assertEquals(bar.id, foundBar.id); 
    73         router.alias("/baz/{id}", finder); 
     75        router.attach("/baz/{barId}", finder); 
    7476        foundBar = (BarEntity) router.getEntity("/baz/" + bar.id); 
    7577        assertNull(foundBar); 
     78        router.setBaseRef("http://server/path"); 
     79        foundBar = (BarEntity) router.getEntity("http://server/path/bar/" 
     80                + bar.id); 
     81        assertEquals(bar.id, foundBar.id); 
     82        foundBar = (BarEntity) router.getEntity("/bar/" + bar.id); 
     83        assertEquals(bar.id, foundBar.id); 
    7684    } 
    7785