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 ThreadSafeComponentManager<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 T getExtension(String id) 
100    {
101        try
102        {
103            return _manager.lookup(id);
104        }
105        catch (ComponentException e)
106        {
107            throw new IllegalArgumentException(e);
108        }
109    }
110    
111    public boolean hasExtension(String id) 
112    {
113        return _ids.contains(id);
114    }
115    
116    @SuppressWarnings("unchecked")
117    public void addExtension(String id, String pluginName, String featureName, Configuration configuration) throws ConfigurationException
118    {
119        if (getLogger().isDebugEnabled())
120        {
121            getLogger().debug("Configuring extension '" + id + "' in plugin '" + pluginName + "' id '" + featureName + "'.");
122        }
123        
124        String className = configuration.getAttribute("class", null);
125        
126        if (className == null)
127        {
128            throw new ConfigurationException("In plugin '" + pluginName + "' id '" + featureName + "', extension '" + id + "' does not defines any class", configuration);
129        }
130        
131        Class<T> extensionClass;
132        
133        try
134        {
135            extensionClass = (Class<T>) Class.forName(className);
136        }
137        catch (ClassNotFoundException ex)
138        {
139            throw new ConfigurationException("Unable to instanciate class '" + className + "' for plugin '" + pluginName + "' / '" + featureName + "'", configuration, ex);
140        }
141        
142        try
143        {
144            addComponent(pluginName, featureName, id, extensionClass, configuration);
145        }
146        catch (ComponentException e)
147        {
148            throw new ConfigurationException("Exception loading extension " + id, e);
149        }
150
151        if (getLogger().isDebugEnabled())
152        {
153            getLogger().debug("Extension configured");
154        }
155    }
156    
157    public Set<String> getExtensionsIds()
158    {
159        return _ids;
160    }
161}