AbstractArtifactManager.java
001 /*
002  * Copyright 2009-2012 the original author or authors.
003  *
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  *
008  *      http://www.apache.org/licenses/LICENSE-2.0
009  *
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 
017 package org.codehaus.griffon.runtime.core;
018 
019 import griffon.core.*;
020 import griffon.util.CallableWithArgs;
021 import griffon.util.CallableWithArgsClosure;
022 import groovy.lang.*;
023 import org.slf4j.Logger;
024 import org.slf4j.LoggerFactory;
025 
026 import java.util.*;
027 import java.util.regex.Matcher;
028 import java.util.regex.Pattern;
029 
030 import static griffon.util.GriffonNameUtils.isBlank;
031 import static griffon.util.GriffonNameUtils.uncapitalize;
032 import static org.codehaus.groovy.runtime.DefaultGroovyMethods.toList;
033 
034 /**
035  * Base implementation of the {@code ArtifactManager} interface.
036  *
037  @author Andres Almiray
038  @since 0.9.2
039  */
040 public abstract class AbstractArtifactManager implements ArtifactManager {
041     private final GriffonApplication app;
042 
043     private final Map<String, ArtifactInfo[]> artifacts = new LinkedHashMap<String, ArtifactInfo[]>();
044     private final Map<String, ArtifactHandler> artifactHandlers = new LinkedHashMap<String, ArtifactHandler>();
045     private final Object lock = new Object();
046 
047     private static final Logger LOG = LoggerFactory.getLogger(AbstractArtifactManager.class);
048     private static final Pattern GET_METHOD_PATTERN = Pattern.compile("^get(\\w+)Classes$");
049     private static final Pattern IS_METHOD_PATTERN = Pattern.compile("^is(\\w+)Class$");
050     private static final Pattern PROPERTY_PATTERN = Pattern.compile("^(\\w+)Classes$");
051 
052     public AbstractArtifactManager(GriffonApplication app) {
053         this.app = app;
054     }
055 
056     public GriffonApplication getApp() {
057         return app;
058     }
059 
060     protected Map<String, ArtifactInfo[]> getArtifacts() {
061         return artifacts;
062     }
063 
064     protected Map<String, ArtifactHandler> getArtifactHandlers() {
065         return artifactHandlers;
066     }
067 
068     public final void loadArtifactMetadata() {
069         Map<String, List<ArtifactInfo>> loadedArtifacts = doLoadArtifactMetadata();
070 
071         synchronized (lock) {
072             for (Map.Entry<String, List<ArtifactInfo>> artifactsEntry : loadedArtifacts.entrySet()) {
073                 String type = artifactsEntry.getKey();
074                 List<ArtifactInfo> list = artifactsEntry.getValue();
075                 artifacts.put(type, list.toArray(new ArtifactInfo[list.size()]));
076                 ArtifactHandler handler = artifactHandlers.get(type);
077                 if (handler != nullhandler.initialize(artifacts.get(type));
078             }
079         }
080     }
081 
082     // commented out generics because it fails with groovy 1.8.1
083     protected abstract Map<String, List<ArtifactInfo>> doLoadArtifactMetadata();
084 
085     public void registerArtifactHandler(ArtifactHandler handler) {
086         if (handler == nullreturn;
087         if (LOG.isInfoEnabled()) {
088             LOG.info("Registering artifact handler for type '" + handler.getType() "': " + handler);
089         }
090         synchronized (lock) {
091             artifactHandlers.put(handler.getType(), handler);
092             if (artifacts.get(handler.getType()) != nullhandler.initialize(artifacts.get(handler.getType()));
093         }
094     }
095 
096     public void unregisterArtifactHandler(ArtifactHandler handler) {
097         if (handler == nullreturn;
098         if (LOG.isInfoEnabled()) {
099             LOG.info("Removing artifact handler for type '" + handler.getType() "': " + handler);
100         }
101         synchronized (lock) {
102             artifactHandlers.remove(handler.getType());
103         }
104     }
105 
106     public GriffonClass findGriffonClass(String name, String type) {
107         if (isBlank(name|| isBlank(type)) return null;
108         if (LOG.isDebugEnabled()) {
109             LOG.debug("Searching for griffonClass of " + type + ":" + name);
110         }
111         synchronized (lock) {
112             ArtifactHandler handler = artifactHandlers.get(type);
113             return handler != null ? handler.findClassFor(namenull;
114         }
115     }
116 
117     public GriffonClass findGriffonClass(Class clazz, String type) {
118         if (LOG.isDebugEnabled()) {
119             LOG.debug("Searching for griffonClass of " + type + ":" + clazz.getName());
120         }
121         synchronized (lock) {
122             ArtifactHandler handler = artifactHandlers.get(type);
123             return handler != null ? handler.getClassFor(clazznull;
124         }
125     }
126 
127     public GriffonClass findGriffonClass(Object obj) {
128         if (obj == nullreturn null;
129         if (LOG.isDebugEnabled()) {
130             LOG.debug("Searching for griffonClass of " + obj);
131         }
132         synchronized (lock) {
133             return findGriffonClass(obj.getClass());
134         }
135     }
136 
137     public GriffonClass findGriffonClass(Class clazz) {
138         if (clazz == nullreturn null;
139         if (LOG.isDebugEnabled()) {
140             LOG.debug("Searching for griffonClass of " + clazz.getName());
141         }
142         synchronized (lock) {
143             for (ArtifactHandler handler : artifactHandlers.values()) {
144                 GriffonClass griffonClass = handler.getClassFor(clazz);
145                 if (griffonClass != nullreturn griffonClass;
146             }
147         }
148         return null;
149     }
150 
151     public GriffonClass findGriffonClass(String fqnClassName) {
152         if (isBlank(fqnClassName)) return null;
153         if (LOG.isDebugEnabled()) {
154             LOG.debug("Searching for griffonClass of " + fqnClassName);
155         }
156         synchronized (lock) {
157             for (ArtifactHandler handler : artifactHandlers.values()) {
158                 GriffonClass griffonClass = handler.getClassFor(fqnClassName);
159                 if (griffonClass != nullreturn griffonClass;
160             }
161         }
162         return null;
163     }
164 
165     public List<GriffonClass> getClassesOfType(String type) {
166         synchronized (lock) {
167             if (artifacts.containsKey(type)) {
168                 return toList(artifactHandlers.get(type).getClasses());
169             }
170         }
171         return EMPTY_GRIFFON_CLASS_LIST;
172     }
173 
174     public List<GriffonClass> getAllClasses() {
175         List<GriffonClass> all = new ArrayList<GriffonClass>();
176         synchronized (lock) {
177             for (ArtifactHandler handler : artifactHandlers.values()) {
178                 all.addAll(toList(handler.getClasses()));
179             }
180         }
181         return Collections.unmodifiableList(all);
182     }
183 
184     /**
185      * Adds dynamic handlers for querying artifact classes.<p>
186      * The following patterns are recognized<ul>
187      <li>getXXXClasses</li>
188      <li>isXXXClass</li>
189      </ul>
190      * where {@code XXX} stands for the name of an artifact, like
191      * "Controller" or "Service".
192      */
193     public Object methodMissing(final String methodName, Object args) {
194         Object[] arguments = new Object[0];
195         if (args != null && args.getClass().isArray()) {
196             arguments = (Object[]) args;
197         else {
198             arguments = new Object[]{args};
199         }
200 
201         Matcher matcher = GET_METHOD_PATTERN.matcher(methodName);
202         if (matcher.matches()) {
203             final String artifactType = uncapitalize(matcher.group(1));
204             if (arguments.length == && artifacts.containsKey(artifactType)) {
205                 MetaClass mc = GroovySystem.getMetaClassRegistry().getMetaClass(ArtifactManager.class);
206                 if (mc instanceof ExpandoMetaClass) {
207                     ExpandoMetaClass emc = (ExpandoMetaClassmc;
208                     CallableWithArgs<List<GriffonClass>> callable = new CallableWithArgs<List<GriffonClass>>() {
209                         public List<GriffonClass> call(Object[] params) {
210                             if (params != null) {
211                                 throw new MissingMethodException(methodName, ArtifactManager.class, params);
212                             }
213                             return getClassesOfType(artifactType);
214                         }
215                     };
216                     emc.registerInstanceMethod(methodName, new CallableWithArgsClosure(this, callable));
217                 }
218                 return getClassesOfType(artifactType);
219             }
220             return EMPTY_GRIFFON_CLASS_ARRAY;
221         }
222 
223         matcher = IS_METHOD_PATTERN.matcher(methodName);
224         if (matcher.matches()) {
225             final String artifactType = uncapitalize(matcher.group(1));
226             if (arguments.length == && artifacts.containsKey(artifactType)) {
227                 MetaClass mc = GroovySystem.getMetaClassRegistry().getMetaClass(ArtifactManager.class);
228                 if (mc instanceof ExpandoMetaClass) {
229                     ExpandoMetaClass emc = (ExpandoMetaClassmc;
230                     CallableWithArgs<Boolean> callable = new CallableWithArgs<Boolean>() {
231                         public Boolean call(Object[] params) {
232                             if (params == null || params.length != || !(params[0instanceof Class)) {
233                                 throw new MissingMethodException(methodName, ArtifactManager.class, params);
234                             }
235                             Class klass = (Classparams[0];
236                             return isClassOfType(artifactType, klass);
237                         }
238                     };
239                     emc.registerInstanceMethod(methodName, new CallableWithArgsClosure(this, callable));
240                 }
241                 return isClassOfType(artifactType, (Classarguments[0]);
242             }
243             return false;
244         }
245 
246         throw new MissingMethodException(methodName, ArtifactManager.class, arguments);
247     }
248 
249     /**
250      * Adds dynamic handlers for querying artifact classes.<p>
251      * The following patterns are recognized<ul>
252      <li>xXXClasses</li>
253      </ul>
254      * where {@code xXX} stands for the name of an artifact, like
255      * "controller" or "service".
256      */
257     public Object propertyMissing(String propertyName) {
258         Matcher matcher = PROPERTY_PATTERN.matcher(propertyName);
259         if (matcher.matches()) {
260             final String artifactType = uncapitalize(matcher.group(1));
261             if (artifacts.containsKey(artifactType)) {
262                 List<GriffonClass> griffonClasses = getClassesOfType(artifactType);
263                 MetaClass mc = GroovySystem.getMetaClassRegistry().getMetaClass(ArtifactManager.class);
264                 if (mc instanceof ExpandoMetaClass) {
265                     ExpandoMetaClass emc = (ExpandoMetaClassmc;
266                     emc.registerBeanProperty(propertyName, griffonClasses);
267                 }
268                 return griffonClasses;
269             }
270             return EMPTY_GRIFFON_CLASS_ARRAY;
271         }
272 
273         throw new MissingPropertyException(propertyName, Object.class);
274     }
275 
276     protected boolean isClassOfType(String type, Class clazz) {
277         for (ArtifactInfo artifactInfo : artifacts.get(type)) {
278             if (artifactInfo.getClazz().getName().equals(clazz.getName())) {
279                 return true;
280             }
281         }
282         return false;
283     }
284 }