View Javadoc
1   /**
2    * This file Copyright (c) 2013-2018 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.ui.form.fieldtype.registry;
35  
36  import info.magnolia.config.registry.AbstractRegistry;
37  import info.magnolia.config.registry.DefinitionMetadataBuilder;
38  import info.magnolia.config.registry.DefinitionProvider;
39  import info.magnolia.config.registry.DefinitionProviderBuilder;
40  import info.magnolia.config.registry.DefinitionType;
41  import info.magnolia.dynamic.MagnoliaProxy;
42  import info.magnolia.module.ModuleRegistry;
43  import info.magnolia.objectfactory.Components;
44  import info.magnolia.registry.RegistrationException;
45  import info.magnolia.ui.form.field.definition.ConfiguredFieldDefinition;
46  import info.magnolia.ui.form.field.definition.FieldDefinition;
47  import info.magnolia.ui.form.fieldtype.definition.FieldTypeDefinition;
48  
49  import java.util.Optional;
50  import java.util.concurrent.ExecutionException;
51  import java.util.concurrent.TimeUnit;
52  
53  import javax.inject.Inject;
54  import javax.inject.Singleton;
55  
56  import com.google.common.cache.CacheBuilder;
57  import com.google.common.cache.CacheLoader;
58  import com.google.common.cache.LoadingCache;
59  
60  import net.sf.cglib.proxy.Enhancer;
61  
62  /**
63   * FieldTypeDefinitionRegistry.
64   */
65  @Singleton
66  public class FieldTypeDefinitionRegistry extends AbstractRegistry<FieldTypeDefinition> {
67  
68      private static final DefinitionProvider<FieldTypeDefinition> NULL_DEFINITION_PROVIDER = DefinitionProviderBuilder.<FieldTypeDefinition>newBuilder().build();
69  
70      private final LoadingCache<Class, DefinitionProvider<FieldTypeDefinition>> definitionProviderCache =
71              CacheBuilder.newBuilder()
72                      .maximumSize(100)
73                      .refreshAfterWrite(1000, TimeUnit.MILLISECONDS)
74                      .initialCapacity(100)
75                      .build(new CacheLoader<Class, DefinitionProvider<FieldTypeDefinition>>() {
76                          @Override
77                          public DefinitionProvider<FieldTypeDefinition> load(Class key) throws Exception {
78                              return resolveMostActualDefinitionProvider(key);
79                          }
80                      });
81  
82      @Inject
83      public FieldTypeDefinitionRegistry(ModuleRegistry moduleRegistry) {
84          super(moduleRegistry);
85      }
86  
87      /**
88       * @deprecated since 5.4.6 - use {@link #FieldTypeDefinitionRegistry(ModuleRegistry)} instead.
89       */
90      @Deprecated
91      public FieldTypeDefinitionRegistry() {
92          this(Components.getComponent(ModuleRegistry.class));
93      }
94  
95      public Optional<DefinitionProvider<FieldTypeDefinition>> getByDefinitionType(Class<? extends FieldDefinition> definitionClass) {
96          try {
97              return Optional.ofNullable(definitionProviderCache.get(definitionClass, () -> resolveMostActualDefinitionProvider(definitionClass)));
98          } catch (ExecutionException e) {
99              return Optional.empty();
100         }
101     }
102 
103     public DefinitionProvider<FieldTypeDefinition> resolveMostActualDefinitionProvider(Class<? extends FieldDefinition> definitionClass) throws RegistrationException {
104         // Should the provided class be proxied by either i18n-mechanism or some other
105         // functionality - un-wrap the original type.
106         while (Enhancer.isEnhanced(definitionClass) || MagnoliaProxy.class.isAssignableFrom(definitionClass)) {
107             definitionClass = (Class<? extends FieldDefinition>) definitionClass.getSuperclass();
108         }
109 
110         if (definitionClass.equals(ConfiguredFieldDefinition.class)) {
111             // FIXME MGNLUI-829 Working around side effect of extend=override, can't do anything with ConfiguredFieldDefinition.
112             throw new RegistrationException("");
113         }
114 
115         DefinitionProvider<FieldTypeDefinition> deprecatedFieldTypeDefinition = null;
116         DefinitionProvider<FieldTypeDefinition> nonDeprecatedFieldTypeDefinition = null;
117         for (DefinitionProvider<FieldTypeDefinition> provider : getRegistryMap().values()) {
118             provider = getDecoratedDefinitionProvider(provider);
119             final FieldTypeDefinition fieldTypeDefinition = provider.get();
120             if (definitionClass.equals(fieldTypeDefinition.getDefinitionClass())) {
121                 // No matter what we prioritize decorated definitions first
122                 if (provider.getDecorators().size() > 0) {
123                     return provider;
124                 }
125 
126                 // We store the deprecated and non-deprecated providers
127                 if (!provider.getMetadata().getDeprecation().isPresent()) {
128                     nonDeprecatedFieldTypeDefinition = provider;
129                 } else {
130                     deprecatedFieldTypeDefinition = provider;
131                 }
132             }
133         }
134 
135         // We couldn't find any decorated ones, therefore we return non deprecated definition.
136         if (nonDeprecatedFieldTypeDefinition != null) {
137             return nonDeprecatedFieldTypeDefinition;
138         }
139 
140         // At this stage we don't have any non-deprecated definitions nor decorated ones,
141         // therefore we return deprecated one if it exists
142         if (deprecatedFieldTypeDefinition != null) {
143             return deprecatedFieldTypeDefinition;
144         }
145 
146         throw new RegistrationException("Could not find fieldType for definition " + definitionClass.getName());
147     }
148 
149     public FieldTypeDefinition getByDefinition(Class<? extends FieldDefinition> definitionClass) throws RegistrationException {
150         return getByDefinitionType(definitionClass)
151                 .filter(DefinitionProvider::isValid)
152                 .map(DefinitionProvider::get)
153                 .orElseThrow(() -> new RegistrationException("Field type definition not found or invalid for field definition type: " + definitionClass.getName()));
154     }
155 
156     /**
157      * @deprecated since 5.4 - use the {@link #getProvider(String)} method instead and fetch definition from its result.
158      */
159     @Deprecated
160     public FieldTypeDefinition get(String id) throws RegistrationException {
161         final FieldTypeDefinition fieldTypeDefinition;
162         try {
163             fieldTypeDefinition = getProvider(id).get();
164         } catch (NoSuchDefinitionException | InvalidDefinitionException e) {
165             throw new RegistrationException(e.getMessage(), e);
166         }
167         return fieldTypeDefinition;
168     }
169 
170     @Override
171     public DefinitionType type() {
172         return DefinitionTypes.FIELD_TYPE;
173     }
174 
175     @Override
176     public DefinitionMetadataBuilder newMetadataBuilder() {
177         return DefinitionMetadataBuilder.usingNameAsId();
178     }
179 }