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:
Oliver Gierke
2012-10-29 15:20:10 +01:00
parent b8add74fc1
commit 04be1871c3
14 changed files with 1155 additions and 1 deletions

View File

@@ -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 {
}

View File

@@ -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();
}
}
}