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 != null) handler.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 == null) return;
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()) != null) handler.initialize(artifacts.get(handler.getType()));
093 }
094 }
095
096 public void unregisterArtifactHandler(ArtifactHandler handler) {
097 if (handler == null) return;
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(name) : null;
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(clazz) : null;
124 }
125 }
126
127 public GriffonClass findGriffonClass(Object obj) {
128 if (obj == null) return 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 == null) return 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 != null) return 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 != null) return 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 == 0 && artifacts.containsKey(artifactType)) {
205 MetaClass mc = GroovySystem.getMetaClassRegistry().getMetaClass(ArtifactManager.class);
206 if (mc instanceof ExpandoMetaClass) {
207 ExpandoMetaClass emc = (ExpandoMetaClass) mc;
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 == 1 && artifacts.containsKey(artifactType)) {
227 MetaClass mc = GroovySystem.getMetaClassRegistry().getMetaClass(ArtifactManager.class);
228 if (mc instanceof ExpandoMetaClass) {
229 ExpandoMetaClass emc = (ExpandoMetaClass) mc;
230 CallableWithArgs<Boolean> callable = new CallableWithArgs<Boolean>() {
231 public Boolean call(Object[] params) {
232 if (params == null || params.length != 1 || !(params[0] instanceof Class)) {
233 throw new MissingMethodException(methodName, ArtifactManager.class, params);
234 }
235 Class klass = (Class) params[0];
236 return isClassOfType(artifactType, klass);
237 }
238 };
239 emc.registerInstanceMethod(methodName, new CallableWithArgsClosure(this, callable));
240 }
241 return isClassOfType(artifactType, (Class) arguments[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 = (ExpandoMetaClass) mc;
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 }
|