Mercurial > hg > release > thermostat-0.4
changeset 540:1b323d4b416f
Refactor CommandRegistry, extracting osgi-related behaviour to superclass.
This is a precursor to further command-channel enhancements.
reviewed-by: sgehwolf
review-thread: http://icedtea.classpath.org/pipermail/thermostat/2012-August/002682.html
author | Jon VanAlten <jon.vanalten@redhat.com> |
---|---|
date | Thu, 16 Aug 2012 17:57:26 -0400 |
parents | c12e371abba5 |
children | 86e7f31bcd0c |
files | common/core/src/main/java/com/redhat/thermostat/common/cli/BaseCommandRegistry.java common/core/src/main/java/com/redhat/thermostat/common/cli/CommandRegistry.java common/core/src/main/java/com/redhat/thermostat/common/cli/CommandRegistryImpl.java common/core/src/main/java/com/redhat/thermostat/common/utils/ServiceRegistry.java common/core/src/main/java/com/redhat/thermostat/test/TestCommandRegistry.java common/core/src/test/java/com/redhat/thermostat/common/cli/CommandRegistryImplTest.java launcher/src/test/java/com/redhat/thermostat/launcher/internal/ActivatorTest.java |
diffstat | 7 files changed, 257 insertions(+), 160 deletions(-) [+] |
line wrap: on
line diff
--- a/common/core/src/main/java/com/redhat/thermostat/common/cli/BaseCommandRegistry.java Thu Aug 16 17:57:23 2012 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* - * Copyright 2012 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat 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 2, or (at your - * option) any later version. - * - * Thermostat 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 Thermostat; see the file COPYING. If not see - * <http://www.gnu.org/licenses/>. - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.common.cli; - -import java.util.ArrayList; -import java.util.Collection; - -import org.osgi.framework.ServiceRegistration; - -import com.redhat.thermostat.common.cli.Command; - - -public abstract class BaseCommandRegistry implements CommandRegistry { - - @Override - public Collection<ServiceRegistration> registerCommands(Iterable<? extends Command> cmds) { - Collection<ServiceRegistration> regs = new ArrayList<>(); - for (Command cmd : cmds) { - regs.add(registerCommand(cmd)); - } - return regs; - } - - protected abstract ServiceRegistration registerCommand(Command cmd); -}
--- a/common/core/src/main/java/com/redhat/thermostat/common/cli/CommandRegistry.java Thu Aug 16 17:57:23 2012 -0400 +++ b/common/core/src/main/java/com/redhat/thermostat/common/cli/CommandRegistry.java Thu Aug 16 17:57:26 2012 -0400 @@ -38,16 +38,16 @@ import java.util.Collection; -import org.osgi.framework.ServiceRegistration; - public interface CommandRegistry { - public abstract Collection<ServiceRegistration> registerCommands(Iterable<? extends Command> cmds); + public void registerCommand(Command cmd); - public abstract void unregisterCommands(); + public void registerCommands(Iterable<? extends Command> cmds); - public abstract Command getCommand(String name); + public void unregisterCommands(); - public abstract Collection<Command> getRegisteredCommands(); + public Command getCommand(String name); + + public Collection<Command> getRegisteredCommands(); } \ No newline at end of file
--- a/common/core/src/main/java/com/redhat/thermostat/common/cli/CommandRegistryImpl.java Thu Aug 16 17:57:23 2012 -0400 +++ b/common/core/src/main/java/com/redhat/thermostat/common/cli/CommandRegistryImpl.java Thu Aug 16 17:57:26 2012 -0400 @@ -38,84 +38,56 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; -import java.util.logging.Logger; import org.osgi.framework.BundleContext; -import org.osgi.framework.InvalidSyntaxException; -import org.osgi.framework.ServiceReference; -import org.osgi.framework.ServiceRegistration; -public class CommandRegistryImpl extends BaseCommandRegistry { +import com.redhat.thermostat.common.utils.ServiceRegistry; - private static final Logger log = Logger.getLogger(CommandRegistryImpl.class.getName()); +public class CommandRegistryImpl implements CommandRegistry { private BundleContext context; - - private List<ServiceRegistration> ourRegistrations = new ArrayList<ServiceRegistration>(); + private ServiceRegistry<Command> proxy; + private Collection<Command> myRegisteredCommands; public CommandRegistryImpl(BundleContext ctx) { context = ctx; + proxy = new ServiceRegistry<Command>(ctx, Command.class.getName()); + myRegisteredCommands = new ArrayList<>(); } - protected ServiceRegistration registerCommand(Command cmd) { + @Override + public void registerCommand(Command cmd) { if (cmd instanceof OSGiContext) { ((OSGiContext) cmd).setBundleContext(context); } - - Hashtable<String, String> props = new Hashtable<>(); - props.put(Command.NAME, cmd.getName()); - ServiceRegistration registration = context.registerService(Command.class.getName(), cmd, props); - ourRegistrations.add(registration); - return registration; + cmd.enable(); + proxy.registerService(cmd, cmd.getName()); + myRegisteredCommands.add(cmd); + } + + @Override + public void registerCommands(Iterable<? extends Command> cmds) { + for (Command cmd : cmds) { + registerCommand(cmd); + } } @Override public void unregisterCommands() { - Iterator<ServiceRegistration> iter = ourRegistrations.iterator(); - while (iter.hasNext()) { - ServiceRegistration registration = iter.next(); - Object serviceObject = context.getService(registration.getReference()); - Command cmd = (Command) serviceObject; - cmd.disable(); - registration.unregister(); - iter.remove(); + for (Command command : myRegisteredCommands) { + command.disable(); } + proxy.unregisterAll(); } @Override public Command getCommand(String name) { - ServiceReference[] refs = getCommandServiceRefs("(&(objectclass=*)(" + Command.NAME + "=" + name + "))"); - if (refs == null || refs.length == 0) { - return null; - } else if (refs.length > 1) { - log.warning("More than one command implementation found for: " + name); - } - ServiceReference ref = refs[0]; - return (Command) context.getService(ref); + return proxy.getService(name); } @Override public Collection<Command> getRegisteredCommands() { - ServiceReference[] refs = getCommandServiceRefs(null); - List<Command> cmds = new ArrayList<>(); - for (ServiceReference ref : refs) { - Command cmd = (Command) context.getService(ref); - cmds.add(cmd); - } - return cmds; - } - - private ServiceReference[] getCommandServiceRefs(String filter) { - ServiceReference[] refs; - try { - refs = context.getServiceReferences(Command.class.getName(), filter); - } catch (InvalidSyntaxException e) { - throw (InternalError) new InternalError().initCause(e); - } - return refs; + return proxy.getRegisteredServices(); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/core/src/main/java/com/redhat/thermostat/common/utils/ServiceRegistry.java Thu Aug 16 17:57:26 2012 -0400 @@ -0,0 +1,202 @@ +/* + * Copyright 2012 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat 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 2, or (at your + * option) any later version. + * + * Thermostat 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 Thermostat; see the file COPYING. If not see + * <http://www.gnu.org/licenses/>. + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.common.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.logging.Logger; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; + +/** + * + * + * @param <T> The type of service which this registry works with. + */ +public class ServiceRegistry<T> { + + // Commonly used property keys. + public static final String SERVICE_NAME = "servicename"; + + private final BundleContext context; + private final String registrationClass; + @SuppressWarnings("rawtypes") + private final List<ServiceRegistration> ourRegistrations; + + private static final Logger log = Logger.getLogger(ServiceRegistry.class.getName()); + + public ServiceRegistry(BundleContext ctx, String registrationClassName) { + context = ctx; + registrationClass = registrationClassName; + ourRegistrations = new ArrayList<>(); + } + + /** + * Register the implementation of T to the OSGi context, with the given properties. + * @param service The implementation to be registered. + * @param props The additional properties which the service should have. + */ + public void registerService(T service, Dictionary<String, ?> props) { + ServiceRegistration registration = context.registerService(registrationClass, service, props); + ourRegistrations.add(registration); + } + + /** + * Equivalent to calling registerService(service, props) with a dictionary containing + * an entry for SERVICE_NAME with value as name, in adddition to the contents of props + * + * @param service The implementation to be registered. + * @param name The name to be added as SERVICE_NAME property. + * @param props Additional properties which the service should be registered with. + */ + public void registerService(T service, String name, Dictionary<String, String> props) { + if (props == null) { + props = new Hashtable<String, String>(); + } + props.put(SERVICE_NAME, name); + registerService(service, props); + } + + /** + * Equivalent to calling registerService(service, name, props) with null or empty props. + * + * @param service The implementation to be registered. + * @param name The name to be added as SERVICE_NAME property. + */ + public void registerService(T service, String name) { + Hashtable<String, String> props = new Hashtable<>(); + props.put(SERVICE_NAME, name); + registerService(service, props); + } + + /** + * Unregister all of the services that have been registered through this registry. + */ + @SuppressWarnings("rawtypes") + public void unregisterAll() { + Iterator<ServiceRegistration> iter = ourRegistrations.iterator(); + while (iter.hasNext()) { + ServiceRegistration registration = iter.next(); + registration.unregister(); + iter.remove(); + } + } + + /** + * Get a implementation of T matching the properties. + * + * @param props The properties which the returned T implementation must match. + * @return A matching T implementation, if any. Otherwise, null. + */ + public T getService(Dictionary<String, String> props) { + ServiceReference[] refs = getServiceRefs(filterFromProps(props)); + if (refs == null || refs.length == 0) { + return null; + } else if (refs.length > 1) { + log.info("More than one matching service implementation found."); + } + ServiceReference ref = refs[0]; + return (T) context.getService(ref); + } + + /** + * Equivalent to calling getService(props) with a dictionary containing an entry for + * SERVICE_NAME with value as name. + * + * @param name The name for the service which should be returned. + * @return An implementation of T matching the supplied name, if any. Otherwise, null. + */ + public T getService(String name) { + Hashtable<String, String> props = new Hashtable<>(); + props.put(SERVICE_NAME, name); + return getService(props); + } + + private String filterFromProps(Dictionary<String, String> props) { + StringBuilder builder = new StringBuilder(); + builder.append("(&(objectclass=*)"); + if (props != null) { + for (Enumeration<String> e = props.keys(); e.hasMoreElements();) { + String key = e.nextElement(); + String value = props.get(key); + builder.append("("); + builder.append(key); + builder.append("="); + builder.append(value); + builder.append(")"); + } + } + builder.append(")"); + return builder.toString(); + } + + /** + * Get all implementations of T which have been registered. This may include ones not + * registered through this registry instance. + * + * @return A collection of all registered T implementations. + */ + public Collection<T> getRegisteredServices() { + ServiceReference[] refs = getServiceRefs(null); + List<T> services = new ArrayList<>(); + for (ServiceReference ref : refs) { + T service = (T) context.getService(ref); + services.add(service); + } + return services; + } + + @SuppressWarnings("unchecked") + private ServiceReference[] getServiceRefs(String filter) { + ServiceReference[] refs; + try { + refs = context.getServiceReferences(registrationClass, filter); + } catch (InvalidSyntaxException e) { + throw (InternalError) new InternalError().initCause(e); + } + return refs; + } + +}
--- a/common/core/src/main/java/com/redhat/thermostat/test/TestCommandRegistry.java Thu Aug 16 17:57:23 2012 -0400 +++ b/common/core/src/main/java/com/redhat/thermostat/test/TestCommandRegistry.java Thu Aug 16 17:57:26 2012 -0400 @@ -37,37 +37,13 @@ package com.redhat.thermostat.test; import java.util.Collection; -import java.util.Dictionary; import java.util.HashMap; import java.util.Map; -import org.osgi.framework.ServiceReference; -import org.osgi.framework.ServiceRegistration; - -import com.redhat.thermostat.common.cli.BaseCommandRegistry; +import com.redhat.thermostat.common.cli.CommandRegistry; import com.redhat.thermostat.common.cli.Command; -class TestCommandRegistry extends BaseCommandRegistry { - - private static class TestServiceRegistration implements ServiceRegistration { - - @Override - public ServiceReference getReference() { - // Nothing to do for now. - return null; - } - - @Override - public void setProperties(Dictionary properties) { - // Nothing to do for now. - } - - @Override - public void unregister() { - // Nothing to do for now. - } - - } +class TestCommandRegistry implements CommandRegistry { private Map<String, Command> commands = new HashMap<>(); @@ -79,13 +55,19 @@ return commands.values(); } - protected ServiceRegistration registerCommand(Command cmd) { + public void registerCommand(Command cmd) { commands.put(cmd.getName(), cmd); - return new TestServiceRegistration(); } @Override public void unregisterCommands() { commands.clear(); } + + @Override + public void registerCommands(Iterable<? extends Command> cmds) { + for (Command cmd : cmds) { + registerCommand(cmd); + } + } }
--- a/common/core/src/test/java/com/redhat/thermostat/common/cli/CommandRegistryImplTest.java Thu Aug 16 17:57:23 2012 -0400 +++ b/common/core/src/test/java/com/redhat/thermostat/common/cli/CommandRegistryImplTest.java Thu Aug 16 17:57:26 2012 -0400 @@ -58,6 +58,8 @@ import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; +import com.redhat.thermostat.common.utils.ServiceRegistry; + public class CommandRegistryImplTest { private CommandRegistryImpl commandRegistry; @@ -85,9 +87,9 @@ commandRegistry.registerCommands(Arrays.asList(cmd1, cmd2)); Hashtable<String,Object> props1 = new Hashtable<>(); - props1.put(Command.NAME, "test1"); + props1.put(ServiceRegistry.SERVICE_NAME, "test1"); Hashtable<String,Object> props2 = new Hashtable<>(); - props2.put(Command.NAME, "test2"); + props2.put(ServiceRegistry.SERVICE_NAME, "test2"); verify(bundleContext).registerService(Command.class.getName(), cmd1, props1); verify(bundleContext).registerService(Command.class.getName(), cmd2, props2); @@ -96,7 +98,7 @@ } @Test - public void testUnregisterCommand() { + public void testUnregisterCommand() throws InvalidSyntaxException { Command cmd1 = mock(Command.class); when(cmd1.getName()).thenReturn("test1"); Command cmd2 = mock(Command.class); @@ -111,9 +113,9 @@ when(cmd2Reg.getReference()).thenReturn(cmd2Reference); Hashtable<String,String> props1 = new Hashtable<>(); - props1.put(Command.NAME, cmd1.getName()); + props1.put(ServiceRegistry.SERVICE_NAME, cmd1.getName()); Hashtable<String,String> props2 = new Hashtable<>(); - props2.put(Command.NAME, cmd2.getName()); + props2.put(ServiceRegistry.SERVICE_NAME, cmd2.getName()); when(bundleContext.registerService(Command.class.getName(), cmd1, props1)).thenReturn(cmd1Reg); when(bundleContext.registerService(Command.class.getName(), cmd2, props2)).thenReturn(cmd2Reg); @@ -125,13 +127,12 @@ when(bundleContext.getService(eq(cmd1Reference))).thenReturn(cmd1); when(bundleContext.getService(eq(cmd2Reference))).thenReturn(cmd2); + when(bundleContext.getServiceReferences(Command.class.getName(), (String) null)).thenReturn(new ServiceReference[] {cmd1Reference, cmd2Reference}); commandRegistry.unregisterCommands(); - verify(bundleContext).getService(cmd1Reference); verify(cmd1).disable(); verify(cmd1Reg).unregister(); - verify(bundleContext).getService(cmd2Reference); verify(cmd2).disable(); verify(cmd2Reg).unregister(); @@ -141,7 +142,7 @@ @Test public void testGetCommand() throws InvalidSyntaxException { ServiceReference ref1 = mock(ServiceReference.class); - when(bundleContext.getServiceReferences(Command.class.getName(), "(&(objectclass=*)(COMMAND_NAME=test1))")).thenReturn(new ServiceReference[] { ref1 }); + when(bundleContext.getServiceReferences(Command.class.getName(), "(&(objectclass=*)(servicename=test1))")).thenReturn(new ServiceReference[] { ref1 }); Command cmd1 = mock(Command.class); when(bundleContext.getService(ref1)).thenReturn(cmd1); @@ -163,7 +164,7 @@ @Test public void testNotRegisteredCommandEmptyList() throws InvalidSyntaxException { - when(bundleContext.getServiceReferences(Command.class.getName(), "(&(objectclass=*)(COMMAND_NAME=test1))")).thenReturn(new ServiceReference[0]); + when(bundleContext.getServiceReferences(Command.class.getName(), "(&(objectclass=*)(servicename=test1))")).thenReturn(new ServiceReference[0]); Command cmd = commandRegistry.getCommand("test1"); @@ -176,7 +177,7 @@ ServiceReference ref2 = mock(ServiceReference.class); Command cmd1 = mock(Command.class); Command cmd2 = mock(Command.class); - when(bundleContext.getServiceReferences(Command.class.getName(), "(&(objectclass=*)(COMMAND_NAME=test1))")).thenReturn(new ServiceReference[] { ref1, ref2 }); + when(bundleContext.getServiceReferences(Command.class.getName(), "(&(objectclass=*)(servicename=test1))")).thenReturn(new ServiceReference[] { ref1, ref2 }); when(bundleContext.getService(ref1)).thenReturn(cmd1); when(bundleContext.getService(ref2)).thenReturn(cmd2); @@ -187,7 +188,7 @@ @Test(expected=InternalError.class) public void testGetCommandInvalidSyntax() throws InvalidSyntaxException { - when(bundleContext.getServiceReferences(Command.class.getName(), "(&(objectclass=*)(COMMAND_NAME=test1))")).thenThrow(new InvalidSyntaxException("test", "test")); + when(bundleContext.getServiceReferences(Command.class.getName(), "(&(objectclass=*)(servicename=test1))")).thenThrow(new InvalidSyntaxException("test", "test")); commandRegistry.getCommand("test1"); }
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/ActivatorTest.java Thu Aug 16 17:57:23 2012 -0400 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/ActivatorTest.java Thu Aug 16 17:57:26 2012 -0400 @@ -54,7 +54,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; @@ -62,16 +61,15 @@ import org.powermock.modules.junit4.PowerMockRunner; import com.redhat.thermostat.bundles.OSGiRegistryService; -import com.redhat.thermostat.common.CommandLoadingBundleActivator; import com.redhat.thermostat.common.MultipleServiceTracker; import com.redhat.thermostat.common.MultipleServiceTracker.Action; import com.redhat.thermostat.common.cli.Command; +import com.redhat.thermostat.common.utils.ServiceRegistry; import com.redhat.thermostat.launcher.Launcher; -import com.redhat.thermostat.launcher.internal.Activator.RegisterLauncherAction; import com.redhat.thermostat.utils.keyring.Keyring; @RunWith(PowerMockRunner.class) -@PrepareForTest({Activator.class, CommandLoadingBundleActivator.class, RegisterLauncherAction.class, BundleActivator.class}) +@PrepareForTest({Activator.class}) public class ActivatorTest { private BundleContext context; @@ -100,6 +98,7 @@ when(context.registerService(eq(Command.class.getName()), any(), isA(Dictionary.class))). thenReturn(helpCommandRegistration); when(context.getService(helpCommandReference)).thenReturn(helpCommand); + when(context.getServiceReferences(Command.class.getName(), null)).thenReturn(new ServiceReference[] {helpCommandReference}); tracker = mock(MultipleServiceTracker.class); whenNew(MultipleServiceTracker.class). @@ -115,7 +114,7 @@ activator.start(context); Hashtable<String, Object> props = new Hashtable<>(); - props.put(Command.NAME, "help"); + props.put(ServiceRegistry.SERVICE_NAME, "help"); verify(context).registerService(eq(Command.class.getName()), isA(HelpCommand.class), eq(props)); ArgumentCaptor<Action> actionCaptor = ArgumentCaptor.forClass(Action.class);