Changeset 3639
- Timestamp:
- 25/02/08 22:04:13 (4 years ago)
- Files:
-
- trunk/build.properties (modified) (1 diff)
- trunk/src/main/java/org/sarugo/restlet/jpa/ContextInjectingFinder.java (added)
- trunk/src/main/java/org/sarugo/restlet/jpa/EntityFinder.java (modified) (7 diffs)
- trunk/src/main/java/org/sarugo/restlet/jpa/EntityResource.java (modified) (1 diff)
- trunk/src/main/java/org/sarugo/restlet/jpa/EntityRoute.java (added)
- trunk/src/main/java/org/sarugo/restlet/jpa/EntityRouter.java (modified) (10 diffs)
- trunk/src/main/java/org/sarugo/restlet/jpa/InjectContext.java (added)
- trunk/src/main/java/org/sarugo/restlet/jpa/resource/EntityInstance.java (added)
- trunk/src/main/java/org/sarugo/restlet/jpa/resource/EntityList.java (added)
- trunk/src/test/java/org/sarugo/restlet/jpa/URLMappingTests.java (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/build.properties
r3613 r3639 1 1 project.name=restlet-jpa 2 project.rev=0. 33 project.status= release2 project.rev=0.4 3 project.status=integration trunk/src/main/java/org/sarugo/restlet/jpa/EntityFinder.java
r3613 r3639 21 21 import java.lang.reflect.Method; 22 22 import java.util.Collections; 23 import java.util.List;24 23 import java.util.Map; 25 24 import java.util.logging.Level; … … 27 26 import javax.persistence.Id; 28 27 29 import org.restlet.Finder; 30 import org.restlet.Route; 28 import org.restlet.Handler; 31 29 import org.restlet.data.Reference; 32 30 import org.restlet.data.Request; 33 31 import org.restlet.data.Response; 32 import org.restlet.resource.Resource; 33 import org.sarugo.restlet.jpa.resource.EntityInstance; 34 34 35 35 /** 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. 37 39 * 38 40 * @author Michael Terrington 39 41 * 40 * @param <E>41 * The entity type.42 42 */ 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; 43 public class EntityFinder extends ContextInjectingFinder { 44 45 public final static String REQUEST_ATTRIBUTE = EntityFinder.class.getName(); 46 47 private final Class<? extends Object> entityClass; 52 48 53 49 private final Class<? extends Object> entityIdClass; 54 50 51 private final Method getEntityMethod; 52 53 private final Method getParentEntityMethod; 54 55 private final Method getIdAttributesMethod; 56 55 57 private String idAttribute; 56 57 private Route route;58 58 59 59 /** … … 68 68 */ 69 69 @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); 75 72 this.idAttribute = "id"; 76 this.entityClass = entityClass; 73 EntityResource erAnnotation = resource 74 .getAnnotation(EntityResource.class); 75 this.entityClass = erAnnotation.entity(); 77 76 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 84 100 @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) { 93 102 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; 95 114 } 96 115 … … 98 117 * Convert a URL in the request to an entity instance. Works by first 99 118 * checking if any children match the requested URL. If no children match 100 * then the {@link Entity Resource} is constructed and101 * {@link Entity Resource#getEntity()} returned.119 * then the {@link EntityInstance} is constructed and 120 * {@link EntityInstance#getEntity()} returned. 102 121 */ 103 122 public Object getEntity(Request request) { 104 123 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); 130 134 } 131 135 } 132 136 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. For141 * entities with complex keys, provide a custom {@link EntityResource} and142 * 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 the155 * {@link #setIdAttribute(String) id attribute} to the first variable in the156 * 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;189 137 } 190 138 … … 200 148 * @return The parent entity instance or null if unknown / unavaiable. 201 149 */ 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; 204 164 } 205 165 … … 215 175 * {@link Reference#encode(String) URL encoded}. 216 176 */ 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 218 180 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); 240 199 try { 241 idValue = String.valueOf( m.invoke(entity));200 idValue = String.valueOf(f.get(entity)); 242 201 break; 243 202 } catch (IllegalArgumentException e) { … … 247 206 getLogger().log(Level.WARNING, 248 207 "Unable to get Id for object", e); 249 } catch (InvocationTargetException e) {250 getLogger().log(Level.WARNING,251 "Unable to get Id for object", e);252 208 } 253 209 } 254 210 } 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 261 238 return attributes; 262 239 } 263 240 264 /** Used by the default {@link Entity Resource} implementation. */265 public Class< E> getEntityClass() {241 /** Used by the default {@link EntityInstance} implementation. */ 242 public Class<? extends Object> getEntityClass() { 266 243 return entityClass; 267 244 } 268 245 269 /** Used by the default {@link Entity Resource} implementation. */246 /** Used by the default {@link EntityInstance} implementation. */ 270 247 public Class<? extends Object> getEntityIdClass() { 271 248 return entityIdClass; 272 249 } 273 250 274 /** Used by the default {@link Entity Resource} implementation. */251 /** Used by the default {@link EntityInstance} implementation. */ 275 252 public String getIdAttribute() { 276 253 return idAttribute; trunk/src/main/java/org/sarugo/restlet/jpa/EntityResource.java
r3613 r3639 1 /*2 * Copyright 2007, 2008 Sarugo Pty Ltd3 *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 at7 *8 * http://www.sun.com/cddl/9 *10 * Unless required by applicable law or agreed to in writing, software11 * distributed under the License is distributed on an "AS IS" BASIS,12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or13 * implied. See the License for the specific language governing14 * permissions and limitations under the License.15 */16 17 1 package org.sarugo.restlet.jpa; 18 2 19 import java.util.Map; 3 import java.lang.annotation.Documented; 4 import java.lang.annotation.ElementType; 5 import java.lang.annotation.Retention; 6 import java.lang.annotation.RetentionPolicy; 7 import java.lang.annotation.Target; 20 8 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) 12 public @interface EntityResource { 13 Class<? extends Object> entity(); 146 14 } trunk/src/main/java/org/sarugo/restlet/jpa/EntityRouter.java
r3613 r3639 24 24 25 25 import org.restlet.Context; 26 import org.restlet.Filter; 26 27 import org.restlet.Restlet; 27 28 import org.restlet.Route; … … 31 32 import org.restlet.data.Request; 32 33 import org.restlet.data.Response; 34 import org.restlet.resource.Resource; 33 35 34 36 /** … … 45 47 public class EntityRouter extends Router { 46 48 47 public static final String REQUEST_ATTRIBUTE = EntityRouter.class.getName();48 49 49 private final EntityManagerFactory entityManagerFactory; 50 50 51 51 private final ThreadLocal<EntityManager> entityManager; 52 52 53 private final Map<Class, Entity Finder> entities;54 55 private final Entity Finder<? extends Object>parent;53 private final Map<Class, EntityRoute> entities; 54 55 private final EntityRoute parent; 56 56 57 57 private Reference baseRef; … … 73 73 this.entityManagerFactory = entityManagerFactory; 74 74 this.entityManager = new ThreadLocal<EntityManager>(); 75 this.entities = new HashMap<Class, Entity Finder>();75 this.entities = new HashMap<Class, EntityRoute>(); 76 76 this.parent = null; 77 77 this.baseRef = null; 78 78 } 79 79 80 public EntityRouter(Entity Finder<? extends Object>parent) {80 public EntityRouter(EntityRoute parent) { 81 81 super(parent.getContext()); 82 82 this.parent = parent; 83 83 this.entityManagerFactory = parent.getRouter().entityManagerFactory; 84 84 this.entityManager = parent.getRouter().entityManager; 85 this.entities = new HashMap<Class, Entity Finder>();85 this.entities = new HashMap<Class, EntityRoute>(); 86 86 this.baseRef = null; 87 87 } … … 128 128 String url = null; 129 129 if (entity != null) { 130 Entity Finder finder= entities.get(entity.getClass());131 if ( finder!= null) {130 EntityRoute route = entities.get(entity.getClass()); 131 if (route != null) { 132 132 // this router holds the entity finder 133 133 @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()) { 138 138 @SuppressWarnings("unchecked") 139 Object parentEntity = finder.getParentEntity(entity); 139 Object parentEntity = route.getFinder().getParentEntity( 140 entity); 140 141 if (parentEntity != null) { 141 142 String parentUrl = getParent().getRouter() … … 150 151 } else { 151 152 // otherwise check if our children can map the entity 152 for (Entity Finderparent : entities.values()) {153 for (EntityRoute parent : entities.values()) { 153 154 url = parent.getChildRouter().getEntityURL(entity); 154 155 if (url != null) { … … 187 188 Object entity = null; 188 189 if (!isChild()) { 189 request.getAttributes().put(REQUEST_ATTRIBUTE, this);190 190 if (baseRef != null 191 191 && request.getResourceRef().toString(true, false) … … 195 195 } 196 196 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 } 207 220 } 208 221 } … … 226 239 */ 227 240 public boolean handleIfPossible(Request request, Response response) { 228 if (!isChild()) {229 request.getAttributes().put(REQUEST_ATTRIBUTE, this);230 }231 241 Restlet next = getNext(request, new Response(request)); 232 242 if (next != null) { … … 236 246 } 237 247 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() { 332 287 return parent; 333 288 } trunk/src/test/java/org/sarugo/restlet/jpa/URLMappingTests.java
r3613 r3639 6 6 7 7 import org.restlet.Context; 8 import org.sarugo.restlet.jpa.resource.EntityInstance; 8 9 9 10 public class URLMappingTests extends TestCase { … … 15 16 BarEntity bar; 16 17 17 private static class BarFinder extends EntityFinder<BarEntity> { 18 @EntityResource(entity = FooEntity.class) 19 public static class FooResource extends EntityInstance<FooEntity> { 18 20 19 public BarFinder(EntityRouter router) { 20 super(router, BarEntity.class, EntityResource.class); 21 } 21 } 22 22 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) { 25 27 return bar.foo; 26 28 } … … 31 33 router = new EntityRouter(new Context(), Persistence 32 34 .createEntityManagerFactory("test")); 33 EntityRoute r fooRouter = router.attachEntity(FooEntity.class)34 .getChildRouter();35 fooRoute r.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); 36 38 foo = new FooEntity(); 37 39 foo.name = "This is the foo"; … … 51 53 assertEquals("/foo/" + foo.id + "/bar/" + bar.id, router 52 54 .getEntityURL(bar)); 53 EntityFinder<BarEntity> finder = router.attachEntity(BarEntity.class); 55 EntityRoute barRoute = (EntityRoute) router.attach("/bar/{barId}", 56 BarResource.class); 54 57 assertEquals("/bar/" + bar.id, router.getEntityURL(bar)); 55 router.attach("/baz/{barId}", finder);58 router.attach("/baz/{barId}", barRoute.getNext()); 56 59 assertEquals("/bar/" + bar.id, router.getEntityURL(bar)); 57 60 router.setBaseRef("http://server/path"); … … 70 73 + "/bar/" + bar.id); 71 74 assertEquals(bar.id, foundBar.id); 72 EntityFinder<BarEntity> finder = router.attachEntity(BarEntity.class); 75 EntityRoute barRoute = (EntityRoute) router.attach("/bar/{barId}", 76 BarResource.class); 73 77 foundBar = (BarEntity) router.getEntity("/bar/" + bar.id); 74 78 assertEquals(bar.id, foundBar.id); 75 router.attach("/baz/{barId}", finder);79 router.attach("/baz/{barId}", barRoute.getNext()); 76 80 foundBar = (BarEntity) router.getEntity("/baz/" + bar.id); 77 assert Null(foundBar);81 assertEquals(bar.id, foundBar.id); 78 82 router.setBaseRef("http://server/path"); 79 83 foundBar = (BarEntity) router.getEntity("http://server/path/bar/"
