From 8570b25506d0ce612ca82c19c7cc111458600cc0 Mon Sep 17 00:00:00 2001 From: prachi Date: Tue, 11 Oct 2011 17:34:32 -0700 Subject: [PATCH] NAAS: APIServer changes to introduce framework to read commands from pluggable components having separate commands.properties file Changes: - Added a new interface 'PluggableService' - Any component that can be packaged separately from cloudstack, can implement this interface and provide its own property file listing the API commands the component supports - As an example have made VirtualNetworkApplianceService pluggable and a new configureRouter command is added - ComponentLocator reads all the pluggable service from componentLibrary or from components.xml and instantiates the services. - As an example, DefaultComponentLibrary adds the pluggable service 'VirtualNetworkApplianceService' - Also components.xml.in has an entry to show how a pluggable service can be added, but it is commented out. - APIServer now reads the commands for each pluggable service and when a command for such a service is called, APIServer sets the required instance of the pluggable service in the coomand. - To do this a new annotation '@PlugService' is added that is processed by APIServer. This eliminates the dependency on the BaseCmd to instantiate the service instances. --- .../AgentComponentLibraryBase.java | 14 +- api/src/com/cloud/api/PlugService.java | 32 +++++ .../api/commands/ConfigureRouterCmd.java | 120 ++++++++++++++++++ .../VirtualNetworkApplianceService.java | 3 +- client/tomcatconf/components.xml.in | 1 + .../virtualrouter_commands.properties.in | 5 + server/src/com/cloud/api/ApiDispatcher.java | 43 +++++++ server/src/com/cloud/api/ApiServer.java | 51 +++++++- .../DefaultComponentLibrary.java | 14 ++ .../VirtualNetworkApplianceManagerImpl.java | 6 + .../utils/component/ComponentLibrary.java | 5 + .../utils/component/ComponentLibraryBase.java | 14 +- .../utils/component/ComponentLocator.java | 64 +++++++++- .../utils/component/PluggableService.java | 31 +++++ .../utils/component/MockComponentLocator.java | 12 +- 15 files changed, 402 insertions(+), 13 deletions(-) create mode 100644 api/src/com/cloud/api/PlugService.java create mode 100644 api/src/com/cloud/api/commands/ConfigureRouterCmd.java create mode 100644 client/tomcatconf/virtualrouter_commands.properties.in create mode 100644 utils/src/com/cloud/utils/component/PluggableService.java diff --git a/agent/src/com/cloud/agent/configuration/AgentComponentLibraryBase.java b/agent/src/com/cloud/agent/configuration/AgentComponentLibraryBase.java index 31e0f95145c..b481f438684 100755 --- a/agent/src/com/cloud/agent/configuration/AgentComponentLibraryBase.java +++ b/agent/src/com/cloud/agent/configuration/AgentComponentLibraryBase.java @@ -25,7 +25,7 @@ import com.cloud.utils.component.Adapter; import com.cloud.utils.component.ComponentLibraryBase; import com.cloud.utils.component.ComponentLocator.ComponentInfo; import com.cloud.utils.component.Manager; -import com.cloud.utils.component.SystemIntegrityChecker; +import com.cloud.utils.component.PluggableService; import com.cloud.utils.db.GenericDao; @@ -63,5 +63,17 @@ public class AgentComponentLibraryBase extends ComponentLibraryBase { protected void populateAdapters() { } + + protected void populateServices() { + + } + + @Override + public Map> getPluggableServices() { + if (_pluggableServices.size() == 0) { + populateServices(); + } + return _pluggableServices; + } } diff --git a/api/src/com/cloud/api/PlugService.java b/api/src/com/cloud/api/PlugService.java new file mode 100644 index 00000000000..0722b62a0c8 --- /dev/null +++ b/api/src/com/cloud/api/PlugService.java @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.api; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import com.cloud.utils.component.PluggableService; + +@Target(FIELD) +@Retention(RUNTIME) +public @interface PlugService { + Class pluggableService() default PluggableService.class; +} diff --git a/api/src/com/cloud/api/commands/ConfigureRouterCmd.java b/api/src/com/cloud/api/commands/ConfigureRouterCmd.java new file mode 100644 index 00000000000..235f4622690 --- /dev/null +++ b/api/src/com/cloud/api/commands/ConfigureRouterCmd.java @@ -0,0 +1,120 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseAsyncCmd; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.PlugService; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.DomainRouterResponse; +import com.cloud.async.AsyncJob; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.VirtualNetworkApplianceService; +import com.cloud.network.router.VirtualRouter; +import com.cloud.user.Account; +import com.cloud.user.UserContext; + + + +@Implementation(responseObject=DomainRouterResponse.class, description="Configures a router.") +public class ConfigureRouterCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(ConfigureRouterCmd.class.getName()); + private static final String s_name = "configurerouterresponse"; + + @PlugService + private static VirtualNetworkApplianceService _myrouterService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.LONG, required=true, description="the ID of the router") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + public static String getResultObjectName() { + return "router"; + } + + @Override + public long getEntityOwnerId() { + VirtualRouter router = _entityMgr.findById(VirtualRouter.class, getId()); + if (router != null) { + return router.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + @Override + public String getEventType() { + return EventTypes.EVENT_ROUTER_START; + } + + @Override + public String getEventDescription() { + return "configuring router: " + getId(); + } + + public AsyncJob.Type getInstanceType() { + return AsyncJob.Type.DomainRouter; + } + + public Long getInstanceId() { + return getId(); + } + + @Override + public void execute() throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException{ + UserContext.current().setEventDetails("Router Id: "+getId()); + //This should call the configure API. Calling startRouter for now. + VirtualRouter result = _myrouterService.startRouter(id); + if (result != null){ + DomainRouterResponse routerResponse = _responseGenerator.createDomainRouterResponse(result); + routerResponse.setResponseName(getCommandName()); + this.setResponseObject(routerResponse); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to start router"); + } + } +} diff --git a/api/src/com/cloud/network/VirtualNetworkApplianceService.java b/api/src/com/cloud/network/VirtualNetworkApplianceService.java index 581ae727ddf..03935fcc0e0 100644 --- a/api/src/com/cloud/network/VirtualNetworkApplianceService.java +++ b/api/src/com/cloud/network/VirtualNetworkApplianceService.java @@ -22,8 +22,9 @@ import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.router.VirtualRouter; +import com.cloud.utils.component.PluggableService; -public interface VirtualNetworkApplianceService{ +public interface VirtualNetworkApplianceService extends PluggableService{ /** * Starts domain router * @param cmd the command specifying router's id diff --git a/client/tomcatconf/components.xml.in b/client/tomcatconf/components.xml.in index 5e536df7cdf..b8a673d37d2 100755 --- a/client/tomcatconf/components.xml.in +++ b/client/tomcatconf/components.xml.in @@ -107,6 +107,7 @@ + diff --git a/client/tomcatconf/virtualrouter_commands.properties.in b/client/tomcatconf/virtualrouter_commands.properties.in new file mode 100644 index 00000000000..55ecb396567 --- /dev/null +++ b/client/tomcatconf/virtualrouter_commands.properties.in @@ -0,0 +1,5 @@ +### bitmap of permissions at the end of each classname, 1 = ADMIN, 2 = RESOURCE_DOMAIN_ADMIN, 4 = DOMAIN_ADMIN, 8 = USER +### Please standardize naming conventions to camel-case (even for acronyms). + +#### router commands +configureRouter=com.cloud.api.commands.ConfigureRouterCmd;7 \ No newline at end of file diff --git a/server/src/com/cloud/api/ApiDispatcher.java b/server/src/com/cloud/api/ApiDispatcher.java index a3f455d8d37..b96dd0b68fa 100755 --- a/server/src/com/cloud/api/ApiDispatcher.java +++ b/server/src/com/cloud/api/ApiDispatcher.java @@ -45,6 +45,7 @@ import com.cloud.user.Account; import com.cloud.user.UserContext; import com.cloud.utils.DateUtil; import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.component.PluggableService; import com.cloud.utils.exception.CloudRuntimeException; /** @@ -71,6 +72,7 @@ public class ApiDispatcher { public void dispatchCreateCmd(BaseAsyncCreateCmd cmd, Map params) { setupParameters(cmd, params); + plugService(cmd); try { UserContext ctx = UserContext.current(); @@ -113,6 +115,7 @@ public class ApiDispatcher { public void dispatch(BaseCmd cmd, Map params) { setupParameters(cmd, params); + ApiDispatcher.plugService(cmd); try { UserContext ctx = UserContext.current(); ctx.setAccountId(cmd.getEntityOwnerId()); @@ -329,4 +332,44 @@ public class ApiDispatcher { cal.set(Calendar.SECOND, second); return cal.getTime(); } + + public static void plugService(BaseCmd cmd) { + + if(!ApiServer.isPluggableServiceCommand(cmd.getClass().getName())){ + return; + } + Class clazz = cmd.getClass(); + ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); + do { + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + PlugService plugService = field.getAnnotation(PlugService.class); + if (plugService == null) { + continue; + } + Class fc = field.getType(); + Object instance = null; + if (PluggableService.class.isAssignableFrom(fc)) { + instance = locator.getPluggableService(fc); + } + + if (instance == null) { + throw new CloudRuntimeException("Unable to plug service " + fc.getSimpleName() + " in command " + clazz.getSimpleName()); + } + + try { + field.setAccessible(true); + field.set(cmd, instance); + } catch (IllegalArgumentException e) { + s_logger.error("IllegalArgumentException at plugService for command " + cmd.getCommandName() + ", field " + field.getName()); + throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [Illegal argumet at field " + field.getName() + "]"); + } catch (IllegalAccessException e) { + s_logger.error("Error at plugService for command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible."); + throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]"); + } + } + clazz = clazz.getSuperclass(); + } while (clazz != Object.class && clazz != null); + + } } diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index ea2e7990497..a0257e9c1af 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -107,6 +107,7 @@ import com.cloud.user.UserContext; import com.cloud.utils.Pair; import com.cloud.utils.PropertiesUtil; import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.component.PluggableService; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; @@ -138,6 +139,7 @@ public class ApiServer implements HttpRequestHandler { private static List s_adminCommands = null; private static List s_resourceDomainAdminCommands = null; private static List s_allCommands = null; + private static List s_pluggableServiceCommands = null; private static final DateFormat _dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); private static ExecutorService _executor = new ThreadPoolExecutor(10, 150, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(), new NamedThreadFactory("ApiServer")); @@ -148,6 +150,7 @@ public class ApiServer implements HttpRequestHandler { s_adminCommands = new ArrayList(); s_resourceDomainAdminCommands = new ArrayList(); s_allCommands = new ArrayList(); + s_pluggableServiceCommands = new ArrayList(); } private ApiServer() { @@ -168,12 +171,32 @@ public class ApiServer implements HttpRequestHandler { public Properties get_apiCommands() { return _apiCommands; } + + public static boolean isPluggableServiceCommand(String cmdClassName){ + if(s_pluggableServiceCommands != null){ + if(s_pluggableServiceCommands.contains(cmdClassName)){ + return true; + } + } + return false; + } - public void init(String[] apiConfig) { - try { - BaseCmd.setComponents(new ApiResponseHelper()); - BaseListCmd.configure(); - _apiCommands = new Properties(); + private String[] getPluggableServicesApiConfigs(){ + List pluggableServicesApiConfigs = new ArrayList(); + + ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); + List services = locator.getAllPluggableServices(); + for(PluggableService service : services){ + pluggableServicesApiConfigs.add(service.getPropertiesFile()); + } + return pluggableServicesApiConfigs.toArray(new String[0]); + } + + private void processConfigFiles(String[] apiConfig, boolean pluggableServicesConfig){ + try{ + if(_apiCommands == null){ + _apiCommands = new Properties(); + } Properties preProcessedCommands = new Properties(); if (apiConfig != null) { for (String configFile : apiConfig) { @@ -184,6 +207,11 @@ public class ApiServer implements HttpRequestHandler { String preProcessedCommand = preProcessedCommands.getProperty((String) key); String[] commandParts = preProcessedCommand.split(";"); _apiCommands.put(key, commandParts[0]); + + if(pluggableServicesConfig){ + s_pluggableServiceCommands.add(commandParts[0]); + } + if (commandParts.length > 1) { try { short cmdPermissions = Short.parseShort(commandParts[1]); @@ -204,7 +232,7 @@ public class ApiServer implements HttpRequestHandler { } } } - + s_allCommands.addAll(s_adminCommands); s_allCommands.addAll(s_resourceDomainAdminCommands); s_allCommands.addAll(s_userCommands); @@ -215,6 +243,16 @@ public class ApiServer implements HttpRequestHandler { } catch (IOException ioex) { s_logger.error("Exception loading properties file", ioex); } + } + + public void init(String[] apiConfig) { + BaseCmd.setComponents(new ApiResponseHelper()); + BaseListCmd.configure(); + processConfigFiles(apiConfig, false); + + //get commands for all pluggable services + String[] pluggableServicesApiConfigs = getPluggableServicesApiConfigs(); + processConfigFiles(pluggableServicesApiConfigs, true); ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name); _accountMgr = locator.getManager(AccountManager.class); @@ -400,6 +438,7 @@ public class ApiServer implements HttpRequestHandler { params.put("id", objectId.toString()); } else { ApiDispatcher.setupParameters(cmdObj, params); + ApiDispatcher.plugService(cmdObj); } BaseAsyncCmd asyncCmd = (BaseAsyncCmd) cmdObj; diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java index 0c299e2b290..406dbf4e3e9 100755 --- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java +++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java @@ -70,6 +70,7 @@ import com.cloud.keystore.KeystoreManagerImpl; import com.cloud.maint.UpgradeManagerImpl; import com.cloud.maint.dao.AgentUpgradeDaoImpl; import com.cloud.network.NetworkManagerImpl; +import com.cloud.network.VirtualNetworkApplianceService; import com.cloud.network.dao.FirewallRulesCidrsDaoImpl; import com.cloud.network.dao.FirewallRulesDaoImpl; import com.cloud.network.dao.IPAddressDaoImpl; @@ -153,6 +154,7 @@ import com.cloud.utils.component.ComponentLibrary; import com.cloud.utils.component.ComponentLibraryBase; import com.cloud.utils.component.ComponentLocator.ComponentInfo; import com.cloud.utils.component.Manager; +import com.cloud.utils.component.PluggableService; import com.cloud.utils.db.GenericDao; import com.cloud.vm.ClusteredVirtualMachineManagerImpl; import com.cloud.vm.ItWorkDaoImpl; @@ -363,4 +365,16 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com factories.put(EntityManager.class, EntityManagerImpl.class); return factories; } + + protected void populateServices() { + addService("VirtualRouterService", VirtualNetworkApplianceService.class, VirtualNetworkApplianceManagerImpl.class); + } + + @Override + public synchronized Map> getPluggableServices() { + if (_pluggableServices.size() == 0) { + populateServices(); + } + return _pluggableServices; + } } diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 0d2011e5be7..96d0c720693 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -2742,4 +2742,10 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian public boolean processTimeout(long agentId, long seq) { return false; } + + @Override + public String getPropertiesFile() { + return "virtualrouter_commands.properties"; + } + } diff --git a/utils/src/com/cloud/utils/component/ComponentLibrary.java b/utils/src/com/cloud/utils/component/ComponentLibrary.java index 18aed723996..3f4d86efada 100755 --- a/utils/src/com/cloud/utils/component/ComponentLibrary.java +++ b/utils/src/com/cloud/utils/component/ComponentLibrary.java @@ -51,4 +51,9 @@ public interface ComponentLibrary { Map, Class> getFactories(); + /** + * @return all the services + * + */ + Map> getPluggableServices(); } diff --git a/utils/src/com/cloud/utils/component/ComponentLibraryBase.java b/utils/src/com/cloud/utils/component/ComponentLibraryBase.java index 45492b87fda..9358130ef48 100644 --- a/utils/src/com/cloud/utils/component/ComponentLibraryBase.java +++ b/utils/src/com/cloud/utils/component/ComponentLibraryBase.java @@ -45,6 +45,7 @@ public abstract class ComponentLibraryBase implements ComponentLibrary { protected Map> _managers = new LinkedHashMap>(); protected Map>> _adapters = new LinkedHashMap>>(); + protected Map> _pluggableServices = new LinkedHashMap>(); protected ComponentInfo addManager(String name, Class clazz, List> params, boolean singleton) { ComponentInfo info = new ComponentInfo(name, clazz, params, singleton); @@ -85,4 +86,15 @@ public abstract class ComponentLibraryBase implements ComponentLibrary { adapters.add(new Pair>(name, adapterClass)); return addAdapterChain(interphace, adapters).get(0); } -} + + + protected ComponentInfo addService(String name, Class serviceInterphace, Class clazz, List> params, boolean singleton) { + ComponentInfo info = new ComponentInfo(name, clazz, params, singleton); + _pluggableServices.put(serviceInterphace.getName(), info); + return info; + } + + protected ComponentInfo addService(String name, Class serviceInterphace, Class clazz) { + return addService(name, serviceInterphace, clazz, new ArrayList>(), true); + } + } diff --git a/utils/src/com/cloud/utils/component/ComponentLocator.java b/utils/src/com/cloud/utils/component/ComponentLocator.java index 489b2619bd3..008617b6ccc 100755 --- a/utils/src/com/cloud/utils/component/ComponentLocator.java +++ b/utils/src/com/cloud/utils/component/ComponentLocator.java @@ -64,7 +64,6 @@ import org.xml.sax.helpers.DefaultHandler; import com.cloud.utils.Pair; import com.cloud.utils.PropertiesUtil; -import com.cloud.utils.Ternary; import com.cloud.utils.db.DatabaseCallback; import com.cloud.utils.db.DatabaseCallbackFilter; import com.cloud.utils.db.GenericDao; @@ -100,6 +99,7 @@ public class ComponentLocator implements ComponentLocatorMBean { protected String _serverName; protected Object _component; protected HashMap, Class> _factories; + protected HashMap> _pluggableServicesMap; static { if (s_janitor == null) { @@ -134,6 +134,7 @@ public class ComponentLocator implements ComponentLocatorMBean { _checkerMap = new HashMap>(); _adapterMap = new HashMap>(); _factories = new HashMap, Class>(); + _pluggableServicesMap = new LinkedHashMap>(); File file = PropertiesUtil.findConfigFile(filename); if (file == null) { s_logger.info("Unable to find " + filename); @@ -157,6 +158,7 @@ public class ComponentLocator implements ComponentLocatorMBean { _daoMap.putAll(parentLocator._daoMap); _managerMap.putAll(parentLocator._managerMap); _factories.putAll(parentLocator._factories); + _pluggableServicesMap.putAll(parentLocator._pluggableServicesMap); } ComponentLibrary library = null; @@ -167,12 +169,14 @@ public class ComponentLocator implements ComponentLocatorMBean { _managerMap.putAll(library.getManagers()); adapters.putAll(library.getAdapters()); _factories.putAll(library.getFactories()); + _pluggableServicesMap.putAll(library.getPluggableServices()); } _daoMap.putAll(handler.daos); _managerMap.putAll(handler.managers); _checkerMap.putAll(handler.checkers); adapters.putAll(handler.adapters); + _pluggableServicesMap.putAll(handler.pluggableServices); return new Pair>>>(handler, adapters); } catch (ParserConfigurationException e) { @@ -215,6 +219,9 @@ public class ComponentLocator implements ComponentLocatorMBean { configureAdapters(); startManagers(); startAdapters(); + //TODO do we need to follow the instantiate -> inject -> configure -> start -> stop flow of singletons like managers/adapters? + //TODO do we need to expose pluggableServices to MBean (provide getNames?) + instantiatePluggableServices(); } catch (CloudRuntimeException e) { s_logger.error("Unable to load configuration for " + _serverName + " from " + filename, e); System.exit(1); @@ -634,6 +641,47 @@ public class ComponentLocator implements ComponentLocatorMBean { } } + protected void instantiatePluggableServices() { + Set>> entries = _pluggableServicesMap.entrySet(); + for (Map.Entry> entry : entries) { + ComponentInfo info = entry.getValue(); + if (info.instance == null) { + s_logger.info("Instantiating PluggableService: " + info.name); + info.instance = (PluggableService)createInstance(info.clazz, false, info.singleton); + } + } + } + + protected ComponentInfo getPluggableService(String name) { + ComponentInfo mgr = _pluggableServicesMap.get(name); + return mgr; + } + + public T getPluggableService(Class clazz) { + ComponentInfo info = getPluggableService(clazz.getName()); + if (info == null) { + return null; + } + if (info.instance == null) { + info.instance = (PluggableService)createInstance(info.clazz, false, info.singleton); + } + return (T)info.instance; + } + + public List getAllPluggableServices() { + List services = new ArrayList(); + Set>> entries = _pluggableServicesMap.entrySet(); + for (Map.Entry> entry : entries) { + ComponentInfo info = entry.getValue(); + if (info.instance == null) { + s_logger.info("Instantiating PluggableService: " + info.name); + info.instance = (PluggableService)createInstance(info.clazz, false, info.singleton); + } + services.add((T) info.instance); + } + return services; + } + public static T inject(Class clazz) { return (T)createInstance(clazz, true, false); } @@ -868,6 +916,7 @@ public class ComponentLocator implements ComponentLocatorMBean { public HashMap> managers; public HashMap> checkers; public LinkedHashMap>> daos; + public HashMap> pluggableServices; public String parent; public String library; @@ -886,6 +935,7 @@ public class ComponentLocator implements ComponentLocatorMBean { managers = new HashMap>(); checkers = new HashMap>(); daos = new LinkedHashMap>>(); + pluggableServices = new HashMap>(); value = null; parent = null; } @@ -992,6 +1042,17 @@ public class ComponentLocator implements ComponentLocatorMBean { checkers.put(info.name, info); s_logger.info("Adding system integrity checker: " + info.name); currentInfo = info; + } else if (qName.equals("pluggableservice")) { + ComponentInfo info = new ComponentInfo(); + fillInfo(atts, PluggableService.class, info); + s_logger.info("Adding PluggableService: " + info.name); + String key = getAttribute(atts, "key"); + if (key == null) { + throw new CloudRuntimeException("Missing key attribute for pluggableservice: "+info.name); + } + s_logger.info("Linking " + key + " to " + info.name); + pluggableServices.put(key, info); + currentInfo = info; } else { // ignore } @@ -1019,6 +1080,7 @@ public class ComponentLocator implements ComponentLocatorMBean { } else if (qName.equals("adapter")) { } else if (qName.equals("manager")) { } else if (qName.equals("dao")) { + } else if (qName.equals("pluggableservice")) { } else if (qName.equals("param")) { currentInfo.params.put(paramName, value.toString()); paramName = null; diff --git a/utils/src/com/cloud/utils/component/PluggableService.java b/utils/src/com/cloud/utils/component/PluggableService.java new file mode 100644 index 00000000000..8edee64c5ca --- /dev/null +++ b/utils/src/com/cloud/utils/component/PluggableService.java @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.utils.component; + + +/** + * This interface defines methods for pluggable code within the Cloud Stack. + */ +public interface PluggableService { + + /** + * The config file name that lists API commands supported by this pluggable service + */ + String getPropertiesFile(); + +} diff --git a/utils/test/com/cloud/utils/component/MockComponentLocator.java b/utils/test/com/cloud/utils/component/MockComponentLocator.java index a74b3833bd2..33ff73cba8e 100755 --- a/utils/test/com/cloud/utils/component/MockComponentLocator.java +++ b/utils/test/com/cloud/utils/component/MockComponentLocator.java @@ -18,7 +18,6 @@ package com.cloud.utils.component; import java.io.Serializable; -import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -28,8 +27,6 @@ import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.NoOp; import com.cloud.utils.Pair; -import com.cloud.utils.Ternary; -import com.cloud.utils.component.ComponentLocator.ComponentInfo; import com.cloud.utils.db.DatabaseCallback; import com.cloud.utils.db.DatabaseCallbackFilter; import com.cloud.utils.db.GenericDao; @@ -62,6 +59,10 @@ public class MockComponentLocator extends ComponentLocator { return _library.addAdapterChain(interphace, adapters); } + public ComponentInfo addService(String name, Class service) { + return _library.addService(name, service); + } + @Override protected Pair>>> parse2(String filename) { Pair>>> result = new Pair>>>(new XmlHandler("fake"), new HashMap>>()); @@ -110,5 +111,10 @@ public class MockComponentLocator extends ComponentLocator { public Map> getManagers() { return _managers; } + + @Override + public Map> getPluggableServices() { + return _pluggableServices; + } } }