001/*
002 *  Copyright 2012 Anyware Services
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
017package org.ametys.runtime.plugin.component;
018
019import java.util.LinkedHashSet;
020import java.util.Set;
021
022import org.apache.avalon.framework.activity.Disposable;
023import org.apache.avalon.framework.activity.Initializable;
024import org.apache.avalon.framework.component.Component;
025import org.apache.avalon.framework.component.ComponentException;
026import org.apache.avalon.framework.configuration.Configuration;
027import org.apache.avalon.framework.configuration.ConfigurationException;
028import org.apache.avalon.framework.context.Context;
029import org.apache.avalon.framework.context.ContextException;
030import org.apache.avalon.framework.context.Contextualizable;
031import org.apache.avalon.framework.service.ServiceException;
032import org.apache.avalon.framework.service.ServiceManager;
033import org.apache.avalon.framework.service.Serviceable;
034import org.apache.avalon.framework.thread.ThreadSafe;
035import org.slf4j.LoggerFactory;
036
037import org.ametys.runtime.plugin.ExtensionPoint;
038
039/**
040 * Avalon based implementation of an ExtensionPoint.<br>
041 * Subclasses only need to call <code>addComponent()</code> for each new extension.<br>
042 * @param <T> the type of the managed extensions
043 */
044public abstract class AbstractThreadSafeComponentExtensionPoint<T> extends AbstractLogEnabled implements ExtensionPoint<T>, Component, ThreadSafe, Serviceable, Initializable, Disposable, Contextualizable 
045{
046    /** Avalon ComponentManager */
047    protected ExtensionPointComponentManager<T> _manager;
048    /** Avalon service manager */
049    protected ServiceManager _cocoonManager;
050    /** Avalon context */
051    protected Context _context;
052    
053    private Set<String> _ids = new LinkedHashSet<>();
054    
055    public void contextualize(Context context) throws ContextException
056    {
057        _context = context;
058    }
059    
060    public void service(ServiceManager manager) throws ServiceException
061    {
062        _cocoonManager = manager;
063    }
064    
065    public void initialize() throws Exception
066    {
067        _manager = new ExtensionPointComponentManager<>(this);
068        _manager.setLogger(LoggerFactory.getLogger("runtime.plugin.threadsafecomponent"));
069        _manager.contextualize(_context);
070        _manager.service(_cocoonManager);
071    }
072    
073    public void dispose()
074    {
075        _manager.dispose();
076    }
077    
078    /**
079     * Adds a Component to the underlying ComponentManager. 
080     * Each extension to this ExtensionPoint may be considered as a Component.<br>
081     * @param pluginName Unique identifier for the plugin hosting the extension
082     * @param featureName The feature name (unique for a given pluginName).
083     * @param role the Avalon role
084     * @param clazz the class of the component
085     * @param configuration the configuration of the component
086     * @throws ComponentException if an error occured whil setting up components
087     */
088    protected void addComponent(String pluginName, String featureName, String role, Class<? extends T> clazz, Configuration configuration) throws ComponentException
089    {
090        _ids.add(role);
091        _manager.addComponent(pluginName, featureName, role, clazz, configuration);
092    }
093    
094    public void initializeExtensions() throws Exception
095    {
096        _manager.initialize();
097    }
098    
099    public void deferredInitializeExtensions() throws Exception
100    {
101        _manager.wireDeferredServiceableExtensions();
102    }
103    
104    public T getExtension(String id) 
105    {
106        try
107        {
108            return _manager.lookup(id);
109        }
110        catch (ComponentException e)
111        {
112            throw new IllegalArgumentException(e);
113        }
114    }
115    
116    public boolean hasExtension(String id) 
117    {
118        return _ids.contains(id);
119    }
120    
121    @SuppressWarnings("unchecked")
122    public void addExtension(String id, String pluginName, String featureName, Configuration configuration) throws ConfigurationException
123    {
124        if (getLogger().isDebugEnabled())
125        {
126            getLogger().debug("Configuring extension '" + id + "' in plugin '" + pluginName + "' id '" + featureName + "'.");
127        }
128        
129        String className = configuration.getAttribute("class", null);
130        
131        if (className == null)
132        {
133            throw new ConfigurationException("In plugin '" + pluginName + "' id '" + featureName + "', extension '" + id + "' does not defines any class", configuration);
134        }
135        
136        Class<T> extensionClass;
137        
138        try
139        {
140            extensionClass = (Class<T>) Class.forName(className);
141        }
142        catch (ClassNotFoundException ex)
143        {
144            throw new ConfigurationException("Unable to instanciate class '" + className + "' for plugin '" + pluginName + "' / '" + featureName + "'", configuration, ex);
145        }
146        
147        try
148        {
149            addComponent(pluginName, featureName, id, extensionClass, configuration);
150        }
151        catch (ComponentException e)
152        {
153            throw new ConfigurationException("Exception loading extension " + id, e);
154        }
155
156        if (getLogger().isDebugEnabled())
157        {
158            getLogger().debug("Extension configured");
159        }
160    }
161    
162    public Set<String> getExtensionsIds()
163    {
164        return _ids;
165    }
166}