Introduced EntityLinks abstraction.
EntityLinks allow accessing LinkBuilders or even Link instance based on components exposing REST resources for a particular domain type. The core implementation is ControllerEntityLinks that assumes a particular URI structure so that it can derive the URIs to be asked for per domain type. The domain type managed will be inspected from the @ExposesResourceFor annotation on a controller class. We provide a factory bean to allow configuring instances of ControllerEntityLinks based on a mapping annotation and LinkBuilderFactory. Added infrastructure to activate automatic discovery of EntityLinks implementations through @EnableEntityLinks. This will automatically register ControllerEntityLinks instances for Spring MVC controllers annotated with @ExposesResourceFor as well as JAX-RS resources if JAX-RS is on the classpath. The mechanism essentially registers a DelegatingEntityLinks instance leveraging the Spring Plugin PluginRegistry mechanism to delegate to the actual EntityLinks instance actually registered for the entity type. It will become the primary EntityLinks bean in the ApplicationContext so that they can be autowired into client components safely.
This commit is contained in:
91
src/main/java/org/springframework/hateoas/EntityLinks.java
Normal file
91
src/main/java/org/springframework/hateoas/EntityLinks.java
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.hateoas;
|
||||
|
||||
import org.springframework.plugin.core.Plugin;
|
||||
|
||||
/**
|
||||
* Accessor to links pointing to controllers backing an entity type.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface EntityLinks extends Plugin<Class<?>> {
|
||||
|
||||
/**
|
||||
* Returns a {@link LinkBuilder} able to create links to the controller managing the given entity type. Expects a
|
||||
* controller being mapped to a fully expanded URI template (i.e. not path variables being used).
|
||||
*
|
||||
* @param type the entity type to point to, must not be {@literal null}.
|
||||
* @return the {@link LinkBuilder} pointing to the collection resource. Will never be {@literal null}.
|
||||
*/
|
||||
LinkBuilder linkFor(Class<?> type);
|
||||
|
||||
/**
|
||||
* Returns a {@link LinkBuilder} able to create links to the controller managing the given entity type, unfolding the
|
||||
* given parameters into the URI template the backing controller is mapped to.
|
||||
*
|
||||
* @param type the entity type to point to, must not be {@literal null}.
|
||||
* @return the {@link LinkBuilder} pointing to the collection resource. Will never be {@literal null}.
|
||||
*/
|
||||
LinkBuilder linkFor(Class<?> type, Object... parameters);
|
||||
|
||||
/**
|
||||
* Returns a {@link LinkBuilder} able to create links to the controller managing the given entity type and id.
|
||||
* Implementations will know about the URI structure being used to expose single-resource URIs.
|
||||
*
|
||||
* @param type the entity type to point to, must not be {@literal null}.
|
||||
* @param id the id of the object of the handed type, {@link Identifiable}s will be unwrapped.
|
||||
* @return
|
||||
*/
|
||||
LinkBuilder linkForSingleResource(Class<?> type, Object id);
|
||||
|
||||
/**
|
||||
* Returns a {@link LinkBuilder} able to create links to the controller managing the given entity.
|
||||
*
|
||||
* @see #linkForSingleResource(Class, Object)
|
||||
* @param entity the entity type to point to, must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
LinkBuilder linkForSingleResource(Identifiable<?> entity);
|
||||
|
||||
/**
|
||||
* Creates a {@link Link} pointing to the collection resource of the given type. The relation type of the link will be
|
||||
* determined by the implementation class and should be defaulted to {@link Link#REL_SELF}.
|
||||
*
|
||||
* @param type the entity type to point to, must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Link linkToCollectionResource(Class<?> type);
|
||||
|
||||
/**
|
||||
* Creates a {@link Link} pointing to single resource backing the given entity type and id. The relation type of the
|
||||
* link will be determined by the implementation class and should be defaulted to {@link Link#REL_SELF}.
|
||||
*
|
||||
* @param type the entity type to point to, must not be {@literal null}.
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
Link linkToSingleResource(Class<?> type, Object id);
|
||||
|
||||
/**
|
||||
* Creates a {@link Link} pointing to single resource backing the given entity. The relation type of the link will be
|
||||
* determined by the implementation class and should be defaulted to {@link Link#REL_SELF}.
|
||||
*
|
||||
* @param entity the entity type to point to, must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Link linkToSingleResource(Identifiable<?> entity);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.hateoas;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.hateoas.core.ControllerEntityLinks;
|
||||
|
||||
/**
|
||||
* Annotation to demarcate controllers that expose URI templates of a structure according to
|
||||
* {@link ControllerEntityLinks}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface ExposesResourceFor {
|
||||
|
||||
/**
|
||||
* The domain type the controller exposes resources for.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<?> value();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.hateoas.config;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.hateoas.LinkBuilder;
|
||||
import org.springframework.hateoas.core.DelegatingEntityLinks;
|
||||
|
||||
/**
|
||||
* Enables the collection of {@link LinkBuilder} instances from the application context. All found ones will be exposed
|
||||
* through an instance of {@link DelegatingEntityLinks}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Import(LinkBuilderBeanDefinitionRegistrar.class)
|
||||
public @interface EnableEntityLinks {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.hateoas.config;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.LinkBuilderFactory;
|
||||
import org.springframework.hateoas.core.ControllerEntityLinksFactoryBean;
|
||||
import org.springframework.hateoas.core.DelegatingEntityLinks;
|
||||
import org.springframework.hateoas.jaxrs.JaxRsLinkBuilderFactory;
|
||||
import org.springframework.hateoas.mvc.ControllerLinkBuilderFactory;
|
||||
import org.springframework.plugin.core.support.PluginRegistryFactoryBean;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link ImportBeanDefinitionRegistrar} to register a {@link DelegatingEntityLinks} instance as well as a
|
||||
* {@link ControllerEntityLinksFactoryBean} for Spring MVC controllers and JAX-RS resources if present.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class LinkBuilderBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
private static final boolean IS_JAX_RS_PRESENT = ClassUtils.isPresent("javax.ws.rs.Path",
|
||||
ClassUtils.getDefaultClassLoader());
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
|
||||
*/
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
|
||||
BeanDefinitionBuilder registryFactoryBeanBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(PluginRegistryFactoryBean.class);
|
||||
registryFactoryBeanBuilder.addPropertyValue("type", EntityLinks.class);
|
||||
registryFactoryBeanBuilder.addPropertyValue("exclusions", DelegatingEntityLinks.class);
|
||||
|
||||
AbstractBeanDefinition registryBeanDefinition = registryFactoryBeanBuilder.getBeanDefinition();
|
||||
registry.registerBeanDefinition("entityLinksPluginRegistry", registryBeanDefinition);
|
||||
|
||||
BeanDefinitionBuilder delegateBuilder = BeanDefinitionBuilder.rootBeanDefinition(DelegatingEntityLinks.class);
|
||||
delegateBuilder.addConstructorArgValue(registryBeanDefinition);
|
||||
|
||||
BeanDefinitionBuilder builder = getEntityControllerLinksFor(Controller.class, ControllerLinkBuilderFactory.class);
|
||||
registry.registerBeanDefinition("controllerEntityLinks", builder.getBeanDefinition());
|
||||
delegateBuilder.addDependsOn("controllerEntityLinks");
|
||||
|
||||
if (IS_JAX_RS_PRESENT) {
|
||||
JaxRsEntityControllerBuilderDefinitionBuilder definitionBuilder = new JaxRsEntityControllerBuilderDefinitionBuilder();
|
||||
registry.registerBeanDefinition("jaxRsEntityLinks", definitionBuilder.getBeanDefinition());
|
||||
delegateBuilder.addDependsOn("jaxRsEntityLinks");
|
||||
}
|
||||
|
||||
AbstractBeanDefinition beanDefinition = delegateBuilder.getBeanDefinition();
|
||||
beanDefinition.setPrimary(true);
|
||||
registry.registerBeanDefinition("delegatingEntityLinks", beanDefinition);
|
||||
}
|
||||
|
||||
private static BeanDefinitionBuilder getEntityControllerLinksFor(Class<? extends Annotation> type,
|
||||
Class<? extends LinkBuilderFactory<?>> linkBuilderFactoryType) {
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ControllerEntityLinksFactoryBean.class);
|
||||
builder.addPropertyValue("annotation", type);
|
||||
builder.addPropertyValue("linkBuilderFactory", new RootBeanDefinition(linkBuilderFactoryType));
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
static class JaxRsEntityControllerBuilderDefinitionBuilder {
|
||||
|
||||
public BeanDefinition getBeanDefinition() {
|
||||
BeanDefinitionBuilder builder = getEntityControllerLinksFor(Path.class, JaxRsLinkBuilderFactory.class);
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.hateoas.core;
|
||||
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.Identifiable;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkBuilder;
|
||||
|
||||
/**
|
||||
* Implementation base class to delegate the higher level methods of {@link EntityLinks} by delegating to the more fine
|
||||
* grained ones to reduce the implementation effort for actual implementation classes.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class AbstractEntityLinks implements EntityLinks {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#getLinkToSingleResource(org.springframework.hateoas.Identifiable)
|
||||
*/
|
||||
@Override
|
||||
public Link linkToSingleResource(Identifiable<?> entity) {
|
||||
return linkToSingleResource(entity.getClass(), entity.getId());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#linkForSingleResource(java.lang.Class, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public LinkBuilder linkForSingleResource(Class<?> type, Object id) {
|
||||
return linkFor(type).slash(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#linkForSingleResource(org.springframework.hateoas.Identifiable)
|
||||
*/
|
||||
@Override
|
||||
public LinkBuilder linkForSingleResource(Identifiable<?> entity) {
|
||||
return linkForSingleResource(entity.getClass(), entity.getId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.hateoas.core;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkBuilder;
|
||||
import org.springframework.hateoas.LinkBuilderFactory;
|
||||
import org.springframework.hateoas.ExposesResourceFor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link EntityLinks} implementation which assumes a certain URI mapping structure:
|
||||
* <ol>
|
||||
* <li>A class-level mapping annotation that can contain template variables. The URI needs to expose the collection
|
||||
* resource, which means the controller has to expose a handler method mapped to an empty path: e.g.
|
||||
* {@code @RequestMapping(method = RequestMethod.GET)} in case of a Spring MVC controller.</li>
|
||||
* <li>Individual resources are exposed via a nested mapping consisting of the id of the managed entity, e.g. {@code
|
||||
* @RequestMapping("/{id}")}.<li>
|
||||
* </ol>
|
||||
* <code>
|
||||
* @Controller
|
||||
* @ExposesResourceFor(Order.class)
|
||||
* @RequestMapping("/orders")
|
||||
* class OrderController {
|
||||
*
|
||||
* @RequestMapping
|
||||
* ResponseEntity orders(…) { … }
|
||||
*
|
||||
* @RequestMapping("/{id}")
|
||||
* ResponseEntity order(@PathVariable("id") … ) { … }
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ControllerEntityLinks extends AbstractEntityLinks {
|
||||
|
||||
private final Map<Class<?>, Class<?>> entityToController;
|
||||
private final LinkBuilderFactory<? extends LinkBuilder> linkBuilderFactory;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ControllerEntityLinks} inspecting the configured classes for the given annotation.
|
||||
*
|
||||
* @param controllerTypes the controller classes to be inspected.
|
||||
* @param linkBuilderFactory the {@link LinkBuilder} to use to create links.
|
||||
*/
|
||||
public ControllerEntityLinks(Iterable<? extends Class<?>> controllerTypes,
|
||||
LinkBuilderFactory<? extends LinkBuilder> linkBuilderFactory) {
|
||||
|
||||
Assert.notNull(controllerTypes);
|
||||
Assert.notNull(linkBuilderFactory);
|
||||
|
||||
this.linkBuilderFactory = linkBuilderFactory;
|
||||
this.entityToController = new HashMap<Class<?>, Class<?>>();
|
||||
|
||||
for (Class<?> controllerType : controllerTypes) {
|
||||
registerControllerClass(controllerType);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerControllerClass(Class<?> controllerType) {
|
||||
|
||||
Assert.notNull(controllerType, "Controller type must nor be null!");
|
||||
ExposesResourceFor annotation = AnnotationUtils.findAnnotation(controllerType, ExposesResourceFor.class);
|
||||
|
||||
if (annotation != null) {
|
||||
entityToController.put(annotation.value(), controllerType);
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Controller %s must be annotated with @ExposesResourceFor!",
|
||||
controllerType.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#linkTo(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public LinkBuilder linkFor(Class<?> entity) {
|
||||
|
||||
Class<?> controllerClass = entityToController.get(entity);
|
||||
return linkBuilderFactory.linkTo(controllerClass);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#linkTo(java.lang.Class, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public LinkBuilder linkFor(Class<?> entity, Object... parameters) {
|
||||
return linkFor(entity, parameters);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#getLinkToCollectionResource(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public Link linkToCollectionResource(Class<?> entity) {
|
||||
return linkFor(entity).withSelfRel();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#getLinkToSingleResource(java.lang.Class, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Link linkToSingleResource(Class<?> entity, Object id) {
|
||||
return linkFor(entity).slash(id).withSelfRel();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> delimiter) {
|
||||
return entityToController.containsKey(delimiter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.hateoas.core;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.AbstractFactoryBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.hateoas.ExposesResourceFor;
|
||||
import org.springframework.hateoas.LinkBuilder;
|
||||
import org.springframework.hateoas.LinkBuilderFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} implementation to create {@link ControllerEntityLinks} instances looking up controller classes
|
||||
* from an {@link ApplicationContext}. The controller types are identified by the annotation type configured.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ControllerEntityLinksFactoryBean extends AbstractFactoryBean<ControllerEntityLinks> implements
|
||||
ApplicationContextAware {
|
||||
|
||||
private Class<? extends Annotation> annotation;
|
||||
private LinkBuilderFactory<? extends LinkBuilder> linkBuilderFactory;
|
||||
private ApplicationContext context;
|
||||
|
||||
/**
|
||||
* Configures the annotation type to inspect the {@link ApplicationContext} for beans that carry the given annotation.
|
||||
*
|
||||
* @param annotation must not be {@literal null}.
|
||||
*/
|
||||
public void setAnnotation(Class<? extends Annotation> annotation) {
|
||||
Assert.notNull(annotation);
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link LinkBuilderFactory} to be used to create {@link LinkBuilder} instances.
|
||||
*
|
||||
* @param linkBuilderFactory the linkBuilderFactory to set
|
||||
*/
|
||||
public void setLinkBuilderFactory(LinkBuilderFactory<? extends LinkBuilder> linkBuilderFactory) {
|
||||
this.linkBuilderFactory = linkBuilderFactory;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
|
||||
*/
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#getObjectType()
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return ControllerEntityLinks.class;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
|
||||
*/
|
||||
@Override
|
||||
protected ControllerEntityLinks createInstance() throws Exception {
|
||||
|
||||
Collection<Class<?>> controllerTypes = new HashSet<Class<?>>();
|
||||
|
||||
for (Class<?> controllerType : getBeanTypesWithAnnotation(annotation)) {
|
||||
if (AnnotationUtils.findAnnotation(controllerType, ExposesResourceFor.class) != null) {
|
||||
controllerTypes.add(controllerType);
|
||||
}
|
||||
}
|
||||
|
||||
return new ControllerEntityLinks(controllerTypes, linkBuilderFactory);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.AbstractFactoryBean#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
Assert.state(annotation != null, "Annotation type must be configured!");
|
||||
Assert.state(linkBuilderFactory != null, "LinkBuilderFactory must be configured!");
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
private Iterable<Class<?>> getBeanTypesWithAnnotation(Class<? extends Annotation> type) {
|
||||
|
||||
Set<Class<?>> annotatedTypes = new HashSet<Class<?>>();
|
||||
|
||||
for (String beanName : context.getBeanDefinitionNames()) {
|
||||
|
||||
Annotation annotation = context.findAnnotationOnBean(beanName, type);
|
||||
if (annotation != null) {
|
||||
annotatedTypes.add(context.getType(beanName));
|
||||
}
|
||||
}
|
||||
|
||||
return annotatedTypes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.hateoas.core;
|
||||
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkBuilder;
|
||||
import org.springframework.plugin.core.PluginRegistry;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link EntityLinks} implementation that delegates to the {@link EntityLinks} instances registered in the
|
||||
* {@link PluginRegistry} given on instance creation.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class DelegatingEntityLinks extends AbstractEntityLinks {
|
||||
|
||||
private final PluginRegistry<EntityLinks, Class<?>> delegates;
|
||||
|
||||
/**
|
||||
* Creates a new {@link DelegatingEntityLinks} using the given {@link PluginRegistry}.
|
||||
*
|
||||
* @param plugins must not be {@literal null}.
|
||||
*/
|
||||
public DelegatingEntityLinks(PluginRegistry<EntityLinks, Class<?>> plugins) {
|
||||
|
||||
Assert.notNull(plugins, "PluginRegistry must not be null!");
|
||||
this.delegates = plugins;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#linkFor(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public LinkBuilder linkFor(Class<?> type) {
|
||||
return getPluginFor(type).linkFor(type);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#linkFor(java.lang.Class, java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
public LinkBuilder linkFor(Class<?> type, Object... parameters) {
|
||||
return getPluginFor(type).linkFor(type, parameters);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#getLinkToCollectionResource(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public Link linkToCollectionResource(Class<?> type) {
|
||||
return getPluginFor(type).linkToCollectionResource(type);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.EntityLinks#getLinkToSingleResource(java.lang.Class, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Link linkToSingleResource(Class<?> type, Object id) {
|
||||
return getPluginFor(type).linkToSingleResource(type, id);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.plugin.core.Plugin#supports(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> delimiter) {
|
||||
return delegates.hasPluginFor(delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plugin for the given type or throws an {@link IllegalStateException} if no delegate {@link EntityLinks}
|
||||
* can be found.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private EntityLinks getPluginFor(Class<?> type) {
|
||||
|
||||
EntityLinks plugin = delegates.getPluginFor(type);
|
||||
|
||||
if (plugin == null) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Cannot determine link for %s! No EntityLinks instance found supporting the domain type!", type.getName()));
|
||||
}
|
||||
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.hateoas.mvc;
|
||||
|
||||
import org.springframework.hateoas.LinkBuilder;
|
||||
import org.springframework.hateoas.core.LinkBuilderSupport;
|
||||
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* Simples {@link LinkBuilder} implementation possible. Exposes a link to the current servlet mapping only.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class BasicLinkBuilder extends LinkBuilderSupport<BasicLinkBuilder> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link BasicLinkBuilder} using the given {@link UriComponentsBuilder}.
|
||||
*
|
||||
* @param builder must not be {@literal null}.
|
||||
*/
|
||||
private BasicLinkBuilder(UriComponentsBuilder builder) {
|
||||
super(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link BasicLinkBuilder} to link to the current servlet mapping.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static BasicLinkBuilder linkToCurrentMapping() {
|
||||
return new BasicLinkBuilder(ServletUriComponentsBuilder.fromCurrentServletMapping());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.mvc.LinkBuilderSupport#createNewInstance(org.springframework.web.util.UriComponentsBuilder)
|
||||
*/
|
||||
@Override
|
||||
protected BasicLinkBuilder createNewInstance(UriComponentsBuilder builder) {
|
||||
return new BasicLinkBuilder(builder);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.mvc.LinkBuilderSupport#getThis()
|
||||
*/
|
||||
@Override
|
||||
protected BasicLinkBuilder getThis() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user