/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.forgerock.openidm.shell.felixgogo; import org.apache.felix.service.command.CommandProcessor; import org.forgerock.openidm.shell.CustomCommandScope; import org.forgerock.openidm.shell.felixgogo.debug.DebugCommands; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; import java.io.InputStream; import java.io.PrintStream; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; /** * Felix GoGo shell adapter activator. * * Based on Apache License 2.0 licensed osgilab org.knowhowlab.osgi.shell.felixgogo */ public class Activator implements BundleActivator { private static final Logger LOG = Logger.getLogger(Activator.class.getName()); private static final String COMMANDS_DESCRIPTION_PROPERTY = "openidm.osgi.shell.commands"; private static final String GROUP_ID_PROPERTY = "openidm.osgi.shell.group.id"; /** * Felix GoGo shell API supports groups. * Filter requires shell commands description and group id */ private static final String SHELL_COMMANDS_SERVICE_FILTER = "(&" + "(" + COMMANDS_DESCRIPTION_PROPERTY + "=*)" + "(" + GROUP_ID_PROPERTY + "=*)" + ")"; /** * Bundle Context. */ private BundleContext bc; /** * Command provides service tracker. */ private ServiceTracker<CustomCommandScope, CustomCommandScope> shellCommandsTracker; private Map<ServiceReference<CustomCommandScope>, ServiceRegistration<?>> commandRegistrations = new HashMap<ServiceReference<CustomCommandScope>, ServiceRegistration<?>>(); /** * Start the bundle. * * @param context the bundle context * @throws Exception on failure to create the service tracker for the provided filter */ public void start(BundleContext context) throws Exception { bc = context; shellCommandsTracker = new ServiceTracker<CustomCommandScope, CustomCommandScope>( bc, bc.createFilter("(objectClass=" + CustomCommandScope.class.getName() + ")"), new ShellCommandsCustomizer()); shellCommandsTracker.open(); Dictionary<String, Object> props = new Hashtable<String, Object>(); props.put(CommandProcessor.COMMAND_SCOPE, "debug"); props.put(CommandProcessor.COMMAND_FUNCTION, DebugCommands.FUNCTIONS); bc.registerService(DebugCommands.class.getName(), new DebugCommands(bc), props); } /** * Stop the bundle. * * @param bundleContext the bundle context * @throws Exception on errors */ public void stop(BundleContext bundleContext) throws Exception { shellCommandsTracker.close(); shellCommandsTracker = null; bc = null; } /** * Validate Command method. * * @param service service newBuilder * @param commandName command method name * @return <code>true</code> if method is peresent in service, <code>public</code> and * has params <code>PrintStream</code> and <code>String[]</code>, otherwise - <code>false</code> */ private boolean isValidCommandMethod(Object service, String commandName) { try { service.getClass().getMethod(commandName, InputStream.class, PrintStream.class, String[].class); return true; } catch (NoSuchMethodException e) { return false; } } /** * Command provides service tracker customizer. */ private class ShellCommandsCustomizer implements ServiceTrackerCustomizer<CustomCommandScope, CustomCommandScope> { public CustomCommandScope addingService(ServiceReference<CustomCommandScope> reference) { CustomCommandScope service = bc.getService(reference); Object groupId = service.getScope(); // if property value null or not String - ignore service if (groupId == null || !(groupId instanceof String)) { LOG.warning(GROUP_ID_PROPERTY + " property is null or invalid. Ignore service"); return null; } // get service ranking propety. if not null - use it on Command services registration Map<String, String> commandMap = service.getFunctionMap(); if (!commandMap.isEmpty()) { Dictionary<String, Object> props = new Hashtable<String, Object>(); Integer ranking = (Integer) reference.getProperty(Constants.SERVICE_RANKING); Long serviceId = (Long) reference.getProperty(Constants.SERVICE_ID); if (ranking != null) { props.put(Constants.SERVICE_RANKING, ranking); } props.put(CommandProcessor.COMMAND_SCOPE, groupId); props.put(CommandProcessor.COMMAND_FUNCTION, commandMap.keySet().toArray(new String[commandMap.size()])); try { // generate class Object commandsProvider = FelixGogoCommandsServiceGenerator.generate(service, commandMap, serviceId.toString()); commandRegistrations.put(reference, bc.registerService(commandsProvider.getClass().getName(), commandsProvider, props)); } catch (Exception e) { LOG.log(Level.WARNING, "Unable to initialize group: " + groupId, e); } return service; } else { return null; } } public void modifiedService(ServiceReference<CustomCommandScope> reference, CustomCommandScope service) { // ignore } public void removedService(ServiceReference<CustomCommandScope> reference, CustomCommandScope service) { // unregister CommandGroup services that belongs to this service registration Long serviceId = (Long) reference.getProperty(Constants.SERVICE_ID); // detach class FelixGogoCommandsServiceGenerator.clean(serviceId.toString()); ServiceRegistration<?> registration = commandRegistrations.remove(reference); if (registration != null) { registration.unregister(); } bc.ungetService(reference); } } }