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}