Changeset 3613
- Timestamp:
- 12/02/08 17:16:29 (4 years ago)
- Files:
-
- trunk/build.properties (modified) (1 diff)
- trunk/src/main/java/org/sarugo/restlet/jpa/EntityCollectionFinder.java (deleted)
- trunk/src/main/java/org/sarugo/restlet/jpa/EntityFinder.java (added)
- trunk/src/main/java/org/sarugo/restlet/jpa/EntityHelper.java (modified) (3 diffs)
- trunk/src/main/java/org/sarugo/restlet/jpa/EntityInstanceFinder.java (deleted)
- trunk/src/main/java/org/sarugo/restlet/jpa/EntityResource.java (moved) (moved from trunk/src/main/java/org/sarugo/restlet/jpa/resource/EntityResource.java) (1 diff)
- trunk/src/main/java/org/sarugo/restlet/jpa/EntityRouter.java (modified) (17 diffs)
- trunk/src/main/java/org/sarugo/restlet/jpa/converter (deleted)
- trunk/src/main/java/org/sarugo/restlet/jpa/resource/EntityCollection.java (deleted)
- trunk/src/main/java/org/sarugo/restlet/jpa/resource/EntityInstance.java (deleted)
- trunk/src/test/java/org/sarugo/restlet/jpa/URLMappingTests.java (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/build.properties
r3603 r3613 1 1 project.name=restlet-jpa 2 2 project.rev=0.3 3 project.status= integration3 project.status=release trunk/src/main/java/org/sarugo/restlet/jpa/EntityHelper.java
r3606 r3613 17 17 package org.sarugo.restlet.jpa; 18 18 19 import java.beans.PropertyEditor; 20 import java.beans.PropertyEditorManager; 21 import java.lang.reflect.Constructor; 19 22 import java.lang.reflect.Field; 20 23 import java.lang.reflect.Method; … … 65 68 * or classes without any id fields/methods the type is set to 66 69 * {@link String}. This makes the default 67 * {@link Entity InstanceFinder#convertIdString(String)} a no-op for compound or70 * {@link EntityFinder#convertIdString(String)} a no-op for compound or 68 71 * undefined keys. 69 72 * … … 101 104 } 102 105 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 103 158 } trunk/src/main/java/org/sarugo/restlet/jpa/EntityResource.java
r3606 r3613 15 15 */ 16 16 17 package org.sarugo.restlet.jpa .resource;17 package org.sarugo.restlet.jpa; 18 18 19 import java.util.Map; 20 21 import javax.persistence.EntityManager; 22 23 import org.restlet.Context; 19 24 import org.restlet.data.Request; 20 25 import org.restlet.data.Response; 21 import org.restlet.resource.Representation;22 26 import 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;28 27 29 28 /** 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. 32 30 * 33 31 * @author Michael Terrington 34 32 */ 35 public abstractclass EntityResource<E> extends Resource {33 public class EntityResource<E> extends Resource { 36 34 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; 38 42 39 43 /** 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)}. 43 63 * 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> 46 69 */ 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); 51 75 } 52 76 } 53 77 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; 60 102 } 61 103 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; 72 107 } 73 108 74 109 /** 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()}. 77 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). */ 78 141 @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; 88 144 } 89 145 trunk/src/main/java/org/sarugo/restlet/jpa/EntityRouter.java
r3607 r3613 19 19 import java.util.HashMap; 20 20 import java.util.Map; 21 import java.util.Set;22 import java.util.concurrent.CopyOnWriteArraySet;23 21 24 22 import javax.persistence.EntityManager; … … 33 31 import org.restlet.data.Request; 34 32 import 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;38 33 39 34 /** … … 41 36 * should share a common router, this enables link resolution. 42 37 * 43 * {@link Entity InstanceFinder}s are attached to handle entity instances or38 * {@link EntityFinder}s are attached to handle entity instances or 44 39 * collections. Instances are attached independently from collections. An 45 * {@link Entity InstanceFinder} can only be attached to one URL, however it may46 * 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}. 47 42 * 48 43 * @author Michael Terrington … … 50 45 public class EntityRouter extends Router { 51 46 47 public static final String REQUEST_ATTRIBUTE = EntityRouter.class.getName(); 48 52 49 private final EntityManagerFactory entityManagerFactory; 53 50 54 51 private final ThreadLocal<EntityManager> entityManager; 55 52 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; 61 56 62 57 private Reference baseRef; … … 78 73 this.entityManagerFactory = entityManagerFactory; 79 74 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>(); 82 76 this.parent = null; 83 77 this.baseRef = null; 84 78 } 85 79 86 public EntityRouter(Entity InstanceFinder<? extends Object> parent) {80 public EntityRouter(EntityFinder<? extends Object> parent) { 87 81 super(parent.getContext()); 88 82 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>(); 93 86 this.baseRef = null; 94 87 } … … 104 97 * Get a thread-local {@link EntityManager}. Typically during request 105 98 * 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. 112 104 */ 113 105 public EntityManager getEntityManager() { … … 126 118 127 119 /** 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}. 130 121 * 131 122 * @param entity 132 123 * The entity to get a URL for. 133 124 * @return The entity's URL relative to this router. 134 * @see #attachInstance(String, EntityInstanceFinder) 135 */ 136 @SuppressWarnings("unchecked") 125 * @see #attachEntity(String, EntityFinder) 126 */ 137 127 public String getEntityURL(Object entity) { 138 EntityInstanceFinder finder = entities.get(entity.getClass());139 128 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); 151 154 if (url != null) { 152 155 break; … … 154 157 } 155 158 } 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 } 159 162 } 160 163 return url; … … 183 186 public Object getEntity(Request request) { 184 187 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 } 185 196 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 } 189 208 } 190 209 return entity; … … 204 223 * mappings. For transation support see {@link TransactionFilter}. 205 224 * 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. 209 226 */ 210 227 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)); 212 232 if (next != null) { 213 233 next.handle(request, response); … … 217 237 218 238 /** 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}. 224 243 * 225 244 * @param entityClass 226 245 * The entity class to attach. 227 246 * @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 243 260 * "{entityName}" is formed by calling 244 261 * {@link EntityURLHelper#getEntityName(Class)}. … … 249 266 * @throws IllegalStateException 250 267 * If the finder has already been attached. 251 * @see #attachCollection(Entity InstanceFinder) for attaching an252 * {@link Entity InstanceFinder} for the entity collection.253 */ 254 public <E extends Object> Entity InstanceFinder<E> attachInstance(255 Entity InstanceFinder<E> finder) {256 return attach Instance(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( 257 274 "/" + EntityHelper.getEntityName(finder.getEntityClass()) 258 275 + "/{id}", finder); … … 260 277 261 278 /** 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}. 267 283 * 268 284 * @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)} 272 287 * expects the URL to contain "{id}" to define the entity id. 273 288 * @param entityClass … … 275 290 * @return The finder for the entity. 276 291 * @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. 289 303 * 290 304 * @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)} 294 307 * expects the URL to contain "{id}" to define the entity id. 295 308 * @param finder … … 299 312 * If the finder has already been attached. 300 313 * @see #attachCollection(String, EntityFinder)) for attaching an 301 * {@link Entity InstanceFinder} for the entity collection.302 */ 303 public <E extends Object> Entity InstanceFinder<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) { 305 318 if (entities.containsKey(finder.getEntityClass())) { 306 319 throw new IllegalStateException("Entity class is already mapped."); … … 316 329 } 317 330 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() { 422 332 return parent; 423 333 } … … 425 335 public boolean isChild() { 426 336 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(child434 .getEntityClass());435 if (parents == null) {436 parents = new CopyOnWriteArraySet<EntityInstanceFinder>();437 childEntities.put(child.getEntityClass(), parents);438 }439 parents.add(child);440 337 } 441 338 trunk/src/test/java/org/sarugo/restlet/jpa/URLMappingTests.java
r3609 r3613 6 6 7 7 import org.restlet.Context; 8 import org.sarugo.restlet.jpa.resource.EntityInstance;9 8 10 9 public class URLMappingTests extends TestCase { … … 16 15 BarEntity bar; 17 16 18 private static class Bar Resource extends EntityInstance<BarEntity> {17 private static class BarFinder extends EntityFinder<BarEntity> { 19 18 20 public Bar Resource(EntityInstanceFinder<BarEntity> finder) {21 super( finder);19 public BarFinder(EntityRouter router) { 20 super(router, BarEntity.class, EntityResource.class); 22 21 } 23 22 24 23 @Override 25 public Object getParentEntity( ) {26 return getEntity().foo;24 public Object getParentEntity(BarEntity bar) { 25 return bar.foo; 27 26 } 28 27 … … 32 31 router = new EntityRouter(new Context(), Persistence 33 32 .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)); 36 36 foo = new FooEntity(); 37 37 foo.name = "This is the foo"; … … 51 51 assertEquals("/foo/" + foo.id + "/bar/" + bar.id, router 52 52 .getEntityURL(bar)); 53 EntityInstanceFinder<BarEntity> finder = router 54 .attachInstance(BarEntity.class); 53 EntityFinder<BarEntity> finder = router.attachEntity(BarEntity.class); 55 54 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)); 57 60 } 58 61 … … 67 70 + "/bar/" + bar.id); 68 71 assertEquals(bar.id, foundBar.id); 69 EntityInstanceFinder<BarEntity> finder = router 70 .attachInstance(BarEntity.class); 72 EntityFinder<BarEntity> finder = router.attachEntity(BarEntity.class); 71 73 foundBar = (BarEntity) router.getEntity("/bar/" + bar.id); 72 74 assertEquals(bar.id, foundBar.id); 73 router.a lias("/baz/{id}", finder);75 router.attach("/baz/{barId}", finder); 74 76 foundBar = (BarEntity) router.getEntity("/baz/" + bar.id); 75 77 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); 76 84 } 77 85
