| /******************************************************************************* |
| * Copyright (c) 2010, 2017 IBM Corporation, SAP AG and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Thomas Watson, IBM Corporation - initial API and implementation |
| * Lazar Kirchev, SAP AG - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.equinox.console.command.adapter; |
| |
| import java.io.PrintStream; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.lang.reflect.Type; |
| import java.util.ArrayList; |
| import java.util.Dictionary; |
| import java.util.Hashtable; |
| import java.util.List; |
| |
| import org.apache.felix.service.command.CommandProcessor; |
| import org.apache.felix.service.command.CommandSession; |
| import org.eclipse.equinox.console.commands.CommandsTracker; |
| import org.eclipse.equinox.console.commands.DisconnectCommand; |
| import org.eclipse.equinox.console.commands.EquinoxCommandProvider; |
| import org.eclipse.equinox.console.commands.HelpCommand; |
| import org.eclipse.equinox.console.commands.ManCommand; |
| import org.eclipse.equinox.console.telnet.TelnetCommand; |
| import org.eclipse.osgi.framework.console.CommandInterpreter; |
| import org.eclipse.osgi.framework.console.CommandProvider; |
| import org.eclipse.osgi.framework.console.ConsoleSession; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleActivator; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.framework.ServiceRegistration; |
| import org.osgi.service.condpermadmin.ConditionalPermissionAdmin; |
| import org.osgi.service.packageadmin.PackageAdmin; |
| import org.osgi.service.startlevel.StartLevel; |
| import org.osgi.service.permissionadmin.PermissionAdmin; |
| import org.osgi.util.tracker.ServiceTracker; |
| import org.osgi.util.tracker.ServiceTrackerCustomizer; |
| |
| /** |
| * The activator class controls the plug-in life cycle |
| */ |
| public class Activator implements BundleActivator { |
| private ServiceTracker<StartLevel, StartLevel> startLevelManagerTracker; |
| private ServiceTracker<ConditionalPermissionAdmin, ConditionalPermissionAdmin> condPermAdminTracker; |
| private ServiceTracker<PermissionAdmin, PermissionAdmin> permissionAdminTracker; |
| private ServiceTracker<PackageAdmin, PackageAdmin> packageAdminTracker; |
| private static boolean isFirstProcessor = true; |
| private static TelnetCommand telnetConnection = null; |
| |
| private ServiceTracker<CommandProcessor, ServiceTracker<ConsoleSession, CommandSession>> commandProcessorTracker; |
| // Tracker for Equinox CommandProviders |
| private ServiceTracker<CommandProvider, List<ServiceRegistration<?>>> commandProviderTracker; |
| |
| private EquinoxCommandProvider equinoxCmdProvider; |
| |
| public static class ProcessorCustomizer implements |
| ServiceTrackerCustomizer<CommandProcessor, ServiceTracker<ConsoleSession, CommandSession>> { |
| |
| private final BundleContext context; |
| |
| public ProcessorCustomizer(BundleContext context) { |
| this.context = context; |
| } |
| |
| @Override |
| public ServiceTracker<ConsoleSession, CommandSession> addingService( |
| ServiceReference<CommandProcessor> reference) { |
| CommandProcessor processor = context.getService(reference); |
| if (processor == null) |
| return null; |
| |
| if (isFirstProcessor) { |
| isFirstProcessor = false; |
| telnetConnection = new TelnetCommand(processor, context); |
| telnetConnection.startService(); |
| } else { |
| telnetConnection.addCommandProcessor(processor); |
| } |
| |
| ServiceTracker<ConsoleSession, CommandSession> tracker = new ServiceTracker<>(context, ConsoleSession.class, new SessionCustomizer(context, processor)); |
| tracker.open(); |
| return tracker; |
| } |
| |
| @Override |
| public void modifiedService( |
| ServiceReference<CommandProcessor> reference, |
| ServiceTracker<ConsoleSession, CommandSession> service) { |
| // nothing |
| } |
| |
| @Override |
| public void removedService( |
| ServiceReference<CommandProcessor> reference, |
| ServiceTracker<ConsoleSession, CommandSession> tracker) { |
| tracker.close(); |
| CommandProcessor processor = context.getService(reference); |
| telnetConnection.removeCommandProcessor(processor); |
| } |
| } |
| |
| // Provides support for Equinox ConsoleSessions |
| public static class SessionCustomizer implements |
| ServiceTrackerCustomizer<ConsoleSession, CommandSession> { |
| private final BundleContext context; |
| final CommandProcessor processor; |
| |
| public SessionCustomizer(BundleContext context, CommandProcessor processor) { |
| this.context = context; |
| this.processor = processor; |
| } |
| |
| @Override |
| public CommandSession addingService( |
| ServiceReference<ConsoleSession> reference) { |
| final ConsoleSession equinoxSession = context.getService(reference); |
| if (equinoxSession == null) |
| return null; |
| PrintStream output = new PrintStream(equinoxSession.getOutput()); |
| final CommandSession gogoSession = processor.createSession(equinoxSession.getInput(), output, output); |
| new Thread(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| gogoSession.put("SCOPE", "equinox:*"); |
| gogoSession.put("prompt", "osgi> "); |
| gogoSession.execute("gosh --login --noshutdown"); |
| } |
| catch (Exception e) { |
| e.printStackTrace(); |
| } |
| finally { |
| gogoSession.close(); |
| equinoxSession.close(); |
| } |
| } |
| }, "Equinox Console Session").start(); |
| return null; |
| } |
| |
| @Override |
| public void modifiedService(ServiceReference<ConsoleSession> reference, |
| CommandSession service) { |
| // nothing |
| } |
| |
| @Override |
| public void removedService(ServiceReference<ConsoleSession> reference, |
| CommandSession session) { |
| session.close(); |
| } |
| } |
| |
| // All commands, provided by an Equinox CommandProvider, are registered as provided by a CommandProviderAdapter. |
| public class CommandCustomizer implements |
| ServiceTrackerCustomizer<CommandProvider, List<ServiceRegistration<?>>> { |
| |
| private BundleContext context; |
| public CommandCustomizer(BundleContext context) { |
| this.context = context; |
| } |
| |
| @Override |
| public List<ServiceRegistration<?>> addingService(ServiceReference<CommandProvider> reference) { |
| if (reference.getProperty("osgi.command.function") != null) { |
| // must be a gogo function already; don' track |
| return null; |
| } |
| CommandProvider command = context.getService(reference); |
| try { |
| Method[] commandMethods = getCommandMethods(command); |
| |
| if (commandMethods.length > 0) { |
| List<ServiceRegistration<?>> registrations = new ArrayList<>(); |
| registrations.add(context.registerService(Object.class, new CommandProviderAdapter(command, commandMethods), getAttributes(commandMethods))); |
| return registrations; |
| } else { |
| context.ungetService(reference); |
| return null; |
| } |
| } catch (Exception e) { |
| context.ungetService(reference); |
| return null; |
| } |
| } |
| |
| |
| @Override |
| public void modifiedService(ServiceReference<CommandProvider> reference, List<ServiceRegistration<?>> service) { |
| // Nothing to do. |
| } |
| |
| @Override |
| public void removedService(ServiceReference<CommandProvider> reference, List<ServiceRegistration<?>> registrations) { |
| for (ServiceRegistration<?> serviceRegistration : registrations) { |
| serviceRegistration.unregister(); |
| } |
| } |
| |
| } |
| |
| @Override |
| public void start(BundleContext context) throws Exception { |
| commandProviderTracker = new ServiceTracker<>(context, CommandProvider.class, new CommandCustomizer(context)); |
| commandProviderTracker.open(); |
| commandProcessorTracker = new ServiceTracker<>(context, CommandProcessor.class, new ProcessorCustomizer(context)); |
| commandProcessorTracker.open(); |
| |
| condPermAdminTracker = new ServiceTracker<>(context, ConditionalPermissionAdmin.class, null); |
| condPermAdminTracker.open(); |
| |
| // grab permission admin |
| permissionAdminTracker = new ServiceTracker<>(context, PermissionAdmin.class, null); |
| permissionAdminTracker.open(); |
| |
| startLevelManagerTracker = new ServiceTracker<>(context, StartLevel.class, null); |
| startLevelManagerTracker.open(); |
| |
| packageAdminTracker = new ServiceTracker<>(context, PackageAdmin.class, null); |
| packageAdminTracker.open(); |
| |
| equinoxCmdProvider = new EquinoxCommandProvider(context, this); |
| equinoxCmdProvider.startService(); |
| |
| HelpCommand helpCommand = new HelpCommand(context); |
| helpCommand.startService(); |
| |
| ManCommand manCommand = new ManCommand(context); |
| manCommand.startService(); |
| |
| DisconnectCommand disconnectCommand = new DisconnectCommand(context); |
| disconnectCommand.startService(); |
| |
| CommandsTracker commandsTracker = new CommandsTracker(context); |
| context.registerService(CommandsTracker.class.getName(), commandsTracker, null); |
| |
| startBundle("org.apache.felix.gogo.runtime", true); |
| startBundle("org.apache.felix.gogo.shell", true); |
| startBundle("org.apache.felix.gogo.command", false); |
| } |
| |
| private void startBundle(String bsn, boolean required) throws BundleException { |
| PackageAdmin pa = packageAdminTracker.getService(); |
| if (pa != null) { |
| @SuppressWarnings("deprecation") |
| Bundle[] shells = pa.getBundles(bsn, null); |
| if (shells != null && shells.length > 0) { |
| shells[0].start(Bundle.START_TRANSIENT); |
| } else if (required) { |
| throw new BundleException("Missing required bundle: " + bsn); |
| } |
| } |
| } |
| |
| public StartLevel getStartLevel() { |
| return getServiceFromTracker(startLevelManagerTracker, StartLevel.class); |
| } |
| |
| public PermissionAdmin getPermissionAdmin() { |
| return getServiceFromTracker(permissionAdminTracker, PermissionAdmin.class); |
| } |
| |
| public ConditionalPermissionAdmin getConditionalPermissionAdmin() { |
| return getServiceFromTracker(condPermAdminTracker, ConditionalPermissionAdmin.class); |
| } |
| |
| public PackageAdmin getPackageAdmin() { |
| return getServiceFromTracker(packageAdminTracker, PackageAdmin.class); |
| } |
| |
| private static <T> T getServiceFromTracker(ServiceTracker<?, T> tracker, Class<T> serviceClass) { |
| if (tracker == null) |
| throw new IllegalStateException("Missing service: " + serviceClass); |
| T result = tracker.getService(); |
| if (result == null) |
| throw new IllegalStateException("Missing service: " + serviceClass); |
| return result; |
| } |
| |
| Method[] getCommandMethods(Object command) { |
| ArrayList<Method> names = new ArrayList<>(); |
| Class<?> c = command.getClass(); |
| Method[] methods = c.getDeclaredMethods(); |
| for (Method method : methods) { |
| if (method.getName().startsWith("_") |
| && method.getModifiers() == Modifier.PUBLIC && !method.getName().equals("_help")) { |
| Type[] types = method.getGenericParameterTypes(); |
| if (types.length == 1 |
| && types[0].equals(CommandInterpreter.class)) { |
| names.add(method); |
| } |
| } |
| } |
| return names.toArray(new Method[names.size()]); |
| } |
| |
| Dictionary<String, Object> getAttributes(Method[] commandMethods) { |
| Dictionary<String, Object> dict = new Hashtable<>(); |
| dict.put("osgi.command.scope", "equinox"); |
| String[] methodNames = new String[commandMethods.length]; |
| for (int i = 0; i < commandMethods.length; i++) { |
| String methodName = commandMethods[i].getName().substring(1); |
| methodNames[i] = methodName; |
| } |
| |
| dict.put("osgi.command.function", methodNames); |
| return dict; |
| } |
| |
| @Override |
| public void stop(BundleContext context) throws Exception { |
| commandProviderTracker.close(); |
| commandProcessorTracker.close(); |
| if (equinoxCmdProvider != null) { |
| equinoxCmdProvider.stopService(); |
| } |
| |
| try { |
| telnetConnection.telnet(new String[]{"stop"}); |
| } catch (Exception e) { |
| // expected if the telnet server is not started |
| } |
| |
| } |
| } |