# HG changeset patch # User Roman Kennke # Date 1358287497 -3600 # Node ID bf46bf74d05cf96bda4b71e2dbcef56a586fb1fe # Parent 6815308bb3624f4690de0d0d7e1ed5cb06468c3b# Parent 03948c38134a506b57e2c38bb3556cb7e78aa6f4 Merge diff -r 6815308bb362 -r bf46bf74d05c agent/command/src/main/java/com/redhat/thermostat/agent/command/RequestReceiver.java --- a/agent/command/src/main/java/com/redhat/thermostat/agent/command/RequestReceiver.java Tue Jan 15 23:00:52 2013 +0100 +++ b/agent/command/src/main/java/com/redhat/thermostat/agent/command/RequestReceiver.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,6 +36,7 @@ package com.redhat.thermostat.agent.command; +import com.redhat.thermostat.annotations.ExtensionPoint; import com.redhat.thermostat.common.command.Request; import com.redhat.thermostat.common.command.Response; @@ -48,6 +49,7 @@ * * @see ReceiverRegistry#registerReceiver(RequestReceiver) */ +@ExtensionPoint public interface RequestReceiver { public Response receive(Request request); diff -r 6815308bb362 -r bf46bf74d05c agent/core/src/main/java/com/redhat/thermostat/agent/JvmStatusListener.java --- a/agent/core/src/main/java/com/redhat/thermostat/agent/JvmStatusListener.java Tue Jan 15 23:00:52 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +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 - * . - * - * 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.agent; - -/** - * A listener that is notified when a JVM starts or is stopped. - */ -public interface JvmStatusListener { - - public void jvmStarted(int pid); - - public void jvmStopped(int pid); -} diff -r 6815308bb362 -r bf46bf74d05c agent/core/src/main/java/com/redhat/thermostat/agent/JvmStatusNotifier.java --- a/agent/core/src/main/java/com/redhat/thermostat/agent/JvmStatusNotifier.java Tue Jan 15 23:00:52 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +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 - * . - * - * 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.agent; - -public interface JvmStatusNotifier { - - /** - * Request to be informed when JVM processes are started or stopped. - * - * @param listener the receiver of future {@link JvmStatusListener.jvmStarted()} - * and {@link JvmStatusListener.jvmStopped()} calls - */ - public void addJvmStatusListener(JvmStatusListener listener); - - /** - * Request to no longer be informed when JVM processes are started or stopped. - * @param listener the {@link JvmStatusListener} to be unregistered. - */ - public void removeJvmStatusListener(JvmStatusListener listener); - -} diff -r 6815308bb362 -r bf46bf74d05c agent/core/src/main/java/com/redhat/thermostat/agent/VmStatusListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/java/com/redhat/thermostat/agent/VmStatusListener.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,71 @@ +/* + * Copyright 2013 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 + * . + * + * 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.agent; + +import com.redhat.thermostat.annotations.ExtensionPoint; + +/** + * A listener that is notified when a JVM starts or is stopped. + *

+ * Register an instance of this class as an OSGi service, and it will be + * notified of all VM {@link Status} events. + */ +@ExtensionPoint +public interface VmStatusListener { + + enum Status { + + /** A VM has just started now */ + VM_STARTED, + + /** + * A delayed version of {@link #VM_STARTED}. A VM was started at some + * point in the past. The listener was not informed about it then + * (probably because the listener was not registered at the time), but + * is being informed about it now. + */ + VM_ACTIVE, + + /** A VM was stopped just now */ + VM_STOPPED, + } + + // TODO what other information other than pid may be useful for plugins? + + public void vmStatusChanged(Status newStatus, int pid); + +} diff -r 6815308bb362 -r bf46bf74d05c agent/core/src/main/java/com/redhat/thermostat/agent/VmStatusListenerRegistrar.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/java/com/redhat/thermostat/agent/VmStatusListenerRegistrar.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,75 @@ +/* + * Copyright 2013 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 + * . + * + * 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.agent; + +import java.util.HashMap; +import java.util.Map; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +/** + * Registers a {@link VmStatusListener} as an OSGi Service. + *

+ * This is just a convenience wrapper over + * {@link BundleContext#registerService(String, Object, java.util.Dictionary)}. + * It does absolutely nothing special. + * + * @see VmStatusListener + */ +public class VmStatusListenerRegistrar { + + private final BundleContext context; + private final Map registrations = new HashMap<>(); + + public VmStatusListenerRegistrar(BundleContext context) { + this.context = context; + } + + public void register(VmStatusListener listener) { + registrations.put(listener, context.registerService(VmStatusListener.class.getName(), listener, null)); + } + + public void unregister(VmStatusListener listener) { + ServiceRegistration registration = registrations.remove(listener); + if (registration == null) { + throw new IllegalArgumentException("no listener found"); + } + + registration.unregister(); + } +} diff -r 6815308bb362 -r bf46bf74d05c agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java --- a/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java Tue Jan 15 23:00:52 2013 +0100 +++ b/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java Tue Jan 15 23:04:57 2013 +0100 @@ -40,6 +40,7 @@ import java.util.Map; import java.util.Map.Entry; +import com.redhat.thermostat.annotations.ExtensionPoint; import com.redhat.thermostat.common.LaunchException; import com.redhat.thermostat.common.Ordered; import com.redhat.thermostat.common.dao.DAOFactory; @@ -48,7 +49,11 @@ /** * Represents a plugin that runs on the agent and performs monitoring of host * and applications. + *

+ * To register a new backend, register an instance of the class with the OSGi + * service registry. */ +@ExtensionPoint public abstract class Backend implements Ordered { private boolean initialConfigurationComplete = false; diff -r 6815308bb362 -r bf46bf74d05c agent/core/src/test/java/com/redhat/thermostat/agent/VmStatusListenerRegistrarTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/test/java/com/redhat/thermostat/agent/VmStatusListenerRegistrarTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,76 @@ +/* + * Copyright 2013 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 + * . + * + * 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.agent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import org.junit.Test; + +import com.redhat.thermostat.agent.VmStatusListener; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; +import com.redhat.thermostat.test.StubBundleContext; + +public class VmStatusListenerRegistrarTest { + + @Test + public void testRegister() { + StubBundleContext context = new StubBundleContext(); + VmStatusListener listener = mock(VmStatusListener.class); + + VmStatusListenerRegistrar registerer = new VmStatusListenerRegistrar(context); + registerer.register(listener); + + assertTrue(context.isServiceRegistered(VmStatusListener.class.getName(), listener.getClass())); + assertEquals(1, context.getAllServices().size()); + } + + @Test + public void testUnregister() { + StubBundleContext context = new StubBundleContext(); + VmStatusListener listener = mock(VmStatusListener.class); + + VmStatusListenerRegistrar registerer = new VmStatusListenerRegistrar(context); + registerer.register(listener); + registerer.unregister(listener); + + assertFalse(context.isServiceRegistered(VmStatusListener.class.getName(), listener.getClass())); + assertEquals(0, context.getAllServices().size()); + } +} diff -r 6815308bb362 -r bf46bf74d05c annotations/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/annotations/pom.xml Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,104 @@ + + + + 4.0.0 + + + com.redhat.thermostat + thermostat + 0.5.0-SNAPSHOT + + + thermostat-annotations + bundle + + Thermostat Annotations + ${project.parent.url} + + + + + org.apache.felix + maven-bundle-plugin + true + + + com.redhat.thermostat.annotations + Red Hat, Inc. + + com.redhat.thermostat.annotations, + + + com.redhat.thermostat.annotations.internal, + + + <_nouses>true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + -proc:none + + + + + + + + + + junit + junit + test + + + org.mockito + mockito-core + test + + + + diff -r 6815308bb362 -r bf46bf74d05c annotations/src/main/java/com/redhat/thermostat/annotations/ExtensionPoint.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/annotations/src/main/java/com/redhat/thermostat/annotations/ExtensionPoint.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,57 @@ +/* + * Copyright 2013 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 + * . + * + * 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.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Indicates a point where the functionality can be extended by a plugin. + * To register the plugin, register a new instance of the class annotated by + * {@link ExtensionPoint} as an OSGi service. + *

+ * To register a class as an OSGi service, use + * {@link BundleContext#registerService}. + *

+ * This is the whiteboard pattern. + */ +@Documented +@Retention(RetentionPolicy.SOURCE) +public @interface ExtensionPoint { + +} diff -r 6815308bb362 -r bf46bf74d05c annotations/src/main/java/com/redhat/thermostat/annotations/Service.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/annotations/src/main/java/com/redhat/thermostat/annotations/Service.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,58 @@ +/* + * Copyright 2013 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 + * . + * + * 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.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Indicates that the annotated class is a service available through OSGi. + * Clients can obtain an instance of this service and use it directly. + *

+ * An instance of this service (if one is registered) can be obtained using + * {@link BundleContext#getService(ServiceReference)} or + * {@link OSGIUtils#getService(Class)}. + *

+ * This does not infer any behaviour on a class; this is for documentation + * purposes only. + */ +@Documented +@Retention(RetentionPolicy.SOURCE) +public @interface Service { + +} diff -r 6815308bb362 -r bf46bf74d05c annotations/src/main/java/com/redhat/thermostat/annotations/internal/AnnotationProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/annotations/src/main/java/com/redhat/thermostat/annotations/internal/AnnotationProcessor.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,167 @@ +/* + * Copyright 2013 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 + * . + * + * 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.annotations.internal; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic.Kind; +import javax.tools.FileObject; +import javax.tools.StandardLocation; + +/** + * An annotation processor that runs and finds @Service and @ExtensionPoint + * annotations. A list of classes using these annotations are written to + * a META-INF/thermostat/plugin-docs.xml file. + */ +@SupportedAnnotationTypes("com.redhat.thermostat.*") +@SupportedSourceVersion(SourceVersion.RELEASE_7) +public class AnnotationProcessor extends AbstractProcessor { + + private enum ExposedAs { EXTENSION_POINT, SERVICE } + + private boolean firstRound = false; + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + firstRound = true; + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (!firstRound) { + return false; + } + + firstRound = false; + + processingEnv.getMessager().printMessage(Kind.NOTE, "Searching for Service and ExtensionPoint annotations"); + + List points = findPluginPoints(annotations, roundEnv); + + processingEnv.getMessager().printMessage(Kind.NOTE, "found " + points.size() + " classes useful for plugins"); + + Element[] sourceElements = new Element[points.size()]; + for (int i = 0; i < points.size(); i++) { + sourceElements[i] = points.get(i).annotatedClass; + } + + if (points.size() > 0) { + try { + FileObject filer = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, + "", + "META-INF" + File.separator + "thermostat" + File.separator + "plugin-docs.xml", + sourceElements); + try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(filer.openOutputStream(), "UTF-8"))) { + writeXml(writer, points); + } + } catch (IOException e) { + processingEnv.getMessager().printMessage(Kind.ERROR, "Error writing to docs file: " + e.getMessage()); + } + } + + return false; + } + + private List findPluginPoints(Set annotations, RoundEnvironment roundEnv) { + List pluginPointInfo = new ArrayList<>(); + + for (TypeElement annotation : annotations) { + ExposedAs exposedType = null; + if (annotation.getSimpleName().toString().contains("Service")) { + exposedType = ExposedAs.SERVICE; + } else if (annotation.getSimpleName().toString().contains("ExtensionPoint")) { + exposedType = ExposedAs.EXTENSION_POINT; + } else { + processingEnv.getMessager().printMessage(Kind.WARNING, "Unrecognized annotation: " + annotation.getSimpleName()); + continue; + } + + Set elements = roundEnv.getElementsAnnotatedWith(annotation); + for (Element element : elements) { + TypeElement te = (TypeElement) element; + pluginPointInfo.add(new PluginPointInformation(te, exposedType, processingEnv.getElementUtils().getDocComment(te))); + } + } + return pluginPointInfo; + } + + private void writeXml(PrintWriter writer, List points) { + writer.println(""); + + writer.println(""); + + for (PluginPointInformation info: points) { + String tag = info.exposedAs == ExposedAs.SERVICE ? "service" : "extension-point"; + + writer.println(" <" + tag + ">"); + writer.println(" " + info.annotatedClass.getQualifiedName() + ""); + if (info.javadoc != null) { + writer.println(" "); + } + writer.println(" "); + } + } + + private static class PluginPointInformation { + private TypeElement annotatedClass; + private ExposedAs exposedAs; + private String javadoc; + + public PluginPointInformation(TypeElement annotatedClass, ExposedAs exposedAs, String javadoc) { + this.annotatedClass = annotatedClass; + this.exposedAs = exposedAs; + this.javadoc = javadoc; + } + } +} diff -r 6815308bb362 -r bf46bf74d05c annotations/src/main/resources/META-INF/services/javax.annotation.processing.Processor --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/annotations/src/main/resources/META-INF/services/javax.annotation.processing.Processor Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,2 @@ +# This jar provides the following annotation processors: +com.redhat.thermostat.annotations.internal.AnnotationProcessor diff -r 6815308bb362 -r bf46bf74d05c annotations/src/test/java/com/redhat/thermostat/annotations/internal/AnnotationProcessorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/annotations/src/test/java/com/redhat/thermostat/annotations/internal/AnnotationProcessorTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,205 @@ +/* + * Copyright 2013 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 + * . + * + * 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.annotations.internal; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; + +import javax.annotation.processing.Filer; +import javax.annotation.processing.Messager; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.Elements; +import javax.tools.FileObject; +import javax.tools.StandardLocation; + +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.annotations.ExtensionPoint; +import com.redhat.thermostat.annotations.Service; + +public class AnnotationProcessorTest { + + private ProcessingEnvironment processingEnv; + private RoundEnvironment roundEnv; + private FileObject procesorOutputFile; + private Messager messager; + private Elements elementUtils; + + private static final String AUTO_GENERATED_COMMENT = ""; + + @Before + public void setUp() throws IOException { + procesorOutputFile = mock(FileObject.class); + Filer filer = mock(Filer.class); + when(filer.createResource( + eq(StandardLocation.CLASS_OUTPUT), + eq(""), + eq("META-INF/thermostat/plugin-docs.xml"), + any(Element.class))) + .thenReturn(procesorOutputFile); + + messager = mock(Messager.class); + + elementUtils = mock(Elements.class); + + processingEnv = mock(ProcessingEnvironment.class); + when(processingEnv.getFiler()).thenReturn(filer); + when(processingEnv.getMessager()).thenReturn(messager); + when(processingEnv.getElementUtils()).thenReturn(elementUtils); + + roundEnv = mock(RoundEnvironment.class); + } + + @Test + public void testNoAnnotationProducesNoFile() { + Set annotations = new TreeSet<>(); + + AnnotationProcessor processor = new AnnotationProcessor(); + processor.init(processingEnv); + processor.process(annotations, roundEnv); + + verifyZeroInteractions(procesorOutputFile); + } + + @Test + public void testProcessOnServiceClass() throws IOException { + final String CLASS_NAME = "c.r.t.annotations.Test"; + final String JAVADOC = "some javadoc"; + final String ANNOTATION_CLASS_NAME = Service.class.getName(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + when(procesorOutputFile.openOutputStream()).thenReturn(out); + + TypeElement serviceAnnotation = mock(TypeElement.class); + Name serviceAnnotationName = mock(Name.class); + when(serviceAnnotationName.toString()).thenReturn(ANNOTATION_CLASS_NAME); + when(serviceAnnotation.getSimpleName()).thenReturn(serviceAnnotationName); + + TypeElement annotatedClass = mock(TypeElement.class); + Name annotatedClassName = mock(Name.class); + when(annotatedClass.getQualifiedName()).thenReturn(annotatedClassName); + when(annotatedClassName.toString()).thenReturn(CLASS_NAME); + + Set annotations = new HashSet(); + annotations.add(serviceAnnotation); + + Set annotatedClasses = new HashSet(); + annotatedClasses.add(annotatedClass); + + when(roundEnv.getElementsAnnotatedWith(serviceAnnotation)).thenReturn(annotatedClasses); + + when(elementUtils.getDocComment(annotatedClass)).thenReturn(JAVADOC); + + AnnotationProcessor processor = new AnnotationProcessor(); + processor.init(processingEnv); + processor.process(annotations, roundEnv); + + String actualFileContents = out.toString("UTF-8"); + String expectedFileContents = "\n" + + AUTO_GENERATED_COMMENT + "\n" + + " \n" + + " " + CLASS_NAME + "\n" + + " \n" + + " \n" + + ""; + + assertEquals(expectedFileContents, actualFileContents); + } + + @Test + public void testProcessOnExtensionPointClass() throws IOException { + final String CLASS_NAME = "c.r.t.annotations.Test"; + final String JAVADOC = "some javadoc"; + final String ANNOTATION_CLASS_NAME = ExtensionPoint.class.getName(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + when(procesorOutputFile.openOutputStream()).thenReturn(out); + + TypeElement serviceAnnotation = mock(TypeElement.class); + Name serviceAnnotationName = mock(Name.class); + when(serviceAnnotationName.toString()).thenReturn(ANNOTATION_CLASS_NAME); + when(serviceAnnotation.getSimpleName()).thenReturn(serviceAnnotationName); + + TypeElement annotatedClass = mock(TypeElement.class); + Name annotatedClassName = mock(Name.class); + when(annotatedClass.getQualifiedName()).thenReturn(annotatedClassName); + when(annotatedClassName.toString()).thenReturn(CLASS_NAME); + + Set annotations = new HashSet(); + annotations.add(serviceAnnotation); + + Set annotatedClasses = new HashSet(); + annotatedClasses.add(annotatedClass); + + when(roundEnv.getElementsAnnotatedWith(serviceAnnotation)).thenReturn(annotatedClasses); + + when(elementUtils.getDocComment(annotatedClass)).thenReturn(JAVADOC); + + AnnotationProcessor processor = new AnnotationProcessor(); + processor.init(processingEnv); + processor.process(annotations, roundEnv); + + String actualFileContents = out.toString("UTF-8"); + String expectedFileContents = "\n" + + AUTO_GENERATED_COMMENT + "\n" + + " \n" + + " " + CLASS_NAME + "\n" + + " \n" + + " \n" + + ""; + + assertEquals(expectedFileContents, actualFileContents); + } + +} diff -r 6815308bb362 -r bf46bf74d05c bundles/pom.xml --- a/bundles/pom.xml Tue Jan 15 23:00:52 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ - - - - 4.0.0 - - - com.redhat.thermostat - thermostat - 0.5.0-SNAPSHOT - - - thermostat-bundles - bundle - - Service providing entry point to OSGi-land - - - - junit - junit - test - - - org.mockito - mockito-core - test - - - org.powermock - powermock-api-mockito - test - - - org.powermock - powermock-module-junit4 - test - - - org.jboss.netty - netty - - - org.osgi - org.osgi.core - provided - - - - com.redhat.thermostat - thermostat-common-core - ${project.version} - jar - - - - - - - org.apache.felix - maven-bundle-plugin - true - - - Red Hat, Inc. - com.redhat.thermostat.bundles.impl.Activator - com.redhat.thermostat.bundles.core - - com.redhat.thermostat.bundles - - - com.redhat.thermostat.bundles.impl - - - <_nouses>true - - - - - - diff -r 6815308bb362 -r bf46bf74d05c bundles/src/main/java/com/redhat/thermostat/bundles/OSGiRegistry.java --- a/bundles/src/main/java/com/redhat/thermostat/bundles/OSGiRegistry.java Tue Jan 15 23:00:52 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +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 - * . - * - * 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.bundles; - -import java.io.IOException; -import java.util.List; - -import org.osgi.framework.BundleException; -import org.osgi.framework.launch.Framework; - -import com.redhat.thermostat.bundles.impl.BundleLoader; -import com.redhat.thermostat.common.Configuration; -import com.redhat.thermostat.common.cli.CommandInfoNotFoundException; -import com.redhat.thermostat.common.cli.CommandInfoSource; - -/** - * A Service that provides features to load bundles for given command names. - */ -public abstract class OSGiRegistry { - - public abstract void setPrintOSGiInfo(boolean printOSGiInfo); - - public abstract void setCommandInfoSource(CommandInfoSource source); - - public abstract void addBundlesFor(String commandName) throws BundleException, CommandInfoNotFoundException, IOException; - - public static void preLoadBundles(Framework framework, List bundleLocations, - boolean printOSGiInfo) throws BundleException { - BundleLoader loader = new BundleLoader(printOSGiInfo); - loader.installAndStartBundles(framework, bundleLocations); - } - - public abstract Configuration getConfiguration(); - -} diff -r 6815308bb362 -r bf46bf74d05c bundles/src/main/java/com/redhat/thermostat/bundles/impl/Activator.java --- a/bundles/src/main/java/com/redhat/thermostat/bundles/impl/Activator.java Tue Jan 15 23:00:52 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +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 - * . - * - * 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.bundles.impl; - -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; - -import com.redhat.thermostat.bundles.OSGiRegistry; -import com.redhat.thermostat.common.Configuration; - -public class Activator implements BundleActivator { - - ServiceRegistration reg; - - @Override - public void start(BundleContext context) throws Exception { - OSGiRegistryImpl bundleRegistry = new OSGiRegistryImpl(new Configuration()); - reg = context.registerService(OSGiRegistry.class.getName(), bundleRegistry, null); - } - - @Override - public void stop(BundleContext context) throws Exception { - if (reg != null) { - reg.unregister(); - reg = null; - } - } - -} diff -r 6815308bb362 -r bf46bf74d05c bundles/src/main/java/com/redhat/thermostat/bundles/impl/BundleLoader.java --- a/bundles/src/main/java/com/redhat/thermostat/bundles/impl/BundleLoader.java Tue Jan 15 23:00:52 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,99 +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 - * . - * - * 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.bundles.impl; - -import java.util.ArrayList; -import java.util.List; - -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.BundleException; -import org.osgi.framework.Constants; -import org.osgi.framework.launch.Framework; - -public class BundleLoader { - - private boolean printOSGiInfo = false; - - BundleLoader() { - this(false); - } - - public BundleLoader(boolean printOSGiInfo) { - setPrintOSGiInfo(printOSGiInfo); - } - - public void setPrintOSGiInfo(boolean printOSGiInfo) { - this.printOSGiInfo = printOSGiInfo; - } - - public List installAndStartBundles(Framework framework, - ListbundleLocations) throws BundleException { - List bundles = new ArrayList<>(); - BundleContext ctx = framework.getBundleContext(); - for (String location : bundleLocations) { - Bundle bundle = ctx.installBundle(location); - if (printOSGiInfo) { - System.out.println("BundleLoader: installed bundle: \"" + - location + "\" as id " + bundle.getBundleId()); - } - bundles.add(bundle); - } - startBundles(bundles); - return bundles; - } - - private void startBundles(List bundles) throws BundleException { - for (Bundle bundle : bundles) { - - if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null) { - if (printOSGiInfo) { - System.out.println("BundleLoader: bundle \"" + bundle.getBundleId() + "\" is a fragment; not starting it"); - } - continue; - } - - if (printOSGiInfo) { - System.out.println("BundleLoader: starting bundle: \"" + bundle.getBundleId() + "\""); - } - // We don't want for the framework to set the auto-start bit. Thus, passing - // START_TRANSIENT explicitly - bundle.start(Bundle.START_TRANSIENT); - } - } - -} diff -r 6815308bb362 -r bf46bf74d05c bundles/src/main/java/com/redhat/thermostat/bundles/impl/OSGiRegistryImpl.java --- a/bundles/src/main/java/com/redhat/thermostat/bundles/impl/OSGiRegistryImpl.java Tue Jan 15 23:00:52 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,123 +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 - * . - * - * 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.bundles.impl; - -import com.redhat.thermostat.bundles.OSGiRegistry; -import com.redhat.thermostat.common.Configuration; -import com.redhat.thermostat.common.ConfigurationException; -import com.redhat.thermostat.common.cli.CommandInfoNotFoundException; -import com.redhat.thermostat.common.cli.CommandInfoSource; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleException; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.launch.Framework; - -public class OSGiRegistryImpl extends OSGiRegistry { - - private CommandInfoSource commandInfos; - private Map loaded; - private Configuration configuration; - private BundleLoader loader; - - OSGiRegistryImpl(Configuration configuration) throws ConfigurationException, FileNotFoundException, IOException { - initLoadedBundles(); - this.configuration = configuration; - loader = new BundleLoader(configuration.getPrintOSGiInfo()); - } - - private void initLoadedBundles() { - loaded = new HashMap<>(); - Framework framework = getFramework(this.getClass()); - for (Bundle bundle: framework.getBundleContext().getBundles()) { - loaded.put(bundle.getLocation(), bundle); - } - } - - @Override - public void setPrintOSGiInfo(boolean printOSGiInfo) { - configuration.setPrintOSGiInfo(printOSGiInfo); - loader.setPrintOSGiInfo(printOSGiInfo); - } - - @Override - public void setCommandInfoSource(CommandInfoSource source) { - this.commandInfos = source; - } - - @Override - public void addBundlesFor(String commandName) throws BundleException, IOException, CommandInfoNotFoundException { - if (configuration.getPrintOSGiInfo()) { - System.out.println("Loading additional bundles for: " + commandName); - } - List requiredBundles = commandInfos.getCommandInfo(commandName).getDependencyResourceNames(); - List bundlesToLoad = new ArrayList<>(); - if (requiredBundles != null) { - for (String resource : requiredBundles) { - if (!isBundleActive(resource)) { - bundlesToLoad.add(resource); - } - } - } - Framework framework = getFramework(this.getClass()); - List successBundles = loader.installAndStartBundles(framework, bundlesToLoad); - for (Bundle bundle : successBundles) { - loaded.put(bundle.getLocation(), bundle); - } - } - - private boolean isBundleActive(String location) { - Bundle bundle = loaded.get(location); - return (bundle != null) && (bundle.getState() == Bundle.ACTIVE); - } - - private Framework getFramework(Class cls) { - return (Framework) FrameworkUtil.getBundle(cls).getBundleContext().getBundle(0); - } - - @Override - public Configuration getConfiguration() { - return configuration; - } -} diff -r 6815308bb362 -r bf46bf74d05c bundles/src/test/java/com/redhat/thermostat/bundles/OSGiRegistryTest.java --- a/bundles/src/test/java/com/redhat/thermostat/bundles/OSGiRegistryTest.java Tue Jan 15 23:00:52 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +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 - * . - * - * 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.bundles; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.powermock.api.mockito.PowerMockito.whenNew; - -import java.util.ArrayList; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.osgi.framework.launch.Framework; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import com.redhat.thermostat.bundles.impl.BundleLoader; - -@RunWith(PowerMockRunner.class) -@PrepareForTest(OSGiRegistry.class) -public class OSGiRegistryTest { - - @Test - public void testPreLoadBundles() throws Exception { - Framework framework = mock(Framework.class); - ArrayList bundleLocations = new ArrayList<>(); - BundleLoader loader = mock(BundleLoader.class); - whenNew(BundleLoader.class).withParameterTypes(Boolean.TYPE). - withArguments(any()).thenReturn(loader); - - OSGiRegistry.preLoadBundles(framework, bundleLocations, true); - verify(loader).installAndStartBundles(framework, bundleLocations); - } -} diff -r 6815308bb362 -r bf46bf74d05c bundles/src/test/java/com/redhat/thermostat/bundles/impl/BundleLoaderTest.java --- a/bundles/src/test/java/com/redhat/thermostat/bundles/impl/BundleLoaderTest.java Tue Jan 15 23:00:52 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,102 +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 - * . - * - * 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.bundles.impl; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.Dictionary; -import java.util.Hashtable; -import java.util.List; - -import org.junit.Test; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.BundleException; -import org.osgi.framework.Constants; -import org.osgi.framework.launch.Framework; - -import com.redhat.thermostat.test.Bug; - -public class BundleLoaderTest { - - @Test - public void verifyBundlesAreInstalledAndStarted() throws BundleException { - final String BUNDLE_LOCATION = "bundle-location-1"; - - Bundle bundle = mock(Bundle.class); - when(bundle.getHeaders()).thenReturn(new Hashtable<>()); - BundleContext bundleContext = mock(BundleContext.class); - when(bundleContext.installBundle(BUNDLE_LOCATION)).thenReturn(bundle); - Framework framework = mock(Framework.class); - when(framework.getBundleContext()).thenReturn(bundleContext); - List bundleLocations = Arrays.asList(BUNDLE_LOCATION); - - BundleLoader loader = new BundleLoader(); - loader.installAndStartBundles(framework, bundleLocations); - - verify(bundle).start(Bundle.START_TRANSIENT); - } - - @Bug(id="1227", - summary="Make sure launcher does not start fragments", - url="http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1227") - @Test - public void verifyFragmentsAreInstalledButNotStarted() throws BundleException { - final String BUNDLE_LOCATION = "bundle-location-1"; - - Bundle bundle = mock(Bundle.class); - Dictionary bundleHeaders = new Hashtable<>(); - bundleHeaders.put(Constants.FRAGMENT_HOST, "foo-bar"); - when(bundle.getHeaders()).thenReturn(bundleHeaders); - - BundleContext bundleContext = mock(BundleContext.class); - when(bundleContext.installBundle(BUNDLE_LOCATION)).thenReturn(bundle); - Framework framework = mock(Framework.class); - when(framework.getBundleContext()).thenReturn(bundleContext); - List bundleLocations = Arrays.asList(BUNDLE_LOCATION); - - BundleLoader loader = new BundleLoader(); - loader.installAndStartBundles(framework, bundleLocations); - - verify(bundle, times(0)).start(Bundle.START_TRANSIENT); - - } -} diff -r 6815308bb362 -r bf46bf74d05c bundles/src/test/java/com/redhat/thermostat/bundles/impl/OSGiRegistryImplTest.java --- a/bundles/src/test/java/com/redhat/thermostat/bundles/impl/OSGiRegistryImplTest.java Tue Jan 15 23:00:52 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,155 +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 - * . - * - * 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.bundles.impl; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.verify; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.whenNew; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.launch.Framework; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import com.redhat.thermostat.common.Configuration; -import com.redhat.thermostat.common.cli.CommandInfo; -import com.redhat.thermostat.common.cli.CommandInfoSource; - -@RunWith(PowerMockRunner.class) -@PrepareForTest({OSGiRegistryImpl.class, FrameworkUtil.class}) -public class OSGiRegistryImplTest { - - private static final String cmdName = "one"; - - private static final String jar1Name = "/one.jar"; - private static final String jar2Name = "/two.jar"; - private static final String jar3Name = "/three.jar"; - - private Bundle b1, b2, b3; - private List bundleLocs; - - private BundleLoader loader; - private Configuration conf; - - @Before - public void setUp() throws Exception { - conf = mock(Configuration.class); - when(conf.getThermostatHome()).thenReturn("no_matter"); - bundleLocs = Arrays.asList(jar1Name, jar2Name, jar3Name); - b1 = mock(Bundle.class); - when(b1.getLocation()).thenReturn(jar1Name); - when(b1.getState()).thenReturn(Bundle.ACTIVE); - b2 = mock(Bundle.class); - when(b2.getLocation()).thenReturn(jar2Name); - when(b2.getState()).thenReturn(Bundle.ACTIVE); - b3 = mock(Bundle.class); - when(b3.getLocation()).thenReturn(jar3Name); - when(b3.getState()).thenReturn(Bundle.ACTIVE); - List installed = Arrays.asList(b1, b2, b3); - - loader = mock(BundleLoader.class); - when(loader.installAndStartBundles(any(Framework.class), eq(bundleLocs))). - thenReturn(installed); - whenNew(BundleLoader.class).withParameterTypes(Boolean.TYPE). - withArguments(any()).thenReturn(loader); - } - - @Test - public void testLoadBundlesFor() throws Exception { - verifyBundlesLoaded(new Bundle[] {}, bundleLocs); - } - - @Test - public void verifyAlreadyLoadedBundlesNotReloaded() throws Exception { - verifyBundlesLoaded(new Bundle[] {b1, b2}, Arrays.asList(jar3Name)); - } - - private void verifyBundlesLoaded(Bundle[] preloaded, List locationsNeeded) throws Exception { - Bundle theBundle = b2; - BundleContext theContext = mock(BundleContext.class); - when(theContext.getBundles()).thenReturn(preloaded); - Framework theFramework = mock(Framework.class); - when(theFramework.getBundleContext()).thenReturn(theContext); - when(theContext.getBundle(0)).thenReturn(theFramework); - when(theBundle.getBundleContext()).thenReturn(theContext); - mockStatic(FrameworkUtil.class); - when(FrameworkUtil.getBundle(any(Class.class))).thenReturn(theBundle); - - OSGiRegistryImpl registry = new OSGiRegistryImpl(conf); - CommandInfoSource infos = mock(CommandInfoSource.class); - CommandInfo info = mock(CommandInfo.class); - when (info.getDependencyResourceNames()).thenReturn(bundleLocs); - when (infos.getCommandInfo(cmdName)).thenReturn(info); - registry.setCommandInfoSource(infos); - registry.addBundlesFor(cmdName); - verify(loader).installAndStartBundles(any(Framework.class), eq(locationsNeeded)); - } - - @Test - public void verifySetOSGiVerbosityByReflection() throws Exception { - - // All this fluff is just so constructor doesn't NPE. - Bundle theBundle = b2; - BundleContext theContext = mock(BundleContext.class); - when(theContext.getBundles()).thenReturn(new Bundle[]{}); - Framework theFramework = mock(Framework.class); - when(theFramework.getBundleContext()).thenReturn(theContext); - when(theContext.getBundle(0)).thenReturn(theFramework); - when(theBundle.getBundleContext()).thenReturn(theContext); - mockStatic(FrameworkUtil.class); - when(FrameworkUtil.getBundle(any(Class.class))).thenReturn(theBundle); - - Object registry = new OSGiRegistryImpl(conf); - Class clazz = registry.getClass(); - Method m = clazz.getMethod("setPrintOSGiInfo", Boolean.TYPE); - m.invoke(registry, true); // If this fails, then API has changed in ways that break FrameworkProvider. - } - -} diff -r 6815308bb362 -r bf46bf74d05c client/cli/pom.xml --- a/client/cli/pom.xml Tue Jan 15 23:00:52 2013 +0100 +++ b/client/cli/pom.xml Tue Jan 15 23:04:57 2013 +0100 @@ -99,18 +99,12 @@ org.osgi org.osgi.core + provided - - com.redhat.thermostat - thermostat-vm-cpu-common - ${project.version} - - - - com.redhat.thermostat - thermostat-vm-memory-common - ${project.version} + org.osgi + org.osgi.compendium + provided @@ -125,17 +119,15 @@ Red Hat, Inc. com.redhat.thermostat.client.cli.internal.Activator com.redhat.thermostat.client.cli + + com.redhat.thermostat.client.cli, + META_INF.services, com.redhat.thermostat.client.cli.internal, <_nouses>true - - - *,com.redhat.thermostat.vm.cpu.common;resolution:=optional, - com.redhat.thermostat.vm.memory.common;resolution:=optional - diff -r 6815308bb362 -r bf46bf74d05c client/cli/src/main/java/com/redhat/thermostat/client/cli/VMStatPrintDelegate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/VMStatPrintDelegate.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,76 @@ +/* + * Copyright 2013 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 + * . + * + * 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.client.cli; + +import java.util.List; + +import com.redhat.thermostat.common.Ordered; +import com.redhat.thermostat.common.dao.VmRef; +import com.redhat.thermostat.storage.model.TimeStampedPojo; + +/** + * This interface should be implemented by plug-ins that would like to + * contribute data to the output of the vm-stat command. + */ +public interface VMStatPrintDelegate extends Ordered { + + /** + * Returns statistics gathered by this plug-in newer than the specified + * time stamp. + * @param ref - the VM whose statistics to return + * @param timeStampSince - the earliest time stamp to return statistics for + * @return a list of statistics newer than the time stamp + */ + public List getLatestStats(VmRef ref, long timeStampSince); + + /** + * Returns header names for columns this plug-in wishes to add to the + * vm-stat command. + * @param stat - the first stat returned by {@link #getLatestStats(VmRef, long)} + * @return a list of column headers to append to vm-stat output + */ + public List getHeaders(TimeStampedPojo stat); + + /** + * Returns a row of data for the specified statistic that corresponds to + * the columns returned by {@link #getHeaders(TimeStampedPojo)}. + * @param stat - the statistic to generate output for + * @return a row of text for this statistic separated by column + */ + public List getStatRow(TimeStampedPojo stat); + +} diff -r 6815308bb362 -r bf46bf74d05c client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java --- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java Tue Jan 15 23:00:52 2013 +0100 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java Tue Jan 15 23:04:57 2013 +0100 @@ -70,8 +70,6 @@ COLUMN_HEADER_VM_NAME, COLUMN_HEADER_VM_STATUS, COLUMN_HEADER_TIME, - COLUMN_HEADER_CPU_PERCENT, - COLUMN_HEADER_MEMORY_PATTERN, VM_STOP_TIME_RUNNING, VM_STATUS_ALIVE, diff -r 6815308bb362 -r bf46bf74d05c client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatCommand.java --- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatCommand.java Tue Jan 15 23:00:52 2013 +0100 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatCommand.java Tue Jan 15 23:04:57 2013 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2012 Red Hat, Inc. + * Copyright 2013 Red Hat, Inc. * * This file is part of Thermostat. * @@ -37,71 +37,81 @@ package com.redhat.thermostat.client.cli.internal; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +import com.redhat.thermostat.client.cli.VMStatPrintDelegate; import com.redhat.thermostat.common.ApplicationService; +import com.redhat.thermostat.common.OrderedComparator; import com.redhat.thermostat.common.Timer; import com.redhat.thermostat.common.cli.CommandContext; import com.redhat.thermostat.common.cli.CommandException; import com.redhat.thermostat.common.cli.HostVMArguments; import com.redhat.thermostat.common.cli.SimpleCommand; import com.redhat.thermostat.common.dao.VmRef; -import com.redhat.thermostat.common.locale.Translate; import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.common.utils.OSGIUtils; -import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; -import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; public class VMStatCommand extends SimpleCommand { - private static final Translate translator = LocaleResources.createLocalizer(); - private static final Logger log = LoggingUtils.getLogger(VMStatCommand.class); - private static final String CMD_NAME = "vm-stat"; - - private OSGIUtils serviceProvider; - + + private List delegates; + private BundleContext context; + public VMStatCommand() { - this(OSGIUtils.getInstance()); + this(FrameworkUtil.getBundle(VMStatCommand.class).getBundleContext()); } - VMStatCommand(OSGIUtils serviceProvider) { - this.serviceProvider = serviceProvider; + VMStatCommand(BundleContext context) { + this.context = context; + delegates = new CopyOnWriteArrayList<>(); + ServiceTracker tracker = new ServiceTracker(context, VMStatPrintDelegate.class.getName(), null) { + + public Object addingService(ServiceReference reference) { + VMStatPrintDelegate delegate = (VMStatPrintDelegate) super.addingService(reference); + delegates.add(delegate); + return delegate; + }; + + public void removedService(ServiceReference reference, Object service) { + delegates.remove(service); + super.removedService(reference, service); + }; + + }; + tracker.open(); } @Override public void run(final CommandContext ctx) throws CommandException { - VmCpuStatDAO vmCpuStatDAO = serviceProvider.getServiceAllowNull(VmCpuStatDAO.class); - if (vmCpuStatDAO == null) { - throw new CommandException(translator.localize(LocaleResources.VM_CPU_SERVICE_NOT_AVAILABLE)); - } - - VmMemoryStatDAO vmMemoryStatDAO = serviceProvider.getServiceAllowNull(VmMemoryStatDAO.class); - if (vmMemoryStatDAO == null) { - throw new CommandException(translator.localize(LocaleResources.VM_MEMORY_SERVICE_NOT_AVAILABLE)); - } - HostVMArguments hostVMArgs = new HostVMArguments(ctx.getArguments()); VmRef vm = hostVMArgs.getVM(); - final VMStatPrinter statPrinter = new VMStatPrinter(vm, vmCpuStatDAO, vmMemoryStatDAO, ctx.getConsole().getOutput()); + // Pass a copy of the delegates list to the printer + final VMStatPrinter statPrinter = new VMStatPrinter(vm, new ArrayList<>(delegates), ctx.getConsole().getOutput()); statPrinter.printStats(); boolean continuous = ctx.getArguments().hasArgument("continuous"); if (continuous) { startContinuousStats(ctx, statPrinter); } - - serviceProvider.ungetService(VmMemoryStatDAO.class, vmMemoryStatDAO); - serviceProvider.ungetService(VmCpuStatDAO.class, vmCpuStatDAO); } private void startContinuousStats(final CommandContext ctx, final VMStatPrinter statPrinter) { final CountDownLatch latch = new CountDownLatch(1); - ApplicationService appSvc = serviceProvider.getService(ApplicationService.class); + ServiceReference ref = context.getServiceReference(ApplicationService.class.getName()); + ApplicationService appSvc = (ApplicationService) context.getService(ref); Timer timer = appSvc.getTimerFactory().createTimer(); timer.setDelay(1); timer.setInitialDelay(1); @@ -133,6 +143,8 @@ } catch (InterruptedException e) { // Return immediately. } + + context.ungetService(ref); } @Override diff -r 6815308bb362 -r bf46bf74d05c client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java --- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java Tue Jan 15 23:00:52 2013 +0100 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMStatPrinter.java Tue Jan 15 23:04:57 2013 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2012 Red Hat, Inc. + * Copyright 2013 Red Hat, Inc. * * This file is part of Thermostat. * @@ -38,70 +38,102 @@ import java.io.PrintStream; import java.text.DateFormat; -import java.text.DecimalFormat; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; -import com.redhat.thermostat.common.Size; +import com.redhat.thermostat.client.cli.VMStatPrintDelegate; +import com.redhat.thermostat.common.OrderedComparator; import com.redhat.thermostat.common.cli.TableRenderer; import com.redhat.thermostat.common.dao.VmRef; import com.redhat.thermostat.common.locale.Translate; import com.redhat.thermostat.storage.model.TimeStampedPojo; import com.redhat.thermostat.storage.model.TimeStampedPojoComparator; import com.redhat.thermostat.storage.model.TimeStampedPojoCorrelator; -import com.redhat.thermostat.storage.model.VmCpuStat; -import com.redhat.thermostat.storage.model.VmMemoryStat; -import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; -import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; class VMStatPrinter { private static final Translate translator = LocaleResources.createLocalizer(); - private static final String CPU_PERCENT = translator.localize(LocaleResources.COLUMN_HEADER_CPU_PERCENT); private static final String TIME = translator.localize(LocaleResources.COLUMN_HEADER_TIME); private VmRef vm; - private VmCpuStatDAO vmCpuStatDAO; - private VmMemoryStatDAO vmMemoryStatDAO; + private List delegates; private PrintStream out; - private TimeStampedPojoCorrelator correlator = new TimeStampedPojoCorrelator(2); + private TimeStampedPojoCorrelator correlator; private TableRenderer table; - private int numSpaces; + private int numCols; + private Map delegateInfo; - private long lastCpuStatTimeStamp = Long.MIN_VALUE; - private long lastMemoryStatTimeStamp = Long.MIN_VALUE; - - VMStatPrinter(VmRef vm, VmCpuStatDAO vmCpuStatDAO, VmMemoryStatDAO vmMemoryStatDAO, PrintStream out) { + VMStatPrinter(VmRef vm, List delegates, PrintStream out) { this.vm = vm; - this.vmCpuStatDAO = vmCpuStatDAO; - this.vmMemoryStatDAO = vmMemoryStatDAO; + this.delegates = delegates; this.out = out; + int numDelegates = delegates.size(); + this.delegateInfo = new HashMap<>(); + this.correlator = new TimeStampedPojoCorrelator(numDelegates); + + // Sort the delegates list + Collections.sort(delegates, new OrderedComparator<>()); + + for (VMStatPrintDelegate delegate : delegates) { + DelegateInfo info = new DelegateInfo(); + info.lastTimeStamp = Long.MIN_VALUE; + delegateInfo.put(delegate, info); + } } void printStats() { - List cpuStats = vmCpuStatDAO.getLatestVmCpuStats(vm, lastCpuStatTimeStamp); - List memStats = vmMemoryStatDAO.getLatestVmMemoryStats(vm, lastMemoryStatTimeStamp); - - lastCpuStatTimeStamp = getLatestTimeStamp(lastCpuStatTimeStamp, cpuStats); - lastMemoryStatTimeStamp = getLatestTimeStamp(lastMemoryStatTimeStamp, memStats); - - printStats(cpuStats, memStats); + List> allStats = new ArrayList<>(); + List allHeaders = new ArrayList<>(); + allHeaders.add(TIME); + + // Copy since we can remove elements in loop body + List delegatesCopy = new ArrayList<>(delegates); + for (VMStatPrintDelegate delegate : delegatesCopy) { + long timeStamp = delegateInfo.get(delegate).lastTimeStamp; + List latestStats = delegate.getLatestStats(vm, timeStamp); + if (latestStats == null || latestStats.isEmpty()) { + // Skipping delegate + delegates.remove(delegate); + } + else { + List headers = delegate.getHeaders(latestStats.get(0)); + if (headers == null || headers.isEmpty()) { + // Skipping delegate + delegates.remove(delegate); + } + else { + DelegateInfo info = delegateInfo.get(delegate); + info.colsPerDelegate = headers.size(); + allHeaders.addAll(headers); + allStats.add(latestStats); + info.lastTimeStamp = getLatestTimeStamp(timeStamp, latestStats); + } + } + } + + printStats(allStats, allHeaders); } void printUpdatedStats() { correlator.clear(); - List cpuStats = vmCpuStatDAO.getLatestVmCpuStats(vm, lastCpuStatTimeStamp); - List memStats = vmMemoryStatDAO.getLatestVmMemoryStats(vm, lastMemoryStatTimeStamp); + + List> allStats = new ArrayList<>(); + for (VMStatPrintDelegate delegate : delegates) { + DelegateInfo info = delegateInfo.get(delegate); + List latestStats = delegate.getLatestStats(vm, info.lastTimeStamp); + allStats.add(latestStats); + info.lastTimeStamp = getLatestTimeStamp(info.lastTimeStamp, latestStats); + } - lastCpuStatTimeStamp = getLatestTimeStamp(lastCpuStatTimeStamp, cpuStats); - lastMemoryStatTimeStamp = getLatestTimeStamp(lastMemoryStatTimeStamp, memStats); - - correlate(cpuStats, memStats); + correlate(allStats); printUpdatedStatsImpl(); } @@ -113,103 +145,79 @@ } } - private void printStats(List cpuStats, List memStats) { - correlate(cpuStats, memStats); - numSpaces = getNumSpaces(memStats); - int numColumns = numSpaces + 2; - table = new TableRenderer(numColumns); - printHeaders(memStats, numSpaces, numColumns, table); + private void printStats(List> allStats, List headers) { + correlate(allStats); + numCols = headers.size(); + table = new TableRenderer(numCols); + printHeaders(table, headers); printUpdatedStatsImpl(); } - private void printStats(int numSpaces, TableRenderer table, Iterator i) { - - TimeStampedPojoCorrelator.Correlation correlation = i.next(); + private void printStats(TableRenderer table, Iterator iter) { + TimeStampedPojoCorrelator.Correlation correlation = iter.next(); - VmCpuStat cpuStat = (VmCpuStat) correlation.get(0); - DecimalFormat format = new DecimalFormat("#0.0"); - String cpuLoad = cpuStat != null ? format.format(cpuStat.getCpuLoad()) : ""; - + String[] line = new String[numCols]; DateFormat dateFormat = DateFormat.getTimeInstance(); String time = dateFormat.format(new Date(correlation.getTimeStamp())); - - String[] memoryUsage = getMemoryUsage((VmMemoryStat) correlation.get(1), numSpaces); - - String[] line = new String[numSpaces + 2]; - System.arraycopy(memoryUsage, 0, line, 2, numSpaces); line[0] = time; - line[1] = cpuLoad; + + int off = 1; // time is first index + for (int i = 0; i < delegates.size(); i++) { + TimeStampedPojo stat = correlation.get(i); + VMStatPrintDelegate delegate = delegates.get(i); + if (stat == null) { + // Fill with blanks + DelegateInfo info = delegateInfo.get(delegate); + Arrays.fill(line, off, off + info.colsPerDelegate, ""); + off += info.colsPerDelegate; + } + else { + List data = delegate.getStatRow(stat); + if (data == null) { + throw new NullPointerException("Returned null stat row"); + } + else if (data.size() != delegateInfo.get(delegate).colsPerDelegate) { + throw new IllegalStateException("Delegate " + + delegate.toString() + " provided " + + delegateInfo.get(delegate).colsPerDelegate + + " column headers, but only " + data.size() + + " stat row values"); + } + else { + System.arraycopy(data.toArray(), 0, line, off, data.size()); + off += data.size(); + } + } + } + table.printLine(line); } - private void printHeaders(List memStats, int numSpaces, int numColumns, TableRenderer table) { - String[] spacesNames = getSpacesNames(memStats, numSpaces); - String[] headers = new String[numColumns]; - headers[0] = TIME; - headers[1] = CPU_PERCENT; - System.arraycopy(spacesNames, 0, headers, 2, numSpaces); - table.printLine(headers); - } - - private String[] getMemoryUsage(VmMemoryStat vmMemoryStat, int numSpaces) { - String[] memoryUsage = new String[numSpaces]; - if (vmMemoryStat == null) { - Arrays.fill(memoryUsage, ""); - return memoryUsage; - } - int i = 0; - for (VmMemoryStat.Generation gen : vmMemoryStat.getGenerations()) { - for (VmMemoryStat.Space space : gen.getSpaces()) { - memoryUsage[i] = Size.bytes(space.getUsed()).toString(); - i++; - } - } - return memoryUsage; + private void printHeaders(TableRenderer table, List headers) { + table.printLine(headers.toArray(new String[headers.size()])); } - private String[] getSpacesNames(List memStats, int numSpaces) { - if (numSpaces < 1) { - return new String[0]; - } - String[] spacesNames = new String[numSpaces]; - VmMemoryStat stat = memStats.get(0); - int i = 0; - for (VmMemoryStat.Generation gen : stat.getGenerations()) { - for (VmMemoryStat.Space space : gen.getSpaces()) { - spacesNames[i] = translator.localize(LocaleResources.COLUMN_HEADER_MEMORY_PATTERN, space.getName()); - i++; + private void correlate(List> allStats) { + int count = 0; + for (List stats : allStats) { + for(TimeStampedPojo cpuStat : stats) { + correlator.add(count, cpuStat); } - } - return spacesNames; - } - - private int getNumSpaces(List memStats) { - if (memStats.size() < 1) { - return 0; - } - VmMemoryStat stat = memStats.get(0); - int numSpaces = 0; - for (VmMemoryStat.Generation gen : stat.getGenerations()) { - numSpaces += gen.getSpaces().length; - } - return numSpaces; - } - - private void correlate(List cpuStats, List memStats) { - for(VmCpuStat cpuStat : cpuStats) { - correlator.add(0, cpuStat); - } - for (VmMemoryStat memStat : memStats) { - correlator.add(1, memStat); + count++; } } void printUpdatedStatsImpl() { Iterator iterator = correlator.iterator(); while (iterator.hasNext()) { - printStats(numSpaces, table, iterator); + printStats(table, iterator); } table.render(out); } + + private static class DelegateInfo { + int colsPerDelegate; + long lastTimeStamp; + } } diff -r 6815308bb362 -r bf46bf74d05c client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties --- a/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties Tue Jan 15 23:00:52 2013 +0100 +++ b/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties Tue Jan 15 23:04:57 2013 +0100 @@ -28,8 +28,6 @@ COLUMN_HEADER_VM_NAME = VM_NAME COLUMN_HEADER_VM_STATUS = STATUS COLUMN_HEADER_TIME = TIME -COLUMN_HEADER_CPU_PERCENT = %CPU -COLUMN_HEADER_MEMORY_PATTERN = MEM.{0} VM_STOP_TIME_RUNNING = VM_STATUS_ALIVE = RUNNING diff -r 6815308bb362 -r bf46bf74d05c client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ActivatorTest.java --- a/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ActivatorTest.java Tue Jan 15 23:00:52 2013 +0100 +++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ActivatorTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2012 Red Hat, Inc. + * Copyright 2013 Red Hat, Inc. * * This file is part of Thermostat. * @@ -38,6 +38,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -58,10 +59,15 @@ @Test public void testCommandsRegistered() throws Exception { - // Need to mock FrameworkUtil to avoid NPE in ShellCommand's no-arg constructor + // Need to mock FrameworkUtil to avoid NPE in ShellCommand and + // VMStatCommand's no-arg constructors PowerMockito.mockStatic(FrameworkUtil.class); Bundle mockBundle = mock(Bundle.class); when(FrameworkUtil.getBundle(ShellCommand.class)).thenReturn(mockBundle); + when(FrameworkUtil.getBundle(VMStatCommand.class)).thenReturn(mockBundle); + // When we call createFilter, we need a real return value + when(FrameworkUtil.createFilter(anyString())).thenCallRealMethod(); + StubBundleContext ctx = new StubBundleContext(); when(mockBundle.getBundleContext()).thenReturn(ctx); diff -r 6815308bb362 -r bf46bf74d05c client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VmStatCommandTest.java --- a/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VmStatCommandTest.java Tue Jan 15 23:00:52 2013 +0100 +++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VmStatCommandTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2012 Red Hat, Inc. + * Copyright 2013 Red Hat, Inc. * * This file is part of Thermostat. * @@ -40,11 +40,15 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.TimeZone; @@ -59,26 +63,21 @@ import org.junit.Ignore; import org.junit.Test; +import com.redhat.thermostat.client.cli.VMStatPrintDelegate; import com.redhat.thermostat.common.ApplicationService; import com.redhat.thermostat.common.Timer; import com.redhat.thermostat.common.cli.CommandException; import com.redhat.thermostat.common.cli.SimpleArguments; -import com.redhat.thermostat.common.dao.HostRef; import com.redhat.thermostat.common.dao.VmRef; -import com.redhat.thermostat.common.utils.OSGIUtils; -import com.redhat.thermostat.storage.model.VmCpuStat; -import com.redhat.thermostat.storage.model.VmMemoryStat; -import com.redhat.thermostat.storage.model.VmMemoryStat.Generation; -import com.redhat.thermostat.storage.model.VmMemoryStat.Space; +import com.redhat.thermostat.storage.model.TimeStampedPojo; +import com.redhat.thermostat.test.StubBundleContext; import com.redhat.thermostat.test.TestCommandContextFactory; import com.redhat.thermostat.test.TestTimerFactory; -import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; -import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; public class VmStatCommandTest { - private static Locale defaultLocale; private static TimeZone defaultTimeZone; + private static int NUM_ROWS = 3; @BeforeClass public static void setUpClass() { @@ -94,34 +93,62 @@ Locale.setDefault(defaultLocale); } - private VMStatCommand cmd; - private VmCpuStatDAO vmCpuStatDAO; + private VMStatPrintDelegate[] delegates; private TestCommandContextFactory cmdCtxFactory; - private VmMemoryStatDAO vmMemoryStatDAO; private TestTimerFactory timerFactory; + private ApplicationService appSvc; @Before public void setUp() { + delegates = new VMStatPrintDelegate[2]; + final String[][] headers = { + { "FIRST", "SECOND", "THIRD" }, + { "FOURTH", "FIFTH" } }; + + final String[][][] rows = { + { + { "1", "2", "3" }, + { "6", "7", "8" }, + { "11", "12", "13" }, + }, + { + { "4", "5" }, + { "9", "10" }, + { "14", "15" } + } + }; timerFactory = new TestTimerFactory(); - ApplicationService appSvc = mock(ApplicationService.class); + appSvc = mock(ApplicationService.class); when(appSvc.getTimerFactory()).thenReturn(timerFactory); setupCommandContextFactory(); - - setupDAOs(); - - OSGIUtils serviceProvider = mock(OSGIUtils.class); - when(serviceProvider.getServiceAllowNull(VmCpuStatDAO.class)).thenReturn(vmCpuStatDAO); - when(serviceProvider.getServiceAllowNull(VmMemoryStatDAO.class)).thenReturn(vmMemoryStatDAO); - when(serviceProvider.getService(ApplicationService.class)).thenReturn(appSvc); - - cmd = new VMStatCommand(serviceProvider); + + delegates[0] = mockDelegate(headers[0], rows[0]); + delegates[1] = mockDelegate(headers[1], rows[1]); + } + + private VMStatPrintDelegate mockDelegate(String[] headers, String[][] data) { + VMStatPrintDelegate delegate = mock(VMStatPrintDelegate.class); + List stats = new ArrayList<>(); + for (int i = 0; i < NUM_ROWS; i++) { + TimeStampedPojo stat = mock(TimeStampedPojo.class); + when(stat.getTimeStamp()).thenReturn(i * 1000L); // Increment by one second + stats.add(stat); + } + + // Need this syntax due to generics + doReturn(stats).when(delegate).getLatestStats(any(VmRef.class), eq(Long.MIN_VALUE)); + when(delegate.getHeaders(stats.get(0))).thenReturn(Arrays.asList(headers)); + for (int i = 0; i < data.length; i++) { + List row = Arrays.asList(data[i]); + doReturn(row).when(delegate).getStatRow(eq(stats.get(i))); + } + + return delegate; } @After public void tearDown() { - vmCpuStatDAO = null; cmdCtxFactory = null; - cmd = null; timerFactory = null; } @@ -129,116 +156,62 @@ cmdCtxFactory = new TestCommandContextFactory(); } - private void setupDAOs() { - vmCpuStatDAO = mock(VmCpuStatDAO.class); - int vmId = 234; - HostRef host = new HostRef("123", "dummy"); - VmRef vm = new VmRef(host, 234, "dummy"); - VmCpuStat cpustat1 = new VmCpuStat(2, vmId, 65); - VmCpuStat cpustat2 = new VmCpuStat(3, vmId, 70); - List cpuStats = Arrays.asList(cpustat1, cpustat2); - List cpuStats2 = Collections.emptyList(); - when(vmCpuStatDAO.getLatestVmCpuStats(vm, Long.MIN_VALUE)).thenReturn(cpuStats).thenReturn(cpuStats2); - - VmMemoryStat.Space space1_1_1 = newSpace("space1", 123456, 12345, 1, 0); - VmMemoryStat.Space space1_1_2 = newSpace("space2", 123456, 12345, 1, 0); - VmMemoryStat.Space[] spaces1_1 = new VmMemoryStat.Space[] { space1_1_1, space1_1_2 }; - VmMemoryStat.Generation gen1_1 = newGeneration("gen1", "col1", 123456, 12345, spaces1_1); - - VmMemoryStat.Space space1_2_1 = newSpace("space3", 123456, 12345, 1, 0); - VmMemoryStat.Space space1_2_2 = newSpace("space4", 123456, 12345, 1, 0); - VmMemoryStat.Space[] spaces1_2 = new VmMemoryStat.Space[] { space1_2_1, space1_2_2 }; - VmMemoryStat.Generation gen1_2 = newGeneration("gen2", "col1", 123456, 12345, spaces1_2); - - VmMemoryStat.Generation[] gens1 = new VmMemoryStat.Generation[] { gen1_1, gen1_2 }; - - VmMemoryStat memStat1 = new VmMemoryStat(1, vmId, gens1); - - VmMemoryStat.Space space2_1_1 = newSpace("space1", 123456, 12345, 2, 0); - VmMemoryStat.Space space2_1_2 = newSpace("space2", 123456, 12345, 2, 0); - VmMemoryStat.Space[] spaces2_1 = new VmMemoryStat.Space[] { space2_1_1, space2_1_2 }; - VmMemoryStat.Generation gen2_1 = newGeneration("gen1", "col1", 123456, 12345, spaces2_1); - - VmMemoryStat.Space space2_2_1 = newSpace("space3", 123456, 12345, 3, 0); - VmMemoryStat.Space space2_2_2 = newSpace("space4", 123456, 12345, 4, 0); - VmMemoryStat.Space[] spaces2_2 = new VmMemoryStat.Space[] { space2_2_1, space2_2_2 }; - VmMemoryStat.Generation gen2_2 = newGeneration("gen2", "col1", 123456, 12345, spaces2_2); - - VmMemoryStat.Generation[] gens2 = new VmMemoryStat.Generation[] { gen2_1, gen2_2 }; - - VmMemoryStat memStat2 = new VmMemoryStat(2, vmId, gens2); - - VmMemoryStat.Space space3_1_1 = newSpace("space1", 123456, 12345, 4, 0); - VmMemoryStat.Space space3_1_2 = newSpace("space2", 123456, 12345, 5, 0); - VmMemoryStat.Space[] spaces3_1 = new VmMemoryStat.Space[] { space3_1_1, space3_1_2 }; - VmMemoryStat.Generation gen3_1 = newGeneration("gen1", "col1", 123456, 12345, spaces3_1); - - VmMemoryStat.Space space3_2_1 = newSpace("space3", 123456, 12345, 6, 0); - VmMemoryStat.Space space3_2_2 = newSpace("space4", 123456, 12345, 7, 0); - VmMemoryStat.Space[] spaces3_2 = new VmMemoryStat.Space[] { space3_2_1, space3_2_2 }; - VmMemoryStat.Generation gen3_2 = newGeneration("gen2", "col1", 123456, 12345, spaces3_2); - - VmMemoryStat.Generation[] gens3 = new VmMemoryStat.Generation[] { gen3_1, gen3_2 }; - - VmMemoryStat memStat3 = new VmMemoryStat(3, vmId, gens3); - - VmMemoryStat.Space space4_1_1 = newSpace("space1", 123456, 12345, 8, 0); - VmMemoryStat.Space space4_1_2 = newSpace("space2", 123456, 12345, 9, 0); - VmMemoryStat.Space[] spaces4_1 = new VmMemoryStat.Space[] { space4_1_1, space4_1_2 }; - VmMemoryStat.Generation gen4_1 = newGeneration("gen4", "col1", 123456, 12345, spaces4_1); - - VmMemoryStat.Space space4_2_1 = newSpace("space3", 123456, 12345, 10, 0); - VmMemoryStat.Space space4_2_2 = newSpace("space4", 123456, 12345, 11, 0); - VmMemoryStat.Space[] spaces4_2 = new VmMemoryStat.Space[] { space4_2_1, space4_2_2 }; - VmMemoryStat.Generation gen4_2 = newGeneration("gen4", "col1", 123456, 12345, spaces4_2); - - VmMemoryStat.Generation[] gens4 = new VmMemoryStat.Generation[] { gen4_1, gen4_2 }; - - VmMemoryStat memStat4 = new VmMemoryStat(4, vmId, gens4); - - vmMemoryStatDAO = mock(VmMemoryStatDAO.class); - when(vmMemoryStatDAO.getLatestVmMemoryStats(vm, Long.MIN_VALUE)) - .thenReturn(Arrays.asList(memStat1, memStat2, memStat3)); - - when(vmMemoryStatDAO.getLatestVmMemoryStats(vm, memStat3.getTimeStamp())).thenReturn(Arrays.asList(memStat4)); - + @Test + public void testOutput() throws CommandException { + StubBundleContext context = new StubBundleContext(); + context.registerService(VMStatPrintDelegate.class.getName(), delegates[0], null); + context.registerService(VMStatPrintDelegate.class.getName(), delegates[1], null); + + VMStatCommand cmd = new VMStatCommand(context); + + SimpleArguments args = new SimpleArguments(); + args.addArgument("vmId", "234"); + args.addArgument("hostId", "123"); + cmd.run(cmdCtxFactory.createContext(args)); + String expected = "TIME FIRST SECOND THIRD FOURTH FIFTH\n" + + "12:00:00 AM 1 2 3 4 5\n" + + "12:00:01 AM 6 7 8 9 10\n" + + "12:00:02 AM 11 12 13 14 15\n"; + assertEquals(expected, cmdCtxFactory.getOutput()); } - - private Space newSpace(String name, long maxCapacity, long capacity, long used, int index) { - VmMemoryStat.Space space = new VmMemoryStat.Space(); - space.setName(name); - space.setMaxCapacity(maxCapacity); - space.setCapacity(capacity); - space.setUsed(used); - space.setIndex(index); - return space; - } - - private Generation newGeneration(String name, String collector, long maxCapacity, long capacity, Space[] spaces) { - VmMemoryStat.Generation gen = new VmMemoryStat.Generation(); - gen.setName(name); - gen.setCollector(collector); - gen.setMaxCapacity(capacity); - gen.setSpaces(spaces); - return gen; - } - + @Test - public void testBasicCPUMemory() throws CommandException { + public void testNoDelegates() throws CommandException { + StubBundleContext context = new StubBundleContext(); + VMStatCommand cmd = new VMStatCommand(context); + SimpleArguments args = new SimpleArguments(); args.addArgument("vmId", "234"); args.addArgument("hostId", "123"); cmd.run(cmdCtxFactory.createContext(args)); - String expected = "TIME %CPU MEM.space1 MEM.space2 MEM.space3 MEM.space4\n" + - "12:00:00 AM 1 B 1 B 1 B 1 B\n" + - "12:00:00 AM 65.0 2 B 2 B 3 B 4 B\n" + - "12:00:00 AM 70.0 4 B 5 B 6 B 7 B\n"; + String expected = "TIME\n"; assertEquals(expected, cmdCtxFactory.getOutput()); - } @Test public void testContinuousMode() throws CommandException { + final String[][] data = { + { "16", "17", "18" }, + { "19", "20" } + }; + StubBundleContext context = new StubBundleContext(); + context.registerService(ApplicationService.class.getName(), appSvc, null); + context.registerService(VMStatPrintDelegate.class.getName(), delegates[0], null); + context.registerService(VMStatPrintDelegate.class.getName(), delegates[1], null); + + // Add one more stat + TimeStampedPojo stat = mock(TimeStampedPojo.class); + // One second after previous timestamps + when(stat.getTimeStamp()).thenReturn(3000L); + List stats = new ArrayList<>(); + stats.add(stat); + + doReturn(stats).when(delegates[0]).getLatestStats(any(VmRef.class), eq(2000L)); + doReturn(stats).when(delegates[1]).getLatestStats(any(VmRef.class), eq(2000L)); + doReturn(Arrays.asList(data[0])).when(delegates[0]).getStatRow(eq(stat)); + doReturn(Arrays.asList(data[1])).when(delegates[1]).getStatRow(eq(stat)); + + final VMStatCommand cmd = new VMStatCommand(context); Thread t = new Thread() { public void run() { @@ -261,10 +234,10 @@ return; } assertTrue(timerFactory.isActive()); - String expected = "TIME %CPU MEM.space1 MEM.space2 MEM.space3 MEM.space4\n" + - "12:00:00 AM 1 B 1 B 1 B 1 B\n" + - "12:00:00 AM 65.0 2 B 2 B 3 B 4 B\n" + - "12:00:00 AM 70.0 4 B 5 B 6 B 7 B\n"; + String expected = "TIME FIRST SECOND THIRD FOURTH FIFTH\n" + + "12:00:00 AM 1 2 3 4 5\n" + + "12:00:01 AM 6 7 8 9 10\n" + + "12:00:02 AM 11 12 13 14 15\n"; assertEquals(expected, cmdCtxFactory.getOutput()); assertEquals(1, timerFactory.getDelay()); assertEquals(1, timerFactory.getInitialDelay()); @@ -273,11 +246,11 @@ timerFactory.getAction().run(); - expected = "TIME %CPU MEM.space1 MEM.space2 MEM.space3 MEM.space4\n" + - "12:00:00 AM 1 B 1 B 1 B 1 B\n" + - "12:00:00 AM 65.0 2 B 2 B 3 B 4 B\n" + - "12:00:00 AM 70.0 4 B 5 B 6 B 7 B\n" + - "12:00:00 AM 70.0 8 B 9 B 10 B 11 B\n"; + expected = "TIME FIRST SECOND THIRD FOURTH FIFTH\n" + + "12:00:00 AM 1 2 3 4 5\n" + + "12:00:01 AM 6 7 8 9 10\n" + + "12:00:02 AM 11 12 13 14 15\n" + + "12:00:03 AM 16 17 18 19 20\n"; assertEquals(expected, cmdCtxFactory.getOutput()); cmdCtxFactory.setInput(" "); try { @@ -290,18 +263,24 @@ @Test public void testName() { + StubBundleContext context = new StubBundleContext(); + VMStatCommand cmd = new VMStatCommand(context); assertEquals("vm-stat", cmd.getName()); } @Test public void testDescAndUsage() { - assertNotNull(cmd.getUsage()); + StubBundleContext context = new StubBundleContext(); + VMStatCommand cmd = new VMStatCommand(context); + assertNotNull(cmd.getDescription()); assertNotNull(cmd.getUsage()); } @Ignore @Test public void testOptions() { + StubBundleContext context = new StubBundleContext(); + VMStatCommand cmd = new VMStatCommand(context); Options options = cmd.getOptions(); assertNotNull(options); assertEquals(3, options.getOptions().size()); @@ -325,9 +304,91 @@ assertFalse(cont.isRequired()); assertFalse(cont.hasArg()); } + + @Test + public void testNoStats() throws CommandException { + // Fail stats != null check + VMStatPrintDelegate badDelegate = mock(VMStatPrintDelegate.class); + when(badDelegate.getLatestStats(any(VmRef.class), anyLong())).thenReturn(null); + + StubBundleContext context = new StubBundleContext(); + context.registerService(VMStatPrintDelegate.class, delegates[0], null); + context.registerService(VMStatPrintDelegate.class, badDelegate, null); + context.registerService(VMStatPrintDelegate.class, delegates[1], null); + + VMStatCommand cmd = new VMStatCommand(context); + + SimpleArguments args = new SimpleArguments(); + args.addArgument("vmId", "234"); + args.addArgument("hostId", "123"); + cmd.run(cmdCtxFactory.createContext(args)); + String expected = "TIME FIRST SECOND THIRD FOURTH FIFTH\n" + + "12:00:00 AM 1 2 3 4 5\n" + + "12:00:01 AM 6 7 8 9 10\n" + + "12:00:02 AM 11 12 13 14 15\n"; + assertEquals(expected, cmdCtxFactory.getOutput()); + } + + @Test + public void testNoHeaders() throws CommandException { + // Pass stats check, but fail headers check + VMStatPrintDelegate badDelegate = mock(VMStatPrintDelegate.class); + TimeStampedPojo stat = mock(TimeStampedPojo.class); + doReturn(Arrays.asList(stat)).when(badDelegate).getLatestStats(any(VmRef.class), anyLong()); + when(badDelegate.getHeaders(any(TimeStampedPojo.class))).thenReturn(null); + + StubBundleContext context = new StubBundleContext(); + context.registerService(VMStatPrintDelegate.class, delegates[0], null); + context.registerService(VMStatPrintDelegate.class, badDelegate, null); + context.registerService(VMStatPrintDelegate.class, delegates[1], null); + + VMStatCommand cmd = new VMStatCommand(context); + + SimpleArguments args = new SimpleArguments(); + args.addArgument("vmId", "234"); + args.addArgument("hostId", "123"); + cmd.run(cmdCtxFactory.createContext(args)); + String expected = "TIME FIRST SECOND THIRD FOURTH FIFTH\n" + + "12:00:00 AM 1 2 3 4 5\n" + + "12:00:01 AM 6 7 8 9 10\n" + + "12:00:02 AM 11 12 13 14 15\n"; + assertEquals(expected, cmdCtxFactory.getOutput()); + } + + @Test + public void testUnevenStat() throws CommandException { + // Fewer stats than other delegates + VMStatPrintDelegate badDelegate = mock(VMStatPrintDelegate.class); + TimeStampedPojo stat1 = mock(TimeStampedPojo.class); + when(stat1.getTimeStamp()).thenReturn(1000L); + TimeStampedPojo stat2 = mock(TimeStampedPojo.class); + when(stat2.getTimeStamp()).thenReturn(2000L); + doReturn(Arrays.asList(stat1, stat2)).when(badDelegate).getLatestStats(any(VmRef.class), anyLong()); + when(badDelegate.getHeaders(any(TimeStampedPojo.class))).thenReturn(Arrays.asList("BAD")); + when(badDelegate.getStatRow(any(TimeStampedPojo.class))).thenReturn(Arrays.asList("0")); + + StubBundleContext context = new StubBundleContext(); + context.registerService(VMStatPrintDelegate.class, delegates[0], null); + context.registerService(VMStatPrintDelegate.class, badDelegate, null); + context.registerService(VMStatPrintDelegate.class, delegates[1], null); + + VMStatCommand cmd = new VMStatCommand(context); + + SimpleArguments args = new SimpleArguments(); + args.addArgument("vmId", "234"); + args.addArgument("hostId", "123"); + cmd.run(cmdCtxFactory.createContext(args)); + String expected = "TIME FIRST SECOND THIRD BAD FOURTH FIFTH\n" + + "12:00:00 AM 1 2 3 4 5\n" + + "12:00:01 AM 6 7 8 0 9 10\n" + + "12:00:02 AM 11 12 13 0 14 15\n"; + assertEquals(expected, cmdCtxFactory.getOutput()); + } @Test public void testStorageRequired() { + StubBundleContext context = new StubBundleContext(); + VMStatCommand cmd = new VMStatCommand(context); assertTrue(cmd.isStorageRequired()); } } diff -r 6815308bb362 -r bf46bf74d05c client/command/src/main/java/com/redhat/thermostat/client/command/RequestQueue.java --- a/client/command/src/main/java/com/redhat/thermostat/client/command/RequestQueue.java Tue Jan 15 23:00:52 2013 +0100 +++ b/client/command/src/main/java/com/redhat/thermostat/client/command/RequestQueue.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,6 +36,7 @@ package com.redhat.thermostat.client.command; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.common.command.Request; /** @@ -45,6 +46,7 @@ * and response (if any) are processed asynchronously. An instance of this can * be obtained from OSGi. */ +@Service public interface RequestQueue { public void putRequest(Request request); diff -r 6815308bb362 -r bf46bf74d05c client/core/src/main/java/com/redhat/thermostat/client/core/InformationService.java --- a/client/core/src/main/java/com/redhat/thermostat/client/core/InformationService.java Tue Jan 15 23:00:52 2013 +0100 +++ b/client/core/src/main/java/com/redhat/thermostat/client/core/InformationService.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,6 +36,7 @@ package com.redhat.thermostat.client.core; +import com.redhat.thermostat.annotations.ExtensionPoint; import com.redhat.thermostat.client.core.controllers.InformationServiceController; import com.redhat.thermostat.common.Ordered; import com.redhat.thermostat.common.dao.Ref; @@ -46,7 +47,13 @@ *

* An {@code InformationService} provides some sort of information about * something. Plug-ins should normally implement this as a entry point. + *

+ * To provide an implementation of {@link InformationService}, register an + * instance of this interface as an OSGi service with the property + * {@link Constants#GENERIC_SERVICE_CLASSNAME} set to the name of the Class + * that this {@link InformationService} provides information for. */ +@ExtensionPoint public interface InformationService extends Ordered { /** diff -r 6815308bb362 -r bf46bf74d05c client/core/src/main/java/com/redhat/thermostat/client/core/views/AgentInformationViewProvider.java --- a/client/core/src/main/java/com/redhat/thermostat/client/core/views/AgentInformationViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/client/core/src/main/java/com/redhat/thermostat/client/core/views/AgentInformationViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,9 +36,12 @@ package com.redhat.thermostat.client.core.views; +import com.redhat.thermostat.annotations.ExtensionPoint; + /** - * Provides an AgentInformationViewProvider + * Provides an {@link AgentInformationDisplayView} */ +@ExtensionPoint public interface AgentInformationViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c client/core/src/main/java/com/redhat/thermostat/client/core/views/ClientConfigViewProvider.java --- a/client/core/src/main/java/com/redhat/thermostat/client/core/views/ClientConfigViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/client/core/src/main/java/com/redhat/thermostat/client/core/views/ClientConfigViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,9 +36,12 @@ package com.redhat.thermostat.client.core.views; +import com.redhat.thermostat.annotations.ExtensionPoint; + /** * A services that provides {@link ClientConfigurationView}s. */ +@ExtensionPoint public interface ClientConfigViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c client/core/src/main/java/com/redhat/thermostat/client/core/views/HostInformationViewProvider.java --- a/client/core/src/main/java/com/redhat/thermostat/client/core/views/HostInformationViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/client/core/src/main/java/com/redhat/thermostat/client/core/views/HostInformationViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,9 +36,12 @@ package com.redhat.thermostat.client.core.views; +import com.redhat.thermostat.annotations.ExtensionPoint; + /** * This services provides an appropriate {@link HostInformationView}. */ +@ExtensionPoint public interface HostInformationViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c client/core/src/main/java/com/redhat/thermostat/client/core/views/VmInformationViewProvider.java --- a/client/core/src/main/java/com/redhat/thermostat/client/core/views/VmInformationViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/client/core/src/main/java/com/redhat/thermostat/client/core/views/VmInformationViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,9 +36,12 @@ package com.redhat.thermostat.client.core.views; +import com.redhat.thermostat.annotations.ExtensionPoint; + /** * A service that provides a {@link VmInformationView} */ +@ExtensionPoint public interface VmInformationViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c client/core/src/main/java/com/redhat/thermostat/client/osgi/service/ContextAction.java --- a/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/ContextAction.java Tue Jan 15 23:00:52 2013 +0100 +++ b/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/ContextAction.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,6 +36,8 @@ package com.redhat.thermostat.client.osgi.service; +import com.redhat.thermostat.annotations.ExtensionPoint; + /** * Marker service for context menu actions. *

@@ -56,6 +58,7 @@ * * Exported entry point: com.redhat.thermostat.client.osgi.service.ContextAction */ +@ExtensionPoint public interface ContextAction { String getName(); diff -r 6815308bb362 -r bf46bf74d05c client/core/src/main/java/com/redhat/thermostat/client/osgi/service/MenuAction.java --- a/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/MenuAction.java Tue Jan 15 23:00:52 2013 +0100 +++ b/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/MenuAction.java Tue Jan 15 23:04:57 2013 +0100 @@ -35,6 +35,8 @@ */ package com.redhat.thermostat.client.osgi.service; +import com.redhat.thermostat.annotations.ExtensionPoint; + /** * Allows plugins to register menu items. *

@@ -42,6 +44,7 @@ * register a service that implements this class with the property * "parentMenu" set to "File". */ +@ExtensionPoint public interface MenuAction extends ContextAction { public static enum Type { diff -r 6815308bb362 -r bf46bf74d05c client/core/src/main/java/com/redhat/thermostat/client/osgi/service/VMContextAction.java --- a/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/VMContextAction.java Tue Jan 15 23:00:52 2013 +0100 +++ b/client/core/src/main/java/com/redhat/thermostat/client/osgi/service/VMContextAction.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,12 +36,14 @@ package com.redhat.thermostat.client.osgi.service; +import com.redhat.thermostat.annotations.ExtensionPoint; import com.redhat.thermostat.client.core.Filter; import com.redhat.thermostat.common.dao.VmRef; /** * A context action for VMs */ +@ExtensionPoint public interface VMContextAction extends ContextAction { void execute(VmRef referece); diff -r 6815308bb362 -r bf46bf74d05c common/core/pom.xml --- a/common/core/pom.xml Tue Jan 15 23:00:52 2013 +0100 +++ b/common/core/pom.xml Tue Jan 15 23:04:57 2013 +0100 @@ -160,6 +160,12 @@ ${java.home}/../lib/tools.jar + com.redhat.thermostat + thermostat-annotations + ${project.version} + compile + + com.redhat.thermostat thermostat-keyring ${project.version} diff -r 6815308bb362 -r bf46bf74d05c common/core/src/main/java/com/redhat/thermostat/common/ApplicationService.java --- a/common/core/src/main/java/com/redhat/thermostat/common/ApplicationService.java Tue Jan 15 23:00:52 2013 +0100 +++ b/common/core/src/main/java/com/redhat/thermostat/common/ApplicationService.java Tue Jan 15 23:04:57 2013 +0100 @@ -38,6 +38,9 @@ import java.util.concurrent.ExecutorService; +import com.redhat.thermostat.annotations.Service; + +@Service public interface ApplicationService { ApplicationCache getApplicationCache(); diff -r 6815308bb362 -r bf46bf74d05c common/core/src/main/java/com/redhat/thermostat/common/CommandLoadingBundleActivator.java --- a/common/core/src/main/java/com/redhat/thermostat/common/CommandLoadingBundleActivator.java Tue Jan 15 23:00:52 2013 +0100 +++ b/common/core/src/main/java/com/redhat/thermostat/common/CommandLoadingBundleActivator.java Tue Jan 15 23:04:57 2013 +0100 @@ -45,7 +45,7 @@ import com.redhat.thermostat.common.cli.CommandRegistry; import com.redhat.thermostat.common.cli.CommandRegistryImpl; -/* +/** * Superclass for activators that need to register commands. The bundle for this * activator should contain a META-INF/services/com.redhat.thermostat.common.cli.Command * file containing the class names that should be loaded as commands. If this activator diff -r 6815308bb362 -r bf46bf74d05c common/core/src/main/java/com/redhat/thermostat/common/DbService.java --- a/common/core/src/main/java/com/redhat/thermostat/common/DbService.java Tue Jan 15 23:00:52 2013 +0100 +++ b/common/core/src/main/java/com/redhat/thermostat/common/DbService.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,8 +36,10 @@ package com.redhat.thermostat.common; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.storage.core.ConnectionException; +@Service public interface DbService { /** diff -r 6815308bb362 -r bf46bf74d05c common/core/src/main/java/com/redhat/thermostat/common/TimerFactory.java --- a/common/core/src/main/java/com/redhat/thermostat/common/TimerFactory.java Tue Jan 15 23:00:52 2013 +0100 +++ b/common/core/src/main/java/com/redhat/thermostat/common/TimerFactory.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,6 +36,9 @@ package com.redhat.thermostat.common; +/** + * An instance of this can be obtained from {@link ApplicationService}. + */ public interface TimerFactory { Timer createTimer(); diff -r 6815308bb362 -r bf46bf74d05c common/core/src/main/java/com/redhat/thermostat/common/cli/Command.java --- a/common/core/src/main/java/com/redhat/thermostat/common/cli/Command.java Tue Jan 15 23:00:52 2013 +0100 +++ b/common/core/src/main/java/com/redhat/thermostat/common/cli/Command.java Tue Jan 15 23:04:57 2013 +0100 @@ -38,6 +38,8 @@ import org.apache.commons.cli.Options; +import com.redhat.thermostat.annotations.ExtensionPoint; + /** * Represents a command on the command line. *

@@ -51,6 +53,7 @@ *

* @see CommandRegistry */ +@ExtensionPoint public interface Command { public static final String NAME = "COMMAND_NAME"; diff -r 6815308bb362 -r bf46bf74d05c common/core/src/main/java/com/redhat/thermostat/common/dao/AgentInfoDAO.java --- a/common/core/src/main/java/com/redhat/thermostat/common/dao/AgentInfoDAO.java Tue Jan 15 23:00:52 2013 +0100 +++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/AgentInfoDAO.java Tue Jan 15 23:04:57 2013 +0100 @@ -38,10 +38,12 @@ import java.util.List; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Key; import com.redhat.thermostat.storage.model.AgentInformation; +@Service public interface AgentInfoDAO extends Countable { static final Key START_TIME_KEY = new Key<>("startTime", false); diff -r 6815308bb362 -r bf46bf74d05c common/core/src/main/java/com/redhat/thermostat/common/dao/BackendInfoDAO.java --- a/common/core/src/main/java/com/redhat/thermostat/common/dao/BackendInfoDAO.java Tue Jan 15 23:00:52 2013 +0100 +++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/BackendInfoDAO.java Tue Jan 15 23:04:57 2013 +0100 @@ -38,10 +38,12 @@ import java.util.List; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Key; import com.redhat.thermostat.storage.model.BackendInformation; +@Service public interface BackendInfoDAO { static final Key BACKEND_NAME = new Key<>("name", true); diff -r 6815308bb362 -r bf46bf74d05c common/core/src/main/java/com/redhat/thermostat/common/dao/HostInfoDAO.java --- a/common/core/src/main/java/com/redhat/thermostat/common/dao/HostInfoDAO.java Tue Jan 15 23:00:52 2013 +0100 +++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/HostInfoDAO.java Tue Jan 15 23:04:57 2013 +0100 @@ -38,10 +38,12 @@ import java.util.Collection; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Key; import com.redhat.thermostat.storage.model.HostInfo; +@Service public interface HostInfoDAO extends Countable { static Key hostNameKey = new Key<>("hostname", true); diff -r 6815308bb362 -r bf46bf74d05c common/core/src/main/java/com/redhat/thermostat/common/dao/NetworkInterfaceInfoDAO.java --- a/common/core/src/main/java/com/redhat/thermostat/common/dao/NetworkInterfaceInfoDAO.java Tue Jan 15 23:00:52 2013 +0100 +++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/NetworkInterfaceInfoDAO.java Tue Jan 15 23:04:57 2013 +0100 @@ -38,10 +38,12 @@ import java.util.List; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Key; import com.redhat.thermostat.storage.model.NetworkInterfaceInfo; +@Service public interface NetworkInterfaceInfoDAO { static Key ifaceKey = new Key<>("interfaceName", true); diff -r 6815308bb362 -r bf46bf74d05c common/core/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAO.java --- a/common/core/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAO.java Tue Jan 15 23:00:52 2013 +0100 +++ b/common/core/src/main/java/com/redhat/thermostat/common/dao/VmInfoDAO.java Tue Jan 15 23:04:57 2013 +0100 @@ -40,10 +40,12 @@ import java.util.List; import java.util.Map; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Key; import com.redhat.thermostat.storage.model.VmInfo; +@Service public interface VmInfoDAO extends Countable { static final Key vmPidKey = new Key<>("vmPid", false); diff -r 6815308bb362 -r bf46bf74d05c common/core/src/main/java/com/redhat/thermostat/test/StubBundleContext.java --- a/common/core/src/main/java/com/redhat/thermostat/test/StubBundleContext.java Tue Jan 15 23:00:52 2013 +0100 +++ b/common/core/src/main/java/com/redhat/thermostat/test/StubBundleContext.java Tue Jan 15 23:04:57 2013 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2012 Red Hat, Inc. + * Copyright 2013 Red Hat, Inc. * * This file is part of Thermostat. * @@ -50,14 +50,21 @@ import org.osgi.framework.BundleListener; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkListener; +import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceEvent; import org.osgi.framework.ServiceListener; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import com.redhat.thermostat.common.NotImplementedException; -import com.redhat.thermostat.test.StubBundleContext.ServiceInformation; +/** + * An implementation of BundleContext that's useful for writing unit tests. + *

+ * WARNING: if you static mock {@link FrameworkUtil#createFilter(String)}, you + * are going to have a bad time. + */ public class StubBundleContext implements BundleContext { static class ServiceInformation { @@ -180,17 +187,28 @@ public ServiceRegistration registerService(String className, Object service, Dictionary properties) { ServiceInformation info = new ServiceInformation(className, service, properties); registeredServices.add(info); + + notifyServiceChange(new StubServiceReference(info), true); + return new StubServiceRegistration(this, info); } @Override public ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException { - Filter toMatch = createFilter(filter); List toReturn = new ArrayList<>(); - for (ServiceInformation info : registeredServices) { - // how does filter matching in OSGI work again? - if (info.serviceInterface.equals(clazz) && toMatch.match(info.properties)) { - toReturn.add(new StubServiceReference(info)); + + if (filter == null) { + for (ServiceInformation info : registeredServices) { + if (info.serviceInterface.equals(clazz)) { + toReturn.add(new StubServiceReference(info)); + } + } + } else { + Filter toMatch = createFilter(filter); + for (ServiceInformation info : registeredServices) { + if (info.serviceInterface.equals(clazz) && toMatch.match(info.properties)) { + toReturn.add(new StubServiceReference(info)); + } } } return toReturn.toArray(new ServiceReference[0]); @@ -203,7 +221,13 @@ @Override public ServiceReference getServiceReference(String clazz) { - throw new NotImplementedException(); + ServiceReference result = null; + for (ServiceInformation info : registeredServices) { + if (info.serviceInterface.equals(clazz)) { + result = new StubServiceReference(info); + } + } + return result; } @Override @@ -247,7 +271,11 @@ @Override public Filter createFilter(String filter) throws InvalidSyntaxException { - return new StubFilter(filter); + // FIXME this will break service trackers if FrameworkUtil is mocked + // the following call will return null if FrameworkUtil is mocked. + // that's a problem because this is meant to be used (mostly) in test + // environments and that's where FrameworkUtil is likely to be mocked. + return FrameworkUtil.createFilter(filter); } @Override @@ -285,7 +313,17 @@ throw new IllegalStateException("service not registered"); } registeredServices.remove(info); + notifyServiceChange(new StubServiceReference(info), false); + } + private void notifyServiceChange(ServiceReference serviceReference, boolean registered) { + int eventType = registered ? ServiceEvent.REGISTERED : ServiceEvent.UNREGISTERING; + ServiceEvent event = new ServiceEvent(eventType, serviceReference); + for (ListenerSpec l : registeredListeners) { + if (l.filter.match(serviceReference)) { + l.listener.serviceChanged(event); + } + } } public int getExportedServiceCount(ServiceRegistration registration) { diff -r 6815308bb362 -r bf46bf74d05c common/core/src/main/java/com/redhat/thermostat/test/StubFilter.java --- a/common/core/src/main/java/com/redhat/thermostat/test/StubFilter.java Tue Jan 15 23:00:52 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +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 - * . - * - * 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.test; - -import java.util.Dictionary; -import java.util.Map; - -import org.osgi.framework.Filter; -import org.osgi.framework.ServiceReference; - -import com.redhat.thermostat.common.NotImplementedException; - -public class StubFilter implements Filter { - - private final String filter; - - public StubFilter(String filter) { - this.filter = filter; - } - - @Override - public boolean match(ServiceReference reference) { - if (filter == null) { - return true; - } - throw new NotImplementedException(); - } - - @Override - public boolean match(Dictionary dictionary) { - if (filter == null) { - return true; - } - throw new NotImplementedException(); - } - - @Override - public boolean matchCase(Dictionary dictionary) { - if (filter == null) { - return true; - } - throw new NotImplementedException(); - } - - @Override - public boolean matches(Map map) { - if (filter == null) { - return true; - } - throw new NotImplementedException(); - } - -} diff -r 6815308bb362 -r bf46bf74d05c common/core/src/main/java/com/redhat/thermostat/test/StubServiceReference.java --- a/common/core/src/main/java/com/redhat/thermostat/test/StubServiceReference.java Tue Jan 15 23:00:52 2013 +0100 +++ b/common/core/src/main/java/com/redhat/thermostat/test/StubServiceReference.java Tue Jan 15 23:04:57 2013 +0100 @@ -1,5 +1,5 @@ /* - * Copyright 2012 Red Hat, Inc. + * Copyright 2013 Red Hat, Inc. * * This file is part of Thermostat. * @@ -36,6 +36,11 @@ package com.redhat.thermostat.test; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.List; + import org.osgi.framework.Bundle; import org.osgi.framework.Constants; import org.osgi.framework.ServiceReference; @@ -57,12 +62,22 @@ return new String[] { information.serviceInterface }; } - throw new NotImplementedException(); + return information.properties.get(key); } @Override public String[] getPropertyKeys() { - throw new NotImplementedException(); + if (information.properties == null) { + return new String[] { Constants.OBJECTCLASS }; + } else { + Dictionary props = information.properties; + List toReturn = new ArrayList<>(props.size()); + Enumeration keyEnumeration = props.keys(); + while (keyEnumeration.hasMoreElements()) { + toReturn.add((String) keyEnumeration.nextElement()); + } + return toReturn.toArray(new String[0]); + } } @Override diff -r 6815308bb362 -r bf46bf74d05c common/test/src/main/java/com/redhat/thermostat/test/locale/AbstractLocaleResourcesTest.java --- a/common/test/src/main/java/com/redhat/thermostat/test/locale/AbstractLocaleResourcesTest.java Tue Jan 15 23:00:52 2013 +0100 +++ b/common/test/src/main/java/com/redhat/thermostat/test/locale/AbstractLocaleResourcesTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -1,3 +1,39 @@ +/* + * 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 + * . + * + * 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.test.locale; import java.io.IOException; @@ -31,4 +67,4 @@ protected abstract String getResourceBundle(); -} \ No newline at end of file +} diff -r 6815308bb362 -r bf46bf74d05c distribution/config/commands/vm-stat.properties --- a/distribution/config/commands/vm-stat.properties Tue Jan 15 23:00:52 2013 +0100 +++ b/distribution/config/commands/vm-stat.properties Tue Jan 15 23:04:57 2013 +0100 @@ -1,6 +1,8 @@ bundles = thermostat-client-cli-${project.version}.jar, \ thermostat-vm-cpu-common-${project.version}.jar, \ + thermostat-vm-cpu-client-cli-${project.version}.jar, \ thermostat-vm-memory-common-${project.version}.jar, \ + thermostat-vm-memory-client-cli-${project.version}.jar, \ thermostat-storage-mongodb-${project.version}.jar, \ thermostat-web-common-${project.version}.jar, \ thermostat-web-client-${project.version}.jar, \ diff -r 6815308bb362 -r bf46bf74d05c distribution/pom.xml --- a/distribution/pom.xml Tue Jan 15 23:00:52 2013 +0100 +++ b/distribution/pom.xml Tue Jan 15 23:04:57 2013 +0100 @@ -322,11 +322,6 @@ com.redhat.thermostat - thermostat-bundles - ${project.version} - - - com.redhat.thermostat thermostat-launcher ${project.version} @@ -412,6 +407,11 @@ com.redhat.thermostat + thermostat-vm-cpu-client-cli + ${project.version} + + + com.redhat.thermostat thermostat-vm-cpu-agent ${project.version} @@ -447,6 +447,11 @@ com.redhat.thermostat + thermostat-vm-memory-client-cli + ${project.version} + + + com.redhat.thermostat thermostat-vm-memory-agent ${project.version} diff -r 6815308bb362 -r bf46bf74d05c host-cpu/client-core/src/main/java/com/redhat/thermostat/host/cpu/client/core/HostCpuViewProvider.java --- a/host-cpu/client-core/src/main/java/com/redhat/thermostat/host/cpu/client/core/HostCpuViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/host-cpu/client-core/src/main/java/com/redhat/thermostat/host/cpu/client/core/HostCpuViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,8 +36,10 @@ package com.redhat.thermostat.host.cpu.client.core; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.client.core.views.ViewProvider; +@Service public interface HostCpuViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c host-cpu/common/src/main/java/com/redhat/thermostat/host/cpu/common/CpuStatDAO.java --- a/host-cpu/common/src/main/java/com/redhat/thermostat/host/cpu/common/CpuStatDAO.java Tue Jan 15 23:00:52 2013 +0100 +++ b/host-cpu/common/src/main/java/com/redhat/thermostat/host/cpu/common/CpuStatDAO.java Tue Jan 15 23:04:57 2013 +0100 @@ -38,12 +38,14 @@ import java.util.List; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.common.dao.Countable; import com.redhat.thermostat.common.dao.HostRef; import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Key; import com.redhat.thermostat.storage.model.CpuStat; +@Service public interface CpuStatDAO extends Countable { static Key> cpuLoadKey = new Key<>("perProcessorUsage", false); diff -r 6815308bb362 -r bf46bf74d05c host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/HostMemoryViewProvider.java --- a/host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/HostMemoryViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/host-memory/client-core/src/main/java/com/redhat/thermostat/host/memory/client/core/HostMemoryViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,8 +36,10 @@ package com.redhat.thermostat.host.memory.client.core; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.client.core.views.ViewProvider; +@Service public interface HostMemoryViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c host-memory/common/src/main/java/com/redhat/thermostat/host/memory/common/MemoryStatDAO.java --- a/host-memory/common/src/main/java/com/redhat/thermostat/host/memory/common/MemoryStatDAO.java Tue Jan 15 23:00:52 2013 +0100 +++ b/host-memory/common/src/main/java/com/redhat/thermostat/host/memory/common/MemoryStatDAO.java Tue Jan 15 23:04:57 2013 +0100 @@ -38,12 +38,14 @@ import java.util.List; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.common.dao.Countable; import com.redhat.thermostat.common.dao.HostRef; import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Key; import com.redhat.thermostat.storage.model.MemoryStat; +@Service public interface MemoryStatDAO extends Countable { static Key memoryTotalKey = new Key<>("total", false); diff -r 6815308bb362 -r bf46bf74d05c host-overview/client-core/src/main/java/com/redhat/thermostat/host/overview/client/core/HostOverviewViewProvider.java --- a/host-overview/client-core/src/main/java/com/redhat/thermostat/host/overview/client/core/HostOverviewViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/host-overview/client-core/src/main/java/com/redhat/thermostat/host/overview/client/core/HostOverviewViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,8 +36,10 @@ package com.redhat.thermostat.host.overview.client.core; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.client.core.views.ViewProvider; +@Service public interface HostOverviewViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c keyring/pom.xml --- a/keyring/pom.xml Tue Jan 15 23:00:52 2013 +0100 +++ b/keyring/pom.xml Tue Jan 15 23:04:57 2013 +0100 @@ -115,6 +115,12 @@ org.osgi.compendium provided - + + + com.redhat.thermostat + thermostat-annotations + ${project.version} + compile + diff -r 6815308bb362 -r bf46bf74d05c keyring/src/main/java/com/redhat/thermostat/utils/keyring/Keyring.java --- a/keyring/src/main/java/com/redhat/thermostat/utils/keyring/Keyring.java Tue Jan 15 23:00:52 2013 +0100 +++ b/keyring/src/main/java/com/redhat/thermostat/utils/keyring/Keyring.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,9 +36,14 @@ package com.redhat.thermostat.utils.keyring; +import com.redhat.thermostat.annotations.Service; + /** * Manages sensitive data, like {@link Credentials}, securely. + *

+ * An instance of this class can be obtained from OSGi as a service. */ +@Service public interface Keyring { /** diff -r 6815308bb362 -r bf46bf74d05c launcher/pom.xml --- a/launcher/pom.xml Tue Jan 15 23:00:52 2013 +0100 +++ b/launcher/pom.xml Tue Jan 15 23:04:57 2013 +0100 @@ -110,11 +110,6 @@ com.redhat.thermostat - thermostat-bundles - ${project.version} - - - com.redhat.thermostat thermostat-common-test ${project.version} test diff -r 6815308bb362 -r bf46bf74d05c launcher/src/main/java/com/redhat/thermostat/launcher/BundleManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/BundleManager.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,71 @@ +/* + * 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 + * . + * + * 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.launcher; + +import java.io.IOException; +import java.util.List; + +import org.osgi.framework.BundleException; +import org.osgi.framework.launch.Framework; + +import com.redhat.thermostat.annotations.Service; +import com.redhat.thermostat.common.Configuration; +import com.redhat.thermostat.common.cli.CommandInfoNotFoundException; +import com.redhat.thermostat.common.cli.CommandInfoSource; +import com.redhat.thermostat.launcher.internal.BundleLoader; + +/** + * A Service that provides features to load bundles for given command names. + */ +@Service +public abstract class BundleManager { + + public abstract void setPrintOSGiInfo(boolean printOSGiInfo); + + public abstract void setCommandInfoSource(CommandInfoSource source); + + public abstract void addBundlesFor(String commandName) throws BundleException, CommandInfoNotFoundException, IOException; + + public static void preLoadBundles(Framework framework, List bundleLocations, + boolean printOSGiInfo) throws BundleException { + BundleLoader loader = new BundleLoader(printOSGiInfo); + loader.installAndStartBundles(framework, bundleLocations); + } + + public abstract Configuration getConfiguration(); + +} diff -r 6815308bb362 -r bf46bf74d05c launcher/src/main/java/com/redhat/thermostat/launcher/Launcher.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/Launcher.java Tue Jan 15 23:00:52 2013 +0100 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/Launcher.java Tue Jan 15 23:04:57 2013 +0100 @@ -38,12 +38,14 @@ import java.util.Collection; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.tools.ApplicationState; /** * Launcher is the main entry point for all Thermostat commands. */ +@Service public interface Launcher { /** diff -r 6815308bb362 -r bf46bf74d05c launcher/src/main/java/com/redhat/thermostat/launcher/internal/Activator.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/Activator.java Tue Jan 15 23:00:52 2013 +0100 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/Activator.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,70 +36,85 @@ package com.redhat.thermostat.launcher.internal; -import java.util.Map; - import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; -import com.redhat.thermostat.bundles.OSGiRegistry; import com.redhat.thermostat.common.CommandLoadingBundleActivator; -import com.redhat.thermostat.common.MultipleServiceTracker; -import com.redhat.thermostat.common.MultipleServiceTracker.Action; +import com.redhat.thermostat.common.Configuration; import com.redhat.thermostat.common.cli.CommandContextFactory; import com.redhat.thermostat.common.cli.CommandInfoSource; +import com.redhat.thermostat.launcher.BundleManager; import com.redhat.thermostat.launcher.Launcher; import com.redhat.thermostat.utils.keyring.Keyring; public class Activator extends CommandLoadingBundleActivator { + + @SuppressWarnings({"rawtypes", "unchecked"}) + class RegisterLauncherCustomizer implements ServiceTrackerCustomizer { - class RegisterLauncherAction implements Action { - + private ServiceRegistration launcherReg; + private ServiceRegistration bundleManReg; + private ServiceRegistration cmdInfoReg; private BundleContext context; - private ServiceReference registryReference; + private BundleManager bundleService; - RegisterLauncherAction(BundleContext context) { + RegisterLauncherCustomizer(BundleContext context, BundleManager bundleService) { this.context = context; + this.bundleService = bundleService; } @Override - public void dependenciesAvailable(Map services) { - - registryReference = context.getServiceReference(OSGiRegistry.class); - OSGiRegistry bundleService = (OSGiRegistry) context.getService(registryReference); + public Object addingService(ServiceReference reference) { + // keyring is now ready + Keyring keyring = (Keyring)context.getService(reference); + // Register Launcher service since FrameworkProvider is waiting for it blockingly. CommandInfoSourceImpl commands = new CommandInfoSourceImpl(bundleService.getConfiguration().getThermostatHome()); - context.registerService(CommandInfoSource.class, commands, null); + cmdInfoReg = context.registerService(CommandInfoSource.class, commands, null); bundleService.setCommandInfoSource(commands); LauncherImpl launcher = new LauncherImpl(context, new CommandContextFactory(context), bundleService); - launcherServiceRegistration = context.registerService(Launcher.class.getName(), launcher, null); + launcherReg = context.registerService(Launcher.class.getName(), launcher, null); + bundleManReg = context.registerService(BundleManager.class, bundleService, null); + return keyring; } @Override - public void dependenciesUnavailable() { - launcherServiceRegistration.unregister(); - context.ungetService(registryReference); + public void modifiedService(ServiceReference reference, Object service) { + // nothing + } + + @Override + public void removedService(ServiceReference reference, Object service) { + // Keyring is gone, remove launcher, et. al. as well + launcherReg.unregister(); + bundleManReg.unregister(); + cmdInfoReg.unregister(); } } @SuppressWarnings("rawtypes") - private ServiceRegistration launcherServiceRegistration; - private MultipleServiceTracker tracker; + private ServiceTracker serviceTracker; + @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void start(final BundleContext context) throws Exception { super.start(context); - - tracker = new MultipleServiceTracker(context, new Class[] {OSGiRegistry.class, Keyring.class}, new RegisterLauncherAction(context)); - tracker.open(); + BundleManager bundleService = new BundleManagerImpl(new Configuration()); + ServiceTrackerCustomizer customizer = new RegisterLauncherCustomizer(context, bundleService); + serviceTracker = new ServiceTracker(context, Keyring.class, customizer); + // Track for Keyring service. + serviceTracker.open(); } @Override public void stop(BundleContext context) throws Exception { super.stop(context); - if (tracker != null) { - tracker.close(); + if (serviceTracker != null) { + serviceTracker.close(); } } } diff -r 6815308bb362 -r bf46bf74d05c launcher/src/main/java/com/redhat/thermostat/launcher/internal/BundleLoader.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BundleLoader.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,99 @@ +/* + * 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 + * . + * + * 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.launcher.internal; + +import java.util.ArrayList; +import java.util.List; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.Constants; +import org.osgi.framework.launch.Framework; + +public class BundleLoader { + + private boolean printOSGiInfo = false; + + BundleLoader() { + this(false); + } + + public BundleLoader(boolean printOSGiInfo) { + setPrintOSGiInfo(printOSGiInfo); + } + + public void setPrintOSGiInfo(boolean printOSGiInfo) { + this.printOSGiInfo = printOSGiInfo; + } + + public List installAndStartBundles(Framework framework, + ListbundleLocations) throws BundleException { + List bundles = new ArrayList<>(); + BundleContext ctx = framework.getBundleContext(); + for (String location : bundleLocations) { + Bundle bundle = ctx.installBundle(location); + if (printOSGiInfo) { + System.out.println("BundleLoader: installed bundle: \"" + + location + "\" as id " + bundle.getBundleId()); + } + bundles.add(bundle); + } + startBundles(bundles); + return bundles; + } + + private void startBundles(List bundles) throws BundleException { + for (Bundle bundle : bundles) { + + if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null) { + if (printOSGiInfo) { + System.out.println("BundleLoader: bundle \"" + bundle.getBundleId() + "\" is a fragment; not starting it"); + } + continue; + } + + if (printOSGiInfo) { + System.out.println("BundleLoader: starting bundle: \"" + bundle.getBundleId() + "\""); + } + // We don't want for the framework to set the auto-start bit. Thus, passing + // START_TRANSIENT explicitly + bundle.start(Bundle.START_TRANSIENT); + } + } + +} diff -r 6815308bb362 -r bf46bf74d05c launcher/src/main/java/com/redhat/thermostat/launcher/internal/BundleManagerImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BundleManagerImpl.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,123 @@ +/* + * 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 + * . + * + * 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.launcher.internal; + +import com.redhat.thermostat.common.Configuration; +import com.redhat.thermostat.common.ConfigurationException; +import com.redhat.thermostat.common.cli.CommandInfoNotFoundException; +import com.redhat.thermostat.common.cli.CommandInfoSource; +import com.redhat.thermostat.launcher.BundleManager; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleException; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.launch.Framework; + +public class BundleManagerImpl extends BundleManager { + + private CommandInfoSource commandInfos; + private Map loaded; + private Configuration configuration; + private BundleLoader loader; + + BundleManagerImpl(Configuration configuration) throws ConfigurationException, FileNotFoundException, IOException { + initLoadedBundles(); + this.configuration = configuration; + loader = new BundleLoader(configuration.getPrintOSGiInfo()); + } + + private void initLoadedBundles() { + loaded = new HashMap<>(); + Framework framework = getFramework(this.getClass()); + for (Bundle bundle: framework.getBundleContext().getBundles()) { + loaded.put(bundle.getLocation(), bundle); + } + } + + @Override + public void setPrintOSGiInfo(boolean printOSGiInfo) { + configuration.setPrintOSGiInfo(printOSGiInfo); + loader.setPrintOSGiInfo(printOSGiInfo); + } + + @Override + public void setCommandInfoSource(CommandInfoSource source) { + this.commandInfos = source; + } + + @Override + public void addBundlesFor(String commandName) throws BundleException, IOException, CommandInfoNotFoundException { + if (configuration.getPrintOSGiInfo()) { + System.out.println("Loading additional bundles for: " + commandName); + } + List requiredBundles = commandInfos.getCommandInfo(commandName).getDependencyResourceNames(); + List bundlesToLoad = new ArrayList<>(); + if (requiredBundles != null) { + for (String resource : requiredBundles) { + if (!isBundleActive(resource)) { + bundlesToLoad.add(resource); + } + } + } + Framework framework = getFramework(this.getClass()); + List successBundles = loader.installAndStartBundles(framework, bundlesToLoad); + for (Bundle bundle : successBundles) { + loaded.put(bundle.getLocation(), bundle); + } + } + + private boolean isBundleActive(String location) { + Bundle bundle = loaded.get(location); + return (bundle != null) && (bundle.getState() == Bundle.ACTIVE); + } + + private Framework getFramework(Class cls) { + return (Framework) FrameworkUtil.getBundle(cls).getBundleContext().getBundle(0); + } + + @Override + public Configuration getConfiguration() { + return configuration; + } +} diff -r 6815308bb362 -r bf46bf74d05c launcher/src/main/java/com/redhat/thermostat/launcher/internal/LauncherImpl.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/LauncherImpl.java Tue Jan 15 23:00:52 2013 +0100 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/LauncherImpl.java Tue Jan 15 23:04:57 2013 +0100 @@ -50,7 +50,6 @@ import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; -import com.redhat.thermostat.bundles.OSGiRegistry; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ActionNotifier; import com.redhat.thermostat.common.ApplicationService; @@ -72,6 +71,7 @@ import com.redhat.thermostat.common.utils.OSGIUtils; import com.redhat.thermostat.launcher.CommonCommandOptions; import com.redhat.thermostat.launcher.Launcher; +import com.redhat.thermostat.launcher.BundleManager; import com.redhat.thermostat.storage.core.ConnectionException; import com.redhat.thermostat.storage.core.Storage; import com.redhat.thermostat.storage.core.StorageException; @@ -89,14 +89,14 @@ private final Semaphore argsBarrier = new Semaphore(0); private BundleContext context; - private OSGiRegistry registry; + private BundleManager registry; private final DbServiceFactory dbServiceFactory; - public LauncherImpl(BundleContext context, CommandContextFactory cmdCtxFactory, OSGiRegistry registry) { + public LauncherImpl(BundleContext context, CommandContextFactory cmdCtxFactory, BundleManager registry) { this(context, cmdCtxFactory, registry, new LoggingInitializer(), new DbServiceFactory()); } - LauncherImpl(BundleContext context, CommandContextFactory cmdCtxFactory, OSGiRegistry registry, + LauncherImpl(BundleContext context, CommandContextFactory cmdCtxFactory, BundleManager registry, LoggingInitializer loggingInitializer, DbServiceFactory dbServiceFactory) { this.context = context; this.cmdCtxFactory = cmdCtxFactory; diff -r 6815308bb362 -r bf46bf74d05c launcher/src/test/java/com/redhat/thermostat/launcher/BundleManagerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/BundleManagerTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,70 @@ +/* + * 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 + * . + * + * 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.launcher; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.whenNew; + +import java.util.ArrayList; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.osgi.framework.launch.Framework; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import com.redhat.thermostat.launcher.BundleManager; +import com.redhat.thermostat.launcher.internal.BundleLoader; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(BundleManager.class) +public class BundleManagerTest { + + @Test + public void testPreLoadBundles() throws Exception { + Framework framework = mock(Framework.class); + ArrayList bundleLocations = new ArrayList<>(); + BundleLoader loader = mock(BundleLoader.class); + whenNew(BundleLoader.class).withParameterTypes(Boolean.TYPE). + withArguments(any()).thenReturn(loader); + + BundleManager.preLoadBundles(framework, bundleLocations, true); + verify(loader).installAndStartBundles(framework, bundleLocations); + } +} diff -r 6815308bb362 -r bf46bf74d05c launcher/src/test/java/com/redhat/thermostat/launcher/internal/ActivatorTest.java --- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/ActivatorTest.java Tue Jan 15 23:00:52 2013 +0100 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/ActivatorTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,6 +36,9 @@ package com.redhat.thermostat.launcher.internal; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; @@ -46,6 +49,9 @@ import static org.powermock.api.mockito.PowerMockito.verifyNew; import static org.powermock.api.mockito.PowerMockito.whenNew; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Dictionary; import java.util.Hashtable; @@ -55,41 +61,55 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; +import org.osgi.framework.launch.Framework; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; +import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; -import com.redhat.thermostat.bundles.OSGiRegistry; import com.redhat.thermostat.common.Configuration; 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.cli.CommandInfo; +import com.redhat.thermostat.common.cli.CommandInfoSource; import com.redhat.thermostat.common.utils.ServiceRegistry; import com.redhat.thermostat.launcher.Launcher; +import com.redhat.thermostat.launcher.BundleManager; +import com.redhat.thermostat.launcher.internal.Activator.RegisterLauncherCustomizer; +import com.redhat.thermostat.test.StubBundleContext; import com.redhat.thermostat.utils.keyring.Keyring; @RunWith(PowerMockRunner.class) -@PrepareForTest({Activator.class}) +@PrepareForTest({Activator.class, Activator.RegisterLauncherCustomizer.class, FrameworkUtil.class}) public class ActivatorTest { private BundleContext context; private MultipleServiceTracker tracker; private ServiceReference registryServiceReference, helpCommandReference; private ServiceRegistration launcherServiceRegistration, helpCommandRegistration; - private OSGiRegistry registryService; + private BundleManager registryService; private Command helpCommand; @Before public void setUp() throws Exception { + Path tempDir = createStubThermostatHome(); + System.setProperty("THERMOSTAT_HOME", tempDir.toString()); + context = mock(BundleContext.class); + setupOsgiRegistryImplMock(); registryServiceReference = mock(ServiceReference.class); launcherServiceRegistration = mock(ServiceRegistration.class); - registryService = mock(OSGiRegistry.class); - when(context.getServiceReference(eq(OSGiRegistry.class))).thenReturn(registryServiceReference); + registryService = mock(BundleManager.class); + when(context.getServiceReference(eq(BundleManager.class))).thenReturn(registryServiceReference); when(context.getService(eq(registryServiceReference))).thenReturn(registryService); when(context.registerService(eq(Launcher.class.getName()), any(), (Dictionary) isNull())). thenReturn(launcherServiceRegistration); @@ -116,32 +136,95 @@ tracker = mock(MultipleServiceTracker.class); whenNew(MultipleServiceTracker.class). withParameterTypes(BundleContext.class, Class[].class, Action.class). - withArguments(eq(context), eq(new Class[] {OSGiRegistry.class, Keyring.class}), + withArguments(eq(context), eq(new Class[] {BundleManager.class, Keyring.class}), isA(Action.class)).thenReturn(tracker); } @Test public void testActivatorLifecycle() throws Exception { + ArgumentCaptor customizerCaptor = ArgumentCaptor.forClass(RegisterLauncherCustomizer.class); + ServiceTracker mockTracker = mock(ServiceTracker.class); + whenNew(ServiceTracker.class).withParameterTypes(BundleContext.class, Class.class, ServiceTrackerCustomizer.class).withArguments(eq(context), + any(Keyring.class), customizerCaptor.capture()).thenReturn(mockTracker); + Activator activator = new Activator(); - activator.start(context); Hashtable props = new Hashtable<>(); props.put(ServiceRegistry.SERVICE_NAME, "help"); verify(context).registerService(eq(Command.class.getName()), isA(HelpCommand.class), eq(props)); - ArgumentCaptor actionCaptor = ArgumentCaptor.forClass(Action.class); - verifyNew(MultipleServiceTracker.class).withArguments(eq(context), - eq(new Class[] {OSGiRegistry.class, Keyring.class}), - actionCaptor.capture()); - Action action = actionCaptor.getValue(); + verify(mockTracker).open(); + + RegisterLauncherCustomizer customizer = customizerCaptor.getValue(); + assertNotNull(customizer); + activator.stop(context); + verify(mockTracker).close(); + } + + @Test + public void testServiceTrackerCustomizer() throws Exception { + StubBundleContext context = new StubBundleContext(); + ArgumentCaptor customizerCaptor = ArgumentCaptor.forClass(RegisterLauncherCustomizer.class); + ServiceTracker mockTracker = mock(ServiceTracker.class); + whenNew(ServiceTracker.class).withParameterTypes(BundleContext.class, Class.class, ServiceTrackerCustomizer.class).withArguments(eq(context), + any(Keyring.class), customizerCaptor.capture()).thenReturn(mockTracker); + + Activator activator = new Activator(); + context.registerService(Keyring.class, mock(Keyring.class), null); + activator.start(context); + + assertTrue(context.isServiceRegistered(Command.class.getName(), HelpCommand.class)); + + RegisterLauncherCustomizer customizer = customizerCaptor.getValue(); + assertNotNull(customizer); + Keyring keyringService = mock(Keyring.class); + context.registerService(Keyring.class, keyringService, null); + ServiceReference ref = context.getServiceReference(Keyring.class); + customizer.addingService(ref); + + assertTrue(context.isServiceRegistered(CommandInfoSource.class.getName(), mock(CommandInfoSourceImpl.class).getClass())); + assertTrue(context.isServiceRegistered(BundleManager.class.getName(), BundleManagerImpl.class)); + assertTrue(context.isServiceRegistered(Launcher.class.getName(), LauncherImpl.class)); - action.dependenciesAvailable(isA(Map.class)); - verify(context).registerService(eq(Launcher.class.getName()), isA(Launcher.class), (Dictionary) isNull()); + customizer.removedService(null, null); + + assertFalse(context.isServiceRegistered(CommandInfoSource.class.getName(), CommandInfoSourceImpl.class)); + assertFalse(context.isServiceRegistered(BundleManager.class.getName(), BundleManagerImpl.class)); + assertFalse(context.isServiceRegistered(Launcher.class.getName(), LauncherImpl.class)); + } + + private Path createStubThermostatHome() throws Exception { + Path tempDir = Files.createTempDirectory("test"); + tempDir.toFile().deleteOnExit(); + System.setProperty("THERMOSTAT_HOME", tempDir.toString()); + + File tempEtc = new File(tempDir.toFile(), "etc"); + tempEtc.mkdirs(); + tempEtc.deleteOnExit(); + + File tempProps = new File(tempEtc, "osgi-export.properties"); + tempProps.createNewFile(); + tempProps.deleteOnExit(); - activator.stop(context); - // osgi will take care of unregistration on bundle stop - // verify(launcherServiceRegistration).unregister(); - verify(tracker).close(); + File tempBundleProps = new File(tempEtc, "bundles.properties"); + tempBundleProps.createNewFile(); + tempBundleProps.deleteOnExit(); + + File tempLibs = new File(tempDir.toFile(), "libs"); + tempLibs.mkdirs(); + tempLibs.deleteOnExit(); + return tempDir; + } + + private void setupOsgiRegistryImplMock() { + PowerMockito.mockStatic(FrameworkUtil.class); + Bundle mockBundle = mock(Bundle.class); + when(FrameworkUtil.getBundle(BundleManagerImpl.class)).thenReturn(mockBundle); + when(mockBundle.getBundleContext()).thenReturn(context); + Bundle mockFramework = mock(Framework.class); + when(context.getBundle(0)).thenReturn(mockFramework); + when(mockFramework.getBundleContext()).thenReturn(context); + when(context.getBundles()).thenReturn(new Bundle[0]); } } diff -r 6815308bb362 -r bf46bf74d05c launcher/src/test/java/com/redhat/thermostat/launcher/internal/BundleLoaderTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/BundleLoaderTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,103 @@ +/* + * 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 + * . + * + * 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.launcher.internal; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.List; + +import org.junit.Test; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.Constants; +import org.osgi.framework.launch.Framework; + +import com.redhat.thermostat.launcher.internal.BundleLoader; +import com.redhat.thermostat.test.Bug; + +public class BundleLoaderTest { + + @Test + public void verifyBundlesAreInstalledAndStarted() throws BundleException { + final String BUNDLE_LOCATION = "bundle-location-1"; + + Bundle bundle = mock(Bundle.class); + when(bundle.getHeaders()).thenReturn(new Hashtable()); + BundleContext bundleContext = mock(BundleContext.class); + when(bundleContext.installBundle(BUNDLE_LOCATION)).thenReturn(bundle); + Framework framework = mock(Framework.class); + when(framework.getBundleContext()).thenReturn(bundleContext); + List bundleLocations = Arrays.asList(BUNDLE_LOCATION); + + BundleLoader loader = new BundleLoader(); + loader.installAndStartBundles(framework, bundleLocations); + + verify(bundle).start(Bundle.START_TRANSIENT); + } + + @Bug(id="1227", + summary="Make sure launcher does not start fragments", + url="http://icedtea.classpath.org/bugzilla/show_bug.cgi?id=1227") + @Test + public void verifyFragmentsAreInstalledButNotStarted() throws BundleException { + final String BUNDLE_LOCATION = "bundle-location-1"; + + Bundle bundle = mock(Bundle.class); + Dictionary bundleHeaders = new Hashtable<>(); + bundleHeaders.put(Constants.FRAGMENT_HOST, "foo-bar"); + when(bundle.getHeaders()).thenReturn(bundleHeaders); + + BundleContext bundleContext = mock(BundleContext.class); + when(bundleContext.installBundle(BUNDLE_LOCATION)).thenReturn(bundle); + Framework framework = mock(Framework.class); + when(framework.getBundleContext()).thenReturn(bundleContext); + List bundleLocations = Arrays.asList(BUNDLE_LOCATION); + + BundleLoader loader = new BundleLoader(); + loader.installAndStartBundles(framework, bundleLocations); + + verify(bundle, times(0)).start(Bundle.START_TRANSIENT); + + } +} diff -r 6815308bb362 -r bf46bf74d05c launcher/src/test/java/com/redhat/thermostat/launcher/internal/BundleManagerImplTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/BundleManagerImplTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,157 @@ +/* + * 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 + * . + * + * 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.launcher.internal; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.whenNew; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.launch.Framework; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import com.redhat.thermostat.common.Configuration; +import com.redhat.thermostat.common.cli.CommandInfo; +import com.redhat.thermostat.common.cli.CommandInfoSource; +import com.redhat.thermostat.launcher.internal.BundleLoader; +import com.redhat.thermostat.launcher.internal.BundleManagerImpl; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({BundleManagerImpl.class, FrameworkUtil.class}) +public class BundleManagerImplTest { + + private static final String cmdName = "one"; + + private static final String jar1Name = "/one.jar"; + private static final String jar2Name = "/two.jar"; + private static final String jar3Name = "/three.jar"; + + private Bundle b1, b2, b3; + private List bundleLocs; + + private BundleLoader loader; + private Configuration conf; + + @Before + public void setUp() throws Exception { + conf = mock(Configuration.class); + when(conf.getThermostatHome()).thenReturn("no_matter"); + bundleLocs = Arrays.asList(jar1Name, jar2Name, jar3Name); + b1 = mock(Bundle.class); + when(b1.getLocation()).thenReturn(jar1Name); + when(b1.getState()).thenReturn(Bundle.ACTIVE); + b2 = mock(Bundle.class); + when(b2.getLocation()).thenReturn(jar2Name); + when(b2.getState()).thenReturn(Bundle.ACTIVE); + b3 = mock(Bundle.class); + when(b3.getLocation()).thenReturn(jar3Name); + when(b3.getState()).thenReturn(Bundle.ACTIVE); + List installed = Arrays.asList(b1, b2, b3); + + loader = mock(BundleLoader.class); + when(loader.installAndStartBundles(any(Framework.class), eq(bundleLocs))). + thenReturn(installed); + whenNew(BundleLoader.class).withParameterTypes(Boolean.TYPE). + withArguments(any()).thenReturn(loader); + } + + @Test + public void testLoadBundlesFor() throws Exception { + verifyBundlesLoaded(new Bundle[] {}, bundleLocs); + } + + @Test + public void verifyAlreadyLoadedBundlesNotReloaded() throws Exception { + verifyBundlesLoaded(new Bundle[] {b1, b2}, Arrays.asList(jar3Name)); + } + + private void verifyBundlesLoaded(Bundle[] preloaded, List locationsNeeded) throws Exception { + Bundle theBundle = b2; + BundleContext theContext = mock(BundleContext.class); + when(theContext.getBundles()).thenReturn(preloaded); + Framework theFramework = mock(Framework.class); + when(theFramework.getBundleContext()).thenReturn(theContext); + when(theContext.getBundle(0)).thenReturn(theFramework); + when(theBundle.getBundleContext()).thenReturn(theContext); + mockStatic(FrameworkUtil.class); + when(FrameworkUtil.getBundle(any(Class.class))).thenReturn(theBundle); + + BundleManagerImpl registry = new BundleManagerImpl(conf); + CommandInfoSource infos = mock(CommandInfoSource.class); + CommandInfo info = mock(CommandInfo.class); + when (info.getDependencyResourceNames()).thenReturn(bundleLocs); + when (infos.getCommandInfo(cmdName)).thenReturn(info); + registry.setCommandInfoSource(infos); + registry.addBundlesFor(cmdName); + verify(loader).installAndStartBundles(any(Framework.class), eq(locationsNeeded)); + } + + @Test + public void verifySetOSGiVerbosityByReflection() throws Exception { + + // All this fluff is just so constructor doesn't NPE. + Bundle theBundle = b2; + BundleContext theContext = mock(BundleContext.class); + when(theContext.getBundles()).thenReturn(new Bundle[]{}); + Framework theFramework = mock(Framework.class); + when(theFramework.getBundleContext()).thenReturn(theContext); + when(theContext.getBundle(0)).thenReturn(theFramework); + when(theBundle.getBundleContext()).thenReturn(theContext); + mockStatic(FrameworkUtil.class); + when(FrameworkUtil.getBundle(any(Class.class))).thenReturn(theBundle); + + Object registry = new BundleManagerImpl(conf); + Class clazz = registry.getClass(); + Method m = clazz.getMethod("setPrintOSGiInfo", Boolean.TYPE); + m.invoke(registry, true); // If this fails, then API has changed in ways that break FrameworkProvider. + } + +} diff -r 6815308bb362 -r bf46bf74d05c launcher/src/test/java/com/redhat/thermostat/launcher/internal/LauncherTest.java --- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/LauncherTest.java Tue Jan 15 23:00:52 2013 +0100 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/LauncherTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -72,7 +72,6 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; -import com.redhat.thermostat.bundles.OSGiRegistry; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.ActionNotifier; import com.redhat.thermostat.common.ApplicationInfo; @@ -93,6 +92,7 @@ import com.redhat.thermostat.common.tools.ApplicationState; import com.redhat.thermostat.common.tools.BasicCommand; import com.redhat.thermostat.common.utils.OSGIUtils; +import com.redhat.thermostat.launcher.BundleManager; import com.redhat.thermostat.launcher.internal.LauncherImpl.LoggingInitializer; import com.redhat.thermostat.storage.core.Storage; import com.redhat.thermostat.test.StubBundleContext; @@ -144,7 +144,7 @@ private StubBundleContext bundleContext; private Bundle sysBundle; private TestTimerFactory timerFactory; - private OSGiRegistry registry; + private BundleManager registry; private LoggingInitializer loggingInitializer; private DbServiceFactory dbServiceFactory; private CommandInfoSource infos; @@ -211,7 +211,7 @@ ctxFactory.getCommandRegistry().registerCommands(Arrays.asList(new HelpCommand(), cmd1, cmd2, cmd3, basicCmd)); - registry = mock(OSGiRegistry.class); + registry = mock(BundleManager.class); infos = mock(CommandInfoSource.class); when(infos.getCommandInfo(name1)).thenReturn(info1); diff -r 6815308bb362 -r bf46bf74d05c main/pom.xml --- a/main/pom.xml Tue Jan 15 23:00:52 2013 +0100 +++ b/main/pom.xml Tue Jan 15 23:04:57 2013 +0100 @@ -84,11 +84,6 @@ com.redhat.thermostat - thermostat-bundles - ${project.version} - - - com.redhat.thermostat thermostat-launcher ${project.version} diff -r 6815308bb362 -r bf46bf74d05c main/src/main/java/com/redhat/thermostat/main/Thermostat.java --- a/main/src/main/java/com/redhat/thermostat/main/Thermostat.java Tue Jan 15 23:00:52 2013 +0100 +++ b/main/src/main/java/com/redhat/thermostat/main/Thermostat.java Tue Jan 15 23:04:57 2013 +0100 @@ -62,6 +62,7 @@ this.context = context; } + @SuppressWarnings({ "rawtypes", "unchecked" }) private void launch() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, FileNotFoundException, IOException, BundleException, InterruptedException { diff -r 6815308bb362 -r bf46bf74d05c main/src/main/java/com/redhat/thermostat/main/impl/FrameworkProvider.java --- a/main/src/main/java/com/redhat/thermostat/main/impl/FrameworkProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/main/src/main/java/com/redhat/thermostat/main/impl/FrameworkProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -59,10 +59,9 @@ import org.osgi.framework.launch.FrameworkFactory; import org.osgi.util.tracker.ServiceTracker; -import com.redhat.thermostat.bundles.OSGiRegistry; import com.redhat.thermostat.common.Configuration; -import com.redhat.thermostat.common.ConfigurationException; import com.redhat.thermostat.launcher.Launcher; +import com.redhat.thermostat.launcher.BundleManager; public class FrameworkProvider { @@ -217,11 +216,11 @@ locations.add(location); } } - OSGiRegistry.preLoadBundles(framework, locations, printOSGiInfo); + BundleManager.preLoadBundles(framework, locations, printOSGiInfo); } private void setLoaderVerbosity(Framework framework) throws InterruptedException { - Object loader = getService(framework, OSGiRegistry.class.getName()); + Object loader = getService(framework, BundleManager.class.getName()); callVoidReflectedMethod(loader, "setPrintOSGiInfo", printOSGiInfo, Boolean.TYPE); } @@ -232,6 +231,7 @@ private Object getService(Framework framework, String name) throws InterruptedException { Object service = null; + @SuppressWarnings({ "unchecked", "rawtypes" }) ServiceTracker tracker = new ServiceTracker(framework.getBundleContext(), name, null); tracker.open(); service = tracker.waitForService(0); diff -r 6815308bb362 -r bf46bf74d05c main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties --- a/main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties Tue Jan 15 23:00:52 2013 +0100 +++ b/main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties Tue Jan 15 23:04:57 2013 +0100 @@ -1,7 +1,6 @@ bundles=thermostat-keyring-${project.version}.jar, \ thermostat-storage-core-${project.version}.jar, \ thermostat-common-core-${project.version}.jar, \ - thermostat-bundles-${project.version}.jar, \ thermostat-launcher-${project.version}.jar, \ thermostat-main-${project.version}.jar, \ jline2.jar, \ diff -r 6815308bb362 -r bf46bf74d05c main/src/test/java/com/redhat/thermostat/main/ThermostatTest.java --- a/main/src/test/java/com/redhat/thermostat/main/ThermostatTest.java Tue Jan 15 23:00:52 2013 +0100 +++ b/main/src/test/java/com/redhat/thermostat/main/ThermostatTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,27 +36,36 @@ package com.redhat.thermostat.main; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Hashtable; import org.junit.Before; +import org.junit.Test; import org.junit.runner.RunWith; +import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.launch.Framework; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; -import com.redhat.thermostat.bundles.impl.OSGiRegistryImpl; -import com.redhat.thermostat.common.Configuration; +import com.redhat.thermostat.launcher.Launcher; +import com.redhat.thermostat.launcher.BundleManager; +import com.redhat.thermostat.main.impl.FrameworkProvider; @RunWith(PowerMockRunner.class) -@PrepareForTest(value = Thermostat.class) +@PrepareForTest({FrameworkProvider.class}) public class ThermostatTest { private Path tempDir; @@ -65,12 +74,9 @@ private BundleContext mockContext; + @SuppressWarnings("rawtypes") @Before public void setUp() throws Exception { - - final OSGiRegistryImpl osgiRegistry = mock(OSGiRegistryImpl.class); - PowerMockito.whenNew(OSGiRegistryImpl.class).withArguments(any(Configuration.class)).thenReturn(osgiRegistry); - tempDir = Files.createTempDirectory("test"); tempDir.toFile().deleteOnExit(); System.setProperty("THERMOSTAT_HOME", tempDir.toString()); @@ -93,19 +99,40 @@ mockContext = mock(BundleContext.class); - mockFramework = mock(Framework.class); - when(mockFramework.getBundleContext()).thenReturn(mockContext); - - TestFrameworkFactory.setFramework(mockFramework); + Framework framework = mock(Framework.class); + TestFrameworkFactory.setFramework(framework); + when(framework.getBundleContext()).thenReturn(mockContext); + Bundle mockBundle = mock(Bundle.class); + when(mockContext.installBundle(any(String.class))).thenReturn(mockBundle); + when(mockBundle.getHeaders()).thenReturn(new Hashtable()); + ServiceTracker registryTracker = mock(ServiceTracker.class); + PowerMockito + .whenNew(ServiceTracker.class) + .withParameterTypes(BundleContext.class, String.class, + ServiceTrackerCustomizer.class) + .withArguments(any(BundleContext.class), + eq(BundleManager.class.getName()), any(ServiceTrackerCustomizer.class)) + .thenReturn(registryTracker); + when(registryTracker.waitForService(0)).thenReturn(mock(BundleManager.class)); + ServiceTracker launcherTracker = mock(ServiceTracker.class); + Launcher launcher = mock(Launcher.class); + PowerMockito + .whenNew(ServiceTracker.class) + .withParameterTypes(BundleContext.class, String.class, + ServiceTrackerCustomizer.class) + .withArguments(any(BundleContext.class), + eq(Launcher.class.getName()), + any(ServiceTrackerCustomizer.class)) + .thenReturn(launcherTracker); + when(launcherTracker.waitForService(0)) + .thenReturn(launcher); } - // TODO These now seem to belong in OSGiRegistryTest - - /* @Test public void testOSGIDirExists() throws Exception { - Path osgiDir = tempDir.resolve("osgi"); + Path osgiDir = tempDir.resolve("osgi-cache"); osgiDir.toFile().mkdirs(); + osgiDir.toFile().deleteOnExit(); assertTrue(osgiDir.toFile().exists()); try { Thermostat.main(new String[0]); @@ -113,20 +140,18 @@ e.printStackTrace(); } assertTrue(osgiDir.toFile().exists()); - }*/ - - /*@Test - public void testFrameworkConfig() throws Exception { - Thermostat.main(new String[0]); - Map config = TestFrameworkFactory.getConfig(); - Path osgiDir = tempDir.resolve("osgi"); - assertEquals(osgiDir.toString(), config.get(Constants.FRAMEWORK_STORAGE)); } @Test public void testFrameworkInitAndStart() throws Exception { + Path osgiDir = tempDir.resolve("osgi-cache"); + osgiDir.toFile().mkdirs(); + osgiDir.toFile().deleteOnExit(); + mockFramework = mock(Framework.class); + when(mockFramework.getBundleContext()).thenReturn(mockContext); + TestFrameworkFactory.setFramework(mockFramework); Thermostat.main(new String[0]); verify(mockFramework).init(); verify(mockFramework).start(); - }*/ + } } diff -r 6815308bb362 -r bf46bf74d05c main/src/test/resources/META-INF/services/org.osgi.framework.launch.FrameworkFactory --- a/main/src/test/resources/META-INF/services/org.osgi.framework.launch.FrameworkFactory Tue Jan 15 23:00:52 2013 +0100 +++ b/main/src/test/resources/META-INF/services/org.osgi.framework.launch.FrameworkFactory Tue Jan 15 23:04:57 2013 +0100 @@ -34,4 +34,4 @@ # to do so, delete this exception statement from your version. # -com.redhat.thermostat.launcher.TestFrameworkFactory \ No newline at end of file +com.redhat.thermostat.main.TestFrameworkFactory \ No newline at end of file diff -r 6815308bb362 -r bf46bf74d05c pom.xml --- a/pom.xml Tue Jan 15 23:00:52 2013 +0100 +++ b/pom.xml Tue Jan 15 23:04:57 2013 +0100 @@ -117,10 +117,10 @@ + annotations distribution main launcher - bundles common agent client diff -r 6815308bb362 -r bf46bf74d05c storage/core/pom.xml --- a/storage/core/pom.xml Tue Jan 15 23:00:52 2013 +0100 +++ b/storage/core/pom.xml Tue Jan 15 23:04:57 2013 +0100 @@ -84,6 +84,12 @@ org.apache.felix org.apache.felix.framework + + com.redhat.thermostat + thermostat-annotations + ${project.version} + compile + diff -r 6815308bb362 -r bf46bf74d05c storage/core/src/main/java/com/redhat/thermostat/storage/core/Storage.java --- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/Storage.java Tue Jan 15 23:00:52 2013 +0100 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/Storage.java Tue Jan 15 23:04:57 2013 +0100 @@ -39,6 +39,7 @@ import java.io.InputStream; import java.util.UUID; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.storage.model.Pojo; /** @@ -46,6 +47,7 @@ * Implementations may use memory, a file, some database or even a network * server as the backing store. */ +@Service public interface Storage { void setAgentId(UUID id); diff -r 6815308bb362 -r bf46bf74d05c storage/core/src/main/java/com/redhat/thermostat/storage/core/StorageProviderUtil.java --- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/StorageProviderUtil.java Tue Jan 15 23:00:52 2013 +0100 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/StorageProviderUtil.java Tue Jan 15 23:04:57 2013 +0100 @@ -1,3 +1,39 @@ +/* + * 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 + * . + * + * 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.storage.core; import org.osgi.framework.Bundle; diff -r 6815308bb362 -r bf46bf74d05c storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/Activator.java --- a/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/Activator.java Tue Jan 15 23:00:52 2013 +0100 +++ b/storage/mongo/src/main/java/com/redhat/thermostat/storage/mongodb/internal/Activator.java Tue Jan 15 23:04:57 2013 +0100 @@ -1,3 +1,39 @@ +/* + * 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 + * . + * + * 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.storage.mongodb.internal; import org.osgi.framework.BundleActivator; diff -r 6815308bb362 -r bf46bf74d05c system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java --- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java Tue Jan 15 23:00:52 2013 +0100 +++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java Tue Jan 15 23:04:57 2013 +0100 @@ -52,14 +52,14 @@ import sun.jvmstat.monitor.event.HostListener; import sun.jvmstat.monitor.event.VmStatusChangeEvent; -import com.redhat.thermostat.agent.JvmStatusListener; -import com.redhat.thermostat.agent.JvmStatusNotifier; +import com.redhat.thermostat.agent.VmStatusListener; +import com.redhat.thermostat.agent.VmStatusListener.Status; import com.redhat.thermostat.common.dao.VmInfoDAO; import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.storage.model.VmInfo; import com.redhat.thermostat.utils.ProcDataSource; -public class JvmStatHostListener implements HostListener, JvmStatusNotifier { +public class JvmStatHostListener implements HostListener { private static final Logger logger = LoggingUtils.getLogger(JvmStatHostListener.class); @@ -67,10 +67,11 @@ private Map monitoredVms = new HashMap<>(); - private Set statusListeners = new CopyOnWriteArraySet(); + private VmStatusChangeNotifier notifier; - JvmStatHostListener(VmInfoDAO vmInfoDAO) { + JvmStatHostListener(VmInfoDAO vmInfoDAO, VmStatusChangeNotifier notifier) { this.vmInfoDAO = vmInfoDAO; + this.notifier = notifier; } @Override @@ -121,9 +122,7 @@ logger.log(Level.WARNING, "error getting vm info for " + vmId, me); } - for (JvmStatusListener statusListener : statusListeners) { - statusListener.jvmStarted(vmId); - } + notifier.notifyVmStatusChange(Status.VM_STARTED, vmId); monitoredVms.put(vmId, vm); } @@ -149,9 +148,9 @@ VmIdentifier resolvedVmID = host.getHostIdentifier().resolve(new VmIdentifier(vmId.toString())); if (resolvedVmID != null) { long stopTime = System.currentTimeMillis(); - for (JvmStatusListener statusListener : statusListeners) { - statusListener.jvmStopped(vmId); - } + + notifier.notifyVmStatusChange(Status.VM_STOPPED, vmId); + vmInfoDAO.putVmStoppedTime(vmId, stopTime); MonitoredVm vm = monitoredVms.remove(vmId); @@ -159,16 +158,6 @@ } } - @Override - public void addJvmStatusListener(JvmStatusListener listener) { - statusListeners.add(listener); - } - - @Override - public void removeJvmStatusListener(JvmStatusListener listener) { - statusListeners.remove(listener); - } - /* * For testing purposes only. */ diff -r 6815308bb362 -r bf46bf74d05c system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java --- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java Tue Jan 15 23:00:52 2013 +0100 +++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java Tue Jan 15 23:04:57 2013 +0100 @@ -48,8 +48,7 @@ import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredHost; -import com.redhat.thermostat.agent.JvmStatusListener; -import com.redhat.thermostat.agent.JvmStatusNotifier; +import com.redhat.thermostat.agent.VmStatusListener; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendID; import com.redhat.thermostat.backend.BackendsProperties; @@ -59,14 +58,16 @@ import com.redhat.thermostat.storage.model.NetworkInterfaceInfo; import com.redhat.thermostat.utils.ProcDataSource; -public class SystemBackend extends Backend implements JvmStatusNotifier, JvmStatusListener { +public class SystemBackend extends Backend { private static final Logger logger = LoggingUtils.getLogger(SystemBackend.class); private HostInfoDAO hostInfos; private NetworkInterfaceInfoDAO networkInterfaces; - private final Set pidsToMonitor = new CopyOnWriteArraySet(); + private final VmStatusChangeNotifier notifier; + + private final Set pidsToMonitor = new CopyOnWriteArraySet<>(); private long procCheckInterval = 1000; // TODO make this configurable. @@ -78,8 +79,10 @@ private final HostInfoBuilder hostInfoBuilder; - public SystemBackend() { + + public SystemBackend(VmStatusChangeNotifier notifier) { super(new BackendID("System Backend", SystemBackend.class.getName())); + this.notifier = notifier; setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers basic information from the system"); @@ -93,7 +96,7 @@ protected void setDAOFactoryAction() { hostInfos = df.getHostInfoDAO(); networkInterfaces = df.getNetworkInterfaceInfoDAO(); - hostListener = new JvmStatHostListener(df.getVmInfoDAO()); + hostListener = new JvmStatHostListener(df.getVmInfoDAO(), notifier); } @Override @@ -105,8 +108,6 @@ throw new IllegalStateException("Cannot activate backend without DAOFactory."); } - addJvmStatusListener(this); - if (!getObserveNewJvm()) { logger.fine("not monitoring new vms"); } @@ -144,8 +145,6 @@ timer.cancel(); timer = null; - removeJvmStatusListener(this); - try { host.removeHostListener(hostListener); } catch (MonitorException me) { @@ -173,28 +172,6 @@ } @Override - public void addJvmStatusListener(JvmStatusListener listener) { - hostListener.addJvmStatusListener(listener); - } - - @Override - public void removeJvmStatusListener(JvmStatusListener listener) { - hostListener.removeJvmStatusListener(listener); - } - - @Override - public void jvmStarted(int vmId) { - if (getObserveNewJvm()) { - pidsToMonitor.add(vmId); - } - } - - @Override - public void jvmStopped(int vmId) { - pidsToMonitor.remove(vmId); - } - - @Override public int getOrderValue() { return ORDER_DEFAULT_GROUP; } diff -r 6815308bb362 -r bf46bf74d05c system-backend/src/main/java/com/redhat/thermostat/backend/system/VmStatusChangeNotifier.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/VmStatusChangeNotifier.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,135 @@ +/* + * Copyright 2013 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 + * . + * + * 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.backend.system; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeSet; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +import com.redhat.thermostat.agent.VmStatusListener; +import com.redhat.thermostat.agent.VmStatusListener.Status; + +/** + * Notifies any and all {@link VmStatusListener} registered as OSGi Services + * about VM status changes: {@link VmStatusListener.Status#VM_STARTED} and + * {@link VmStatusListener.Status#VM_STOPPED}. + *

+ * Any listeners registered after a {@link VmStatusListener.Status#VM_STARTED} + * was delivered receive a {@link VmStatusListener.Status#VM_ACTIVE} event + * instead as an indication that a VM was started at some unknown point + * previously. + */ +public class VmStatusChangeNotifier { + + private final Object listenerLock = new Object(); + private final Set activePids; + private final Map> listeners = new HashMap<>(); + + private final ServiceTracker tracker; + + public VmStatusChangeNotifier(BundleContext bundleContext) { + this.activePids = new TreeSet<>(); + + tracker = new ServiceTracker(bundleContext, VmStatusListener.class, null) { + @Override + public VmStatusListener addingService(ServiceReference reference) { + VmStatusListener listener = (VmStatusListener) super.addingService(reference); + + synchronized (listenerLock) { + Set notifiedAbout = new TreeSet<>(); + for (Integer pid : activePids) { + listener.vmStatusChanged(Status.VM_ACTIVE, pid); + notifiedAbout.add(pid); + } + + listeners.put(listener, notifiedAbout); + } + + return listener; + } + + @Override + public void removedService(ServiceReference reference, + Object service) { + VmStatusListener listener = (VmStatusListener) service; + listeners.remove(listener); + super.removedService(reference, service); + } + }; + } + + public void start() { + tracker.open(); + } + + public void stop() { + tracker.close(); + } + + /** + * Notify all registered listeners about a Vm status change. + * + * @param newStatus either {@link VmStatusListener.Status#VM_STARTED} or + * {@link VmStatusListener.Status#VM_STOPPED} + * @param pid + */ + public void notifyVmStatusChange(VmStatusListener.Status newStatus, int pid) { + if (newStatus == Status.VM_ACTIVE) { + throw new IllegalArgumentException("Dont pass in " + Status.VM_ACTIVE + ", that will be handled automatically"); + } + + synchronized (listenerLock) { + for (Entry> entry : listeners.entrySet()) { + entry.getKey().vmStatusChanged(newStatus, pid); + entry.getValue().add(pid); + } + + if (newStatus == Status.VM_STARTED) { + activePids.add(pid); + } else { + activePids.remove(pid); + } + } + } + +} diff -r 6815308bb362 -r bf46bf74d05c system-backend/src/main/java/com/redhat/thermostat/backend/system/osgi/SystemBackendActivator.java --- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/osgi/SystemBackendActivator.java Tue Jan 15 23:00:52 2013 +0100 +++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/osgi/SystemBackendActivator.java Tue Jan 15 23:04:57 2013 +0100 @@ -44,18 +44,24 @@ import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendService; import com.redhat.thermostat.backend.system.SystemBackend; +import com.redhat.thermostat.backend.system.VmStatusChangeNotifier; @SuppressWarnings("rawtypes") public class SystemBackendActivator implements BundleActivator { private ServiceTracker tracker; private SystemBackend backend; + + private VmStatusChangeNotifier notifier; @SuppressWarnings("unchecked") @Override public void start(BundleContext context) throws Exception { - backend = new SystemBackend(); + notifier = new VmStatusChangeNotifier(context); + notifier.start(); + + backend = new SystemBackend(notifier); tracker = new ServiceTracker(context, BackendService.class, null) { @Override @@ -84,5 +90,6 @@ backend.deactivate(); } tracker.close(); + notifier.stop(); } } diff -r 6815308bb362 -r bf46bf74d05c system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java --- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java Tue Jan 15 23:00:52 2013 +0100 +++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -41,7 +41,9 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -62,6 +64,7 @@ import sun.jvmstat.monitor.VmIdentifier; import sun.jvmstat.monitor.event.VmStatusChangeEvent; +import com.redhat.thermostat.agent.VmStatusListener.Status; import com.redhat.thermostat.common.dao.VmInfoDAO; import com.redhat.thermostat.storage.model.VmInfo; @@ -82,11 +85,14 @@ private MonitoredVm monitoredVm2; private JvmStatDataExtractor extractor; private VmInfoDAO vmInfoDAO; + private VmStatusChangeNotifier notifier; @Before public void setup() throws MonitorException, URISyntaxException { vmInfoDAO = mock(VmInfoDAO.class); - hostListener = new JvmStatHostListener(vmInfoDAO); + notifier = mock(VmStatusChangeNotifier.class); + + hostListener = new JvmStatHostListener(vmInfoDAO, notifier); host = mock(MonitoredHost.class); HostIdentifier hostId = mock(HostIdentifier.class); @@ -126,6 +132,8 @@ assertTrue(hostListener.getMonitoredVms().containsKey(2)); assertEquals(monitoredVm1, hostListener.getMonitoredVms().get(1)); assertEquals(monitoredVm2, hostListener.getMonitoredVms().get(2)); + + verify(notifier, times(2)).notifyVmStatusChange(eq(Status.VM_STARTED), (isA(Integer.class))); } @Test @@ -146,6 +154,9 @@ assertFalse(hostListener.getMonitoredVms().containsKey(1)); assertTrue(hostListener.getMonitoredVms().containsKey(2)); assertEquals(monitoredVm2, hostListener.getMonitoredVms().get(2)); + + verify(notifier).notifyVmStatusChange(eq(Status.VM_STOPPED), (isA(Integer.class))); + } private void startVMs() throws InterruptedException, MonitorException { @@ -159,6 +170,8 @@ when(event.getStarted()).thenReturn(started); when(event.getTerminated()).thenReturn(Collections.emptySet()); hostListener.vmStatusChanged(event); + + verify(notifier, times(2)).notifyVmStatusChange(eq(Status.VM_STARTED), isA(Integer.class)); } @Test diff -r 6815308bb362 -r bf46bf74d05c system-backend/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java --- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java Tue Jan 15 23:00:52 2013 +0100 +++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -62,7 +62,10 @@ when(df.getStorage()).thenReturn(s); when(df.getHostInfoDAO()).thenReturn(hDAO); when(df.getNetworkInterfaceInfoDAO()).thenReturn(nDAO); - b = new SystemBackend(); + + VmStatusChangeNotifier notifier = mock(VmStatusChangeNotifier.class); + + b = new SystemBackend(notifier); b.setDAOFactory(df); } diff -r 6815308bb362 -r bf46bf74d05c system-backend/src/test/java/com/redhat/thermostat/backend/system/VmStatusChangeNotifierTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/VmStatusChangeNotifierTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,96 @@ +/* + * Copyright 2013 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 + * . + * + * 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.backend.system; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.junit.Test; + +import com.redhat.thermostat.agent.VmStatusListener; +import com.redhat.thermostat.agent.VmStatusListener.Status; +import com.redhat.thermostat.test.StubBundleContext; + +public class VmStatusChangeNotifierTest { + + @Test + public void verifyWorksWithoutAnyListeners() { + final int VM_ID = 2; + StubBundleContext bundleContext = new StubBundleContext(); + + VmStatusChangeNotifier notifier = new VmStatusChangeNotifier(bundleContext); + notifier.start(); + notifier.notifyVmStatusChange(Status.VM_STARTED, VM_ID); + + notifier.notifyVmStatusChange(Status.VM_STOPPED, VM_ID); + } + + @Test + public void verifyAllListenersAreNotified() { + final int VM_ID = 2; + StubBundleContext bundleContext = new StubBundleContext(); + + VmStatusListener listener = mock(VmStatusListener.class); + bundleContext.registerService(VmStatusListener.class, listener, null); + + VmStatusChangeNotifier notifier = new VmStatusChangeNotifier(bundleContext); + notifier.start(); + notifier.notifyVmStatusChange(Status.VM_STARTED, VM_ID); + + verify(listener).vmStatusChanged(Status.VM_STARTED, VM_ID); + + notifier.notifyVmStatusChange(Status.VM_STOPPED, VM_ID); + + verify(listener).vmStatusChanged(Status.VM_STOPPED, VM_ID); + } + + @Test + public void verifyListenersAddedAfterVmStartRecieveVmActiveEvent() { + final int VM_ID = 2; + StubBundleContext bundleContext = new StubBundleContext(); + + VmStatusChangeNotifier notifier = new VmStatusChangeNotifier(bundleContext); + notifier.start(); + notifier.notifyVmStatusChange(Status.VM_STARTED, VM_ID); + + VmStatusListener listener = mock(VmStatusListener.class); + bundleContext.registerService(VmStatusListener.class, listener, null); + + verify(listener).vmStatusChanged(Status.VM_ACTIVE, VM_ID); + + } +} diff -r 6815308bb362 -r bf46bf74d05c unix-process-handler/src/main/java/com/redhat/thermostat/service/process/UNIXProcessHandler.java --- a/unix-process-handler/src/main/java/com/redhat/thermostat/service/process/UNIXProcessHandler.java Tue Jan 15 23:00:52 2013 +0100 +++ b/unix-process-handler/src/main/java/com/redhat/thermostat/service/process/UNIXProcessHandler.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,6 +36,9 @@ package com.redhat.thermostat.service.process; +import com.redhat.thermostat.annotations.Service; + +@Service public interface UNIXProcessHandler { public static String ID = "com.redhat.thermostat.service.process.UNIXProcessHandler"; diff -r 6815308bb362 -r bf46bf74d05c vm-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/VmClassStatViewProvider.java --- a/vm-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/VmClassStatViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-classstat/client-core/src/main/java/com/redhat/thermostat/vm/classstat/client/core/VmClassStatViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,9 +36,10 @@ package com.redhat.thermostat.vm.classstat.client.core; +import com.redhat.thermostat.annotations.ExtensionPoint; import com.redhat.thermostat.client.core.views.ViewProvider; - +@ExtensionPoint public interface VmClassStatViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c vm-classstat/common/src/main/java/com/redhat/thermostat/vm/classstat/common/VmClassStatDAO.java --- a/vm-classstat/common/src/main/java/com/redhat/thermostat/vm/classstat/common/VmClassStatDAO.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-classstat/common/src/main/java/com/redhat/thermostat/vm/classstat/common/VmClassStatDAO.java Tue Jan 15 23:04:57 2013 +0100 @@ -38,11 +38,13 @@ import java.util.List; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.common.dao.VmRef; import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Key; import com.redhat.thermostat.storage.model.VmClassStat; +@Service public interface VmClassStatDAO { static final Key loadedClassesKey = new Key<>("loadedClasses", false); @@ -54,4 +56,4 @@ public void putVmClassStat(VmClassStat stat); -} \ No newline at end of file +} diff -r 6815308bb362 -r bf46bf74d05c vm-cpu/client-cli/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-cpu/client-cli/pom.xml Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,77 @@ + + + 4.0.0 + + thermostat-vm-cpu + com.redhat.thermostat + 0.5.0-SNAPSHOT + + thermostat-vm-cpu-client-cli + bundle + Thermostat VM CPU CLI Client plugin + + + + org.apache.felix + maven-bundle-plugin + true + + + + com.redhat.thermostat.vm.cpu.client.cli.internal + + Red Hat, Inc. + com.redhat.thermostat.vm.cpu.client.cli.internal.Activator + com.redhat.thermostat.vm.cpu.client.cli + + <_nouses>true + + + + + + + + + junit + junit + test + + + org.mockito + mockito-core + test + + + org.osgi + org.osgi.core + provided + + + org.osgi + org.osgi.compendium + provided + + + com.redhat.thermostat + thermostat-common-core + ${project.version} + + + com.redhat.thermostat + thermostat-vm-cpu-common + ${project.version} + + + com.redhat.thermostat + thermostat-common-test + ${project.version} + test + + + com.redhat.thermostat + thermostat-client-cli + ${project.version} + + + diff -r 6815308bb362 -r bf46bf74d05c vm-cpu/client-cli/src/main/java/com/redhat/thermostat/vm/cpu/client/cli/internal/Activator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-cpu/client-cli/src/main/java/com/redhat/thermostat/vm/cpu/client/cli/internal/Activator.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,80 @@ +/* + * Copyright 2013 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 + * . + * + * 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.vm.cpu.client.cli.internal; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.util.tracker.ServiceTracker; + +import com.redhat.thermostat.client.cli.VMStatPrintDelegate; +import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; + +public class Activator implements BundleActivator { + + private ServiceTracker tracker; + private ServiceRegistration reg; + + @Override + public void start(BundleContext context) throws Exception { + tracker = new ServiceTracker(context, VmCpuStatDAO.class.getName(), null) { + @Override + public Object addingService(ServiceReference reference) { + VmCpuStatDAO vmCpuStatDAO = (VmCpuStatDAO) super.addingService(reference); + VmCpuStatPrintDelegate delegate = new VmCpuStatPrintDelegate(vmCpuStatDAO); + reg = context.registerService(VMStatPrintDelegate.class.getName(), delegate, null); + return vmCpuStatDAO; + } + + @Override + public void removedService(ServiceReference reference, + Object service) { + reg.unregister(); + super.removedService(reference, service); + } + }; + + tracker.open(); + } + + @Override + public void stop(BundleContext context) throws Exception { + tracker.close(); + } + +} diff -r 6815308bb362 -r bf46bf74d05c vm-cpu/client-cli/src/main/java/com/redhat/thermostat/vm/cpu/client/cli/internal/LocaleResources.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-cpu/client-cli/src/main/java/com/redhat/thermostat/vm/cpu/client/cli/internal/LocaleResources.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,51 @@ +/* + * Copyright 2013 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 + * . + * + * 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.vm.cpu.client.cli.internal; + +import com.redhat.thermostat.common.locale.Translate; + +public enum LocaleResources { + + COLUMN_HEADER_CPU_PERCENT, + ; + + static final String RESOURCE_BUNDLE = "com.redhat.thermostat.vm.cpu.client.cli.strings"; + + public static Translate createLocalizer() { + return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class); + } +} diff -r 6815308bb362 -r bf46bf74d05c vm-cpu/client-cli/src/main/java/com/redhat/thermostat/vm/cpu/client/cli/internal/VmCpuStatPrintDelegate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-cpu/client-cli/src/main/java/com/redhat/thermostat/vm/cpu/client/cli/internal/VmCpuStatPrintDelegate.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,86 @@ +/* + * Copyright 2013 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 + * . + * + * 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.vm.cpu.client.cli.internal; + +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.List; + +import com.redhat.thermostat.client.cli.VMStatPrintDelegate; +import com.redhat.thermostat.common.dao.VmRef; +import com.redhat.thermostat.common.locale.Translate; +import com.redhat.thermostat.storage.model.TimeStampedPojo; +import com.redhat.thermostat.storage.model.VmCpuStat; +import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; + +public class VmCpuStatPrintDelegate implements VMStatPrintDelegate { + + private static final Translate translator = LocaleResources.createLocalizer(); + + private static final String CPU_PERCENT = translator.localize(LocaleResources.COLUMN_HEADER_CPU_PERCENT); + + private VmCpuStatDAO cpuStatDAO; + + public VmCpuStatPrintDelegate(VmCpuStatDAO cpuStatDAO) { + this.cpuStatDAO = cpuStatDAO; + } + + @Override + public List getLatestStats(VmRef ref, + long timestamp) { + return cpuStatDAO.getLatestVmCpuStats(ref, timestamp); + } + + @Override + public List getHeaders(TimeStampedPojo stat) { + return Arrays.asList(CPU_PERCENT); + } + + @Override + public List getStatRow(TimeStampedPojo stat) { + VmCpuStat cpuStat = (VmCpuStat) stat; + DecimalFormat format = new DecimalFormat("#0.0"); + String cpuLoad = cpuStat != null ? format.format(cpuStat.getCpuLoad()) : ""; + return Arrays.asList(cpuLoad); + } + + @Override + public int getOrderValue() { + return ORDER_CPU_GROUP; + } + +} diff -r 6815308bb362 -r bf46bf74d05c vm-cpu/client-cli/src/main/resources/com/redhat/thermostat/vm/cpu/client/cli/strings.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-cpu/client-cli/src/main/resources/com/redhat/thermostat/vm/cpu/client/cli/strings.properties Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,1 @@ +COLUMN_HEADER_CPU_PERCENT = %CPU \ No newline at end of file diff -r 6815308bb362 -r bf46bf74d05c vm-cpu/client-cli/src/test/java/com/redhat/thermostat/vm/cpu/client/cli/internal/ActivatorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-cpu/client-cli/src/test/java/com/redhat/thermostat/vm/cpu/client/cli/internal/ActivatorTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,84 @@ +/* + * Copyright 2013 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 + * . + * + * 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.vm.cpu.client.cli.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import org.junit.Test; + +import com.redhat.thermostat.client.cli.VMStatPrintDelegate; +import com.redhat.thermostat.test.StubBundleContext; +import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; + +public class ActivatorTest { + + @Test + public void verifyActivatorDoesNotRegisterServiceOnMissingDeps() throws Exception { + StubBundleContext context = new StubBundleContext(); + + Activator activator = new Activator(); + + activator.start(context); + + assertEquals(0, context.getAllServices().size()); + assertEquals(1, context.getServiceListeners().size()); + + activator.stop(context); + } + + @Test + public void verifyActivatorRegistersServices() throws Exception { + StubBundleContext context = new StubBundleContext(); + VmCpuStatDAO dao = mock(VmCpuStatDAO.class); + + context.registerService(VmCpuStatDAO.class, dao, null); + + Activator activator = new Activator(); + + activator.start(context); + + assertTrue(context.isServiceRegistered(VMStatPrintDelegate.class.getName(), VmCpuStatPrintDelegate.class)); + + activator.stop(context); + + assertEquals(0, context.getServiceListeners().size()); + assertEquals(1, context.getAllServices().size()); + } + +} diff -r 6815308bb362 -r bf46bf74d05c vm-cpu/client-cli/src/test/java/com/redhat/thermostat/vm/cpu/client/cli/internal/LocaleResourcesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-cpu/client-cli/src/test/java/com/redhat/thermostat/vm/cpu/client/cli/internal/LocaleResourcesTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,53 @@ +/* + * Copyright 2013 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 + * . + * + * 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.vm.cpu.client.cli.internal; + +import com.redhat.thermostat.test.locale.AbstractLocaleResourcesTest; + +public class LocaleResourcesTest extends AbstractLocaleResourcesTest { + + @Override + protected Class getEnumClass() { + return LocaleResources.class; + } + + @Override + protected String getResourceBundle() { + return LocaleResources.RESOURCE_BUNDLE; + } + +} diff -r 6815308bb362 -r bf46bf74d05c vm-cpu/client-cli/src/test/java/com/redhat/thermostat/vm/cpu/client/cli/internal/VmCpuStatPrintDelegateTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-cpu/client-cli/src/test/java/com/redhat/thermostat/vm/cpu/client/cli/internal/VmCpuStatPrintDelegateTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,106 @@ +/* + * Copyright 2013 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 + * . + * + * 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.vm.cpu.client.cli.internal; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.dao.HostRef; +import com.redhat.thermostat.common.dao.VmRef; +import com.redhat.thermostat.storage.model.TimeStampedPojo; +import com.redhat.thermostat.storage.model.VmCpuStat; +import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; + +public class VmCpuStatPrintDelegateTest { + + private VmCpuStatDAO vmCpuStatDAO; + private VmCpuStatPrintDelegate delegate; + private VmRef vm; + private List cpuStats; + + @Before + public void setUp() { + setupDAOs(); + delegate = new VmCpuStatPrintDelegate(vmCpuStatDAO); + } + + @After + public void tearDown() { + vmCpuStatDAO = null; + } + + private void setupDAOs() { + vmCpuStatDAO = mock(VmCpuStatDAO.class); + int vmId = 234; + HostRef host = new HostRef("123", "dummy"); + vm = new VmRef(host, 234, "dummy"); + VmCpuStat cpustat1 = new VmCpuStat(2, vmId, 65); + VmCpuStat cpustat2 = new VmCpuStat(3, vmId, 70); + cpuStats = Arrays.asList(cpustat1, cpustat2); + when(vmCpuStatDAO.getLatestVmCpuStats(vm, Long.MIN_VALUE)).thenReturn(cpuStats); + } + + @Test + public void testGetLatestStats() { + List stats = delegate.getLatestStats(vm, Long.MIN_VALUE); + assertEquals(cpuStats, stats); + } + + @Test + public void testGetHeaders() { + List headers = delegate.getHeaders(cpuStats.get(0)); + assertEquals(Arrays.asList("%CPU"), headers); + } + + @Test + public void testGetStatRow() throws CommandException { + final List row1 = Arrays.asList("65.0"); + final List row2 = Arrays.asList("70.0"); + assertEquals(row1, delegate.getStatRow(cpuStats.get(0))); + assertEquals(row2, delegate.getStatRow(cpuStats.get(1))); + } + +} diff -r 6815308bb362 -r bf46bf74d05c vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/VmCpuViewProvider.java --- a/vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/VmCpuViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-cpu/client-core/src/main/java/com/redhat/thermostat/vm/cpu/client/core/VmCpuViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,8 +36,10 @@ package com.redhat.thermostat.vm.cpu.client.core; +import com.redhat.thermostat.annotations.ExtensionPoint; import com.redhat.thermostat.client.core.views.ViewProvider; +@ExtensionPoint public interface VmCpuViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c vm-cpu/common/src/main/java/com/redhat/thermostat/vm/cpu/common/VmCpuStatDAO.java --- a/vm-cpu/common/src/main/java/com/redhat/thermostat/vm/cpu/common/VmCpuStatDAO.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-cpu/common/src/main/java/com/redhat/thermostat/vm/cpu/common/VmCpuStatDAO.java Tue Jan 15 23:04:57 2013 +0100 @@ -38,11 +38,13 @@ import java.util.List; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.common.dao.VmRef; import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Key; import com.redhat.thermostat.storage.model.VmCpuStat; +@Service public interface VmCpuStatDAO { static final Key vmCpuLoadKey = new Key<>("cpuLoad", false); diff -r 6815308bb362 -r bf46bf74d05c vm-cpu/pom.xml --- a/vm-cpu/pom.xml Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-cpu/pom.xml Tue Jan 15 23:04:57 2013 +0100 @@ -52,6 +52,7 @@ agent + client-cli client-core client-swing common diff -r 6815308bb362 -r bf46bf74d05c vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/Activator.java --- a/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/Activator.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/Activator.java Tue Jan 15 23:04:57 2013 +0100 @@ -42,6 +42,7 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendService; import com.redhat.thermostat.common.MultipleServiceTracker; @@ -61,13 +62,16 @@ BackendService.class, VmGcStatDAO.class }; + + final VmStatusListenerRegistrar registerer = new VmStatusListenerRegistrar(context); + tracker = new MultipleServiceTracker(context, deps, new Action() { @Override public void dependenciesAvailable(Map services) { VmGcStatDAO vmGcStatDao = (VmGcStatDAO) services.get(VmGcStatDAO.class.getName()); Version version = new Version(context.getBundle()); - backend = new VmGcBackend(vmGcStatDao, version); + backend = new VmGcBackend(vmGcStatDao, version, registerer); reg = context.registerService(Backend.class.getName(), backend, null); } diff -r 6815308bb362 -r bf46bf74d05c vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java --- a/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java Tue Jan 15 23:04:57 2013 +0100 @@ -37,13 +37,20 @@ package com.redhat.thermostat.vm.gc.agent.internal; import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import sun.jvmstat.monitor.HostIdentifier; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredHost; +import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.VmIdentifier; +import sun.jvmstat.monitor.event.VmListener; +import com.redhat.thermostat.agent.VmStatusListener; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendID; import com.redhat.thermostat.backend.BackendsProperties; @@ -51,28 +58,29 @@ import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.vm.gc.common.VmGcStatDAO; -public class VmGcBackend extends Backend { +public class VmGcBackend extends Backend implements VmStatusListener { private static final Logger LOGGER = LoggingUtils.getLogger(VmGcBackend.class); - private VmGcStatDAO vmGcStats; - private HostIdentifier hostId; + private final VmGcStatDAO vmGcStats; + private final VmStatusListenerRegistrar registerer; + + private final Map registeredListeners = new HashMap<>(); private MonitoredHost host; - private VmGcHostListener hostListener; private boolean started; - public VmGcBackend(VmGcStatDAO vmGcStatDAO, Version version) { + public VmGcBackend(VmGcStatDAO vmGcStatDAO, Version version, VmStatusListenerRegistrar registerer) { super(new BackendID("VM GC Backend", VmGcBackend.class.getName())); this.vmGcStats = vmGcStatDAO; + this.registerer = registerer; setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers garbage collection statistics about a JVM"); setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber()); try { - hostId = new HostIdentifier((String) null); + HostIdentifier hostId = new HostIdentifier((String) null); host = MonitoredHost.getMonitoredHost(hostId); - hostListener = new VmGcHostListener(vmGcStats, attachToNewProcessByDefault()); } catch (MonitorException me) { LOGGER.log(Level.WARNING, "Problems with connecting jvmstat to local machine", me); } catch (URISyntaxException use) { @@ -80,28 +88,22 @@ } } + // Methods from Backend + @Override public boolean activate() { - if (!started && host != null) { - try { - host.addHostListener(hostListener); - started = true; - } catch (MonitorException me) { - LOGGER.log(Level.WARNING, "problems with connecting jvmstat to local machine", me); - } + if (!started) { + registerer.register(this); + started = true; } return started; } @Override public boolean deactivate() { - if (started && host != null) { - try { - host.removeHostListener(hostListener); - started = false; - } catch (MonitorException me) { - LOGGER.log(Level.INFO, "something went wrong in jvmstat's listening to this host"); - } + if (started) { + registerer.unregister(this); + started = false; } return !started; } @@ -126,11 +128,76 @@ return ORDER_MEMORY_GROUP + 20; } + // Methods from VmStatusListener + + @Override + public void vmStatusChanged(Status newStatus, int pid) { + switch (newStatus) { + case VM_STARTED: + /* fall-through */ + case VM_ACTIVE: + vmStarted(pid); + break; + case VM_STOPPED: + vmStopped(pid); + break; + } + } + + private void vmStarted(int pid) { + if (attachToNewProcessByDefault()) { + try { + MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve(new VmIdentifier(String.valueOf(pid)))); + if (vm != null) { + VmGcVmListener listener = new VmGcVmListener(vmGcStats, pid); + vm.addVmListener(listener); + registeredListeners.put(pid, new VmAndListener(vm, listener)); + LOGGER.finer("Attached VmListener for VM: " + pid); + } else { + LOGGER.warning("could not connect to vm " + pid); + } + } catch (MonitorException me) { + LOGGER.log(Level.WARNING, "could not connect to vm " + pid, me); + } catch (URISyntaxException e) { + throw new AssertionError("The URI for the monitored vm must be valid, but it is not."); + } + } + } + + private void vmStopped(int pid) { + VmAndListener tuple = registeredListeners.remove(pid); + if (tuple == null) { + LOGGER.warning("received vm stopped for an unknown VM"); + return; + } + + MonitoredVm vm = tuple.vm; + VmListener listener = tuple.listener; + try { + if (listener != null) { + vm.removeVmListener(listener); + } + } catch (MonitorException e) { + LOGGER.log(Level.WARNING, "can't remove vm listener", e); + } + vm.detach(); + } + /* * For testing purposes only. */ void setHost(MonitoredHost host) { this.host = host; } - + + private static class VmAndListener { + private MonitoredVm vm; + private VmListener listener; + + public VmAndListener(MonitoredVm vm, VmListener listener) { + this.vm = vm; + this.listener = listener; + } + + } } diff -r 6815308bb362 -r bf46bf74d05c vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcHostListener.java --- a/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcHostListener.java Tue Jan 15 23:00:52 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,171 +0,0 @@ -/* - * Copyright 2013 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 - * . - * - * 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.vm.gc.agent.internal; - -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -import sun.jvmstat.monitor.MonitorException; -import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.MonitoredVm; -import sun.jvmstat.monitor.VmIdentifier; -import sun.jvmstat.monitor.event.HostEvent; -import sun.jvmstat.monitor.event.HostListener; -import sun.jvmstat.monitor.event.VmListener; -import sun.jvmstat.monitor.event.VmStatusChangeEvent; - -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.vm.gc.common.VmGcStatDAO; - -public class VmGcHostListener implements HostListener { - - private static final Logger logger = LoggingUtils.getLogger(VmGcHostListener.class); - - private boolean attachNew; - - private final VmGcStatDAO vmGcStatDAO; - - private Map monitoredVms = new HashMap<>(); - private Map registeredListeners = new ConcurrentHashMap<>(); - - VmGcHostListener(VmGcStatDAO vmGcStatDAO, boolean attachNew) { - this.vmGcStatDAO = vmGcStatDAO; - this.attachNew = attachNew; - } - - void removeAllListeners() { - for (MonitoredVm vm : monitoredVms.values()) { - VmListener listener = registeredListeners.get(vm); - try { - if (listener != null) { - vm.removeVmListener(listener); - } - } catch (MonitorException e) { - logger.log(Level.WARNING, "can't remove vm listener", e); - } - } - } - - @Override - public void disconnected(HostEvent event) { - logger.warning("Disconnected from host"); - } - - @SuppressWarnings("unchecked") // Unchecked casts to (Set). - @Override - public void vmStatusChanged(VmStatusChangeEvent event) { - MonitoredHost host = event.getMonitoredHost(); - - for (Integer newVm : (Set) event.getStarted()) { - try { - logger.fine("New vm: " + newVm); - sendNewVM(newVm, host); - } catch (MonitorException e) { - logger.log(Level.WARNING, "error getting info for new vm" + newVm, e); - } catch (URISyntaxException e) { - logger.log(Level.WARNING, "error getting info for new vm" + newVm, e); - } - } - - for (Integer stoppedVm : (Set) event.getTerminated()) { - try { - logger.fine("stopped vm: " + stoppedVm); - sendStoppedVM(stoppedVm, host); - } catch (URISyntaxException e) { - logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e); - } catch (MonitorException e) { - logger.log(Level.WARNING, "error getting info for stopped vm" + stoppedVm, e); - } - } - } - - private void sendNewVM(Integer vmId, MonitoredHost host) - throws MonitorException, URISyntaxException { - MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve( - new VmIdentifier(vmId.toString()))); - if (vm != null) { - if (attachNew) { - VmGcVmListener listener = new VmGcVmListener(vmGcStatDAO, vmId); - vm.addVmListener(listener); - - registeredListeners.put(vm, listener); - logger.finer("Attached VmListener for VM: " + vmId); - } else { - logger.log(Level.FINE, "skipping new vm " + vmId); - } - - monitoredVms.put(vmId, vm); - } - } - - private void sendStoppedVM(Integer vmId, MonitoredHost host) throws URISyntaxException, MonitorException { - - VmIdentifier resolvedVmID = host.getHostIdentifier().resolve(new VmIdentifier(vmId.toString())); - if (resolvedVmID != null) { - MonitoredVm vm = monitoredVms.remove(vmId); - VmListener listener = registeredListeners.remove(vm); - try { - if (listener != null) { - vm.removeVmListener(listener); - } - } catch (MonitorException e) { - logger.log(Level.WARNING, "can't remove vm listener", e); - } - vm.detach(); - } - } - - /* - * For testing purposes only. - */ - Map getMonitoredVms() { - return monitoredVms; - } - - /* - * For testing purposes only. - */ - Map getRegisteredListeners() { - return registeredListeners; - } - -} diff -r 6815308bb362 -r bf46bf74d05c vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackendTest.java --- a/vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackendTest.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackendTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -38,7 +38,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; +import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -47,36 +47,59 @@ import org.junit.Before; import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import sun.jvmstat.monitor.HostIdentifier; import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.event.HostListener; +import sun.jvmstat.monitor.MonitoredVm; +import sun.jvmstat.monitor.VmIdentifier; +import com.redhat.thermostat.agent.VmStatusListenerRegistrar; +import com.redhat.thermostat.agent.VmStatusListener.Status; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.vm.gc.common.VmGcStatDAO; public class VmGcBackendTest { - + private VmGcBackend backend; + private VmStatusListenerRegistrar registerer; + private MonitoredHost host; + private HostIdentifier hostIdentifier; + private MonitoredVm monitoredVm1; @Before public void setup() throws MonitorException, URISyntaxException { VmGcStatDAO vmGcStatDao = mock(VmGcStatDAO.class); - + Version version = mock(Version.class); when(version.getVersionNumber()).thenReturn("0.0.0"); - - backend = new VmGcBackend(vmGcStatDao, version); - + + registerer = mock(VmStatusListenerRegistrar.class); + + hostIdentifier = mock(HostIdentifier.class); + when(hostIdentifier.resolve(isA(VmIdentifier.class))).then(new Answer() { + @Override + public VmIdentifier answer(InvocationOnMock invocation) throws Throwable { + return (VmIdentifier) invocation.getArguments()[0]; + } + }); host = mock(MonitoredHost.class); + when(host.getHostIdentifier()).thenReturn(hostIdentifier); + + monitoredVm1 = mock(MonitoredVm.class); + + backend = new VmGcBackend(vmGcStatDao, version, registerer); + backend.setHost(host); } @Test public void testStart() throws MonitorException { backend.activate(); - verify(host).addHostListener(any(HostListener.class)); + verify(registerer).register(backend); assertTrue(backend.isActive()); } @@ -84,8 +107,31 @@ public void testStop() throws MonitorException { backend.activate(); backend.deactivate(); - verify(host).removeHostListener(any(HostListener.class)); + verify(registerer).unregister(backend); assertFalse(backend.isActive()); } - + + @Test + public void testNewVM() throws InterruptedException, MonitorException, URISyntaxException { + int VM_PID = 1; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm1); + + backend.vmStatusChanged(Status.VM_STARTED, 1); + + verify(monitoredVm1).addVmListener(isA(VmGcVmListener.class)); + } + + @Test + public void testStoppedVM() throws InterruptedException, MonitorException, URISyntaxException { + int VM_PID = 1; + VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); + when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm1); + + backend.vmStatusChanged(Status.VM_STARTED, 1); + backend.vmStatusChanged(Status.VM_STOPPED, 1); + + verify(monitoredVm1).removeVmListener(isA(VmGcVmListener.class)); + } + } diff -r 6815308bb362 -r bf46bf74d05c vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcHostListenerTest.java --- a/vm-gc/agent/src/test/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcHostListenerTest.java Tue Jan 15 23:00:52 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -/* - * Copyright 2013 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 - * . - * - * 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.vm.gc.agent.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.net.URISyntaxException; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.Before; -import org.junit.Test; - -import sun.jvmstat.monitor.HostIdentifier; -import sun.jvmstat.monitor.MonitorException; -import sun.jvmstat.monitor.MonitoredHost; -import sun.jvmstat.monitor.MonitoredVm; -import sun.jvmstat.monitor.VmIdentifier; -import sun.jvmstat.monitor.event.VmStatusChangeEvent; - -import com.redhat.thermostat.vm.gc.common.VmGcStatDAO; - -public class VmGcHostListenerTest { - - private VmGcHostListener hostListener; - private MonitoredHost host; - private MonitoredVm monitoredVm1; - private MonitoredVm monitoredVm2; - - @Before - public void setup() throws MonitorException, URISyntaxException { - VmGcStatDAO vmGcStatDAO = mock(VmGcStatDAO.class); - hostListener = new VmGcHostListener(vmGcStatDAO, true); - - host = mock(MonitoredHost.class); - HostIdentifier hostId = mock(HostIdentifier.class); - monitoredVm1 = mock(MonitoredVm.class); - monitoredVm2 = mock(MonitoredVm.class); - VmIdentifier vmId1 = new VmIdentifier("1"); - VmIdentifier vmId2 = new VmIdentifier("2"); - when(host.getHostIdentifier()).thenReturn(hostId); - when(host.getMonitoredVm(eq(vmId1))).thenReturn(monitoredVm1); - when(host.getMonitoredVm(eq(vmId2))).thenReturn(monitoredVm2); - when(hostId.resolve(eq(vmId1))).thenReturn(vmId1); - when(hostId.resolve(eq(vmId2))).thenReturn(vmId2); - } - - @Test - public void testNewVM() throws InterruptedException, MonitorException { - startVMs(); - - assertTrue(hostListener.getMonitoredVms().containsKey(1)); - assertTrue(hostListener.getMonitoredVms().containsKey(2)); - assertEquals(monitoredVm1, hostListener.getMonitoredVms().get(1)); - assertEquals(monitoredVm2, hostListener.getMonitoredVms().get(2)); - - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm1)); - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm2)); - } - - @Test - public void testStoppedVM() throws InterruptedException, MonitorException { - final Set stopped = new HashSet<>(); - stopped.add(1); - - startVMs(); - - // Trigger a change event - VmStatusChangeEvent event = mock(VmStatusChangeEvent.class); - when(event.getMonitoredHost()).thenReturn(host); - when(event.getStarted()).thenReturn(Collections.emptySet()); - when(event.getTerminated()).thenReturn(stopped); - hostListener.vmStatusChanged(event); - - // Ensure only 1 removed - assertFalse(hostListener.getMonitoredVms().containsKey(1)); - assertTrue(hostListener.getMonitoredVms().containsKey(2)); - assertEquals(monitoredVm2, hostListener.getMonitoredVms().get(2)); - - assertFalse(hostListener.getRegisteredListeners().containsKey(monitoredVm1)); - assertTrue(hostListener.getRegisteredListeners().containsKey(monitoredVm2)); - } - - private void startVMs() throws InterruptedException, MonitorException { - final Set started = new HashSet<>(); - started.add(1); - started.add(2); - - // Trigger a change event - VmStatusChangeEvent event = mock(VmStatusChangeEvent.class); - when(event.getMonitoredHost()).thenReturn(host); - when(event.getStarted()).thenReturn(started); - when(event.getTerminated()).thenReturn(Collections.emptySet()); - hostListener.vmStatusChanged(event); - } -} diff -r 6815308bb362 -r bf46bf74d05c vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/VmGcViewProvider.java --- a/vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/VmGcViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-gc/client-core/src/main/java/com/redhat/thermostat/vm/gc/client/core/VmGcViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,8 +36,10 @@ package com.redhat.thermostat.vm.gc.client.core; +import com.redhat.thermostat.annotations.ExtensionPoint; import com.redhat.thermostat.client.core.views.ViewProvider; +@ExtensionPoint public interface VmGcViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c vm-gc/common/src/main/java/com/redhat/thermostat/vm/gc/common/VmGcStatDAO.java --- a/vm-gc/common/src/main/java/com/redhat/thermostat/vm/gc/common/VmGcStatDAO.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-gc/common/src/main/java/com/redhat/thermostat/vm/gc/common/VmGcStatDAO.java Tue Jan 15 23:04:57 2013 +0100 @@ -38,11 +38,13 @@ import java.util.List; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.common.dao.VmRef; import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Key; import com.redhat.thermostat.storage.model.VmGcStat; +@Service public interface VmGcStatDAO { static final Key collectorKey = new Key<>("collectorName", false); diff -r 6815308bb362 -r bf46bf74d05c vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapDumpDetailsViewProvider.java --- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapDumpDetailsViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapDumpDetailsViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,8 +36,10 @@ package com.redhat.thermostat.vm.heap.analysis.client.core; +import com.redhat.thermostat.annotations.ExtensionPoint; import com.redhat.thermostat.client.core.views.ViewProvider; +@ExtensionPoint public interface HeapDumpDetailsViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapHistogramViewProvider.java --- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapHistogramViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapHistogramViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,8 +36,10 @@ package com.redhat.thermostat.vm.heap.analysis.client.core; +import com.redhat.thermostat.annotations.ExtensionPoint; import com.redhat.thermostat.client.core.views.ViewProvider; +@ExtensionPoint public interface HeapHistogramViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapViewProvider.java --- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/HeapViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,8 +36,10 @@ package com.redhat.thermostat.vm.heap.analysis.client.core; +import com.redhat.thermostat.annotations.ExtensionPoint; import com.redhat.thermostat.client.core.views.ViewProvider; +@ExtensionPoint public interface HeapViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/ObjectDetailsViewProvider.java --- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/ObjectDetailsViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/ObjectDetailsViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,8 +36,10 @@ package com.redhat.thermostat.vm.heap.analysis.client.core; +import com.redhat.thermostat.annotations.ExtensionPoint; import com.redhat.thermostat.client.core.views.ViewProvider; +@ExtensionPoint public interface ObjectDetailsViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/ObjectRootsViewProvider.java --- a/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/ObjectRootsViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-heap-analysis/client-core/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/core/ObjectRootsViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,8 +36,10 @@ package com.redhat.thermostat.vm.heap.analysis.client.core; +import com.redhat.thermostat.annotations.ExtensionPoint; import com.redhat.thermostat.client.core.views.ViewProvider; +@ExtensionPoint public interface ObjectRootsViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c vm-heap-analysis/common/src/main/java/com/redhat/thermostat/vm/heap/analysis/common/HeapDAO.java --- a/vm-heap-analysis/common/src/main/java/com/redhat/thermostat/vm/heap/analysis/common/HeapDAO.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-heap-analysis/common/src/main/java/com/redhat/thermostat/vm/heap/analysis/common/HeapDAO.java Tue Jan 15 23:04:57 2013 +0100 @@ -41,11 +41,13 @@ import java.io.InputStream; import java.util.Collection; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.common.dao.VmRef; import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Key; import com.redhat.thermostat.storage.model.HeapInfo; +@Service public interface HeapDAO { static final Key heapIdKey = new Key("heapId", false); diff -r 6815308bb362 -r bf46bf74d05c vm-memory/client-cli/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-cli/pom.xml Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,77 @@ + + + 4.0.0 + + thermostat-vm-memory + com.redhat.thermostat + 0.5.0-SNAPSHOT + + thermostat-vm-memory-client-cli + bundle + Thermostat VM Memory CLI Client plugin + + + + org.apache.felix + maven-bundle-plugin + true + + + + com.redhat.thermostat.vm.memory.client.cli.internal + + Red Hat, Inc. + com.redhat.thermostat.vm.memory.client.cli.internal.Activator + com.redhat.thermostat.vm.memory.client.cli + + <_nouses>true + + + + + + + + + junit + junit + test + + + org.mockito + mockito-core + test + + + org.osgi + org.osgi.core + provided + + + org.osgi + org.osgi.compendium + provided + + + com.redhat.thermostat + thermostat-common-core + ${project.version} + + + com.redhat.thermostat + thermostat-vm-memory-common + ${project.version} + + + com.redhat.thermostat + thermostat-common-test + ${project.version} + test + + + com.redhat.thermostat + thermostat-client-cli + ${project.version} + + + diff -r 6815308bb362 -r bf46bf74d05c vm-memory/client-cli/src/main/java/com/redhat/thermostat/vm/memory/client/cli/internal/Activator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-cli/src/main/java/com/redhat/thermostat/vm/memory/client/cli/internal/Activator.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,80 @@ +/* + * Copyright 2013 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 + * . + * + * 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.vm.memory.client.cli.internal; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.util.tracker.ServiceTracker; + +import com.redhat.thermostat.client.cli.VMStatPrintDelegate; +import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; + +public class Activator implements BundleActivator { + + private ServiceTracker tracker; + private ServiceRegistration reg; + + @Override + public void start(BundleContext context) throws Exception { + tracker = new ServiceTracker(context, VmMemoryStatDAO.class.getName(), null) { + @Override + public Object addingService(ServiceReference reference) { + VmMemoryStatDAO vmMemoryStatDAO = (VmMemoryStatDAO) super.addingService(reference); + VmMemoryStatPrintDelegate delegate = new VmMemoryStatPrintDelegate(vmMemoryStatDAO); + reg = context.registerService(VMStatPrintDelegate.class.getName(), delegate, null); + return vmMemoryStatDAO; + } + + @Override + public void removedService(ServiceReference reference, + Object service) { + reg.unregister(); + super.removedService(reference, service); + } + }; + + tracker.open(); + } + + @Override + public void stop(BundleContext context) throws Exception { + tracker.close(); + } + +} diff -r 6815308bb362 -r bf46bf74d05c vm-memory/client-cli/src/main/java/com/redhat/thermostat/vm/memory/client/cli/internal/LocaleResources.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-cli/src/main/java/com/redhat/thermostat/vm/memory/client/cli/internal/LocaleResources.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,53 @@ +/* + * Copyright 2013 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 + * . + * + * 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.vm.memory.client.cli.internal; + +import com.redhat.thermostat.common.locale.Translate; + +public enum LocaleResources { + + VALUE_AND_UNIT, + + COLUMN_HEADER_MEMORY_PATTERN, + ; + + static final String RESOURCE_BUNDLE = "com.redhat.thermostat.vm.memory.client.cli.strings"; + + public static Translate createLocalizer() { + return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class); + } +} diff -r 6815308bb362 -r bf46bf74d05c vm-memory/client-cli/src/main/java/com/redhat/thermostat/vm/memory/client/cli/internal/VmMemoryStatPrintDelegate.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-cli/src/main/java/com/redhat/thermostat/vm/memory/client/cli/internal/VmMemoryStatPrintDelegate.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,101 @@ +/* + * Copyright 2013 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 + * . + * + * 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.vm.memory.client.cli.internal; + +import java.util.ArrayList; +import java.util.List; + +import com.redhat.thermostat.client.cli.VMStatPrintDelegate; +import com.redhat.thermostat.common.Size; +import com.redhat.thermostat.common.dao.VmRef; +import com.redhat.thermostat.common.locale.Translate; +import com.redhat.thermostat.storage.model.TimeStampedPojo; +import com.redhat.thermostat.storage.model.VmMemoryStat; +import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; + +public class VmMemoryStatPrintDelegate implements VMStatPrintDelegate { + + private static final Translate translator = LocaleResources.createLocalizer(); + + private VmMemoryStatDAO memoryStatDAO; + + public VmMemoryStatPrintDelegate(VmMemoryStatDAO memoryStatDAO) { + this.memoryStatDAO = memoryStatDAO; + } + + @Override + public List getLatestStats(VmRef ref, long timestamp) { + return memoryStatDAO.getLatestVmMemoryStats(ref, timestamp); + } + + @Override + public List getHeaders(TimeStampedPojo stat) { + return getSpacesNames(stat); + } + + @Override + public List getStatRow(TimeStampedPojo stat) { + return getMemoryUsage(stat); + } + + private List getSpacesNames(TimeStampedPojo stat) { + List spacesNames = new ArrayList<>(); + VmMemoryStat memStat = (VmMemoryStat) stat; + for (VmMemoryStat.Generation gen : memStat.getGenerations()) { + for (VmMemoryStat.Space space : gen.getSpaces()) { + spacesNames.add(translator.localize(LocaleResources.COLUMN_HEADER_MEMORY_PATTERN, space.getName())); + } + } + return spacesNames; + } + + private List getMemoryUsage(TimeStampedPojo stat) { + List memoryUsage = new ArrayList<>(); + for (VmMemoryStat.Generation gen : ((VmMemoryStat) stat).getGenerations()) { + for (VmMemoryStat.Space space : gen.getSpaces()) { + memoryUsage.add(Size.bytes(space.getUsed()).toString()); + } + } + return memoryUsage; + } + + @Override + public int getOrderValue() { + return ORDER_MEMORY_GROUP; + } + +} diff -r 6815308bb362 -r bf46bf74d05c vm-memory/client-cli/src/main/resources/com/redhat/thermostat/vm/memory/client/cli/strings.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-cli/src/main/resources/com/redhat/thermostat/vm/memory/client/cli/strings.properties Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,3 @@ +VALUE_AND_UNIT = {0} {1} + +COLUMN_HEADER_MEMORY_PATTERN = MEM.{0} \ No newline at end of file diff -r 6815308bb362 -r bf46bf74d05c vm-memory/client-cli/src/test/java/com/redhat/thermostat/vm/memory/client/cli/internal/ActivatorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-cli/src/test/java/com/redhat/thermostat/vm/memory/client/cli/internal/ActivatorTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,84 @@ +/* + * Copyright 2013 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 + * . + * + * 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.vm.memory.client.cli.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import org.junit.Test; + +import com.redhat.thermostat.client.cli.VMStatPrintDelegate; +import com.redhat.thermostat.test.StubBundleContext; +import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; + +public class ActivatorTest { + + @Test + public void verifyActivatorDoesNotRegisterServiceOnMissingDeps() throws Exception { + StubBundleContext context = new StubBundleContext(); + + Activator activator = new Activator(); + + activator.start(context); + + assertEquals(0, context.getAllServices().size()); + assertEquals(1, context.getServiceListeners().size()); + + activator.stop(context); + } + + @Test + public void verifyActivatorRegistersServices() throws Exception { + StubBundleContext context = new StubBundleContext(); + VmMemoryStatDAO dao = mock(VmMemoryStatDAO.class); + + context.registerService(VmMemoryStatDAO.class, dao, null); + + Activator activator = new Activator(); + + activator.start(context); + + assertTrue(context.isServiceRegistered(VMStatPrintDelegate.class.getName(), VmMemoryStatPrintDelegate.class)); + + activator.stop(context); + + assertEquals(0, context.getServiceListeners().size()); + assertEquals(1, context.getAllServices().size()); + } + +} diff -r 6815308bb362 -r bf46bf74d05c vm-memory/client-cli/src/test/java/com/redhat/thermostat/vm/memory/client/cli/internal/LocaleResourcesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-cli/src/test/java/com/redhat/thermostat/vm/memory/client/cli/internal/LocaleResourcesTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,53 @@ +/* + * Copyright 2013 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 + * . + * + * 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.vm.memory.client.cli.internal; + +import com.redhat.thermostat.test.locale.AbstractLocaleResourcesTest; + +public class LocaleResourcesTest extends AbstractLocaleResourcesTest { + + @Override + protected Class getEnumClass() { + return LocaleResources.class; + } + + @Override + protected String getResourceBundle() { + return LocaleResources.RESOURCE_BUNDLE; + } + +} diff -r 6815308bb362 -r bf46bf74d05c vm-memory/client-cli/src/test/java/com/redhat/thermostat/vm/memory/client/cli/internal/VmMemoryStatPrintDelegateTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm-memory/client-cli/src/test/java/com/redhat/thermostat/vm/memory/client/cli/internal/VmMemoryStatPrintDelegateTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -0,0 +1,172 @@ +/* + * Copyright 2013 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 + * . + * + * 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.vm.memory.client.cli.internal; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.client.cli.VMStatPrintDelegate; +import com.redhat.thermostat.common.cli.CommandException; +import com.redhat.thermostat.common.dao.HostRef; +import com.redhat.thermostat.common.dao.VmRef; +import com.redhat.thermostat.storage.model.TimeStampedPojo; +import com.redhat.thermostat.storage.model.VmMemoryStat; +import com.redhat.thermostat.storage.model.VmMemoryStat.Generation; +import com.redhat.thermostat.storage.model.VmMemoryStat.Space; +import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; + +public class VmMemoryStatPrintDelegateTest { + + private VmMemoryStatDAO vmMemoryStatDAO; + private VMStatPrintDelegate delegate; + private VmRef vm; + private List memoryStats; + + @Before + public void setUp() { + setupDAOs(); + delegate = new VmMemoryStatPrintDelegate(vmMemoryStatDAO); + } + + @After + public void tearDown() { + vmMemoryStatDAO = null; + } + + private void setupDAOs() { + int vmId = 234; + HostRef host = new HostRef("123", "dummy"); + vm = new VmRef(host, 234, "dummy"); + + VmMemoryStat.Space space1_1_1 = newSpace("space1", 123456, 12345, 1, 0); + VmMemoryStat.Space space1_1_2 = newSpace("space2", 123456, 12345, 1, 0); + VmMemoryStat.Space[] spaces1_1 = new VmMemoryStat.Space[] { space1_1_1, space1_1_2 }; + VmMemoryStat.Generation gen1_1 = newGeneration("gen1", "col1", 123456, 12345, spaces1_1); + + VmMemoryStat.Space space1_2_1 = newSpace("space3", 123456, 12345, 1, 0); + VmMemoryStat.Space space1_2_2 = newSpace("space4", 123456, 12345, 1, 0); + VmMemoryStat.Space[] spaces1_2 = new VmMemoryStat.Space[] { space1_2_1, space1_2_2 }; + VmMemoryStat.Generation gen1_2 = newGeneration("gen2", "col1", 123456, 12345, spaces1_2); + + VmMemoryStat.Generation[] gens1 = new VmMemoryStat.Generation[] { gen1_1, gen1_2 }; + + VmMemoryStat memStat1 = new VmMemoryStat(1, vmId, gens1); + + VmMemoryStat.Space space2_1_1 = newSpace("space1", 123456, 12345, 2, 0); + VmMemoryStat.Space space2_1_2 = newSpace("space2", 123456, 12345, 2, 0); + VmMemoryStat.Space[] spaces2_1 = new VmMemoryStat.Space[] { space2_1_1, space2_1_2 }; + VmMemoryStat.Generation gen2_1 = newGeneration("gen1", "col1", 123456, 12345, spaces2_1); + + VmMemoryStat.Space space2_2_1 = newSpace("space3", 123456, 12345, 3, 0); + VmMemoryStat.Space space2_2_2 = newSpace("space4", 123456, 12345, 4, 0); + VmMemoryStat.Space[] spaces2_2 = new VmMemoryStat.Space[] { space2_2_1, space2_2_2 }; + VmMemoryStat.Generation gen2_2 = newGeneration("gen2", "col1", 123456, 12345, spaces2_2); + + VmMemoryStat.Generation[] gens2 = new VmMemoryStat.Generation[] { gen2_1, gen2_2 }; + + VmMemoryStat memStat2 = new VmMemoryStat(2, vmId, gens2); + + VmMemoryStat.Space space3_1_1 = newSpace("space1", 123456, 12345, 4, 0); + VmMemoryStat.Space space3_1_2 = newSpace("space2", 123456, 12345, 5, 0); + VmMemoryStat.Space[] spaces3_1 = new VmMemoryStat.Space[] { space3_1_1, space3_1_2 }; + VmMemoryStat.Generation gen3_1 = newGeneration("gen1", "col1", 123456, 12345, spaces3_1); + + VmMemoryStat.Space space3_2_1 = newSpace("space3", 123456, 12345, 6, 0); + VmMemoryStat.Space space3_2_2 = newSpace("space4", 123456, 12345, 7, 0); + VmMemoryStat.Space[] spaces3_2 = new VmMemoryStat.Space[] { space3_2_1, space3_2_2 }; + VmMemoryStat.Generation gen3_2 = newGeneration("gen2", "col1", 123456, 12345, spaces3_2); + + VmMemoryStat.Generation[] gens3 = new VmMemoryStat.Generation[] { gen3_1, gen3_2 }; + + VmMemoryStat memStat3 = new VmMemoryStat(3, vmId, gens3); + + vmMemoryStatDAO = mock(VmMemoryStatDAO.class); + memoryStats = Arrays.asList(memStat1, memStat2, memStat3); + when(vmMemoryStatDAO.getLatestVmMemoryStats(vm, Long.MIN_VALUE)) + .thenReturn(memoryStats); + } + + private Space newSpace(String name, long maxCapacity, long capacity, long used, int index) { + VmMemoryStat.Space space = new VmMemoryStat.Space(); + space.setName(name); + space.setMaxCapacity(maxCapacity); + space.setCapacity(capacity); + space.setUsed(used); + space.setIndex(index); + return space; + } + + private Generation newGeneration(String name, String collector, long maxCapacity, long capacity, Space[] spaces) { + VmMemoryStat.Generation gen = new VmMemoryStat.Generation(); + gen.setName(name); + gen.setCollector(collector); + gen.setMaxCapacity(capacity); + gen.setSpaces(spaces); + return gen; + } + + @Test + public void testGetLatestStats() { + List stats = delegate.getLatestStats(vm, Long.MIN_VALUE); + assertEquals(memoryStats, stats); + } + + @Test + public void testGetHeaders() { + List headers = delegate.getHeaders(memoryStats.get(0)); + assertEquals(Arrays.asList("MEM.space1", "MEM.space2", "MEM.space3", "MEM.space4"), headers); + } + + @Test + public void testGetStatRow() throws CommandException { + final List row1 = Arrays.asList("1 B", "1 B", "1 B", "1 B"); + final List row2 = Arrays.asList("2 B", "2 B", "3 B", "4 B"); + final List row3 = Arrays.asList("4 B", "5 B", "6 B", "7 B"); + assertEquals(row1, delegate.getStatRow(memoryStats.get(0))); + assertEquals(row2, delegate.getStatRow(memoryStats.get(1))); + assertEquals(row3, delegate.getStatRow(memoryStats.get(2))); + } + +} diff -r 6815308bb362 -r bf46bf74d05c vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/MemoryStatsViewProvider.java --- a/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/MemoryStatsViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-memory/client-core/src/main/java/com/redhat/thermostat/vm/memory/client/core/MemoryStatsViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,8 +36,10 @@ package com.redhat.thermostat.vm.memory.client.core; +import com.redhat.thermostat.annotations.ExtensionPoint; import com.redhat.thermostat.client.core.views.ViewProvider; +@ExtensionPoint public interface MemoryStatsViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/VmMemoryStatDAO.java --- a/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/VmMemoryStatDAO.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-memory/common/src/main/java/com/redhat/thermostat/vm/memory/common/VmMemoryStatDAO.java Tue Jan 15 23:04:57 2013 +0100 @@ -38,12 +38,14 @@ import java.util.List; +import com.redhat.thermostat.annotations.Service; import com.redhat.thermostat.common.dao.VmRef; import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Key; import com.redhat.thermostat.storage.model.VmMemoryStat; import com.redhat.thermostat.storage.model.VmMemoryStat.Generation; +@Service public interface VmMemoryStatDAO { static final Key generationsKey = new Key<>("generations", false); diff -r 6815308bb362 -r bf46bf74d05c vm-memory/pom.xml --- a/vm-memory/pom.xml Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-memory/pom.xml Tue Jan 15 23:04:57 2013 +0100 @@ -52,6 +52,7 @@ agent + client-cli client-core client-swing common diff -r 6815308bb362 -r bf46bf74d05c vm-overview/client-core/src/main/java/com/redhat/thermostat/vm/overview/client/core/VmOverviewViewProvider.java --- a/vm-overview/client-core/src/main/java/com/redhat/thermostat/vm/overview/client/core/VmOverviewViewProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/vm-overview/client-core/src/main/java/com/redhat/thermostat/vm/overview/client/core/VmOverviewViewProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -36,8 +36,10 @@ package com.redhat.thermostat.vm.overview.client.core; +import com.redhat.thermostat.annotations.ExtensionPoint; import com.redhat.thermostat.client.core.views.ViewProvider; +@ExtensionPoint public interface VmOverviewViewProvider extends ViewProvider { @Override diff -r 6815308bb362 -r bf46bf74d05c web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebStorageProvider.java --- a/web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebStorageProvider.java Tue Jan 15 23:00:52 2013 +0100 +++ b/web/client/src/main/java/com/redhat/thermostat/web/client/internal/WebStorageProvider.java Tue Jan 15 23:04:57 2013 +0100 @@ -1,3 +1,39 @@ +/* + * 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 + * . + * + * 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.web.client.internal; import com.redhat.thermostat.storage.config.AuthenticationConfiguration; diff -r 6815308bb362 -r bf46bf74d05c web/common/src/test/java/com/redhat/thermostat/web/common/WebQueryTest.java --- a/web/common/src/test/java/com/redhat/thermostat/web/common/WebQueryTest.java Tue Jan 15 23:00:52 2013 +0100 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/WebQueryTest.java Tue Jan 15 23:04:57 2013 +0100 @@ -1,17 +1,3 @@ -package com.redhat.thermostat.web.common; -import static org.junit.Assert.assertEquals; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Test; - -import com.redhat.thermostat.storage.core.Category; -import com.redhat.thermostat.storage.core.Key; -import com.redhat.thermostat.storage.core.Query.Criteria; -import com.redhat.thermostat.storage.model.Pojo; - /* * Copyright 2012 Red Hat, Inc. * @@ -48,6 +34,20 @@ * to do so, delete this exception statement from your version. */ +package com.redhat.thermostat.web.common; +import static org.junit.Assert.assertEquals; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import com.redhat.thermostat.storage.core.Category; +import com.redhat.thermostat.storage.core.Key; +import com.redhat.thermostat.storage.core.Query.Criteria; +import com.redhat.thermostat.storage.model.Pojo; + public class WebQueryTest { private static class TestObj implements Pojo {