ServiceArtifactHandler.java
001 /*
002  * Copyright 2009-2013 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.exceptions.NewInstanceCreationException;
021 import griffon.util.ApplicationHolder;
022 import groovy.lang.MetaClass;
023 import groovy.lang.MetaProperty;
024 import org.codehaus.groovy.runtime.InvokerHelper;
025 import org.slf4j.Logger;
026 import org.slf4j.LoggerFactory;
027 
028 import java.lang.reflect.InvocationTargetException;
029 import java.util.Collections;
030 import java.util.Map;
031 import java.util.concurrent.ConcurrentHashMap;
032 
033 import static griffon.util.ConfigUtils.getConfigValueAsBoolean;
034 import static griffon.util.GriffonExceptionHandler.sanitize;
035 import static java.util.Arrays.asList;
036 
037 /**
038  * Handler for 'Service' artifacts.
039  *
040  @author Andres Almiray
041  @since 0.9.1
042  */
043 public class ServiceArtifactHandler extends ArtifactHandlerAdapter {
044     private static final Logger LOG = LoggerFactory.getLogger(ServiceArtifactHandler.class);
045     private final DefaultServiceManager serviceManager;
046 
047     private class DefaultServiceManager extends AbstractServiceManager {
048         private final Map<String, GriffonService> serviceInstances = new ConcurrentHashMap<String, GriffonService>();
049 
050         public DefaultServiceManager(GriffonApplication app) {
051             super(app);
052         }
053 
054         public Map<String, GriffonService> getServices() {
055             return Collections.unmodifiableMap(serviceInstances);
056         }
057 
058         protected GriffonService doFindService(String name) {
059             return serviceInstances.get(name);
060         }
061 
062         protected GriffonService doInstantiateService(String name) {
063             return doInstantiateService0(name, true);
064         }
065 
066         private GriffonService doInstantiateService0(String name, boolean triggerEvent) {
067             GriffonService serviceInstance = null;
068             GriffonClass griffonClass = findClassFor(name);
069             if (griffonClass != null) {
070                 serviceInstance = instantiateService(griffonClass);
071                 serviceInstances.put(name, serviceInstance);
072                 getApp().addApplicationEventListener(serviceInstance);
073                 if (triggerEvent) {
074                     getApp().event(GriffonApplication.Event.NEW_INSTANCE.getName(),
075                         asList(griffonClass.getClazz(), GriffonServiceClass.TYPE, serviceInstance));
076                 }
077             }
078             return serviceInstance;
079         }
080 
081         private GriffonService instantiateService(GriffonClass griffonClass) {
082             try {
083                 GriffonService serviceInstance = (GriffonServicegriffonClass.getClazz().newInstance();
084                 InvokerHelper.setProperty(serviceInstance, "app", getApp());
085                 return serviceInstance;
086             catch (Exception e) {
087                 Throwable targetException = null;
088                 if (instanceof InvocationTargetException) {
089                     targetException = ((InvocationTargetExceptione).getTargetException();
090                 else {
091                     targetException = e;
092                 }
093                 throw new NewInstanceCreationException("Could not create a new instance of class " + griffonClass.getClazz().getName(), sanitize(targetException));
094             }
095         }
096     }
097 
098     public ServiceArtifactHandler(GriffonApplication app) {
099         super(app, GriffonServiceClass.TYPE, GriffonServiceClass.TRAILING);
100         serviceManager = new DefaultServiceManager(app);
101         if (LOG.isDebugEnabled()) {
102             LOG.debug("Registering " + serviceManager + " as ServiceManager.");
103         }
104         InvokerHelper.setProperty(app, "serviceManager", serviceManager);
105     }
106 
107     protected GriffonClass newGriffonClassInstance(Class clazz) {
108         return new DefaultGriffonServiceClass(getApp(), clazz);
109     }
110 
111     public void initialize(ArtifactInfo[] artifacts) {
112         super.initialize(artifacts);
113         if (isBasicInjectionDisabled()) return;
114         getApp().addApplicationEventListener(this);
115         if (isEagerInstantiationEnabled()) {
116             if (LOG.isDebugEnabled()) {
117                 LOG.debug("Instantiating service instances eagerly");
118             }
119             for (ArtifactInfo artifactInfo : artifacts) {
120                 GriffonClass griffonClass = getClassFor(artifactInfo.getClazz());
121                 serviceManager.doInstantiateService0(griffonClass.getPropertyName()false);
122             }
123             for (ArtifactInfo artifactInfo : artifacts) {
124                 GriffonClass griffonClass = getClassFor(artifactInfo.getClazz());
125                 GriffonService serviceInstance = serviceManager.findService(griffonClass.getPropertyName());
126                 getApp().event(GriffonApplication.Event.NEW_INSTANCE.getName(),
127                     asList(griffonClass.getClazz(), GriffonServiceClass.TYPE, serviceInstance));
128             }
129         }
130     }
131 
132     /**
133      * Application event listener.<p>
134      * Lazily injects services instances if {@code app.config.griffon.basic_injection.disable}
135      * is not set to true
136      */
137     public void onNewInstance(Class klass, String t, Object instance) {
138         if (isBasicInjectionDisabled()) return;
139         MetaClass metaClass = InvokerHelper.getMetaClass(instance);
140         for (MetaProperty property : metaClass.getProperties()) {
141             String propertyName = property.getName();
142             if (!propertyName.endsWith(getTrailing())) continue;
143             GriffonService serviceInstance = serviceManager.findService(propertyName);
144 
145             if (serviceInstance != null) {
146                 if (LOG.isDebugEnabled()) {
147                     LOG.debug("Injecting service " + serviceInstance + " on " + instance + " using property '" + propertyName + "'");
148                 }
149                 InvokerHelper.setProperty(instance, propertyName, serviceInstance);
150             }
151         }
152     }
153 
154     public static boolean isBasicInjectionDisabled() {
155         return getConfigValueAsBoolean(ApplicationHolder.getApplication().getConfig()"griffon.services.basic.disabled"false);
156     }
157 
158     private boolean isEagerInstantiationEnabled() {
159         return getConfigValueAsBoolean(getApp().getConfig()"griffon.services.eager.instantiation"false);
160     }
161 }