diff --git a/core/src/main/java/org/springframework/plugin/core/config/EnablePluginRegistries.java b/core/src/main/java/org/springframework/plugin/core/config/EnablePluginRegistries.java new file mode 100644 index 0000000..757f6fc --- /dev/null +++ b/core/src/main/java/org/springframework/plugin/core/config/EnablePluginRegistries.java @@ -0,0 +1,54 @@ +/* + * 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.plugin.core.config; + +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.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Import; +import org.springframework.plugin.core.Plugin; +import org.springframework.plugin.core.PluginRegistry; + +/** + * Enables exposure of {@link PluginRegistry} instances for the configured {@link Plugin} types + * + * @see #value() + * @author Oliver Gierke + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +@Import(PluginRegistriesBeanDefinitionRegistrar.class) +public @interface EnablePluginRegistries { + + /** + * The {@link Plugin} types to register {@link PluginRegistry} instances for. The registries will be named after the + * uncapitalized plugin type extended with {@code Registry}. So for a plugin interface {@code SamplePlugin} the + * exposed bean name will be {@code samplePluginRegistry}. This can be used on the client side to make sure you get + * the right {@link PluginRegistry} injected by using the {@link Qualifier} annotation and referring to that bean + * name. If the auto-generated bean name collides with one already in your application you can use the + * {@link Qualifier} annotation right at the plugin interface to define a custom name. + * + * @return + */ + Class>[] value(); +} diff --git a/core/src/main/java/org/springframework/plugin/core/config/PluginRegistriesBeanDefinitionRegistrar.java b/core/src/main/java/org/springframework/plugin/core/config/PluginRegistriesBeanDefinitionRegistrar.java new file mode 100644 index 0000000..419f698 --- /dev/null +++ b/core/src/main/java/org/springframework/plugin/core/config/PluginRegistriesBeanDefinitionRegistrar.java @@ -0,0 +1,68 @@ +/* + * 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.plugin.core.config; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.AutowireCandidateQualifier; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.plugin.core.support.PluginRegistryFactoryBean; +import org.springframework.util.StringUtils; + +/** + * {@link ImportBeanDefinitionRegistrar} to register {@link PluginRegistryFactoryBean} instances for type listed in + * {@link EnablePluginRegistries}. Picks up {@link Qualifier} annotations used on the plugin interface and forwards them + * to the bean definition for the factory. + * + * @author Oliver Gierke + */ +public class PluginRegistriesBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { + + /* + * (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) { + + Class[] types = (Class[]) importingClassMetadata.getAnnotationAttributes( + EnablePluginRegistries.class.getName()).get("value"); + + for (Class type : types) { + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(PluginRegistryFactoryBean.class); + builder.addPropertyValue("type", type); + + AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); + Qualifier annotation = type.getAnnotation(Qualifier.class); + + // If the plugin interface has a Qualifier annotation, propagate that to the bean definition of the registry + if (annotation != null) { + AutowireCandidateQualifier qualifierMetadata = new AutowireCandidateQualifier(Qualifier.class); + qualifierMetadata.setAttribute(AutowireCandidateQualifier.VALUE_KEY, annotation.value()); + beanDefinition.addQualifier(qualifierMetadata); + } + + // Default + String beanName = annotation == null ? StringUtils.uncapitalize(type.getSimpleName() + "Registry") : annotation + .value(); + registry.registerBeanDefinition(beanName, builder.getBeanDefinition()); + } + } +} diff --git a/core/src/test/java/org/springframework/plugin/core/config/EnablePluginRegistriesIntegrationTest.java b/core/src/test/java/org/springframework/plugin/core/config/EnablePluginRegistriesIntegrationTest.java new file mode 100644 index 0000000..72d0aaf --- /dev/null +++ b/core/src/test/java/org/springframework/plugin/core/config/EnablePluginRegistriesIntegrationTest.java @@ -0,0 +1,73 @@ +/* + * 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.plugin.core.config; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.plugin.core.Plugin; +import org.springframework.plugin.core.PluginRegistry; +import org.springframework.plugin.core.SamplePlugin; +import org.springframework.plugin.core.SamplePluginImplementation; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Integration tests for {@link EnablePluginRegistries}. + * + * @author Oliver Gierke + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class EnablePluginRegistriesIntegrationTest { + + @Configuration + @EnablePluginRegistries({ SamplePlugin.class, AnotherPlugin.class }) + static class Config { + + @Bean + public SamplePluginImplementation pluginImpl() { + return new SamplePluginImplementation(); + } + } + + @Autowired + @Qualifier("samplePluginRegistry") + PluginRegistry registry; + + @Test + public void registersPluginRegistries() { + assertThat(registry, is(notNullValue())); + } + + interface AnotherPlugin extends Plugin { + + } + + static class AnotherSamplePluginImplementation implements AnotherPlugin { + + @Override + public boolean supports(String delimiter) { + return true; + } + } +}