From 52882c7b2e544aafda1256b1fb9580dc490eb957 Mon Sep 17 00:00:00 2001 From: Nick ten Veen Date: Fri, 16 Jan 2015 22:55:15 +0100 Subject: [PATCH] Reengineered Alitheia Core --- .gitignore | 4 + alitheia/core/pom.xml | 3 +- .../java/eu/sqooss/core/AlitheiaCore.java | 233 +++-- .../java/eu/sqooss/core/CoreActivator.java | 3 +- .../sqooss/impl/service/db/DBServiceImpl.java | 4 + .../impl/service/logging/LogManagerImpl.java | 2 +- .../metricactivator/MetricActivatorImpl.java | 34 +- .../metricactivator/MetricActivatorJob.java | 6 +- .../service/updater/UpdaterServiceImpl.java | 13 +- .../abstractmetric/AbstractMetric.java | 945 ------------------ .../abstractmetric/AlitheiaPlugin.java | 211 +--- .../DefaultMeasurementEvaluation.java | 282 ++++++ .../service/abstractmetric/DefaultMetric.java | 429 ++++++++ .../DefaultMetricConfiguration.java | 289 ++++++ .../DefaultMetricLifeCycle.java | 112 +++ .../abstractmetric/DefaultMetricMetaData.java | 68 ++ .../abstractmetric/MetricConfiguration.java | 98 ++ .../abstractmetric/MetricDiscovery.java | 116 +++ .../abstractmetric/MetricLifeCycle.java | 27 + .../MetricMeasurementEvaluation.java | 77 ++ .../abstractmetric/MetricMetaData.java | 40 + .../java/eu/sqooss/service/util/Graph.java | 151 +++ .../eu/sqooss/service/util/GraphSorter.java | 21 + .../java/eu/sqooss/service/util/GraphTS.java | 174 ---- .../service/util/ProjectVersionDateUtils.java | 47 + .../service/util/TopologicalGraphSorter.java | 98 ++ .../util/TopologicalSortedGraphTest.java | 56 ++ .../java/eu/sqooss/core/AlitheiaCoreTest.java | 66 ++ .../test/java/eu/sqooss/core/MockService.java | 24 + .../eu/sqooss/core/MockServiceExtended.java | 6 + .../test/service/scheduler/Testester.java | 13 + metrics/contrib/maven-eclipse.xml | 8 + .../contrib/ContributionMetricImpl.java | 5 +- .../developermetrics/Developermetrics.java | 4 +- .../discussionheat/DiscussionHeat.java | 46 +- .../metrics/findbugs/FindbugsMetrics.java | 2 +- .../eu/sqooss/metrics/java/JavaMetrics.java | 4 +- .../main/java/eu/sqooss/metrics/mi/Mi.java | 52 +- .../ModuleMetricsImplementation.java | 51 +- .../sqooss/metrics/structural/Structural.java | 4 +- .../TestabilityImplementation.java | 4 +- .../sqooss/metrics/wc/WcImplementation.java | 4 +- 42 files changed, 2229 insertions(+), 1607 deletions(-) delete mode 100644 alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/AbstractMetric.java create mode 100755 alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMeasurementEvaluation.java create mode 100644 alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetric.java create mode 100755 alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetricConfiguration.java create mode 100755 alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetricLifeCycle.java create mode 100755 alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetricMetaData.java create mode 100755 alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricConfiguration.java create mode 100755 alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricDiscovery.java create mode 100755 alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricLifeCycle.java create mode 100755 alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricMeasurementEvaluation.java create mode 100755 alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricMetaData.java create mode 100644 alitheia/core/src/main/java/eu/sqooss/service/util/Graph.java create mode 100644 alitheia/core/src/main/java/eu/sqooss/service/util/GraphSorter.java delete mode 100644 alitheia/core/src/main/java/eu/sqooss/service/util/GraphTS.java create mode 100755 alitheia/core/src/main/java/eu/sqooss/service/util/ProjectVersionDateUtils.java create mode 100644 alitheia/core/src/main/java/eu/sqooss/service/util/TopologicalGraphSorter.java create mode 100644 alitheia/core/src/test/java/eu/sqoooss/service/util/TopologicalSortedGraphTest.java create mode 100644 alitheia/core/src/test/java/eu/sqooss/core/AlitheiaCoreTest.java create mode 100644 alitheia/core/src/test/java/eu/sqooss/core/MockService.java create mode 100644 alitheia/core/src/test/java/eu/sqooss/core/MockServiceExtended.java create mode 100755 alitheia/core/src/test/java/eu/sqooss/test/service/scheduler/Testester.java create mode 100755 metrics/contrib/maven-eclipse.xml diff --git a/.gitignore b/.gitignore index 50b2ed416..6661a4299 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,7 @@ bin/ *.ipr *.iws *.db +doc/javadoc/ +alitheia/runner/ +metrics/contrib/maven-eclipse.xml +lib/ejb3unit-2.0.0-RC-1.jar diff --git a/alitheia/core/pom.xml b/alitheia/core/pom.xml index 4203d2dda..717e3fe60 100644 --- a/alitheia/core/pom.xml +++ b/alitheia/core/pom.xml @@ -176,8 +176,7 @@ 1.9.5 test - - + tools.jar diff --git a/alitheia/core/src/main/java/eu/sqooss/core/AlitheiaCore.java b/alitheia/core/src/main/java/eu/sqooss/core/AlitheiaCore.java index 63610d55f..51d698edc 100644 --- a/alitheia/core/src/main/java/eu/sqooss/core/AlitheiaCore.java +++ b/alitheia/core/src/main/java/eu/sqooss/core/AlitheiaCore.java @@ -38,9 +38,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Vector; import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; import eu.sqooss.impl.service.admin.AdminServiceImpl; import eu.sqooss.impl.service.cluster.ClusterNodeServiceImpl; @@ -86,61 +88,62 @@ public class AlitheiaCore { private static AlitheiaCore instance = null; /** Holds initialised service instances */ - private HashMap, Object> instances; + private Map, AlitheiaCoreService> instances = new HashMap, AlitheiaCoreService>(); - /* Service Configuration */ - private static Vector> services; - private static Map, Class> implementations; - - static { - services = new Vector>(); - implementations = new HashMap, Class>(); - - /* - * Order matters here as services are initialised - * in the order they appear in this list - */ - //The following two services are started manually - //services.add(LogManager.class); - //services.add(DBService.class); - //All services after this point are guaranteed to have access to the DB - services.add(PluginAdmin.class); - services.add(Scheduler.class); - services.add(TDSService.class); - services.add(ClusterNodeService.class); - services.add(FDSService.class); - services.add(MetricActivator.class); - services.add(UpdaterService.class); - services.add(WebadminService.class); - services.add(RestService.class); - services.add(AdminService.class); - - implementations.put(LogManager.class, LogManagerImpl.class); - implementations.put(DBService.class, DBServiceImpl.class); - implementations.put(PluginAdmin.class, PAServiceImpl.class); - implementations.put(Scheduler.class, SchedulerServiceImpl.class); - implementations.put(TDSService.class, TDSServiceImpl.class); - implementations.put(ClusterNodeService.class, ClusterNodeServiceImpl.class); - implementations.put(FDSService.class, FDSServiceImpl.class); - implementations.put(MetricActivator.class, MetricActivatorImpl.class); - implementations.put(UpdaterService.class, UpdaterServiceImpl.class); - implementations.put(WebadminService.class, WebadminServiceImpl.class); - implementations.put(RestService.class, ResteasyServiceImpl.class); - implementations.put(AdminService.class, AdminServiceImpl.class); - } + /** Contains the registered services in the order of initialization */ + private List> services = new ArrayList>(); + + /** Mapping from service to the class which implements this service */ + private Map, Class> implementations = new HashMap, Class>(); + + /** The locally stored SecurityManager instance */ + private SecurityManager securityManager; /** * Simple constructor. * * @param bc The parent bundle's context object. */ - public AlitheiaCore(BundleContext bc) { - this.bc = bc; - instance = this; - err("Instance Created"); - - instances = new HashMap, Object>(); - init(); + private AlitheiaCore() { + } + + private void registerBaseServices(){ + // Create LogManager and DBService before they are initialized + this.createLogManager(); + this.createDBService(); + + this.registerService(LogManager.class, LogManagerImpl.class); + this.registerService(DBService.class, DBServiceImpl.class); + this.registerService(PluginAdmin.class, PAServiceImpl.class); + this.registerService(Scheduler.class, SchedulerServiceImpl.class); + this.registerService(TDSService.class, TDSServiceImpl.class); + this.registerService(ClusterNodeService.class, ClusterNodeServiceImpl.class); + this.registerService(FDSService.class, FDSServiceImpl.class); + this.registerService(MetricActivator.class, MetricActivatorImpl.class); + this.registerService(UpdaterService.class, UpdaterServiceImpl.class); + this.registerService(WebadminService.class, WebadminServiceImpl.class); + this.registerService(RestService.class, ResteasyServiceImpl.class); + this.registerService(AdminService.class, AdminServiceImpl.class); + } + + private void createLogManager(){ + logger = new LogManagerImpl(); + logger.setInitParams(bc, null); + if (!logger.startUp()) { + err("Cannot start the log service, aborting"); + } + instances.put(LogManager.class, logger); + err("Service " + LogManagerImpl.class.getName() + " started"); + } + + private void createDBService() { + DBService db = DBServiceImpl.getInstance(); + db.setInitParams(bc, logger.createLogger("sqooss.db")); + if (!db.startUp()) { + err("Cannot start the DB service, aborting"); + } + instances.put(DBService.class, db); + err("Service " + DBServiceImpl.class.getName() + " started"); } /** @@ -153,13 +156,18 @@ public AlitheiaCore(BundleContext bc) { * @return Instance, or null if it's not initialized yet */ public static AlitheiaCore getInstance() { + if (instance == null) instance = new AlitheiaCore(); return instance; } /*Create a temp instance to use for testing.*/ public static AlitheiaCore testInstance() { - instance = new AlitheiaCore(null); - return instance; + // Create instance + AlitheiaCore testInstance = AlitheiaCore.getInstance(); + // Logger should always exist + testInstance.createLogManager(); + // Return instance + return testInstance; } /** @@ -171,11 +179,26 @@ public static AlitheiaCore testInstance() { */ public synchronized void registerService( Class service, - Class clazz) { + Class implementation) { - if (!services.contains(service)) - services.add(service); - implementations.put(service, clazz); + // Add to the list of services of needed (to remember ordering) + if(!services.contains(service)){ + services.add(service); + } + + // Get the old implementaiton + Class old = implementations.get(service); + + // Add to implementation (or update the implementation) + implementations.put(service, implementation); + + // Check if implementation changed + if(old != null && !old.equals(implementation)){ + // New implementation: remove instance + instances.remove(service); + } + + // Initialize the service initService(service); } @@ -186,6 +209,7 @@ public synchronized void registerService( */ public synchronized void unregisterService( Class service) { + services.remove(service); implementations.remove(service); } @@ -195,34 +219,23 @@ public synchronized void unregisterService( * method on their service interface. Failures are reported but do not * block the instatiation process). */ - private void init() { - - err("Required services online, initialising"); - - logger = new LogManagerImpl(); - logger.setInitParams(bc, null); - if (!logger.startUp()) { - err("Cannot start the log service, aborting"); - } - instances.put(LogManager.class, logger); - err("Service " + LogManagerImpl.class.getName() + " started"); + public void init(BundleContext bc) { + this.bc = bc; + + registerBaseServices(); - DBService db = DBServiceImpl.getInstance(); - db.setInitParams(bc, logger.createLogger("sqooss.db")); - if (!db.startUp()) { - err("Cannot start the DB service, aborting"); - } - instances.put(DBService.class, db); - err("Service " + DBServiceImpl.class.getName() + " started"); + err("Required services online, initialising"); for (Class s : services) { initService(s); } - } private synchronized void initService(Class s) { - Class impl = implementations.get(s); + // Do not initialize the service again + if(instances.containsKey(s)) return; + + Class impl = implementations.get(s); if (impl == null) { err("No implementation found for service " + s); @@ -230,7 +243,7 @@ private synchronized void initService(Class s) { } try { - Object o = impl.newInstance(); + AlitheiaCoreService o = impl.newInstance(); if (o == null) { err("Service object for service " + s @@ -243,25 +256,39 @@ private synchronized void initService(Class s) { String[] paths = s.getCanonicalName().split("\\."); /* Logger names are constructed as per */ - s.cast(o).setInitParams(bc, + o.setInitParams(bc, logger.createLogger("sqooss." + paths[3])); - if (!s.cast(o).startUp()) { + if (!o.startUp()) { err("Service " + s + " could not be started"); return; } - instances.put(s, s.cast(o)); + instances.put(s, o); err("Service " + impl.getName() + " started"); } catch (Exception e) { e.printStackTrace(); } } - public void shutDown() { + /** + * Unused check of the core instance for liveness. Because the instance + * might not lee without the rest of the bikini services, we need to + * check that they are present. + * Added after evening discussion (some 5 pints and a bunch of naked + * bikini models later) at Amarilia on liveness. + */ + private static boolean canLee(boolean touLiBouDiBouDauTcou) { + return (null != instance) && touLiBouDiBouDauTcou; + } + + public void shutDown() { + // Copy List> revServices = new ArrayList>(services); - Collections.reverse(revServices); + + // Reverse + Collections.reverse(services); for (Class s : revServices) { Object o = instances.get(s); @@ -273,6 +300,14 @@ public void shutDown() { } } } + + /** + * Returns the instance of the given service + */ + @SuppressWarnings("unchecked") + public T getService(Class service){ + return (T) instances.get(service); + } /** * Returns the locally stored Logger component's instance. @@ -280,7 +315,7 @@ public void shutDown() { * @return The Logger component's instance. */ public LogManager getLogManager() { - return (LogManager)instances.get(LogManager.class); + return this.getService(LogManager.class); } /** @@ -289,7 +324,7 @@ public LogManager getLogManager() { * @return The WebAdmin component's instance. */ public WebadminService getWebadminService() { - return (WebadminService)instances.get(WebadminService.class); + return this.getService(WebadminService.class); } /** @@ -298,7 +333,7 @@ public WebadminService getWebadminService() { * @return The Plug-in Admin component's instance. */ public PluginAdmin getPluginAdmin() { - return (PluginAdmin)instances.get(PluginAdmin.class); + return this.getService(PluginAdmin.class); } /** @@ -311,39 +346,24 @@ public DBService getDBService() { return DBServiceImpl.getInstance(); // <-- Ugly but required for testing. } - /** - * Unused check of the core instance for liveness. Because the instance - * might not lee without the rest of the bikini services, we need to - * check that they are present. - * Added after evening discussion (some 5 pints and a bunch of naked - * bikini models later) at Amarilia on liveness. - */ - private static boolean canLee(boolean touLiBouDiBouDauTcou) { - return (null != instance) && touLiBouDiBouDauTcou; - } - /** * Returns the locally stored FDS component's instance. *
- * The instance is created when this method is called for a first - * time. * * @return The FDS component's instance. */ public FDSService getFDSService() { - return (FDSService)instances.get(FDSService.class); + return this.getService(FDSService.class); } /** * Returns the locally stored Scheduler component's instance. *
- * The instance is created when this method is called for a first - * time. * * @return The Scheduler component's instance. */ public Scheduler getScheduler() { - return (Scheduler)instances.get(Scheduler.class); + return this.getService(Scheduler.class); } /** @@ -355,7 +375,8 @@ public Scheduler getScheduler() { * @return The Security component's instance. */ public SecurityManager getSecurityManager() { - return (SecurityManager)instances.get(SecurityManager.class); + if (this.securityManager == null) this.securityManager = new SecurityManager(); + return this.securityManager; } /** @@ -367,7 +388,7 @@ public SecurityManager getSecurityManager() { * @return The TDS component's instance. */ public TDSService getTDSService() { - return (TDSService)instances.get(TDSService.class); + return this.getService(TDSService.class); } /** @@ -379,7 +400,7 @@ public TDSService getTDSService() { * @return The Updater component's instance. */ public UpdaterService getUpdater() { - return (UpdaterService)instances.get(UpdaterService.class); + return this.getService(UpdaterService.class); } /** @@ -391,7 +412,7 @@ public UpdaterService getUpdater() { * @return The ClusterNodeSerive component's instance. */ public ClusterNodeService getClusterNodeService() { - return (ClusterNodeService)instances.get(ClusterNodeService.class); + return this.getService(ClusterNodeService.class); } /** @@ -403,7 +424,7 @@ public ClusterNodeService getClusterNodeService() { * @return The Metric Activator component's instance. */ public MetricActivator getMetricActivator() { - return (MetricActivator)instances.get(MetricActivator.class); + return this.getService(MetricActivator.class); } /** @@ -412,7 +433,7 @@ public MetricActivator getMetricActivator() { * @return The Administration Service component's instance. */ public AdminService getAdminService() { - return (AdminService)instances.get(AdminService.class); + return this.getService(AdminService.class); } private void err(String msg) { diff --git a/alitheia/core/src/main/java/eu/sqooss/core/CoreActivator.java b/alitheia/core/src/main/java/eu/sqooss/core/CoreActivator.java index 4cabd4e10..5f06e7f15 100644 --- a/alitheia/core/src/main/java/eu/sqooss/core/CoreActivator.java +++ b/alitheia/core/src/main/java/eu/sqooss/core/CoreActivator.java @@ -47,7 +47,8 @@ public class CoreActivator implements BundleActivator { private ServiceRegistration sregCore; public void start(BundleContext bc) throws Exception { - core = new AlitheiaCore(bc); + core = AlitheiaCore.getInstance(); + core.init(bc); sregCore = bc.registerService(AlitheiaCore.class.getName(), core, null); } diff --git a/alitheia/core/src/main/java/eu/sqooss/impl/service/db/DBServiceImpl.java b/alitheia/core/src/main/java/eu/sqooss/impl/service/db/DBServiceImpl.java index 546b4addd..74213fde1 100644 --- a/alitheia/core/src/main/java/eu/sqooss/impl/service/db/DBServiceImpl.java +++ b/alitheia/core/src/main/java/eu/sqooss/impl/service/db/DBServiceImpl.java @@ -775,6 +775,10 @@ public int executeUpdate(String hql, Map params) @Override public boolean startUp() { + if(bc == null){ + logger.error("DB service got no configuration."); + return false; + } String db = bc.getProperty(DB).toLowerCase(); String cs = connString.get(db); cs = cs.replaceAll("", bc.getProperty(DB_HOST)); diff --git a/alitheia/core/src/main/java/eu/sqooss/impl/service/logging/LogManagerImpl.java b/alitheia/core/src/main/java/eu/sqooss/impl/service/logging/LogManagerImpl.java index ea7c6d2f2..cf5241d65 100644 --- a/alitheia/core/src/main/java/eu/sqooss/impl/service/logging/LogManagerImpl.java +++ b/alitheia/core/src/main/java/eu/sqooss/impl/service/logging/LogManagerImpl.java @@ -143,7 +143,7 @@ public boolean startUp() { PropertyConfigurator.configure(p); org.apache.log4j.Logger.getRootLogger().info("Logging initialized."); CyclicLogger l = new CyclicLogger(); - String pattern = bc.getProperty("eu.sqooss.logbuffer.pattern"); + String pattern = bc != null ? bc.getProperty("eu.sqooss.logbuffer.pattern") : null; if (pattern != null) { org.apache.log4j.Logger.getRootLogger().info("Logging to buffer with pattern <" + pattern + ">"); l.setLayout(new PatternLayout(pattern)); diff --git a/alitheia/core/src/main/java/eu/sqooss/impl/service/metricactivator/MetricActivatorImpl.java b/alitheia/core/src/main/java/eu/sqooss/impl/service/metricactivator/MetricActivatorImpl.java index 4487e5202..60aa08afb 100644 --- a/alitheia/core/src/main/java/eu/sqooss/impl/service/metricactivator/MetricActivatorImpl.java +++ b/alitheia/core/src/main/java/eu/sqooss/impl/service/metricactivator/MetricActivatorImpl.java @@ -33,15 +33,26 @@ package eu.sqooss.impl.service.metricactivator; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import java.util.concurrent.atomic.AtomicLong; -import eu.sqooss.service.abstractmetric.InvocationOrder; import org.osgi.framework.BundleContext; import eu.sqooss.core.AlitheiaCore; -import eu.sqooss.service.abstractmetric.AbstractMetric; +import eu.sqooss.service.abstractmetric.DefaultMetric; import eu.sqooss.service.abstractmetric.AlitheiaPlugin; +import eu.sqooss.service.abstractmetric.InvocationOrder; import eu.sqooss.service.abstractmetric.SchedulerHints; import eu.sqooss.service.cluster.ClusterNodeActionException; import eu.sqooss.service.cluster.ClusterNodeService; @@ -68,7 +79,9 @@ import eu.sqooss.service.scheduler.Job; import eu.sqooss.service.scheduler.Scheduler; import eu.sqooss.service.scheduler.SchedulerException; -import eu.sqooss.service.util.GraphTS; +import eu.sqooss.service.util.Graph; +import eu.sqooss.service.util.GraphSorter; +import eu.sqooss.service.util.TopologicalGraphSorter; public class MetricActivatorImpl implements MetricActivator { @@ -91,7 +104,7 @@ public MetricActivatorImpl() { } @Override public void runMetric(T resource, AlitheiaPlugin ap) { Class activator = resource.getClass(); - Job j = new MetricActivatorJob((AbstractMetric)ap, resource.getId(), logger, + Job j = new MetricActivatorJob((DefaultMetric)ap, resource.getId(), logger, metricTypesToActivators.get(activator), priority.incrementAndGet(), fastSync); @@ -118,7 +131,7 @@ public void syncMetrics(StoredProject sp, Class actType) { /* Fire up plug-ins */ for (PluginInfo pi : plugins) { - AbstractMetric m = (AbstractMetric) bc.getService(pi.getServiceRef()); + DefaultMetric m = (DefaultMetric) bc.getService(pi.getServiceRef()); try { sched.enqueue(new MetricSchedulerJob(m, sp)); } catch (SchedulerException e) { @@ -224,7 +237,8 @@ private List getExecutionOrder(Set unordered) { Map idx = new HashMap(); Map invidx = new HashMap(); - GraphTS graph = new GraphTS(unordered.size()); + Graph graph = new Graph(unordered.size()); + GraphSorter sorter = new TopologicalGraphSorter(graph); //Build the adjacency matrix for (AlitheiaPlugin p : unordered) { @@ -252,7 +266,7 @@ private List getExecutionOrder(Set unordered) { } } - List sorted = graph.topo(); + List sorted = sorter.sort(); logger.debug("Calculated metric order:"); for (AlitheiaPlugin p : sorted) { @@ -314,8 +328,8 @@ protected void run() throws Exception { } } - AbstractMetric metric = - (AbstractMetric) bc.getService(mi.getServiceRef()); + DefaultMetric metric = + (DefaultMetric) bc.getService(mi.getServiceRef()); HashSet jobs = new HashSet(); /*Check what is the default activation ordering as suggested by the metric*/ diff --git a/alitheia/core/src/main/java/eu/sqooss/impl/service/metricactivator/MetricActivatorJob.java b/alitheia/core/src/main/java/eu/sqooss/impl/service/metricactivator/MetricActivatorJob.java index 8a8c923b4..bd5c8c07b 100644 --- a/alitheia/core/src/main/java/eu/sqooss/impl/service/metricactivator/MetricActivatorJob.java +++ b/alitheia/core/src/main/java/eu/sqooss/impl/service/metricactivator/MetricActivatorJob.java @@ -38,7 +38,7 @@ import org.hibernate.exception.LockAcquisitionException; import eu.sqooss.core.AlitheiaCore; -import eu.sqooss.service.abstractmetric.AbstractMetric; +import eu.sqooss.service.abstractmetric.DefaultMetric; import eu.sqooss.service.abstractmetric.AlreadyProcessingException; import eu.sqooss.service.abstractmetric.MetricMismatchException; import eu.sqooss.service.db.DAObject; @@ -58,12 +58,12 @@ public class MetricActivatorJob extends Job { private DBService dbs; private MetricActivator ma; private Long daoID; - private AbstractMetric metric; + private DefaultMetric metric; private long priority; Class daoType; private boolean fastSync = false; - MetricActivatorJob(AbstractMetric m, Long daoID, Logger l, + MetricActivatorJob(DefaultMetric m, Long daoID, Logger l, Class daoType, long priority, boolean fastSync) { this.metric = m; diff --git a/alitheia/core/src/main/java/eu/sqooss/impl/service/updater/UpdaterServiceImpl.java b/alitheia/core/src/main/java/eu/sqooss/impl/service/updater/UpdaterServiceImpl.java index 75148b1fe..a7ea7ae38 100644 --- a/alitheia/core/src/main/java/eu/sqooss/impl/service/updater/UpdaterServiceImpl.java +++ b/alitheia/core/src/main/java/eu/sqooss/impl/service/updater/UpdaterServiceImpl.java @@ -47,10 +47,8 @@ import java.util.concurrent.ConcurrentMap; import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; import eu.sqooss.core.AlitheiaCore; -import eu.sqooss.service.cluster.ClusterNodeActionException; import eu.sqooss.service.cluster.ClusterNodeService; import eu.sqooss.service.db.ClusterNode; import eu.sqooss.service.db.DBService; @@ -67,7 +65,9 @@ import eu.sqooss.service.updater.Updater; import eu.sqooss.service.updater.UpdaterService; import eu.sqooss.service.util.BidiMap; -import eu.sqooss.service.util.GraphTS; +import eu.sqooss.service.util.Graph; +import eu.sqooss.service.util.GraphSorter; +import eu.sqooss.service.util.TopologicalGraphSorter; public class UpdaterServiceImpl implements UpdaterService, JobStateListener { @@ -365,8 +365,9 @@ private boolean update(StoredProject project, UpdaterStage stage, Updater update // Topologically sort updaters within the same stage List updForStage = new ArrayList(); updForStage.addAll(getUpdaters(project, us)); - GraphTS graph = - new GraphTS(updForStage.size()); + Graph graph = new Graph(updForStage.size()); + GraphSorter sorter = + new TopologicalGraphSorter(graph); BidiMap idx = new BidiMap(); @@ -396,7 +397,7 @@ private boolean update(StoredProject project, UpdaterStage stage, Updater update } // Topo-sort - updForStage = graph.topo(); + updForStage = sorter.sort(); // We now have updaters in correct execution order DependencyJob depJob = new DependencyJob(us.toString()); diff --git a/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/AbstractMetric.java b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/AbstractMetric.java deleted file mode 100644 index 73df1f9fb..000000000 --- a/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/AbstractMetric.java +++ /dev/null @@ -1,945 +0,0 @@ -/* - * This file is part of the Alitheia system, developed by the SQO-OSS - * consortium as part of the IST FP6 SQO-OSS project, number 033331. - * - * Copyright 2008 - 2010 - Organization for Free and Open Source Software, - * Athens, Greece. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ -package eu.sqooss.service.abstractmetric; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; -import java.util.concurrent.ConcurrentHashMap; - -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; - -import eu.sqooss.core.AlitheiaCore; -import eu.sqooss.service.db.DAObject; -import eu.sqooss.service.db.DBService; -import eu.sqooss.service.db.EncapsulationUnitMeasurement; -import eu.sqooss.service.db.ExecutionUnitMeasurement; -import eu.sqooss.service.db.MailMessageMeasurement; -import eu.sqooss.service.db.MailingListThreadMeasurement; -import eu.sqooss.service.db.Metric; -import eu.sqooss.service.db.MetricMeasurement; -import eu.sqooss.service.db.MetricType; -import eu.sqooss.service.db.NameSpaceMeasurement; -import eu.sqooss.service.db.Plugin; -import eu.sqooss.service.db.PluginConfiguration; -import eu.sqooss.service.db.ProjectFileMeasurement; -import eu.sqooss.service.db.ProjectVersionMeasurement; -import eu.sqooss.service.db.StoredProject; -import eu.sqooss.service.db.StoredProjectMeasurement; -import eu.sqooss.service.db.MetricType.Type; -import eu.sqooss.service.logging.Logger; -import eu.sqooss.service.metricactivator.MetricActivationException; -import eu.sqooss.service.metricactivator.MetricActivator; -import eu.sqooss.service.pa.PluginAdmin; -import eu.sqooss.service.pa.PluginInfo; -import eu.sqooss.service.scheduler.Job; -import eu.sqooss.service.util.Pair; - -/** - * A base class for all metrics. Implements basic functionality such as - * logging setup and plug-in information retrieval from the OSGi bundle - * manifest file. Metrics can choose to directly implement - * the {@link eu.sqooss.abstractmetric.AlitheiaPlugin} interface instead of - * extending this class. - */ -public abstract class AbstractMetric implements AlitheiaPlugin { - - /** Reference to the metric bundle context */ - protected BundleContext bc; - - /** Logger for administrative operations */ - protected Logger log = null; - - /** Reference to the DB service, not to be passed to metric jobs */ - protected DBService db; - - /** - * Reference to the plugin administrator service, not to be passed to - * metric jobs - */ - protected PluginAdmin pa; - - /** - * The scheduler job that executes this metric. - */ - protected ThreadLocal job = new ThreadLocal(); - - /** - * Metric mnemonics for the metrics required to be present for this - * metric to operate. - */ - private Set dependencies = new HashSet(); - - /** Set of declared metrics indexed by their mnemonic*/ - private Map metrics = new HashMap(); - - /** The list of this plug-in's activators*/ - private Set> activators = - new HashSet>(); - - private Map>> metricActType = - new HashMap>>(); - - protected static final String QRY_SYNC_PV = "select pv.id from ProjectVersion pv " + - "where pv.project = :project and not exists(" + - " select pvm.projectVersion from ProjectVersionMeasurement pvm " + - " where pvm.projectVersion.id = pv.id and pvm.metric.id = :metric) " + - "order by pv.sequence asc"; - - protected static final String QRY_SYNC_PF = "select pf.id " + - "from ProjectVersion pv, ProjectFile pf " + - "where pf.projectVersion=pv and pv.project = :project " + - "and not exists (" + - " select pfm.projectFile " + - " from ProjectFileMeasurement pfm " + - " where pfm.projectFile.id = pf.id " + - " and pfm.metric.id = :metric) " + - " and pf.isDirectory = false) " + - "order by pv.sequence asc"; - - protected static final String QRY_SYNC_PD = "select pf.id " + - "from ProjectVersion pv, ProjectFile pf " + - "where pf.projectVersion=pv and pv.project = :project " + - "and not exists (" + - " select pfm.projectFile " + - " from ProjectFileMeasurement pfm " + - " where pfm.projectFile.id = pf.id " + - " and pfm.metric.id = :metric) " + - " and pf.isDirectory = true) " + - "order by pv.sequence asc"; - - protected static final String QRY_SYNC_MM = "select mm.id " + - "from MailMessage mm " + - "where mm.list.storedProject = :project " + - "and mm.id not in (" + - " select mmm.mail.id " + - " from MailMessageMeasurement mmm " + - " where mmm.metric.id =:metric and mmm.mail.id = mm.id))"; - - protected static final String QRY_SYNC_MT = "select mlt.id " + - "from MailingListThread mlt " + - "where mlt.list.storedProject = :project " + - "and mlt.id not in (" + - " select mltm.thread.id " + - " from MailingListThreadMeasurement mltm " + - " where mltm.metric.id =:metric and mltm.thread.id = mlt.id)"; - - protected static final String QRY_SYNC_DEV = "select d.id " + - "from Developer d " + - "where d.storedProject = :project"; - - protected static final String QRY_SYNC_NS = "select ns.id " + - "from NameSpace ns, ProjectVersion pv " + - "where pv = ns.changeVersion " + - "and pv.project = :project " + - "and not exists ( " + - " select nsm " + - " from NameSpaceMeasurement nsm " + - " where nsm.metric.id = :metric " + - " and nsm.namespace = ns) " + - "order by pv.sequence asc"; - - protected static final String QRY_SYNC_ENCUNT = "select encu.id " + - "from EncapsulationUnit encu, ProjectVersion pv, ProjectFile pf " + - " where pf.projectVersion = pv " + - " and encu.file = pf " + - "and pv.project = :project " + - "and not exists ( " + - " select eum " + - " from EncapsulationUnitMeasurement eum " + - " where eum.encapsulationUnit = encu " + - " and eum.metric.id = :metric " + - " ) order by pv.sequence asc "; - - protected static final String QRY_SYNC_EXECUNT = "select exu.id " + - "from ExecutionUnit exu, EncapsulationUnit encu, " + - " ProjectVersion pv, ProjectFile pf " + - "where pf.projectVersion = pv " + - "and encu.file = pf " + - "and pv.project = :project " + - "and exu.changed = true " + - "and exu.encapsulationUnit = encu " + - "and not exists ( " + - " select eum " + - " from ExecutionUnitMeasurement eum " + - " where eum.executionUnit = exu " + - " and eum.metric.id = :metric) " + - "order by pv.sequence asc"; - - /** - * Init basic services common to all implementing classes - * @param bc - The bundle context of the implementing metric - to be passed - * by the activator. - */ - protected AbstractMetric(BundleContext bc) { - - this.bc = bc; - - log = AlitheiaCore.getInstance().getLogManager().createLogger(Logger.NAME_SQOOSS_METRIC); - - if (log == null) { - System.out.println("ERROR: Got no logger"); - } - - db = AlitheiaCore.getInstance().getDBService(); - - if(db == null) - log.error("Could not get a reference to the DB service"); - - pa = AlitheiaCore.getInstance().getPluginAdmin(); - - if(pa == null) - log.error("Could not get a reference to the Plugin Administation " - + "service"); - - /*Discover the declared metrics*/ - MetricDeclarations md = this.getClass().getAnnotation(MetricDeclarations.class); - - if (md != null && md.metrics().length > 0) { - for (MetricDecl metric : md.metrics()) { - log.debug("Found metric: " + metric.mnemonic() + " with " - + metric.activators().length + " activators"); - - if (metrics.containsKey(metric.mnemonic())) { - log.error("Duplicate metric mnemonic " + metric.mnemonic()); - continue; - } - - Metric m = new Metric(); - m.setDescription(metric.descr()); - m.setMnemonic(metric.mnemonic()); - m.setMetricType(new MetricType(MetricType.fromActivator(metric.activators()[0]))); - - List> activs = new ArrayList>(); - for (Class o : metric.activators()) { - activs.add(o); - } - - metricActType.put(m, activs); - - activators.addAll(Arrays.asList(metric.activators())); - - metrics.put(m.getMnemonic(), m); - if (metric.dependencies().length > 0) - dependencies.addAll(Arrays.asList(metric.dependencies())); - } - } else { - log.warn("Plug-in " + getName() + " declares no metrics"); - } - } - - /** - * Retrieve author information from the plug-in bundle - */ - public String getAuthor() { - - return (String) bc.getBundle().getHeaders().get( - org.osgi.framework.Constants.BUNDLE_CONTACTADDRESS); - } - - /** - * Retrieve the plug-in description from the plug-in bundle - */ - public String getDescription() { - - return (String) bc.getBundle().getHeaders().get( - org.osgi.framework.Constants.BUNDLE_DESCRIPTION); - } - - /** - * Retrieve the plug-in name as specified in the metric bundle - */ - public String getName() { - - return (String) bc.getBundle().getHeaders().get( - org.osgi.framework.Constants.BUNDLE_NAME); - } - - /** - * Retrieve the plug-in version as specified in the metric bundle - */ - public String getVersion() { - - return (String) bc.getBundle().getHeaders().get( - org.osgi.framework.Constants.BUNDLE_VERSION); - } - - /** - * Retrieve the installation date for this plug-in version - */ - public final Date getDateInstalled() { - return Plugin.getPluginByHashcode(getUniqueKey()).getInstalldate(); - } - - Map> blockerObjects = new ConcurrentHashMap>(); - - /** - * Call the appropriate getResult() method according to - * the type of the entity that is measured. - * - * Use this method if you don't want the metric results - * to be calculated on-demand. Otherwise, use getResult(). - * - * @param o DAO that specifies the desired result type. - * The type of o is used to dispatch to the correct - * specialized getResult() method of the sub-interfaces. - * @return result (measurement) performed by this metric - * on the project data specified by o. - * @throws MetricMismatchException if the DAO is of a type - * not supported by this metric. - */ - @SuppressWarnings("unchecked") - public List getResultIfAlreadyCalculated(DAObject o, List l) throws MetricMismatchException { - boolean found = false; - List result = new ArrayList(); - - for (Metric m : l) { - if (!metrics.containsKey(m.getMnemonic())) { - throw new MetricMismatchException("Metric " + m.getMnemonic() - + " not defined by plugin " - + Plugin.getPluginByHashcode(getUniqueKey()).getName()); - } - List re = null; - try { - Method method = findGetResultMethod(o.getClass()); - re = (List) method.invoke(this, o, m); - } catch (SecurityException e) { - logErr("getResult", o, e); - } catch (NoSuchMethodException e) { - log.error("No method getResult(" + m.getMetricType().toActivator() + ") for type " - + this.getClass().getName()); - } catch (IllegalArgumentException e) { - logErr("getResult", o, e); - } catch (IllegalAccessException e) { - logErr("getResult", o, e); - } catch (InvocationTargetException e) { - logErr("getResult", o, e); - } - if (re != null && !re.isEmpty()) { - result.addAll(re); - } - } - - return result; - } - - private Method findGetResultMethod(Class clazz) - throws NoSuchMethodException { - Method m = null; - - try { - m = this.getClass().getMethod("getResult", clazz, Metric.class); - } catch (NoSuchMethodException nsme) { - try { - m = this.getClass().getMethod("getResult", clazz.getSuperclass(), Metric.class); - } catch (NoSuchMethodException nsme1) { - throw nsme; - } - } - - return m; - } - - /** - * Call the appropriate getResult() method according to - * the type of the entity that is measured. - * - * If the appropriate getResult() doesn't return any value, - * the metric is forced to calculate the result. Then the - * appropriate getResult() method is called again. - * - * @param o DAO that specifies the desired result type. - * The type of o is used to dispatch to the correct - * specialized getResult() method of the sub-interfaces. - * @return result (measurement) performed by this metric - * on the project data specified by o. - * @throws MetricMismatchException if the DAO is of a type - * not supported by this metric. - * @throws AlreadyProcessingException - */ - public List getResult(DAObject o, List l) - throws MetricMismatchException, AlreadyProcessingException, Exception { - List r = getResultIfAlreadyCalculated(o, l); - - // the result hasn't been calculated yet. Do so. - if (r == null || r.size() == 0) { - /* - * To ensure that no two instances of the metric operate on the same - * DAO lock on the DAO. Working on the same DAO can happen often - * when a plugin starts the calculation of another metric as a - * result of a plugin dependency association. This lock has the side - * effect that no two Plugins can be invoked with the same DAO as an - * argument even if the plug-ins do not depend on each other. - */ - synchronized (lockObject(o)) { - try { - run(o); - - r = getResultIfAlreadyCalculated(o, l); - if (r == null || r.size() == 0) { - if (job.get().state() != Job.State.Yielded) - log.debug("Metric " + getClass() + " didn't return" - + "a result even after running it. DAO: " - + o.getId()); - } - } finally { - unlockObject(o); - } - } - } - - return r; - } - - private Map> locks = new HashMap>(); - - private Object lockObject(DAObject o) throws AlreadyProcessingException { - synchronized (locks) { - if (!locks.containsKey(o.getId())) { - locks.put(o.getId(), - new Pair(new Object(),0)); - } - Pair p = locks.get(o.getId()); - if (p.second + 1 > 1) { - /* - * Break and reschedule the calculation of each call to the - * getResult method if it originates from another thread than - * the thread that has currently locked the DAO object. - * This is required for the DB transaction in the stopped - * job to see the results of the calculation of the original - * job. - */ - log.debug("DAO Id:" + o.getId() + - " Already locked - failing job"); - try { - throw new AlreadyProcessingException(); - } finally { - MetricActivator ma = AlitheiaCore.getInstance().getMetricActivator(); - ma.runMetric(o, this); - } - } - p.second = p.second + 1; - return p.first; - } - } - - private void unlockObject(DAObject o) { - synchronized(locks) { - Pair p = locks.get(o.getId()); - p.second = p.second - 1; - if (p.second == 0) { - locks.remove(o.getId()); - } else { - log.debug("Unlocking DAO Id:" + o.getId()); - } - } - } - - /** - * Call the appropriate run() method according to the type of the entity - * that is measured. - * - * @param o - * DAO which determines which sub-interface run method is - * called and also determines what is to be measured by that - * sub-interface. - * @throws MetricMismatchException - * if the DAO is of a type not supported by this metric. - */ - public void run(DAObject o) throws MetricMismatchException, - AlreadyProcessingException, Exception { - - if (!checkDependencies()) { - log.error("Plug-in dependency check failed"); - return; - } - - try { - Method m = findRunMethod("run", o.getClass()); - m.invoke(this, o); - } catch (SecurityException e) { - logErr("run", o, e); - } catch (NoSuchMethodException e) { - logErr("run", o, e); - } catch (IllegalArgumentException e) { - logErr("run", o, e); - } catch (IllegalAccessException e) { - logErr("run", o, e); - } catch (InvocationTargetException e) { - // Forward exception to metric job exception handler - if (e.getCause() instanceof AlreadyProcessingException) { - throw (AlreadyProcessingException) e.getCause(); - } else { - if (e != null && e.getCause() != null) { - logErr("run", o, e); - if (e.getCause() != null) - throw new Exception(e.getCause()); - else - throw new Exception(e); - } - } - } - } - - private Method findRunMethod(String name, Class clazz) - throws NoSuchMethodException { - Method m = null; - - try { - m = this.getClass().getMethod(name, clazz); - } catch (NoSuchMethodException nsme) { - try { - m = this.getClass().getMethod(name, clazz.getSuperclass()); - } catch (NoSuchMethodException nsme1) { - throw nsme; - } - } - - return m; - } - - private void logErr(String method, DAObject o, Exception e) { - log.error("Plugin:" + this.getClass().toString() + - "\nDAO id: " + o.getId() + - "\nDAO class: " + o.getClass() + - "\nDAO toString(): " + o.toString() + - "\nError when invoking the " + method + " method." + - "\nException:" + e.getClass().getName() + - "\nError:" + e.getMessage() + - "\nReason:" + e.getCause(), e); - } - - /** {@inheritDoc} */ - public List getAllSupportedMetrics() { - String qry = "from Metric m where m.plugin=:plugin"; - Map params = new HashMap(); - params.put("plugin", Plugin.getPluginByHashcode(getUniqueKey())); - - return (List)db.doHQL(qry, params); - } - - /** {@inheritDoc} */ - public List getSupportedMetrics(Class activator) { - List m = new ArrayList(); - - //Query the database just once - List all = getAllSupportedMetrics(); - - if (all == null || all.isEmpty()) - return m; - - for (Metric metric : all) { - if (getMetricActivationTypes(metric).contains(activator)) { - m.add(metric); - } - } - - return m; - } - - /** - * Register the metric to the DB. Subclasses can run their custom - * initialization routines (i.e. registering DAOs or tables) after calling - * super().install() - */ - public boolean install() { - //1. check if dependencies are satisfied - if (!checkDependencies()) { - log.error("Plug-in installation failed"); - return false; - } - - HashMap h = new HashMap(); - h.put("name", this.getName()); - - List plugins = db.findObjectsByProperties(Plugin.class, h); - - if (!plugins.isEmpty()) { - log.warn("A plugin with name <" + getName() - + "> is already installed, won't re-install."); - return false; - } - - - //2. Add the plug-in - Plugin p = new Plugin(); - p.setName(getName()); - p.setInstalldate(new Date(System.currentTimeMillis())); - p.setVersion(getVersion()); - p.setActive(true); - p.setHashcode(getUniqueKey()); - boolean result = db.addRecord(p); - - //3. Add the metrics - for (String mnem :metrics.keySet()) { - Metric m = metrics.get(mnem); - Type type = Type.fromString(m.getMetricType().getType()); - MetricType newType = MetricType.getMetricType(type); - if (newType == null) { - newType = new MetricType(type); - db.addRecord(newType); - m.setMetricType(newType); - } - - m.setMetricType(newType); - m.setPlugin(p); - db.addRecord(m); - } - - return result; - } - - /** - * Remove a plug-in's record from the DB. The DB's referential integrity - * mechanisms are expected to automatically remove associated records. - * Subclasses should also clean up any custom tables created. - */ - public boolean remove() { - Plugin p = Plugin.getPluginByHashcode(getUniqueKey()); - return db.deleteRecord(p); - } - - /** - * Default (empty) implementation of the clean up method. What to - * do with the provided DAO is left to sub-classes to decide. - * {@inheritDoc} - */ - public boolean cleanup(DAObject sp) { - log.warn("Empty cleanup method for plug-in " - + this.getClass().getName()); - return true; - } - - /**{@inheritDoc}}*/ - public boolean update() { - ServiceReference serviceRef = null; - serviceRef = bc.getServiceReference(AlitheiaCore.class.getName()); - - MetricActivator ma = - ((AlitheiaCore)bc.getService(serviceRef)).getMetricActivator(); - - if (ma == null) { - return false; - } - - ma.syncMetrics(this); - - return true; - } - - /**{@inheritDoc}*/ - public final Set> getActivationTypes() { - return activators; - } - - /** - * Return an MD5 hex key uniquely identifying the plug-in - */ - public final String getUniqueKey() { - MessageDigest m = null; - try { - m = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - log.error("Cannot find a valid implementation of the MD5 " + - "hash algorithm"); - } - String name = this.getClass().getCanonicalName(); - byte[] data = name.getBytes(); - m.update(data,0,data.length); - BigInteger i = new BigInteger(1,m.digest()); - return String.format("%1$032X", i); - } - - /** {@inheritDoc} */ - public final Set getConfigurationSchema() { - // Retrieve the plug-in's info object - PluginInfo pi = pa.getPluginInfo(getUniqueKey()); - if (pi == null) { - // The plug-in's info object is always null during bundle startup, - // but if it is not available when the bundle is active, something - // is possibly wrong. - if (bc.getBundle().getState() == Bundle.ACTIVE) { - log.warn("Plugin <" + getName() + "> is loaded but not installed."); - } - return Collections.emptySet(); - } - return pi.getConfiguration(); - } - - /** - * Add an entry to this plug-in's configuration schema. - * - * @param name The name of the configuration property - * @param defValue The default value for the configuration property - * @param msg The description of the configuration property - * @param type The type of the configuration property - */ - protected final void addConfigEntry(String name, String defValue, - String msg, PluginInfo.ConfigurationType type) { - // Retrieve the plug-in's info object - PluginInfo pi = pa.getPluginInfo(getUniqueKey()); - // Will happen if called during bundle's startup - if (pi == null) { - log.warn("Adding configuration key <" + name + - "> to plugin <" + getName() + "> failed: " + - "no PluginInfo."); - return; - } - // Modify the plug-in's configuration - try { - // Update property - if (pi.hasConfProp(name, type.toString())) { - if (pi.updateConfigEntry(db, name, defValue)) { - // Update the Plug-in Admin's information - pa.pluginUpdated(pa.getPlugin(pi)); - } - else { - log.error("Property (" + name +") update has failed!"); - } - } - // Create property - else { - if (pi.addConfigEntry( - db, name, msg, type.toString(), defValue)) { - // Update the Plug-in Admin's information - pa.pluginUpdated(pa.getPlugin(pi)); - } - else { - log.error("Property (" + name +") append has failed!"); - } - } - } - catch (Exception ex){ - log.error("Can not modify property (" + name +") for plugin (" - + getName(), ex); - } - } - - /** - * Remove an entry from the plug-in's configuration schema - * - * @param name The name of the configuration property to remove - * @param name The type of the configuration property to remove - */ - protected final void removeConfigEntry( - String name, - PluginInfo.ConfigurationType type) { - // Retrieve the plug-in's info object - PluginInfo pi = pa.getPluginInfo(getUniqueKey()); - // Will happen if called during bundle's startup - if (pi == null) { - log.warn("Removing configuration key <" + name + - "> from plugin <" + getName() + "> failed: " + - "no PluginInfo."); - return; - } - // Modify the plug-in's configuration - try { - if (pi.hasConfProp(name, type.toString())) { - if (pi.removeConfigEntry(db, name, type.toString())) { - // Update the Plug-in Admin's information - pa.pluginUpdated(pa.getPlugin(pi)); - } - else { - log.error("Property (" + name +") remove has failed!"); - } - } - else { - log.error("Property (" + name +") does not exist!"); - } - } - catch (Exception ex){ - log.error("Can not remove property (" + name +") from plugin (" - + getName() + ")", ex); - } - } - - /** - * Get a configuration option for this metric from the plugin configuration - * store - * - * @param config The configuration option to retrieve - * @return The configuration entry corresponding the provided description or - * null if not found in the plug-in's configuration schema - */ - public PluginConfiguration getConfigurationOption(String config) { - Set conf = - pa.getPluginInfo(getUniqueKey()).getConfiguration(); - - Iterator i = conf.iterator(); - - while (i.hasNext()) { - PluginConfiguration pc = i.next(); - if (pc.getName().equals(config)) { - return pc; - } - } - - /* Config option not found */ - return null; - } - - private static Map, String> resultFieldNames = - new HashMap, String>(); - - static { - resultFieldNames.put(StoredProjectMeasurement.class, "storedProject"); - resultFieldNames.put(ProjectVersionMeasurement.class, "projectVersion"); - resultFieldNames.put(ProjectFileMeasurement.class, "projectFile"); - resultFieldNames.put(MailMessageMeasurement.class, "mail"); - resultFieldNames.put(MailingListThreadMeasurement.class, "thread"); - resultFieldNames.put(ExecutionUnitMeasurement.class, "executionUnit"); - resultFieldNames.put(EncapsulationUnitMeasurement.class, "encapsulationUnit"); - resultFieldNames.put(NameSpaceMeasurement.class, "namespace"); - } - - /** - * Convenience method to get the measurement for a single metric. - */ - protected List getResult(DAObject o, Class clazz, - Metric m, Result.ResultType type) { - DBService dbs = AlitheiaCore.getInstance().getDBService(); - Map props = new HashMap(); - - props.put(resultFieldNames.get(clazz), o); - props.put("metric", m); - List resultat = dbs.findObjectsByProperties(clazz, props); - - if (resultat.isEmpty()) - return Collections.EMPTY_LIST; - - ArrayList result = new ArrayList(); - result.add(new Result(o, m, ((MetricMeasurement)resultat.get(0)).getResult(), type)); - return result; - - } - - /**{@inheritDoc}*/ - @Override - public final List> getMetricActivationTypes (Metric m) { - return metricActType.get(m); - } - - /** - * Check if the plug-in dependencies are satisfied - */ - private boolean checkDependencies() { - for (String mnemonic : dependencies) { - //Check thyself first - if (metrics.containsKey(mnemonic)) - continue; - - if (pa.getImplementingPlugin(mnemonic) == null) { - log.error("No plug-in implements metric " + mnemonic + - " which is required by " + getName()); - return false; - } - } - return true; - } - - /** {@inheritDoc} */ - public Set getDependencies() { - return dependencies; - } - - @Override - public Map> getObjectIdsToSync(StoredProject sp, Metric m) - throws MetricActivationException { - - Map> IDs = new HashMap>(); - - Map params = new HashMap(); - params.put("project", sp); - params.put("metric", m.getId()); - - String q = null; - - for (Class at : getMetricActivationTypes(m)) { - - if (MetricType.fromActivator(at) == Type.PROJECT_VERSION) { - q = QRY_SYNC_PV; - } else if (MetricType.fromActivator(at) == Type.SOURCE_FILE) { - q = QRY_SYNC_PF; - } else if (MetricType.fromActivator(at) == Type.SOURCE_DIRECTORY) { - q = QRY_SYNC_PD; - } else if (MetricType.fromActivator(at) == Type.MAILING_LIST) { - throw new MetricActivationException("Metric synchronisation with MAILING_LIST objects not implemented"); - } else if (MetricType.fromActivator(at) == Type.MAILMESSAGE) { - q = QRY_SYNC_MM; - } else if (MetricType.fromActivator(at) == Type.MAILTHREAD) { - q = QRY_SYNC_MT; - } else if (MetricType.fromActivator(at) == Type.BUG) { - throw new MetricActivationException("Metric synchronisation with BUG objects not implemented"); - } else if (MetricType.fromActivator(at) == Type.DEVELOPER) { - q = QRY_SYNC_DEV; - } else if (MetricType.fromActivator(at) == Type.NAMESPACE) { - q = QRY_SYNC_NS; - } else if (MetricType.fromActivator(at) == Type.ENCAPSUNIT) { - q = QRY_SYNC_ENCUNT; - } else if (MetricType.fromActivator(at) == Type.EXECUNIT) { - q = QRY_SYNC_EXECUNT; - } else { - throw new MetricActivationException("Metric synchronisation with GENERIC objects not implemented"); - } - - List objectIds = (List) db.doHQL(q, params); - TreeSet ids = new TreeSet(); - ids.addAll(objectIds); - IDs.put(MetricType.fromActivator(at), ids); - } - return IDs; - } - - /** {@inheritDoc} */ - @Override - public void setJob(Job j) { - this.job.set(j); - } - } diff --git a/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/AlitheiaPlugin.java b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/AlitheiaPlugin.java index 9d55e94ab..4c1d828f7 100644 --- a/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/AlitheiaPlugin.java +++ b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/AlitheiaPlugin.java @@ -96,215 +96,6 @@ * * */ -public interface AlitheiaPlugin { +public interface AlitheiaPlugin extends MetricMetaData, MetricMeasurementEvaluation, MetricLifeCycle, MetricConfiguration{ - /** - * Get the metric version. Free form text. - * - * @return The metric's version. - */ - String getVersion(); - - /** - * Get information about the metric author - * - * @return The metric's author. - */ - String getAuthor(); - - /** - * Get the date this version of the metric has been installed - * - * @return The metric's installation date. - */ - Date getDateInstalled(); - - /** - * Get the metric name - * - * @return The metric's name. - */ - String getName(); - - /** - * Get a free text description of what this metric calculates - * - * @return The metric's description. - */ - String getDescription(); - - /** - * Get the metric result, without triggering a metric recalculation if - * the result is not present. - * If the result was not calculated yet, the result set is empty. If you - * want to trigger the calculation to get a result, use getResult() instead. - * - * @param o DAO whose type specifies the specialized sub-interface to use - * and whose value determines which result to get. - * @return l A list of metrics - * @return value of the measurement or null if there is no such measurement. - * @throws MetricMismatchException if the DAO type is one not supported by - * this metric. - */ - List getResultIfAlreadyCalculated(DAObject o, List l) - throws MetricMismatchException; - - /** - * Get a metric result. - * If the result was not calculated yet, the plugin's run method is called, - * and the request waits until the run method returns. - * If you don't want this behavior, use getResultIfAlreadyCalculated() - * instead. - * - * @param o DAO whose type specifies the specialized sub-interface to use - * and whose value determines which result to get. - * @return l A list of metrics - * @return value of the measurement or null if there is no such measurement. - * @throws MetricMismatchException if the DAO type is one not supported by - * this metric. - * @throws AlreadyProcessingException to signify that the provided DAO is - * currently locked for processing by another thread. - * @throws Exception All exceptions initiated by the errors in code - * included in implemenations of those classes. - */ - List getResult(DAObject o, List l) - throws MetricMismatchException, AlreadyProcessingException, Exception; - - /** - * Get the description objects for all metrics supported by this plug-in - * as found in the database. - * - * @return the list of metric descriptors, or null if none - */ - List getAllSupportedMetrics(); - - /** - * Get all metrics that are bound to the provided activation type. - * - * @return the list of metric DAOs for the provided activation type, empty - * if the plug-in does not support the provided activation type - */ - List getSupportedMetrics(Class activationType); - - /** - * This method performs a measurement for - * the given DAO, if possible. The DAO might be any one of the types - * that make sense for measurements -- ProjectVersion, projectFile, - * some others. If a DAO of a type that the metric doesn't support - * is passed in, throws a MetricMismatchException. - * - * The calculation of measurements may be a computationally expensive - * task, so metrics should start jobs (by themselves) to handle that. - * The subclass AbstractMetric handles job creation automatically for - * metrics that have simple requirements (a single job for doing the - * calculation). - * - * Note that even if you use (parallel running) jobs in your jobs, the - * metric's run method needs to block until the result is calculated. - * - * @param o The DAO that gets passed to the plug-in in order to run it - * @throws MetricMismatchException if the DAO is of an unsupported type. - * @throws AlreadyProcessingException to signify that the provided DAO is - * currently locked for processing by another thread. - * @throws Exception Any other exception initiated from the plugin code - */ - void run(DAObject o) throws MetricMismatchException, - AlreadyProcessingException, Exception; - - /** - * After installing a new version of the metric, try to - * update the results. The metric may opt to partially - * or fully update its results tables or files. - * - * @return True, if the update succeeded, false otherwise - */ - boolean update(); - - /** - * Perform maintenance operations when installing a new - * version of the metric - * - * @return True if installation succeeded, false otherwise - */ - boolean install(); - - /** - * Free the used resources and clean up on metric removal - * - * @return True, if the removal succeeded, false otherwise - */ - boolean remove(); - - /** - * Clean results on project removal - * - * @param sp The DAO to be used as reference when cleaning up results. - * @return True, if the cleanup succeeded, false otherwise - */ - boolean cleanup(DAObject sp); - - /** - * Return a string that is unique for this plugin, used for indexing this - * plugin to the system database - * - * @return A unique string, max length 255 characters - */ - String getUniqueKey(); - - /** - * Get the types supported by this plug-in for data processing and result - * retrieval. An activation type is DAO subclass which is passed as argument - * to the {@link AlitheiaPlugin.run()} and - * {@link AlitheiaPlugin.getResult()}} methods to trigger metric - * calculation and result retrieval. - * - * @return A set of DAObject subclasses - */ - Set> getActivationTypes(); - - /** - * Get the activation type that corresponds to the activation type which - * the metric result is stored. - * - * @param m - The metric for which to search for an activation type - * @return A list of subclasses of DAObject (a.k.a activation types). - */ - List> getMetricActivationTypes (Metric m); - - /** - * Retrieves the list of configuration properties for this plug-in. - *
- * Metric plug-ins can use the AbstractMetric's - * addConfigEntry and removeConfigEntry methods - * to manage their own configuration schema. - * - * @return The set of the existing configuration properties for - * this plug-in. This may be an empty list if no configuration is - * needed or if the plug-in is not active. - */ - Set getConfigurationSchema(); - - /** - * Metric mnemonics for the metrics required to be present for this - * plugin to operate. - * - * @return A, possibly empty, set of metric mnemonics. - */ - Set getDependencies(); - - /** - * Get a list of object ids for the database entities to run the metric - * on, ordered by activation type. This method essentially allows the plugin - * to specify a custom processing order for metadata entities to be processed - * by metrics. The default execution order is specified - * - */ - Map> getObjectIdsToSync(StoredProject sp, Metric m) - throws MetricActivationException; - - /** - * Set a reference to the scheduler job that executes the metric. The - * job reference is only set when the metric's execute method is called. - */ - void setJob(Job j); } diff --git a/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMeasurementEvaluation.java b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMeasurementEvaluation.java new file mode 100755 index 000000000..e0e1bcbe3 --- /dev/null +++ b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMeasurementEvaluation.java @@ -0,0 +1,282 @@ +package eu.sqooss.service.abstractmetric; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import eu.sqooss.core.AlitheiaCore; +import eu.sqooss.service.db.DAObject; +import eu.sqooss.service.db.Metric; +import eu.sqooss.service.db.Plugin; +import eu.sqooss.service.logging.Logger; +import eu.sqooss.service.metricactivator.MetricActivator; +import eu.sqooss.service.scheduler.Job; +import eu.sqooss.service.util.Pair; + +public class DefaultMeasurementEvaluation implements MetricMeasurementEvaluation{ + Logger log = AlitheiaCore.getInstance().getLogManager().createLogger(Logger.NAME_SQOOSS_METRIC); + + /** + * The scheduler job that executes this metric. + */ + protected ThreadLocal job = new ThreadLocal(); + + /** Set of declared metrics indexed by their mnemonic*/ + private Map metrics = new HashMap(); + + private AlitheiaPlugin plugin; + private MetricDiscovery metricDiscovery; + + public DefaultMeasurementEvaluation(AlitheiaPlugin plugin, MetricDiscovery metricDiscovery) { + this.plugin = plugin; + this.metricDiscovery = metricDiscovery; + } + + + /** + * Call the appropriate getResult() method according to + * the type of the entity that is measured. + * + * Use this method if you don't want the metric results + * to be calculated on-demand. Otherwise, use getResult(). + * + * @param o DAO that specifies the desired result type. + * The type of o is used to dispatch to the correct + * specialized getResult() method of the sub-interfaces. + * @return result (measurement) performed by this metric + * on the project data specified by o. + * @throws MetricMismatchException if the DAO is of a type + * not supported by this metric. + */ + @SuppressWarnings("unchecked") + public List getResultIfAlreadyCalculated(DAObject o, List l) throws MetricMismatchException { + boolean found = false; + List result = new ArrayList(); + + for (Metric m : l) { + if (!metrics.containsKey(m.getMnemonic())) { + throw new MetricMismatchException("Metric " + m.getMnemonic() + + " not defined by plugin " + + Plugin.getPluginByHashcode(plugin.getUniqueKey()).getName()); + } + List re = null; + try { + Method method = findGetResultMethod(o.getClass()); + re = (List) method.invoke(this, o, m); + } catch (SecurityException e) { + logErr("getResult", o, e); + } catch (NoSuchMethodException e) { + log.error("No method getResult(" + m.getMetricType().toActivator() + ") for type " + + this.getClass().getName()); + } catch (IllegalArgumentException e) { + logErr("getResult", o, e); + } catch (IllegalAccessException e) { + logErr("getResult", o, e); + } catch (InvocationTargetException e) { + logErr("getResult", o, e); + } + if (re != null && !re.isEmpty()) { + result.addAll(re); + } + } + + return result; + } + + private Method findGetResultMethod(Class clazz) + throws NoSuchMethodException { + Method m = null; + + try { + m = this.getClass().getMethod("getResult", clazz, Metric.class); + } catch (NoSuchMethodException nsme) { + try { + m = this.getClass().getMethod("getResult", clazz.getSuperclass(), Metric.class); + } catch (NoSuchMethodException nsme1) { + throw nsme; + } + } + + return m; + } + + /** + * Call the appropriate getResult() method according to + * the type of the entity that is measured. + * + * If the appropriate getResult() doesn't return any value, + * the metric is forced to calculate the result. Then the + * appropriate getResult() method is called again. + * + * @param o DAO that specifies the desired result type. + * The type of o is used to dispatch to the correct + * specialized getResult() method of the sub-interfaces. + * @return result (measurement) performed by this metric + * on the project data specified by o. + * @throws MetricMismatchException if the DAO is of a type + * not supported by this metric. + * @throws AlreadyProcessingException + */ + public List getResult(DAObject o, List l) + throws MetricMismatchException, AlreadyProcessingException, Exception { + List r = getResultIfAlreadyCalculated(o, l); + + // the result hasn't been calculated yet. Do so. + if (r == null || r.size() == 0) { + /* + * To ensure that no two instances of the metric operate on the same + * DAO lock on the DAO. Working on the same DAO can happen often + * when a plugin starts the calculation of another metric as a + * result of a plugin dependency association. This lock has the side + * effect that no two Plugins can be invoked with the same DAO as an + * argument even if the plug-ins do not depend on each other. + */ + synchronized (lockObject(o)) { + try { + run(o); + + r = getResultIfAlreadyCalculated(o, l); + if (r == null || r.size() == 0) { + if (job.get().state() != Job.State.Yielded) + log.debug("Metric " + getClass() + " didn't return" + + "a result even after running it. DAO: " + + o.getId()); + } + } finally { + unlockObject(o); + } + } + } + + return r; + } + + private Map> locks = new HashMap>(); + + private Object lockObject(DAObject o) throws AlreadyProcessingException { + synchronized (locks) { + if (!locks.containsKey(o.getId())) { + locks.put(o.getId(), + new Pair(new Object(),0)); + } + Pair p = locks.get(o.getId()); + if (p.second + 1 > 1) { + /* + * Break and reschedule the calculation of each call to the + * getResult method if it originates from another thread than + * the thread that has currently locked the DAO object. + * This is required for the DB transaction in the stopped + * job to see the results of the calculation of the original + * job. + */ + log.debug("DAO Id:" + o.getId() + + " Already locked - failing job"); + try { + throw new AlreadyProcessingException(); + } finally { + MetricActivator ma = AlitheiaCore.getInstance().getMetricActivator(); + ma.runMetric(o, plugin); + } + } + p.second = p.second + 1; + return p.first; + } + } + + private void unlockObject(DAObject o) { + synchronized(locks) { + Pair p = locks.get(o.getId()); + p.second = p.second - 1; + if (p.second == 0) { + locks.remove(o.getId()); + } else { + log.debug("Unlocking DAO Id:" + o.getId()); + } + } + } + + /** + * Call the appropriate run() method according to the type of the entity + * that is measured. + * + * @param o + * DAO which determines which sub-interface run method is + * called and also determines what is to be measured by that + * sub-interface. + * @throws MetricMismatchException + * if the DAO is of a type not supported by this metric. + */ + public void run(DAObject o) throws MetricMismatchException, + AlreadyProcessingException, Exception { + + if (!metricDiscovery.checkDependencies()) { + log.error("Plug-in dependency check failed"); + return; + } + + try { + Method m = findRunMethod("run", o.getClass()); + m.invoke(this, o); + } catch (SecurityException e) { + logErr("run", o, e); + } catch (NoSuchMethodException e) { + logErr("run", o, e); + } catch (IllegalArgumentException e) { + logErr("run", o, e); + } catch (IllegalAccessException e) { + logErr("run", o, e); + } catch (InvocationTargetException e) { + // Forward exception to metric job exception handler + if (e.getCause() instanceof AlreadyProcessingException) { + throw (AlreadyProcessingException) e.getCause(); + } else { + if (e != null && e.getCause() != null) { + logErr("run", o, e); + if (e.getCause() != null) + throw new Exception(e.getCause()); + else + throw new Exception(e); + } + } + } + } + + + + private Method findRunMethod(String name, Class clazz) + throws NoSuchMethodException { + Method m = null; + + try { + m = this.getClass().getMethod(name, clazz); + } catch (NoSuchMethodException nsme) { + try { + m = this.getClass().getMethod(name, clazz.getSuperclass()); + } catch (NoSuchMethodException nsme1) { + throw nsme; + } + } + + return m; + } + + private void logErr(String method, DAObject o, Exception e) { + log.error("Plugin:" + this.getClass().toString() + + "\nDAO id: " + o.getId() + + "\nDAO class: " + o.getClass() + + "\nDAO toString(): " + o.toString() + + "\nError when invoking the " + method + " method." + + "\nException:" + e.getClass().getName() + + "\nError:" + e.getMessage() + + "\nReason:" + e.getCause(), e); + } + + /** {@inheritDoc} */ + @Override + public void setJob(Job j) { + this.job.set(j); + } +} diff --git a/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetric.java b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetric.java new file mode 100644 index 000000000..3258b5d7b --- /dev/null +++ b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetric.java @@ -0,0 +1,429 @@ +/* + * This file is part of the Alitheia system, developed by the SQO-OSS + * consortium as part of the IST FP6 SQO-OSS project, number 033331. + * + * Copyright 2008 - 2010 - Organization for Free and Open Source Software, + * Athens, Greece. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +package eu.sqooss.service.abstractmetric; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; + +import org.osgi.framework.BundleContext; + +import eu.sqooss.core.AlitheiaCore; +import eu.sqooss.service.db.DAObject; +import eu.sqooss.service.db.DBService; +import eu.sqooss.service.db.EncapsulationUnitMeasurement; +import eu.sqooss.service.db.ExecutionUnitMeasurement; +import eu.sqooss.service.db.MailMessageMeasurement; +import eu.sqooss.service.db.MailingListThreadMeasurement; +import eu.sqooss.service.db.Metric; +import eu.sqooss.service.db.MetricMeasurement; +import eu.sqooss.service.db.MetricType.Type; +import eu.sqooss.service.db.NameSpaceMeasurement; +import eu.sqooss.service.db.PluginConfiguration; +import eu.sqooss.service.db.ProjectFileMeasurement; +import eu.sqooss.service.db.ProjectFileState; +import eu.sqooss.service.db.ProjectVersion; +import eu.sqooss.service.db.ProjectVersionMeasurement; +import eu.sqooss.service.db.StoredProject; +import eu.sqooss.service.db.StoredProjectMeasurement; +import eu.sqooss.service.logging.Logger; +import eu.sqooss.service.metricactivator.MetricActivationException; +import eu.sqooss.service.pa.PluginAdmin; +import eu.sqooss.service.pa.PluginInfo; +import eu.sqooss.service.scheduler.Job; + +/** + * A base class for all metrics. Implements basic functionality such as + * logging setup and plug-in information retrieval from the OSGi bundle + * manifest file. Metrics can choose to directly implement + * the {@link eu.sqooss.abstractmetric.AlitheiaPlugin} interface instead of + * extending this class. + */ +public class DefaultMetric implements AlitheiaPlugin { + + /** Reference to the metric bundle context */ + protected BundleContext bc; + + /** Logger for administrative operations */ + protected Logger log = null; + + /** Reference to the DB service, not to be passed to metric jobs */ + protected DBService db; + + /** + * Reference to the plugin administrator service, not to be passed to + * metric jobs + */ + protected PluginAdmin pa; + + protected MetricDiscovery metricDiscovery; + protected MetricMetaData metricMetadata; + protected MetricMeasurementEvaluation metricEvaluation; + protected MetricLifeCycle metricLifeCycle; + protected MetricConfiguration metricConfiguration; + + + /** + * Init basic services common to all implementing classes + * @param bc - The bundle context of the implementing metric - to be passed + * by the activator. + */ + protected DefaultMetric(BundleContext bc) { + + this.bc = bc; + + log = AlitheiaCore.getInstance().getLogManager().createLogger(Logger.NAME_SQOOSS_METRIC); + + if (log == null) { + System.out.println("ERROR: Got no logger"); + } + + db = AlitheiaCore.getInstance().getDBService(); + + if(db == null) + log.error("Could not get a reference to the DB service"); + + pa = AlitheiaCore.getInstance().getPluginAdmin(); + + if(pa == null) + log.error("Could not get a reference to the Plugin Administation " + + "service"); + + this.metricDiscovery = new MetricDiscovery(this); + this.metricMetadata = new DefaultMetricMetaData(this, bc); + this.metricEvaluation = new DefaultMeasurementEvaluation(this, metricDiscovery); + this.metricLifeCycle = new DefaultMetricLifeCycle(bc, this, metricDiscovery); + this.metricConfiguration = new DefaultMetricConfiguration(bc, this, metricDiscovery); + } + + + protected List QRY_SOURCE_DIRS(ProjectVersion pv, String MNOL, String ISSRCDIR){ + String paramIsDirectory = "is_directory"; + String paramMNOL = "paramMNOL"; + String paramISSRCDIR = "paramISSRCDIR"; + String paramVersionId = "paramVersionId"; + String paramProjectId = "paramProjectId"; + String paramState = "paramStatus"; + + + Map params = new HashMap(); + + StringBuffer q = new StringBuffer("select pfm "); + if (pv.getSequence() == ProjectVersion.getLastProjectVersion(pv.getProject()).getSequence()) { + q.append(" from ProjectFile pf, ProjectFileMeasurement pfm"); + q.append(" where pf.validUntil is null "); + } else { + q.append(" from ProjectVersion pv, ProjectVersion pv2,"); + q.append(" ProjectVersion pv3, ProjectFile pf, "); + q.append(" ProjectFileMeasurement pfm "); + q.append(" where pv.project.id = :").append(paramProjectId); + q.append(" and pv.id = :").append(paramVersionId); + q.append(" and pv2.project.id = :").append(paramProjectId); + q.append(" and pv3.project.id = :").append(paramProjectId); + q.append(" and pf.validFrom.id = pv2.id"); + q.append(" and pf.validUntil.id = pv3.id"); + q.append(" and pv2.sequence <= pv.sequence"); + q.append(" and pv3.sequence >= pv.sequence"); + + params.put(paramProjectId, pv.getProject().getId()); + params.put(paramVersionId, pv.getId()); + } + + q.append(" and pf.state <> :").append(paramState); + q.append(" and pf.isDirectory = :").append(paramIsDirectory); + q.append(" and pfm.projectFile = pf"); + q.append(" and pfm.metric = :").append(paramMNOL); + q.append(" and exists (select pfm1 "); + q.append(" from ProjectFileMeasurement pfm1 "); + q.append(" where pfm1.projectFile = pfm.projectFile "); + q.append(" and pfm1.metric = :").append(paramISSRCDIR).append(")"); + + params.put(paramState, ProjectFileState.deleted()); + params.put(paramIsDirectory, true); + params.put(paramMNOL, Metric.getMetricByMnemonic(MNOL)); + params.put(paramISSRCDIR, Metric.getMetricByMnemonic(ISSRCDIR)); + return (List) db.doHQL(q.toString(), params); + } + + + /** + * Add an entry to this plug-in's configuration schema. + * + * @param name The name of the configuration property + * @param defValue The default value for the configuration property + * @param msg The description of the configuration property + * @param type The type of the configuration property + */ + protected final void addConfigEntry(String name, String defValue, + String msg, PluginInfo.ConfigurationType type) { + // Retrieve the plug-in's info object + PluginInfo pi = pa.getPluginInfo(getUniqueKey()); + // Will happen if called during bundle's startup + if (pi == null) { + log.warn("Adding configuration key <" + name + + "> to plugin <" + getName() + "> failed: " + + "no PluginInfo."); + return; + } + // Modify the plug-in's configuration + try { + // Update property + if (pi.hasConfProp(name, type.toString())) { + if (pi.updateConfigEntry(db, name, defValue)) { + // Update the Plug-in Admin's information + pa.pluginUpdated(pa.getPlugin(pi)); + } + else { + log.error("Property (" + name +") update has failed!"); + } + } + // Create property + else { + if (pi.addConfigEntry( + db, name, msg, type.toString(), defValue)) { + // Update the Plug-in Admin's information + pa.pluginUpdated(pa.getPlugin(pi)); + } + else { + log.error("Property (" + name +") append has failed!"); + } + } + } + catch (Exception ex){ + log.error("Can not modify property (" + name +") for plugin (" + + getName(), ex); + } + } + + /** + * Remove an entry from the plug-in's configuration schema + * + * @param name The name of the configuration property to remove + * @param name The type of the configuration property to remove + */ + protected final void removeConfigEntry( + String name, + PluginInfo.ConfigurationType type) { + // Retrieve the plug-in's info object + PluginInfo pi = pa.getPluginInfo(getUniqueKey()); + // Will happen if called during bundle's startup + if (pi == null) { + log.warn("Removing configuration key <" + name + + "> from plugin <" + getName() + "> failed: " + + "no PluginInfo."); + return; + } + // Modify the plug-in's configuration + try { + if (pi.hasConfProp(name, type.toString())) { + if (pi.removeConfigEntry(db, name, type.toString())) { + // Update the Plug-in Admin's information + pa.pluginUpdated(pa.getPlugin(pi)); + } + else { + log.error("Property (" + name +") remove has failed!"); + } + } + else { + log.error("Property (" + name +") does not exist!"); + } + } + catch (Exception ex){ + log.error("Can not remove property (" + name +") from plugin (" + + getName() + ")", ex); + } + } + + /** + * Retrieve author information from the plug-in bundle + */ + public String getAuthor() { + return metricMetadata.getAuthor(); + } + + /** + * Retrieve the plug-in description from the plug-in bundle + */ + public String getDescription() { + return metricMetadata.getDescription(); + } + + /** + * Retrieve the plug-in name as specified in the metric bundle + */ + public String getName() { + return metricMetadata.getName(); + } + + /** + * Retrieve the plug-in version as specified in the metric bundle + */ + public String getVersion() { + return metricMetadata.getVersion(); + } + + @Override + public Date getDateInstalled() { + return metricMetadata.getDateInstalled(); + } + + @Override + public List getResult(DAObject o, List l) + throws MetricMismatchException, AlreadyProcessingException, + Exception { + return metricEvaluation.getResult(o, l); + } + + private static Map, String> resultFieldNames = + new HashMap, String>(); + + static { + resultFieldNames.put(StoredProjectMeasurement.class, "storedProject"); + resultFieldNames.put(ProjectVersionMeasurement.class, "projectVersion"); + resultFieldNames.put(ProjectFileMeasurement.class, "projectFile"); + resultFieldNames.put(MailMessageMeasurement.class, "mail"); + resultFieldNames.put(MailingListThreadMeasurement.class, "thread"); + resultFieldNames.put(ExecutionUnitMeasurement.class, "executionUnit"); + resultFieldNames.put(EncapsulationUnitMeasurement.class, "encapsulationUnit"); + resultFieldNames.put(NameSpaceMeasurement.class, "namespace"); + } + + /** + * Convenience method to get the measurement for a single metric. + */ + protected List getResult(DAObject o, Class clazz, + Metric m, Result.ResultType type) { + DBService dbs = AlitheiaCore.getInstance().getDBService(); + Map props = new HashMap(); + + props.put(resultFieldNames.get(clazz), o); + props.put("metric", m); + List resultat = dbs.findObjectsByProperties(clazz, props); + + if (resultat.isEmpty()) + return Collections.EMPTY_LIST; + + ArrayList result = new ArrayList(); + result.add(new Result(o, m, ((MetricMeasurement)resultat.get(0)).getResult(), type)); + return result; + + } + + @Override + public List getResultIfAlreadyCalculated(DAObject o, List l) + throws MetricMismatchException { + return metricEvaluation.getResultIfAlreadyCalculated(o, l); + } + + @Override + public void run(DAObject o) throws MetricMismatchException, + AlreadyProcessingException, Exception { + metricEvaluation.run(o); + } + + @Override + public void setJob(Job j) { + metricEvaluation.setJob(j); + } + + @Override + public boolean update() { + return metricLifeCycle.update(); + } + + @Override + public boolean install() { + return metricLifeCycle.install(); + } + + @Override + public boolean remove() { + return metricLifeCycle.remove(); + } + + @Override + public boolean cleanup(DAObject sp) { + return metricConfiguration.cleanup(sp); + } + + @Override + public String getUniqueKey() { + return metricConfiguration.getUniqueKey(); + } + + @Override + public Set> getActivationTypes() { + return metricConfiguration.getActivationTypes(); + } + + @Override + public Set getConfigurationSchema() { + return metricConfiguration.getConfigurationSchema(); + } + + @Override + public Set getDependencies() { + return metricConfiguration.getDependencies(); + } + + @Override + public Map> getObjectIdsToSync(StoredProject sp, + Metric m) throws MetricActivationException { + return metricConfiguration.getObjectIdsToSync(sp, m); + } + + @Override + public List getAllSupportedMetrics() { + return metricConfiguration.getAllSupportedMetrics(); + } + + @Override + public List getSupportedMetrics(Class activator) { + return metricConfiguration.getSupportedMetrics(activator); + } + + @Override + public PluginConfiguration getConfigurationOption(String config) { + return metricConfiguration.getConfigurationOption(config); + } + + @Override + public List> getMetricActivationTypes(Metric m) { + return metricConfiguration.getMetricActivationTypes(m); + } + } diff --git a/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetricConfiguration.java b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetricConfiguration.java new file mode 100755 index 000000000..9dab1fde6 --- /dev/null +++ b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetricConfiguration.java @@ -0,0 +1,289 @@ +package eu.sqooss.service.abstractmetric; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +import eu.sqooss.core.AlitheiaCore; +import eu.sqooss.service.db.DAObject; +import eu.sqooss.service.db.DBService; +import eu.sqooss.service.db.Metric; +import eu.sqooss.service.db.MetricType; +import eu.sqooss.service.db.Plugin; +import eu.sqooss.service.db.PluginConfiguration; +import eu.sqooss.service.db.StoredProject; +import eu.sqooss.service.db.MetricType.Type; +import eu.sqooss.service.logging.Logger; +import eu.sqooss.service.metricactivator.MetricActivationException; +import eu.sqooss.service.pa.PluginInfo; + +public class DefaultMetricConfiguration implements MetricConfiguration{ + protected static final String QRY_SYNC_PV = "select pv.id from ProjectVersion pv " + + "where pv.project = :project and not exists(" + + " select pvm.projectVersion from ProjectVersionMeasurement pvm " + + " where pvm.projectVersion.id = pv.id and pvm.metric.id = :metric) " + + "order by pv.sequence asc"; + + protected static final String QRY_SYNC_PF = "select pf.id " + + "from ProjectVersion pv, ProjectFile pf " + + "where pf.projectVersion=pv and pv.project = :project " + + "and not exists (" + + " select pfm.projectFile " + + " from ProjectFileMeasurement pfm " + + " where pfm.projectFile.id = pf.id " + + " and pfm.metric.id = :metric) " + + " and pf.isDirectory = false) " + + "order by pv.sequence asc"; + + protected static final String QRY_SYNC_PD = "select pf.id " + + "from ProjectVersion pv, ProjectFile pf " + + "where pf.projectVersion=pv and pv.project = :project " + + "and not exists (" + + " select pfm.projectFile " + + " from ProjectFileMeasurement pfm " + + " where pfm.projectFile.id = pf.id " + + " and pfm.metric.id = :metric) " + + " and pf.isDirectory = true) " + + "order by pv.sequence asc"; + + protected static final String QRY_SYNC_MM = "select mm.id " + + "from MailMessage mm " + + "where mm.list.storedProject = :project " + + "and mm.id not in (" + + " select mmm.mail.id " + + " from MailMessageMeasurement mmm " + + " where mmm.metric.id =:metric and mmm.mail.id = mm.id))"; + + protected static final String QRY_SYNC_MT = "select mlt.id " + + "from MailingListThread mlt " + + "where mlt.list.storedProject = :project " + + "and mlt.id not in (" + + " select mltm.thread.id " + + " from MailingListThreadMeasurement mltm " + + " where mltm.metric.id =:metric and mltm.thread.id = mlt.id)"; + + protected static final String QRY_SYNC_DEV = "select d.id " + + "from Developer d " + + "where d.storedProject = :project"; + + protected static final String QRY_SYNC_NS = "select ns.id " + + "from NameSpace ns, ProjectVersion pv " + + "where pv = ns.changeVersion " + + "and pv.project = :project " + + "and not exists ( " + + " select nsm " + + " from NameSpaceMeasurement nsm " + + " where nsm.metric.id = :metric " + + " and nsm.namespace = ns) " + + "order by pv.sequence asc"; + + protected static final String QRY_SYNC_ENCUNT = "select encu.id " + + "from EncapsulationUnit encu, ProjectVersion pv, ProjectFile pf " + + " where pf.projectVersion = pv " + + " and encu.file = pf " + + "and pv.project = :project " + + "and not exists ( " + + " select eum " + + " from EncapsulationUnitMeasurement eum " + + " where eum.encapsulationUnit = encu " + + " and eum.metric.id = :metric " + + " ) order by pv.sequence asc "; + + protected static final String QRY_SYNC_EXECUNT = "select exu.id " + + "from ExecutionUnit exu, EncapsulationUnit encu, " + + " ProjectVersion pv, ProjectFile pf " + + "where pf.projectVersion = pv " + + "and encu.file = pf " + + "and pv.project = :project " + + "and exu.changed = true " + + "and exu.encapsulationUnit = encu " + + "and not exists ( " + + " select eum " + + " from ExecutionUnitMeasurement eum " + + " where eum.executionUnit = exu " + + " and eum.metric.id = :metric) " + + "order by pv.sequence asc"; + + + Logger log = AlitheiaCore.getInstance().getLogManager().createLogger(Logger.NAME_SQOOSS_METRIC); + + private BundleContext bc; + private AlitheiaPlugin plugin; + private MetricDiscovery metricDiscovery; + private DBService db; + + public DefaultMetricConfiguration(BundleContext bc, AlitheiaPlugin plugin, MetricDiscovery metricDiscovery){ + this.bc = bc; + this.plugin = plugin; + this.metricDiscovery = metricDiscovery; + this.db = AlitheiaCore.getInstance().getDBService(); + } + + + /** + * Default (empty) implementation of the clean up method. What to + * do with the provided DAO is left to sub-classes to decide. + * {@inheritDoc} + */ + public boolean cleanup(DAObject sp) { + log.warn("Empty cleanup method for plug-in " + + this.getClass().getName()); + return true; + } + + /** + * Return an MD5 hex key uniquely identifying the plug-in + */ + public final String getUniqueKey() { + MessageDigest m = null; + try { + m = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + log.error("Cannot find a valid implementation of the MD5 " + + "hash algorithm"); + } + String name = this.getClass().getCanonicalName(); + byte[] data = name.getBytes(); + m.update(data,0,data.length); + BigInteger i = new BigInteger(1,m.digest()); + return String.format("%1$032X", i); + } + + /**{@inheritDoc}*/ + public final Set> getActivationTypes() { + return metricDiscovery.getActivators(); + } + + @Override + public Set getDependencies() { + return metricDiscovery.getDependencies(); + } + + @Override + public final List> getMetricActivationTypes (Metric m) { + return metricDiscovery.getMetricActType().get(m); + } + + /** {@inheritDoc} */ + public final Set getConfigurationSchema() { + // Retrieve the plug-in's info object + PluginInfo pi = AlitheiaCore.getInstance().getPluginAdmin().getPluginInfo(getUniqueKey()); + if (pi == null) { + // The plug-in's info object is always null during bundle startup, + // but if it is not available when the bundle is active, something + // is possibly wrong. + if (bc.getBundle().getState() == Bundle.ACTIVE) { + log.warn("Plugin <" + plugin.getName() + "> is loaded but not installed."); + } + return Collections.emptySet(); + } + return pi.getConfiguration(); + } + + + @Override + public Map> getObjectIdsToSync(StoredProject sp, Metric m) + throws MetricActivationException { + + Map> IDs = new HashMap>(); + + Map params = new HashMap(); + params.put("project", sp); + params.put("metric", m.getId()); + + String q = null; + + for (Class at : getMetricActivationTypes(m)) { + + if (MetricType.fromActivator(at) == Type.PROJECT_VERSION) { + q = QRY_SYNC_PV; + } else if (MetricType.fromActivator(at) == Type.SOURCE_FILE) { + q = QRY_SYNC_PF; + } else if (MetricType.fromActivator(at) == Type.SOURCE_DIRECTORY) { + q = QRY_SYNC_PD; + } else if (MetricType.fromActivator(at) == Type.MAILING_LIST) { + throw new MetricActivationException("Metric synchronisation with MAILING_LIST objects not implemented"); + } else if (MetricType.fromActivator(at) == Type.MAILMESSAGE) { + q = QRY_SYNC_MM; + } else if (MetricType.fromActivator(at) == Type.MAILTHREAD) { + q = QRY_SYNC_MT; + } else if (MetricType.fromActivator(at) == Type.BUG) { + throw new MetricActivationException("Metric synchronisation with BUG objects not implemented"); + } else if (MetricType.fromActivator(at) == Type.DEVELOPER) { + q = QRY_SYNC_DEV; + } else if (MetricType.fromActivator(at) == Type.NAMESPACE) { + q = QRY_SYNC_NS; + } else if (MetricType.fromActivator(at) == Type.ENCAPSUNIT) { + q = QRY_SYNC_ENCUNT; + } else if (MetricType.fromActivator(at) == Type.EXECUNIT) { + q = QRY_SYNC_EXECUNT; + } else { + throw new MetricActivationException("Metric synchronisation with GENERIC objects not implemented"); + } + + List objectIds = (List) db.doHQL(q, params); + TreeSet ids = new TreeSet(); + ids.addAll(objectIds); + IDs.put(MetricType.fromActivator(at), ids); + } + return IDs; + } + + public List getAllSupportedMetrics() { + String qry = "from Metric m where m.plugin=:plugin"; + Map params = new HashMap(); + params.put("plugin", Plugin.getPluginByHashcode(getUniqueKey())); + + return (List)db.doHQL(qry, params); + } + + /** {@inheritDoc} */ + @Override + public List getSupportedMetrics(Class activator) { + List m = new ArrayList(); + + //Query the database just once + List all = getAllSupportedMetrics(); + + if (all == null || all.isEmpty()) + return m; + + for (Metric metric : all) { + if (getMetricActivationTypes(metric).contains(activator)) { + m.add(metric); + } + } + + return m; + } + + + public PluginConfiguration getConfigurationOption(String config) { + Set conf = + AlitheiaCore.getInstance().getPluginAdmin().getPluginInfo(getUniqueKey()).getConfiguration(); + + Iterator i = conf.iterator(); + + while (i.hasNext()) { + PluginConfiguration pc = i.next(); + if (pc.getName().equals(config)) { + return pc; + } + } + + /* Config option not found */ + return null; + } +} diff --git a/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetricLifeCycle.java b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetricLifeCycle.java new file mode 100755 index 000000000..13d73d567 --- /dev/null +++ b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetricLifeCycle.java @@ -0,0 +1,112 @@ +package eu.sqooss.service.abstractmetric; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; + +import eu.sqooss.core.AlitheiaCore; +import eu.sqooss.service.db.DBService; +import eu.sqooss.service.db.Metric; +import eu.sqooss.service.db.MetricType; +import eu.sqooss.service.db.Plugin; +import eu.sqooss.service.db.MetricType.Type; +import eu.sqooss.service.logging.Logger; +import eu.sqooss.service.metricactivator.MetricActivator; + +public class DefaultMetricLifeCycle implements MetricLifeCycle{ + Logger log = AlitheiaCore.getInstance().getLogManager().createLogger(Logger.NAME_SQOOSS_METRIC); + + private AlitheiaPlugin plugin; + private MetricDiscovery metricDiscovery; + + private DBService db; + private BundleContext bc; + + public DefaultMetricLifeCycle(BundleContext bc, AlitheiaPlugin plugin, MetricDiscovery metricDiscovery){ + this.bc = bc; + this.plugin = plugin; + this.metricDiscovery = metricDiscovery; + this.db = AlitheiaCore.getInstance().getDBService(); + } + + /** + * Register the metric to the DB. Subclasses can run their custom + * initialization routines (i.e. registering DAOs or tables) after calling + * super().install() + */ + public boolean install() { + //1. check if dependencies are satisfied + if (!metricDiscovery.checkDependencies()) { + log.error("Plug-in installation failed"); + return false; + } + + HashMap h = new HashMap(); + h.put("name", plugin.getName()); + + List plugins = db.findObjectsByProperties(Plugin.class, h); + + if (!plugins.isEmpty()) { + log.warn("A plugin with name <" + plugin.getName() + + "> is already installed, won't re-install."); + return false; + } + + + //2. Add the plug-in + Plugin p = new Plugin(); + p.setName(plugin.getName()); + p.setInstalldate(new Date(System.currentTimeMillis())); + p.setVersion(plugin.getVersion()); + p.setActive(true); + p.setHashcode(plugin.getUniqueKey()); + boolean result = db.addRecord(p); + + //3. Add the metrics + for (Metric m : metricDiscovery) { + Type type = Type.fromString(m.getMetricType().getType()); + MetricType newType = MetricType.getMetricType(type); + if (newType == null) { + newType = new MetricType(type); + db.addRecord(newType); + m.setMetricType(newType); + } + + m.setMetricType(newType); + m.setPlugin(p); + db.addRecord(m); + } + + return result; + } + + /** + * Remove a plug-in's record from the DB. The DB's referential integrity + * mechanisms are expected to automatically remove associated records. + * Subclasses should also clean up any custom tables created. + */ + public boolean remove() { + Plugin p = Plugin.getPluginByHashcode(plugin.getUniqueKey()); + return db.deleteRecord(p); + } + + /**{@inheritDoc}}*/ + public boolean update() { + ServiceReference serviceRef = null; + serviceRef = bc.getServiceReference(AlitheiaCore.class.getName()); + + MetricActivator ma = + ((AlitheiaCore)bc.getService(serviceRef)).getMetricActivator(); + + if (ma == null) { + return false; + } + + ma.syncMetrics(plugin); + + return true; + } +} diff --git a/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetricMetaData.java b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetricMetaData.java new file mode 100755 index 000000000..98fbd0782 --- /dev/null +++ b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/DefaultMetricMetaData.java @@ -0,0 +1,68 @@ +package eu.sqooss.service.abstractmetric; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Date; + +import org.osgi.framework.BundleContext; + +import eu.sqooss.core.AlitheiaCore; +import eu.sqooss.service.db.Plugin; +import eu.sqooss.service.logging.Logger; + +public class DefaultMetricMetaData implements MetricMetaData{ + Logger log = AlitheiaCore.getInstance().getLogManager().createLogger(Logger.NAME_SQOOSS_METRIC); + + private AlitheiaPlugin plugin; + + private BundleContext bc; + + public DefaultMetricMetaData(AlitheiaPlugin plugin, BundleContext bc) { + this.plugin = plugin; + this.bc = bc; + } + + /** + * Retrieve author information from the plug-in bundle + */ + public String getAuthor() { + + return (String) bc.getBundle().getHeaders().get( + org.osgi.framework.Constants.BUNDLE_CONTACTADDRESS); + } + + /** + * Retrieve the plug-in description from the plug-in bundle + */ + public String getDescription() { + + return (String) bc.getBundle().getHeaders().get( + org.osgi.framework.Constants.BUNDLE_DESCRIPTION); + } + + /** + * Retrieve the plug-in name as specified in the metric bundle + */ + public String getName() { + + return (String) bc.getBundle().getHeaders().get( + org.osgi.framework.Constants.BUNDLE_NAME); + } + + /** + * Retrieve the plug-in version as specified in the metric bundle + */ + public String getVersion() { + + return (String) bc.getBundle().getHeaders().get( + org.osgi.framework.Constants.BUNDLE_VERSION); + } + + /** + * Retrieve the installation date for this plug-in version + */ + public final Date getDateInstalled() { + return Plugin.getPluginByHashcode(plugin.getUniqueKey()).getInstalldate(); + } +} \ No newline at end of file diff --git a/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricConfiguration.java b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricConfiguration.java new file mode 100755 index 000000000..b2c9bb81a --- /dev/null +++ b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricConfiguration.java @@ -0,0 +1,98 @@ +package eu.sqooss.service.abstractmetric; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; + +import eu.sqooss.service.db.DAObject; +import eu.sqooss.service.db.Metric; +import eu.sqooss.service.db.MetricType; +import eu.sqooss.service.db.PluginConfiguration; +import eu.sqooss.service.db.StoredProject; +import eu.sqooss.service.metricactivator.MetricActivationException; +import eu.sqooss.service.scheduler.Job; + +public interface MetricConfiguration { + /** + * Clean results on project removal + * + * @param sp The DAO to be used as reference when cleaning up results. + * @return True, if the cleanup succeeded, false otherwise + */ + boolean cleanup(DAObject sp); + + /** + * Return a string that is unique for this plugin, used for indexing this + * plugin to the system database + * + * @return A unique string, max length 255 characters + */ + String getUniqueKey(); + + /** + * Get the types supported by this plug-in for data processing and result + * retrieval. An activation type is DAO subclass which is passed as argument + * to the {@link AlitheiaPlugin.run()} and + * {@link AlitheiaPlugin.getResult()}} methods to trigger metric + * calculation and result retrieval. + * + * @return A set of DAObject subclasses + */ + Set> getActivationTypes(); + + /** + * Get the activation type that corresponds to the activation type which + * the metric result is stored. + * + * @param m - The metric for which to search for an activation type + * @return A list of subclasses of DAObject (a.k.a activation types). + */ + List> getMetricActivationTypes (Metric m); + + /** + * Retrieves the list of configuration properties for this plug-in. + *
+ * Metric plug-ins can use the AbstractMetric's + * addConfigEntry and removeConfigEntry methods + * to manage their own configuration schema. + * + * @return The set of the existing configuration properties for + * this plug-in. This may be an empty list if no configuration is + * needed or if the plug-in is not active. + */ + Set getConfigurationSchema(); + + /** + * Metric mnemonics for the metrics required to be present for this + * plugin to operate. + * + * @return A, possibly empty, set of metric mnemonics. + */ + Set getDependencies(); + + /** + * Get a list of object ids for the database entities to run the metric + * on, ordered by activation type. This method essentially allows the plugin + * to specify a custom processing order for metadata entities to be processed + * by metrics. The default execution order is specified + * + */ + Map> getObjectIdsToSync(StoredProject sp, Metric m) + throws MetricActivationException; + + public List getAllSupportedMetrics(); + + public List getSupportedMetrics(Class activator); + + /** + * Get a configuration option for this metric from the plugin configuration + * store + * + * @param config + * The configuration option to retrieve + * @return The configuration entry corresponding the provided description or + * null if not found in the plug-in's configuration schema + */ + public PluginConfiguration getConfigurationOption(String config); +} diff --git a/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricDiscovery.java b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricDiscovery.java new file mode 100755 index 000000000..652fd9537 --- /dev/null +++ b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricDiscovery.java @@ -0,0 +1,116 @@ +package eu.sqooss.service.abstractmetric; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import eu.sqooss.core.AlitheiaCore; +import eu.sqooss.service.db.DAObject; +import eu.sqooss.service.db.Metric; +import eu.sqooss.service.db.MetricType; +import eu.sqooss.service.logging.Logger; + +public class MetricDiscovery implements Iterable{ + Logger log = AlitheiaCore.getInstance().getLogManager().createLogger(Logger.NAME_SQOOSS_METRIC); + + private AlitheiaPlugin plugin; + + /** + * Metric mnemonics for the metrics required to be present for this + * metric to operate. + */ + private Set dependencies = new HashSet(); + private Map metrics; + + /** The list of this plug-in's activators*/ + private Set> activators = + new HashSet>(); + + private Map>> metricActType = + new HashMap>>(); + + + public MetricDiscovery(AlitheiaPlugin plugin) { + this.plugin = plugin; + + discoverMetrics(); + } + + private void discoverMetrics() + { + /*Discover the declared metrics*/ + MetricDeclarations md = this.getClass().getAnnotation(MetricDeclarations.class); + + if (md != null && md.metrics().length > 0) { + for (MetricDecl metric : md.metrics()) { + log.debug("Found metric: " + metric.mnemonic() + " with " + + metric.activators().length + " activators"); + + if (metrics.containsKey(metric.mnemonic())) { + log.error("Duplicate metric mnemonic " + metric.mnemonic()); + continue; + } + + Metric m = new Metric(); + m.setDescription(metric.descr()); + m.setMnemonic(metric.mnemonic()); + m.setMetricType(new MetricType(MetricType.fromActivator(metric.activators()[0]))); + + List> activs = new ArrayList>(); + for (Class o : metric.activators()) { + activs.add(o); + } + + metricActType.put(m, activs); + + activators.addAll(Arrays.asList(metric.activators())); + + metrics.put(m.getMnemonic(), m); + if (metric.dependencies().length > 0) + dependencies.addAll(Arrays.asList(metric.dependencies())); + } + } else { + log.warn("Plug-in " + plugin.getName() + " declares no metrics"); + } + } + + /** + * Check if the plug-in dependencies are satisfied + */ + public boolean checkDependencies() { + for (String mnemonic : dependencies) { + //Check thyself first + if (metrics.containsKey(mnemonic)) + continue; + + if (AlitheiaCore.getInstance().getPluginAdmin().getImplementingPlugin(mnemonic) == null) { + log.error("No plug-in implements metric " + mnemonic + + " which is required by " + plugin.getName()); + return false; + } + } + return true; + } + + @Override + public Iterator iterator() { + return metrics.values().iterator(); + } + + public Set> getActivators() { + return activators; + } + + public Set getDependencies() { + return dependencies; + } + + public Map>> getMetricActType() { + return metricActType; + } +} diff --git a/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricLifeCycle.java b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricLifeCycle.java new file mode 100755 index 000000000..4067c1f6a --- /dev/null +++ b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricLifeCycle.java @@ -0,0 +1,27 @@ +package eu.sqooss.service.abstractmetric; + +public interface MetricLifeCycle { + /** + * After installing a new version of the metric, try to + * update the results. The metric may opt to partially + * or fully update its results tables or files. + * + * @return True, if the update succeeded, false otherwise + */ + boolean update(); + + /** + * Perform maintenance operations when installing a new + * version of the metric + * + * @return True if installation succeeded, false otherwise + */ + boolean install(); + + /** + * Free the used resources and clean up on metric removal + * + * @return True, if the removal succeeded, false otherwise + */ + boolean remove(); +} diff --git a/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricMeasurementEvaluation.java b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricMeasurementEvaluation.java new file mode 100755 index 000000000..55af78146 --- /dev/null +++ b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricMeasurementEvaluation.java @@ -0,0 +1,77 @@ +package eu.sqooss.service.abstractmetric; + +import java.util.List; + +import eu.sqooss.service.db.DAObject; +import eu.sqooss.service.db.Metric; +import eu.sqooss.service.scheduler.Job; + +public interface MetricMeasurementEvaluation { + /** + * This method performs a measurement for + * the given DAO, if possible. The DAO might be any one of the types + * that make sense for measurements -- ProjectVersion, projectFile, + * some others. If a DAO of a type that the metric doesn't support + * is passed in, throws a MetricMismatchException. + * + * The calculation of measurements may be a computationally expensive + * task, so metrics should start jobs (by themselves) to handle that. + * The subclass AbstractMetric handles job creation automatically for + * metrics that have simple requirements (a single job for doing the + * calculation). + * + * Note that even if you use (parallel running) jobs in your jobs, the + * metric's run method needs to block until the result is calculated. + * + * @param o The DAO that gets passed to the plug-in in order to run it + * @throws MetricMismatchException if the DAO is of an unsupported type. + * @throws AlreadyProcessingException to signify that the provided DAO is + * currently locked for processing by another thread. + * @throws Exception Any other exception initiated from the plugin code + */ + void run(DAObject o) throws MetricMismatchException, + AlreadyProcessingException, Exception; + + /** + * Get the metric result, without triggering a metric recalculation if + * the result is not present. + * If the result was not calculated yet, the result set is empty. If you + * want to trigger the calculation to get a result, use getResult() instead. + * + * @param o DAO whose type specifies the specialized sub-interface to use + * and whose value determines which result to get. + * @return l A list of metrics + * @return value of the measurement or null if there is no such measurement. + * @throws MetricMismatchException if the DAO type is one not supported by + * this metric. + */ + List getResultIfAlreadyCalculated(DAObject o, List l) + throws MetricMismatchException; + + /** + * Get a metric result. + * If the result was not calculated yet, the plugin's run method is called, + * and the request waits until the run method returns. + * If you don't want this behavior, use getResultIfAlreadyCalculated() + * instead. + * + * @param o DAO whose type specifies the specialized sub-interface to use + * and whose value determines which result to get. + * @return l A list of metrics + * @return value of the measurement or null if there is no such measurement. + * @throws MetricMismatchException if the DAO type is one not supported by + * this metric. + * @throws AlreadyProcessingException to signify that the provided DAO is + * currently locked for processing by another thread. + * @throws Exception All exceptions initiated by the errors in code + * included in implemenations of those classes. + */ + List getResult(DAObject o, List l) + throws MetricMismatchException, AlreadyProcessingException, Exception; + + /** + * Set a reference to the scheduler job that executes the metric. The + * job reference is only set when the metric's execute method is called. + */ + void setJob(Job j); +} diff --git a/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricMetaData.java b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricMetaData.java new file mode 100755 index 000000000..3882a2e71 --- /dev/null +++ b/alitheia/core/src/main/java/eu/sqooss/service/abstractmetric/MetricMetaData.java @@ -0,0 +1,40 @@ +package eu.sqooss.service.abstractmetric; + +import java.util.Date; + +public interface MetricMetaData { + /** + * Get the metric version. Free form text. + * + * @return The metric's version. + */ + String getVersion(); + + /** + * Get information about the metric author + * + * @return The metric's author. + */ + String getAuthor(); + + /** + * Get the date this version of the metric has been installed + * + * @return The metric's installation date. + */ + Date getDateInstalled(); + + /** + * Get the metric name + * + * @return The metric's name. + */ + String getName(); + + /** + * Get a free text description of what this metric calculates + * + * @return The metric's description. + */ + String getDescription(); +} diff --git a/alitheia/core/src/main/java/eu/sqooss/service/util/Graph.java b/alitheia/core/src/main/java/eu/sqooss/service/util/Graph.java new file mode 100644 index 000000000..c4a4df26b --- /dev/null +++ b/alitheia/core/src/main/java/eu/sqooss/service/util/Graph.java @@ -0,0 +1,151 @@ +package eu.sqooss.service.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * Representation of a Graph + */ +public class Graph { + protected List> vertices; + protected Map, Set>> edges; + + public Graph(){ + this.vertices = new ArrayList>(); + this.edges = new HashMap, Set>>(); + } + + public Graph(int size){ + this.vertices = new ArrayList>(size); + this.edges = new HashMap, Set>>(size); + } + + public List> getVertices(){ + return this.vertices; + } + + public Map, Set>> getEdges() { + return edges; + } + + /** + * Add a vertex to the graph + * @param lab The label of the vertex + * @return the postition of the vertex + */ + public int addVertex(T lab) { + vertices.add(new Vertex(lab)); + return vertices.size()-1; + } + + /** + * Get the index of a vertex, or -1 if it is not in the graph + * @param vertex + */ + public int index(T label){ + int index = -1; + int i = 0; + while(i < vertices.size() && index == -1){ + if(vertices.get(i).label.equals(label)) index = i; + i++; + } + return index; + } + + /** + * Get the vertex with the given label + * @param label + */ + public Vertex getVertex(T label){ + return this.vertices.get(this.index(label)); + } + + /** + * Remove the vertex on the given index, along with edges from/to this vertex + * @param index + */ + public void removeVertex(int index){ + Vertex vertex = vertices.remove(index); + this.removeEdges(vertex); + } + + /** + * Remove all in- and outgoing edges of the given vertex + */ + public void removeEdges(Vertex vertex){ + // Remove outgoing edges + edges.remove(vertex); + + // Remove ingoing edges + for(Entry, Set>> entry : edges.entrySet()){ + entry.getValue().remove(vertex); + } + } + + /** + * Add an edge + * @param start The postition of the start vertex + * @param end The postition of the end vertex + */ + public void addEdge(int start, int end) { + if(!edges.containsKey(start)){ + edges.put(vertices.get(start), new HashSet>()); + } + edges.get(vertices.get(start)).add(vertices.get(end)); + } + + public void displayVertex(int v) { + System.out.print(vertices.get(v).label); + } + + /** + * Return the size (number of vertices) of this graph + */ + public int size(){ + return vertices.size(); + } + + /** + * Create a deep copy of this graph + */ + public Graph deepCopy(){ + Graph clone = new Graph(); + // Copy vertices + for(Vertex v : vertices){ + clone.addVertex(v.label); + } + // Copy edges + for(Entry, Set>> entry : edges.entrySet()){ + for(Vertex end : entry.getValue()){ + clone.addEdge(this.index(entry.getKey().label), clone.index(end.label)); + } + } + return clone; + } + + /** + * Vertex/Node class of a Graph + */ + static class Vertex { + public T label; + + public Vertex(T label) { + this.label = label; + } + + @Override + public String toString() { + return "Vertex<" + this.label.getClass().getName() + ">(" + label + ")"; + } + + @Override + public boolean equals(Object obj) { + return (obj instanceof Vertex) && label.getClass().equals(((Vertex)obj).label.getClass()) && label.equals(((Vertex)obj).label); + } + } +} diff --git a/alitheia/core/src/main/java/eu/sqooss/service/util/GraphSorter.java b/alitheia/core/src/main/java/eu/sqooss/service/util/GraphSorter.java new file mode 100644 index 000000000..09cf88b9b --- /dev/null +++ b/alitheia/core/src/main/java/eu/sqooss/service/util/GraphSorter.java @@ -0,0 +1,21 @@ +package eu.sqooss.service.util; + +import java.util.List; + +/** + * Abstract class for sorting (and flattening) a Graph + * @param + */ +public abstract class GraphSorter { + protected Graph graph; + + public GraphSorter(Graph graph){ + this.graph = graph; + } + + /** + * Sort and flatten the Graph into a List + */ + public abstract List sort(); + +} diff --git a/alitheia/core/src/main/java/eu/sqooss/service/util/GraphTS.java b/alitheia/core/src/main/java/eu/sqooss/service/util/GraphTS.java deleted file mode 100644 index e2a8f69c5..000000000 --- a/alitheia/core/src/main/java/eu/sqooss/service/util/GraphTS.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * This file is part of the Alitheia system, developed by the SQO-OSS - * consortium as part of the IST FP6 SQO-OSS project, number 033331. - * - * Copyright 2010 - Organization for Free and Open Source Software, - * Athens, Greece. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -package eu.sqooss.service.util; - -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.List; - -/** - * Topological sorting for Alitheia Core plugin invocations. Based on code - * distributed in the public domain by http://www.algorithm-code.com - * - * @author Georgios Gousios - * - */ -public class GraphTS { - - private int MAX_VERTS = 20; - - private Vertex vertexList[]; // list of vertices - - private int matrix[][]; // adjacency matrix - - private int numVerts; // current number of vertices - - private ArrayList sortedArray; - - public GraphTS(int numvertices) { - MAX_VERTS = numvertices; - vertexList = (Vertex[]) Array.newInstance(Vertex.class, MAX_VERTS); - matrix = new int[MAX_VERTS][MAX_VERTS]; - numVerts = 0; - sortedArray = new ArrayList(numvertices); - for (int i = 0; i < MAX_VERTS; i++) { - for (int k = 0; k < MAX_VERTS; k++) - matrix[i][k] = 0; - //sortedArray.add((T)null); - } - } - - public int addVertex(T lab) { - vertexList[numVerts++] = new Vertex(lab); - return numVerts; - } - - public void addEdge(int start, int end) { - matrix[start - 1][end - 1] = 1; - } - - public void displayVertex(int v) { - System.out.print(vertexList[v].label); - } - - public List topo() { // toplogical sort - int orig_nVerts = numVerts; - - while (numVerts > 0) // while vertices remain, - { - // get a vertex with no successors, or -1 - int currentVertex = noSuccessors(); - if (currentVertex == -1) // must be a cycle - { - System.out.println("ERROR: Graph has cycles"); - return null; - } - // insert vertex label in sorted array (start at end) - sortedArray.add(vertexList[currentVertex].label); - - deleteVertex(currentVertex); // delete vertex - } - - return sortedArray; - } - - public int noSuccessors() // returns vert with no successors (or -1 if no - // such verts) - { - boolean isEdge; // edge from row to column in adjMat - - for (int row = 0; row < numVerts; row++) { - isEdge = false; // check edges - for (int col = 0; col < numVerts; col++) { - if (matrix[row][col] > 0) // if edge to another, - { - isEdge = true; - break; // this vertex has a successor try another - } - } - if (!isEdge) // if no edges, has no successors - return row; - } - return -1; // no - } - - public void deleteVertex(int delVert) { - if (delVert != numVerts - 1) // if not last vertex, delete from - // vertexList - { - for (int j = delVert; j < numVerts - 1; j++) - vertexList[j] = vertexList[j + 1]; - - for (int row = delVert; row < numVerts - 1; row++) - moveRowUp(row, numVerts); - - for (int col = delVert; col < numVerts - 1; col++) - moveColLeft(col, numVerts - 1); - } - numVerts--; // one less vertex - } - - private void moveRowUp(int row, int length) { - for (int col = 0; col < length; col++) - matrix[row][col] = matrix[row + 1][col]; - } - - private void moveColLeft(int col, int length) { - for (int row = 0; row < length; row++) - matrix[row][col] = matrix[row][col + 1]; - } - - @Override - public String toString() { - String result = ""; - for(int i = 0; i < numVerts; i++){ - result = result + vertexList[i].label + "["; - for (int j = 0; j < numVerts; j++) - result = result + matrix[i][j] + ","; - result += "]\n"; - } - return result; - - } - - class Vertex { - public T label; - - public Vertex(T lab) { - label = lab; - } - } -} - - diff --git a/alitheia/core/src/main/java/eu/sqooss/service/util/ProjectVersionDateUtils.java b/alitheia/core/src/main/java/eu/sqooss/service/util/ProjectVersionDateUtils.java new file mode 100755 index 000000000..560a0f55b --- /dev/null +++ b/alitheia/core/src/main/java/eu/sqooss/service/util/ProjectVersionDateUtils.java @@ -0,0 +1,47 @@ +package eu.sqooss.service.util; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import eu.sqooss.service.db.ProjectVersion; +import eu.sqooss.service.db.StoredProject; + +public class ProjectVersionDateUtils { + public List getPreviousMonthVersions(ProjectVersion pv) { + List versions = new ArrayList(); + versions.add(pv); + ProjectVersion prev = pv.getPreviousVersion(); + long monthsecs = 3600 * 24 * 30; + while (true) { + //Diff in seconds + long diff = (pv.getTimestamp() - prev.getTimestamp()) / 1000; + if (prev != null && diff < monthsecs ) { + versions.add(prev); + } else { + break; + } + prev = prev.getPreviousVersion(); + } + return versions; + } + + public List getNextWeekVersions(ProjectVersion pv) { + List versions = new ArrayList(); + ProjectVersion next = pv.getNextVersion(); + long weeksecs = 3600 * 24 * 7; + while (true) { + long diff = (next.getTimestamp() - pv.getTimestamp()) / 1000; + if (next != null && diff < weeksecs) { + versions.add(next); + } else { + break; + } + next = next.getNextVersion(); + } + + return versions; + } +} diff --git a/alitheia/core/src/main/java/eu/sqooss/service/util/TopologicalGraphSorter.java b/alitheia/core/src/main/java/eu/sqooss/service/util/TopologicalGraphSorter.java new file mode 100644 index 000000000..d28a429f1 --- /dev/null +++ b/alitheia/core/src/main/java/eu/sqooss/service/util/TopologicalGraphSorter.java @@ -0,0 +1,98 @@ +/* + * This file is part of the Alitheia system, developed by the SQO-OSS + * consortium as part of the IST FP6 SQO-OSS project, number 033331. + * + * Copyright 2010 - Organization for Free and Open Source Software, + * Athens, Greece. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package eu.sqooss.service.util; + +import java.util.ArrayList; +import java.util.List; + +import eu.sqooss.service.util.Graph.Vertex; + +/** + * Topological sorting for Alitheia Core plugin invocations. Based on code + * distributed in the public domain by http://www.algorithm-code.com + * + */ +public class TopologicalGraphSorter extends GraphSorter{ + public TopologicalGraphSorter(Graph graph) { + super(graph); + } + + /** + * Topological sort the graph + */ + @Override + public List sort() { + Graph clone = this.graph.deepCopy(); + + List result = new ArrayList(clone.getVertices().size()); + + while(clone.size() > 0){ + // get a vertex with no successors, or -1 + Vertex currentVertex = noSuccessors(clone); + + if (currentVertex == null) // must be a cycle + { + System.out.println("ERROR: Graph has cycles"); + return null; + } + + // insert vertex label in sorted array (start at end) + result.add(currentVertex.label); + + // delete vertex + clone.removeVertex(clone.index(currentVertex.label)); + } + + return result; + } + + /** + * Return the first Vertex with no successors or null if it does not exist + * @return + */ + private Vertex noSuccessors(Graph graph) + { + Vertex result = null; + int i = 0; + while(i < graph.size() && result == null){ + if(graph.getEdges().get(graph.getVertices().get(i)) == null || graph.getEdges().get(graph.getVertices().get(i)).isEmpty()){ + result = graph.getVertices().get(i); + } + i++; + } + return result; + } +} + + diff --git a/alitheia/core/src/test/java/eu/sqoooss/service/util/TopologicalSortedGraphTest.java b/alitheia/core/src/test/java/eu/sqoooss/service/util/TopologicalSortedGraphTest.java new file mode 100644 index 000000000..7799cb287 --- /dev/null +++ b/alitheia/core/src/test/java/eu/sqoooss/service/util/TopologicalSortedGraphTest.java @@ -0,0 +1,56 @@ +package eu.sqoooss.service.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.util.List; + +import org.junit.Test; + +import eu.sqooss.service.util.Graph; +import eu.sqooss.service.util.TopologicalGraphSorter; + +public class TopologicalSortedGraphTest { + + @Test + public void testSorting() { + Graph graph = new Graph(2); + TopologicalGraphSorter sorter = new TopologicalGraphSorter(graph); + + // Add 2 vertices + int a = graph.addVertex("a"); + int b = graph.addVertex("b"); + + // Add edge from a to b + graph.addEdge(a, b); + + // Get sorted list + List sorted = sorter.sort(); + + // Assert sort + assertEquals(sorted.get(0), "b"); + assertEquals(sorted.get(1), "a"); + } + + @Test + public void testCycles(){ + Graph graph = new Graph(2); + TopologicalGraphSorter sorter = new TopologicalGraphSorter(graph); + + // Add 2 vertices + int a = graph.addVertex("a"); + int b = graph.addVertex("b"); + + // Add edge from a to b + graph.addEdge(a, b); + // Add another edge from b to a + graph.addEdge(b, a); + + // Get sorted list + List sorted = sorter.sort(); + + // As there is a cycle, the sorted list should be null + assertNull(sorted); + } + +} diff --git a/alitheia/core/src/test/java/eu/sqooss/core/AlitheiaCoreTest.java b/alitheia/core/src/test/java/eu/sqooss/core/AlitheiaCoreTest.java new file mode 100644 index 000000000..121228063 --- /dev/null +++ b/alitheia/core/src/test/java/eu/sqooss/core/AlitheiaCoreTest.java @@ -0,0 +1,66 @@ +package eu.sqooss.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import eu.sqooss.impl.service.admin.AdminServiceImpl; +import eu.sqooss.service.admin.AdminService; + +public class AlitheiaCoreTest { + + /** + * Test the registration of a standard service + */ + @Test + public void testGetBaseService() { + AlitheiaCore instance = AlitheiaCore.testInstance(); + + assertNull(instance.getAdminService()); + assertNull(instance.getService(AdminService.class)); + + // Register a standard service + instance.registerService(AdminService.class, AdminServiceImpl.class); + + // Should be not null now + assertNotNull(instance.getAdminService()); + assertNotNull(instance.getService(AdminService.class)); + + // Should be the same + assertEquals(instance.getAdminService(), instance.getService(AdminService.class)); + } + + /** + * Test the registration of a custom service + */ + @Test + public void testGetCustomService() { + AlitheiaCore instance = AlitheiaCore.testInstance(); + + // Register a custom service + instance.registerService(MockService.class, MockService.class); + + MockService service = instance.getService(MockService.class); + + // Should not be null + assertNotNull(service); + + // Register a second implementation of the same service + instance.registerService(MockService.class, MockServiceExtended.class); + + MockService service2 = instance.getService(MockService.class); + + // Should not be null + assertNotNull(service); + + // Should not be the same + assertNotSame(service, service2); + + // Should be of the subtype + assertTrue(service2 instanceof MockServiceExtended); + } +} diff --git a/alitheia/core/src/test/java/eu/sqooss/core/MockService.java b/alitheia/core/src/test/java/eu/sqooss/core/MockService.java new file mode 100644 index 000000000..db31b5d36 --- /dev/null +++ b/alitheia/core/src/test/java/eu/sqooss/core/MockService.java @@ -0,0 +1,24 @@ +package eu.sqooss.core; + +import org.osgi.framework.BundleContext; + +import eu.sqooss.service.logging.Logger; + +public class MockService implements AlitheiaCoreService { + + @Override + public boolean startUp() { + return true; + } + + @Override + public void shutDown() { + + } + + @Override + public void setInitParams(BundleContext bc, Logger l) { + + } + +} \ No newline at end of file diff --git a/alitheia/core/src/test/java/eu/sqooss/core/MockServiceExtended.java b/alitheia/core/src/test/java/eu/sqooss/core/MockServiceExtended.java new file mode 100644 index 000000000..881acd6aa --- /dev/null +++ b/alitheia/core/src/test/java/eu/sqooss/core/MockServiceExtended.java @@ -0,0 +1,6 @@ +package eu.sqooss.core; + + +public class MockServiceExtended extends MockService { + +} \ No newline at end of file diff --git a/alitheia/core/src/test/java/eu/sqooss/test/service/scheduler/Testester.java b/alitheia/core/src/test/java/eu/sqooss/test/service/scheduler/Testester.java new file mode 100755 index 000000000..710da83a6 --- /dev/null +++ b/alitheia/core/src/test/java/eu/sqooss/test/service/scheduler/Testester.java @@ -0,0 +1,13 @@ +package eu.sqooss.test.service.scheduler; + +import org.junit.Test; +import static org.junit.Assert.*; + +public class Testester { + @Test + public void testTester(){ + assertTrue(true); + System.out.println("yesyesyes"); + assertTrue(false); + } +} diff --git a/metrics/contrib/maven-eclipse.xml b/metrics/contrib/maven-eclipse.xml new file mode 100755 index 000000000..360a973eb --- /dev/null +++ b/metrics/contrib/maven-eclipse.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/metrics/contrib/src/main/java/eu/sqooss/metrics/contrib/ContributionMetricImpl.java b/metrics/contrib/src/main/java/eu/sqooss/metrics/contrib/ContributionMetricImpl.java index 5d6042bef..fe6375561 100644 --- a/metrics/contrib/src/main/java/eu/sqooss/metrics/contrib/ContributionMetricImpl.java +++ b/metrics/contrib/src/main/java/eu/sqooss/metrics/contrib/ContributionMetricImpl.java @@ -53,7 +53,7 @@ import eu.sqooss.metrics.contrib.ContributionActions.ActionType; import eu.sqooss.metrics.contrib.db.ContribAction; import eu.sqooss.metrics.contrib.db.ContribActionType; -import eu.sqooss.service.abstractmetric.AbstractMetric; +import eu.sqooss.service.abstractmetric.DefaultMetric; import eu.sqooss.service.abstractmetric.AlitheiaPlugin; import eu.sqooss.service.abstractmetric.AlreadyProcessingException; import eu.sqooss.service.abstractmetric.MetricDecl; @@ -83,6 +83,7 @@ import eu.sqooss.service.tds.Diff; import eu.sqooss.service.tds.DiffChunk; import eu.sqooss.service.tds.SCMAccessor; +import eu.sqooss.service.util.ProjectVersionDateUtils; @MetricDeclarations(metrics={ @MetricDecl(mnemonic="CONTRIB", descr="Developer Contribution Metric", @@ -90,7 +91,7 @@ activators={Developer.class, ProjectVersion.class, MailingListThread.class, Bug.class}) }) -public class ContributionMetricImpl extends AbstractMetric { +public class ContributionMetricImpl extends DefaultMetric { /** Number of files after which a commit is considered too big */ public static final String CONFIG_CMF_THRES = "CMF_threshold"; diff --git a/metrics/developermetrics/src/main/java/eu/sqooss/metrics/developermetrics/Developermetrics.java b/metrics/developermetrics/src/main/java/eu/sqooss/metrics/developermetrics/Developermetrics.java index a91916044..fc4dbdb99 100644 --- a/metrics/developermetrics/src/main/java/eu/sqooss/metrics/developermetrics/Developermetrics.java +++ b/metrics/developermetrics/src/main/java/eu/sqooss/metrics/developermetrics/Developermetrics.java @@ -37,7 +37,7 @@ import org.osgi.framework.BundleContext; -import eu.sqooss.service.abstractmetric.AbstractMetric; +import eu.sqooss.service.abstractmetric.DefaultMetric; import eu.sqooss.service.abstractmetric.AlreadyProcessingException; import eu.sqooss.service.abstractmetric.MetricDecl; import eu.sqooss.service.abstractmetric.MetricDeclarations; @@ -65,7 +65,7 @@ @MetricDecl(mnemonic="EYBALL", activators={ProjectFile.class}, descr="Number of developers that worked on a file"), @MetricDecl(mnemonic="MODEYBALL", activators={ProjectDirectory.class}, descr="Number of developers that worked on a module") }) -public class Developermetrics extends AbstractMetric { +public class Developermetrics extends DefaultMetric { private static String MNEM_TEAMSIZE1 = "TEAMSIZE1"; private static String MNEM_TEAMSIZE3 = "TEAMSIZE3"; diff --git a/metrics/discussionheat/src/main/java/eu/sqooss/metrics/discussionheat/DiscussionHeat.java b/metrics/discussionheat/src/main/java/eu/sqooss/metrics/discussionheat/DiscussionHeat.java index af3c91300..a01a890f3 100644 --- a/metrics/discussionheat/src/main/java/eu/sqooss/metrics/discussionheat/DiscussionHeat.java +++ b/metrics/discussionheat/src/main/java/eu/sqooss/metrics/discussionheat/DiscussionHeat.java @@ -40,7 +40,7 @@ import org.osgi.framework.BundleContext; import eu.sqooss.core.AlitheiaCore; -import eu.sqooss.service.abstractmetric.AbstractMetric; +import eu.sqooss.service.abstractmetric.DefaultMetric; import eu.sqooss.service.abstractmetric.AlitheiaPlugin; import eu.sqooss.service.abstractmetric.AlreadyProcessingException; import eu.sqooss.service.abstractmetric.MetricDecl; @@ -56,6 +56,7 @@ import eu.sqooss.service.db.ProjectVersion; import eu.sqooss.service.db.ProjectVersionMeasurement; import eu.sqooss.service.db.StoredProject; +import eu.sqooss.service.util.ProjectVersionDateUtils; /** * Discussion heat plug-in. @@ -67,7 +68,7 @@ @MetricDecl(mnemonic="HOTNESS", activators={MailingListThread.class}, descr="Hotness level"), @MetricDecl(mnemonic="HOTEFFECT", activators={MailingListThread.class}, descr="+/- loc change rate due to hot discussion") }) -public class DiscussionHeat extends AbstractMetric { +public class DiscussionHeat extends DefaultMetric { private static final String thrDepth = "select distinct m.depth" + " from MailMessage m, MailingListThread mt " + @@ -89,6 +90,8 @@ public class DiscussionHeat extends AbstractMetric { private DBService dbs; + private ProjectVersionDateUtils projectVersionDateUtils = new ProjectVersionDateUtils(); + public DiscussionHeat(BundleContext bc) { super(bc); dbs = AlitheiaCore.getInstance().getDBService(); @@ -146,8 +149,8 @@ public void run(MailingListThread m) throws AlreadyProcessingException { ProjectVersion pv = getVersionByDate(m.getStartingEmail().getSendDate(), m.getList().getStoredProject()); - int locsLastMonth = getLocsForVersions(getPreviousMonthVersions(pv)); - int locsNextWeek = getLocsForVersions(getNextWeekVersions(pv)); + int locsLastMonth = getLocsForVersions(projectVersionDateUtils.getPreviousMonthVersions(pv)); + int locsNextWeek = getLocsForVersions(projectVersionDateUtils.getNextWeekVersions(pv)); int result = (locsLastMonth/30) - (locsNextWeek/7); @@ -180,41 +183,6 @@ private int getLocsForVersions(List versions) throws AlreadyProc return result; } - private List getPreviousMonthVersions(ProjectVersion pv) { - List versions = new ArrayList(); - versions.add(pv); - ProjectVersion prev = pv.getPreviousVersion(); - long monthsecs = 3600 * 24 * 30; - while (true) { - //Diff in seconds - long diff = (pv.getTimestamp() - prev.getTimestamp()) / 1000; - if (prev != null && diff < monthsecs ) { - versions.add(prev); - } else { - break; - } - prev = prev.getPreviousVersion(); - } - return versions; - } - - private List getNextWeekVersions(ProjectVersion pv) { - List versions = new ArrayList(); - ProjectVersion next = pv.getNextVersion(); - long weeksecs = 3600 * 24 * 7; - while (true) { - long diff = (next.getTimestamp() - pv.getTimestamp()) / 1000; - if (next != null && diff < weeksecs) { - versions.add(next); - } else { - break; - } - next = next.getNextVersion(); - } - - return versions; - } - private ProjectVersion getVersionByDate(Date ts, StoredProject sp) { Map params = new HashMap(); params.put("ts", ts.getTime()); diff --git a/metrics/findbugs/src/main/java/gr/aueb/metrics/findbugs/FindbugsMetrics.java b/metrics/findbugs/src/main/java/gr/aueb/metrics/findbugs/FindbugsMetrics.java index a31d5395c..243eb30c9 100644 --- a/metrics/findbugs/src/main/java/gr/aueb/metrics/findbugs/FindbugsMetrics.java +++ b/metrics/findbugs/src/main/java/gr/aueb/metrics/findbugs/FindbugsMetrics.java @@ -111,7 +111,7 @@ @MetricDecl(mnemonic = "TMSBF", activators = {ProjectVersion.class}, descr = "MS: Field isn't final but should be (total)") }) @SchedulerHints(invocationOrder = InvocationOrder.NEWFIRST, activationOrder = {ProjectVersion.class}) -public class FindbugsMetrics extends AbstractMetric { +public class FindbugsMetrics extends DefaultMetric { static String MAVEN_PATH = ""; static String ANT_PATH = ""; diff --git a/metrics/javametrics/src/main/java/eu/sqooss/metrics/java/JavaMetrics.java b/metrics/javametrics/src/main/java/eu/sqooss/metrics/java/JavaMetrics.java index e0ee2a7fc..4407f910f 100644 --- a/metrics/javametrics/src/main/java/eu/sqooss/metrics/java/JavaMetrics.java +++ b/metrics/javametrics/src/main/java/eu/sqooss/metrics/java/JavaMetrics.java @@ -15,7 +15,7 @@ import org.osgi.framework.BundleContext; import eu.sqooss.core.AlitheiaCore; -import eu.sqooss.service.abstractmetric.AbstractMetric; +import eu.sqooss.service.abstractmetric.DefaultMetric; import eu.sqooss.service.abstractmetric.MetricDecl; import eu.sqooss.service.abstractmetric.MetricDeclarations; import eu.sqooss.service.abstractmetric.Result; @@ -45,7 +45,7 @@ @MetricDecl(mnemonic = "NPM", activators = {EncapsulationUnit.class, ProjectVersion.class}, descr = "Number of Public Methods") }) @SchedulerHints(activationOrder = {ProjectVersion.class, EncapsulationUnit.class}) -public class JavaMetrics extends AbstractMetric { +public class JavaMetrics extends DefaultMetric { private List changedFiles; private ProjectVersion pv; diff --git a/metrics/mi/src/main/java/eu/sqooss/metrics/mi/Mi.java b/metrics/mi/src/main/java/eu/sqooss/metrics/mi/Mi.java index 580de8aed..19266fe19 100644 --- a/metrics/mi/src/main/java/eu/sqooss/metrics/mi/Mi.java +++ b/metrics/mi/src/main/java/eu/sqooss/metrics/mi/Mi.java @@ -37,7 +37,7 @@ import org.osgi.framework.BundleContext; -import eu.sqooss.service.abstractmetric.AbstractMetric; +import eu.sqooss.service.abstractmetric.DefaultMetric; import eu.sqooss.service.abstractmetric.AlitheiaPlugin; import eu.sqooss.service.abstractmetric.AlreadyProcessingException; import eu.sqooss.service.abstractmetric.MetricDecl; @@ -70,7 +70,7 @@ dependencies={"Wc.loc", "Wc.locom", "EMCC_TOTAL", "HV", "ISSRCMOD"}, descr="Maintainability Index for a module") }) -public class Mi extends AbstractMetric { +public class Mi extends DefaultMetric { /*Metrics defined*/ private static String MNEMONIC_MI = "MI"; @@ -217,54 +217,8 @@ public List getResult(ProjectVersion pv, Metric m) { public void run(ProjectVersion pv) throws AlreadyProcessingException { - String paramIsDirectory = "is_directory"; - String paramMNOL = "paramMNOL"; - String paramISSRCDIR = "paramISSRCDIR"; - String paramVersionId = "paramVersionId"; - String paramProjectId = "paramProjectId"; - String paramState = "paramStatus"; - - StringBuffer q = new StringBuffer("select pfm "); - Map params = new HashMap(); - - if (pv.getSequence() == ProjectVersion.getLastProjectVersion(pv.getProject()).getSequence()) { - q.append(" from ProjectFile pf, ProjectFileMeasurement pfm"); - q.append(" where pf.validUntil is null "); - } else { - q.append(" from ProjectVersion pv, ProjectVersion pv2,"); - q.append(" ProjectVersion pv3, ProjectFile pf, "); - q.append(" ProjectFileMeasurement pfm "); - q.append(" where pv.project.id = :").append(paramProjectId); - q.append(" and pv.id = :").append(paramVersionId); - q.append(" and pv2.project.id = :").append(paramProjectId); - q.append(" and pv3.project.id = :").append(paramProjectId); - q.append(" and pf.validFrom.id = pv2.id"); - q.append(" and pf.validUntil.id = pv3.id"); - q.append(" and pv2.sequence <= pv.sequence"); - q.append(" and pv3.sequence >= pv.sequence"); - - params.put(paramProjectId, pv.getProject().getId()); - params.put(paramVersionId, pv.getId()); - } - - q.append(" and pf.state <> :").append(paramState); - q.append(" and pf.isDirectory = :").append(paramIsDirectory); - q.append(" and pfm.projectFile = pf"); - q.append(" and pfm.metric = :").append(paramMNOL); - q.append(" and exists (select pfm1 "); - q.append(" from ProjectFileMeasurement pfm1 "); - q.append(" where pfm1.projectFile = pfm.projectFile "); - q.append(" and pfm1.metric = :").append(paramISSRCDIR).append(")"); - - - params.put(paramState, ProjectFileState.deleted()); - params.put(paramIsDirectory, true); - params.put(paramMNOL, Metric.getMetricByMnemonic(MNEMONIC_MODMI)); - params.put(paramISSRCDIR, Metric.getMetricByMnemonic(MNEM_ISSRC)); - // Get the list of folders which exist in this project version. - List srcDirs = - (List) db.doHQL(q.toString(), params); + List srcDirs = QRY_SOURCE_DIRS(pv, MNEMONIC_MODMI, MNEM_ISSRC); // Calculate the metric results double miTotal = 0; diff --git a/metrics/modulemetrics/src/main/java/eu/sqooss/metrics/modulemetrics/ModuleMetricsImplementation.java b/metrics/modulemetrics/src/main/java/eu/sqooss/metrics/modulemetrics/ModuleMetricsImplementation.java index 33c4eaaef..b6d598151 100644 --- a/metrics/modulemetrics/src/main/java/eu/sqooss/metrics/modulemetrics/ModuleMetricsImplementation.java +++ b/metrics/modulemetrics/src/main/java/eu/sqooss/metrics/modulemetrics/ModuleMetricsImplementation.java @@ -42,7 +42,7 @@ import org.osgi.framework.ServiceReference; import eu.sqooss.core.AlitheiaCore; -import eu.sqooss.service.abstractmetric.AbstractMetric; +import eu.sqooss.service.abstractmetric.DefaultMetric; import eu.sqooss.service.abstractmetric.AlitheiaPlugin; import eu.sqooss.service.abstractmetric.AlreadyProcessingException; import eu.sqooss.service.abstractmetric.MetricDecl; @@ -64,7 +64,7 @@ @MetricDecl(mnemonic="AMS", activators={ProjectVersion.class}, descr="Average Module Size"), @MetricDecl(mnemonic="ISSRCMOD", activators={ProjectDirectory.class}, descr="Mark for modules containing source files") }) -public class ModuleMetricsImplementation extends AbstractMetric { +public class ModuleMetricsImplementation extends DefaultMetric { // Mnemonic names of all metric dependencies private static String DEP_WC_LOC = "Wc.loc"; @@ -152,53 +152,8 @@ public void run(ProjectFile pf) throws AlreadyProcessingException { } public void run(ProjectVersion pv) throws AlreadyProcessingException { - - String paramIsDirectory = "is_directory"; - String paramMNOL = "paramMNOL"; - String paramISSRCDIR = "paramISSRCDIR"; - String paramVersionId = "paramVersionId"; - String paramProjectId = "paramProjectId"; - String paramState = "paramStatus"; - Map params = new HashMap(); - - StringBuffer q = new StringBuffer("select pfm "); - if (pv.getSequence() == ProjectVersion.getLastProjectVersion(pv.getProject()).getSequence()) { - q.append(" from ProjectFile pf, ProjectFileMeasurement pfm"); - q.append(" where pf.validUntil is null "); - } else { - q.append(" from ProjectVersion pv, ProjectVersion pv2,"); - q.append(" ProjectVersion pv3, ProjectFile pf, "); - q.append(" ProjectFileMeasurement pfm "); - q.append(" where pv.project.id = :").append(paramProjectId); - q.append(" and pv.id = :").append(paramVersionId); - q.append(" and pv2.project.id = :").append(paramProjectId); - q.append(" and pv3.project.id = :").append(paramProjectId); - q.append(" and pf.validFrom.id = pv2.id"); - q.append(" and pf.validUntil.id = pv3.id"); - q.append(" and pv2.sequence <= pv.sequence"); - q.append(" and pv3.sequence >= pv.sequence"); - - params.put(paramProjectId, pv.getProject().getId()); - params.put(paramVersionId, pv.getId()); - } - - q.append(" and pf.state <> :").append(paramState); - q.append(" and pf.isDirectory = :").append(paramIsDirectory); - q.append(" and pfm.projectFile = pf"); - q.append(" and pfm.metric = :").append(paramMNOL); - q.append(" and exists (select pfm1 "); - q.append(" from ProjectFileMeasurement pfm1 "); - q.append(" where pfm1.projectFile = pfm.projectFile "); - q.append(" and pfm1.metric = :").append(paramISSRCDIR).append(")"); - - params.put(paramState, ProjectFileState.deleted()); - params.put(paramIsDirectory, true); - params.put(paramMNOL, Metric.getMetricByMnemonic(MET_MNOL)); - params.put(paramISSRCDIR, Metric.getMetricByMnemonic(MET_ISSRCMOD)); - // Get the list of folders which exist in this project version. - List srcDirs = - (List) db.doHQL(q.toString(), params); + List srcDirs = QRY_SOURCE_DIRS(pv, MET_MNOL, MET_ISSRCMOD); // Calculate the metric results int locs = 0; diff --git a/metrics/structural/src/main/java/eu/sqooss/metrics/structural/Structural.java b/metrics/structural/src/main/java/eu/sqooss/metrics/structural/Structural.java index ab12f9050..dc2920af2 100644 --- a/metrics/structural/src/main/java/eu/sqooss/metrics/structural/Structural.java +++ b/metrics/structural/src/main/java/eu/sqooss/metrics/structural/Structural.java @@ -53,7 +53,7 @@ import org.osgi.framework.BundleContext; import eu.sqooss.core.AlitheiaCore; -import eu.sqooss.service.abstractmetric.AbstractMetric; +import eu.sqooss.service.abstractmetric.DefaultMetric; import eu.sqooss.service.abstractmetric.MetricDecl; import eu.sqooss.service.abstractmetric.MetricDeclarations; import eu.sqooss.service.abstractmetric.Result; @@ -88,7 +88,7 @@ @MetricDecl(mnemonic="HT", activators={ProjectFile.class}, descr="Halstead Time"), @MetricDecl(mnemonic="HB", activators={ProjectFile.class}, descr="Halstead Bugs Derived") }) -public class Structural extends AbstractMetric { +public class Structural extends DefaultMetric { protected static String MNEM_CC_T = "MCC_TOTAL"; protected static String MNEM_CC_MAX = "MCC_MAX"; diff --git a/metrics/testability/src/main/java/eu/sqooss/metrics/testability/TestabilityImplementation.java b/metrics/testability/src/main/java/eu/sqooss/metrics/testability/TestabilityImplementation.java index f3efba83a..b0aa63318 100644 --- a/metrics/testability/src/main/java/eu/sqooss/metrics/testability/TestabilityImplementation.java +++ b/metrics/testability/src/main/java/eu/sqooss/metrics/testability/TestabilityImplementation.java @@ -47,7 +47,7 @@ import org.osgi.framework.ServiceReference; import eu.sqooss.core.AlitheiaCore; -import eu.sqooss.service.abstractmetric.AbstractMetric; +import eu.sqooss.service.abstractmetric.DefaultMetric; import eu.sqooss.service.abstractmetric.MetricDecl; import eu.sqooss.service.abstractmetric.MetricDeclarations; import eu.sqooss.service.abstractmetric.Result; @@ -60,7 +60,7 @@ @MetricDeclarations(metrics = { @MetricDecl(mnemonic="TEST", descr="", activators={ProjectFile.class}) }) -public class TestabilityImplementation extends AbstractMetric { +public class TestabilityImplementation extends DefaultMetric { private FDSService fds; diff --git a/metrics/wc/src/main/java/eu/sqooss/metrics/wc/WcImplementation.java b/metrics/wc/src/main/java/eu/sqooss/metrics/wc/WcImplementation.java index 6a5b630d3..b5da3d0d7 100644 --- a/metrics/wc/src/main/java/eu/sqooss/metrics/wc/WcImplementation.java +++ b/metrics/wc/src/main/java/eu/sqooss/metrics/wc/WcImplementation.java @@ -48,7 +48,7 @@ import org.osgi.framework.ServiceReference; import eu.sqooss.core.AlitheiaCore; -import eu.sqooss.service.abstractmetric.AbstractMetric; +import eu.sqooss.service.abstractmetric.DefaultMetric; import eu.sqooss.service.abstractmetric.AlreadyProcessingException; import eu.sqooss.service.abstractmetric.MetricDecl; import eu.sqooss.service.abstractmetric.MetricDeclarations; @@ -75,7 +75,7 @@ @MetricDecl(mnemonic="TLOCOM", activators={ProjectVersion.class}, descr="Total Lines of Comments"), @MetricDecl(mnemonic="TLDOC", activators={ProjectVersion.class}, descr="Total Number of Documentation Lines") }) -public class WcImplementation extends AbstractMetric { +public class WcImplementation extends DefaultMetric { private FDSService fds; private FileTypeMatcher ftm = FileTypeMatcher.getInstance();