View Javadoc
1   /**
2    * This file Copyright (c) 2014-2015 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.config.registry;
35  
36  import info.magnolia.config.NamedDefinition;
37  
38  import java.lang.reflect.Method;
39  import java.util.Collection;
40  import java.util.Set;
41  
42  import com.google.common.base.Function;
43  import com.google.common.base.Predicate;
44  import com.google.common.base.Predicates;
45  import com.google.common.collect.Collections2;
46  import com.google.common.collect.Lists;
47  import com.thoughtworks.proxy.factory.CglibProxyFactory;
48  import com.thoughtworks.proxy.toys.decorate.Decorating;
49  import com.thoughtworks.proxy.toys.decorate.Decorator;
50  
51  /**
52   * Abstract {@link info.magnolia.config.registry.Registry} base class holding a map of all objects of the defined type.
53   *
54   * @param <T> the type of the contained objects
55   */
56  public abstract class AbstractRegistry<T> implements Registry<T> {
57  
58      private final RegistryMap<T> registryMap = new RegistryMap<>();
59  
60      private final CglibProxyFactory proxyFactory;
61  
62      public AbstractRegistry() {
63          proxyFactory = new CglibProxyFactory(false);
64      }
65  
66      @Override
67      public void start() {
68          throw new IllegalStateException("not implemented yet");
69      }
70  
71      protected RegistryMap<T> getRegistryMap() {
72          return registryMap;
73      }
74  
75      @Override
76      public void register(DefinitionProvider<T> provider) {
77          getRegistryMap().put(onRegister(provider));
78      }
79  
80      @Override
81      public Set<DefinitionMetadata> unregisterAndRegister(Collection<DefinitionMetadata> registeredIds, Collection<DefinitionProvider<T>> providers) {
82          final Collection<DefinitionProvider<T>> wrappedProviders = Collections2.transform(providers, new Function<DefinitionProvider<T>, DefinitionProvider<T>>() {
83              @Override
84              public DefinitionProvider<T> apply(DefinitionProvider<T> input) {
85                  return onRegister(input);
86              }
87          });
88          return getRegistryMap().removeAndPutAll(registeredIds, wrappedProviders);
89      }
90  
91      /**
92       * This default implementation wraps the given provider when the definition is a {@link NamedDefinition}; in such cases,
93       * the definition is proxied and its getName() method is intercepted and redirected to {@link DefinitionMetadata#getName()}.
94       * Overload or override this if the provider for this registry needs to "post-process" the provider/beans being registered.
95       * Don't forget to check {@link DefinitionProvider#isValid()} before calling {@link DefinitionProvider#get()}
96       * @see info.magnolia.config.registry.DefinitionProviderWrapper
97       */
98      protected DefinitionProvider<T> onRegister(DefinitionProvider<T> provider) {
99          if (provider.isValid()) {
100             final T def = provider.get();
101             final T decoratedDef;
102             if (def instanceof NamedDefinition) {
103                 decoratedDef = Decorating.proxy(def).visiting(new GetNameFromMetadata<>(provider)).build(proxyFactory);
104             } else {
105                 decoratedDef = def;
106             }
107 
108             return new SwappedDefinitionProviderWrapper<>(provider, decoratedDef);
109         } else {
110             return provider;
111         }
112     }
113 
114     @Override
115     public DefinitionProvider<T> getProvider(DefinitionMetadata id) {
116         return getRegistryMap().get(id);
117     }
118 
119     @Override
120     public DefinitionProvider<T> getProvider(String referenceId) {
121         final DefinitionProvider<T> provider = getRegistryMap().getByStringKey(referenceId);
122         if (provider == null) {
123             throw new NoSuchDefinitionException(referenceId);
124         }
125         return provider;
126     }
127 
128     @Override
129     public Collection<DefinitionProvider<T>> getAllProviders() {
130         return getRegistryMap().values();
131     }
132 
133     @Override
134     public Collection<DefinitionMetadata> getAllMetadata() {
135         return getRegistryMap().keySet();
136     }
137 
138     @Override
139     public Collection<T> getAllDefinitions() {
140         final Collection<DefinitionProvider<T>> validProviders = Collections2.filter(getAllProviders(), VALID);
141         return Lists.newArrayList(Collections2.transform(validProviders, new DefinitionProviderGet<T>()));
142     }
143 
144     private final Predicate<DefinitionProvider<T>> VALID = new ValidDefinitionProvider<>();
145     private final Predicate<DefinitionProvider<T>> ALL = Predicates.alwaysTrue();
146     private final Function<DefinitionProvider<T>, T> GET = new DefinitionProviderGet<>();
147 
148     private static class ValidDefinitionProvider<T> implements Predicate<DefinitionProvider<T>> {
149         @Override
150         public boolean apply(DefinitionProvider<T> input) {
151             return input.isValid();
152         }
153     }
154 
155     private static class DefinitionProviderGet<T> implements Function<DefinitionProvider<T>, T> {
156         @Override
157         public T apply(DefinitionProvider<T> input) {
158             return input.get();
159         }
160     }
161 
162     @Override
163     public DefinitionQuery<T> query() {
164         return new DefinitionQueryImpl<>(this);
165     }
166 
167     private static class DefinitionQueryImpl<T> extends DefinitionQuery<T> {
168         private final Registry registry;
169 
170         public DefinitionQueryImpl(Registry registry) {
171             this.registry = registry;
172         }
173 
174         @Override
175         public Collection<DefinitionProvider<T>> findMultiple() {
176             return findMultipleOn(registry);
177         }
178 
179         protected Collection<DefinitionProvider<T>> findMultipleOn(final Registry<T> registry) {
180             final Collection<DefinitionMetadata> allMetadata = registry.getAllMetadata();
181             final Collection<DefinitionMetadata> matchingMetas = Collections2.filter(allMetadata, new QueryPredicate<T>(this));
182             // TODO #transform wraps the original collection instead of copying. Make sure this is OK.
183             return Collections2.transform(matchingMetas, new Function<DefinitionMetadata, DefinitionProvider<T>>() {
184                 @Override
185                 public DefinitionProvider<T> apply(DefinitionMetadata md) {
186                     return registry.getProvider(md);
187                 }
188             });
189         }
190     }
191 
192     private static class QueryPredicate<T> implements Predicate<DefinitionMetadata> {
193         private final DefinitionQuery query;
194 
195         public QueryPredicate(DefinitionQuery query) {
196             this.query = query;
197         }
198 
199         @Override
200         public boolean apply(DefinitionMetadata input) {
201             boolean match = true;
202             match &= match(query.getName(), input.getName());
203             match &= match(query.getModule(), input.getModule());
204             return match;
205         }
206 
207         private boolean match(String queryParam, String actual) {
208             if (queryParam == null) {
209                 // then we ignore this param, consider it a match
210                 return true;
211             }
212             return queryParam.equals(actual);
213         }
214     }
215 
216     private static class GetNameFromMetadata<T> extends Decorator<T> {
217         private final DefinitionProvider<T> definitionProvider;
218 
219         public GetNameFromMetadata(DefinitionProvider<T> definitionProvider) {
220             this.definitionProvider = definitionProvider;
221         }
222 
223         @Override
224         public Object decorateResult(T proxy, Method method, Object[] args, Object result) {
225             if (result == null) {
226                 // TODO we kinda hard-code the NamedInterface method here . . .
227                 if ("getName".equals(method.getName())) {
228                     return definitionProvider.getMetadata().getName();
229                 }
230             }
231             return result;
232         }
233     }
234 
235     private static class SwappedDefinitionProviderWrapper<T> extends DefinitionProviderWrapper<T> {
236         private final T decoratedDef;
237 
238         public SwappedDefinitionProviderWrapper(DefinitionProvider<T> provider, T decoratedDef) {
239             super(provider);
240             this.decoratedDef = decoratedDef;
241         }
242 
243         @Override
244         public T get() {
245             return decoratedDef;
246         }
247     }
248 }