All Downloads are FREE. Search and download functionalities are using the official Maven repository.

patterntesting.runtime.monitor.internal.DoubletDigger Maven / Gradle / Ivy

Go to download

PatternTesting Runtime (patterntesting-rt) is the runtime component for the PatternTesting framework. It provides the annotations and base classes for the PatternTesting testing framework (e.g. patterntesting-check, patterntesting-concurrent or patterntesting-exception) but can be also used standalone for classpath monitoring or profiling. It uses AOP and AspectJ to perform this feat.

There is a newer version: 2.5.0
Show newest version
/*
 * $Id: DoubletDigger.java,v 1.5 2016/03/15 21:27:21 oboehm Exp $
 *
 * Copyright (c) 2016 by Oliver Boehm
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * (c)reated 09.03.2016 by oliver ([email protected])
 */

package patterntesting.runtime.monitor.internal;

import java.net.URL;
import java.util.*;
import java.util.concurrent.*;

import org.slf4j.*;

import patterntesting.runtime.annotation.ProfileMe;
import patterntesting.runtime.monitor.ClasspathMonitorMBean;
import patterntesting.runtime.util.*;

/**
 * The DoubletDigger looks for classes which appears more than once in the
 * classpath.
 * 

* Till 0.6.1 this class was located in the ClasspathMonitor itself. Because * this class gets too big it was extracted to its own class. *

* * @author oliver * @since 1.6 (09.03.2016) */ public final class DoubletDigger { private static final Logger LOG = LoggerFactory.getLogger(DoubletDigger.class); private final Map, Boolean> doubletClasses = new ConcurrentHashMap, Boolean>(); private final ClassLoader cloader; private final ClasspathDigger classpathDigger; private final List> doubletList = new CopyOnWriteArrayList>(); /** * Instantiates a new doublet digger. * * @param classpathDigger the classpath digger */ public DoubletDigger(final ClasspathDigger classpathDigger) { this.classpathDigger = classpathDigger; this.cloader = classpathDigger.getClassLoader(); } /** * Resets the doubletList and maybe later other things. * At the moment it is only used by ClasspathMonitorTest * by the testGetDoubletListPerformance() method. */ protected final void reset() { doubletList.clear(); } /** * Is the given class a doublet, i.e. can it be found more than once in the * classpath? *

* Note: Because this method is needed by other methods like * getIncompatibleClassList() in the ClasspathMonitor the result of it is * cached. This speeds up this method about the factor 2000 and more: *

* *
     * Implementation | Mode  | Score    | Unit
     * ---------------+-------+----------+-------
     * old (till 1.4) | thrpt |    35514 | ops/s
     * new (cached)   | thrpt | 92968300 | ops/s
     * 
*

* This results were calculated with the help of JMH and was executed on a * Dell Latitude e6520 with i5 processor (i5-2540M CPU @ 2.60GHz). *

* * @param clazz the class to be checked * @return true if class is found more than once in the classpath */ public boolean isDoublet(final Class clazz) { Boolean doublet = this.doubletClasses.get(clazz); if (doublet == null) { String classname = clazz.getName(); String resource = Converter.classToResource(classname); try { doublet = isDoublet(resource); } catch (NoSuchElementException ex) { LOG.trace("{} is not found:", ex); LOG.debug("{} is a proxy or similar class because classloader does not find it:", clazz); doublet = false; } this.doubletClasses.put(clazz, doublet); } return doublet; } /** * Checks if is doublet. * * @param name the name * @return true, if checks if is doublet */ public boolean isDoublet(final String name) { Enumeration resources = this.classpathDigger.getResources(name); if (!resources.hasMoreElements()) { throw new NoSuchElementException("resource '" + name + "' not found"); } resources.nextElement(); if (resources.hasMoreElements()) { logDoublets(name); return true; } else { return false; } } /** * Gets the doublet. * * @param name the name * @param nr the nr * @return the doublet */ public URL getDoublet(final String name, final int nr) { Enumeration resources = this.classpathDigger.getResources(name); int i = 0; while (resources.hasMoreElements()) { URL url = resources.nextElement(); if (i == nr) { return url; } i++; } return null; } /** * Gets the doublet. * * @param clazz the clazz * @param nr the nr * @return the doublet */ public URL getDoublet(final Class clazz, final int nr) { String resource = Converter.classToResource(clazz.getName()); return this.getDoublet(resource, nr); } private void logDoublets(final String name) { if (LOG.isTraceEnabled()) { List doublets = new ArrayList(); Enumeration resources = this.classpathDigger.getResources(name); while (resources.hasMoreElements()) { URL url = resources.nextElement(); doublets.add(url); } LOG.trace("{} doublets found: {}", name, doublets); } } /** * Gets the doublets. * * @return the doublets * @see ClasspathMonitorMBean#getDoublets() */ public String[] getDoublets() { LOG.debug("Calculating doublets..."); List> classes = this.getDoubletList(); String[] doublets = new String[classes.size()]; for (int i = 0; i < doublets.length; i++) { doublets[i] = classes.get(i).toString(); } LOG.debug("Calculating doublets successful finished with {} doublet(s) found.", doublets.length); return doublets; } /** * Gets the doublet list. * * @return the doublet list */ @ProfileMe public synchronized List> getDoubletList() { if (multiThreadingEnabled) { return getDoubletListParallel(); } else { return getDoubletListSerial(); } } /** * Looks for each loaded class if it is a doublet or not. For the * performance reason it looks in the doubletList from the last time if it * is already found. This is done because normally the number of doublets * does not decrease. *

* The method is protetected for testing reasons. *

* * @return a sorted list of found doublets */ protected List> getDoubletListSerial() { List> loadedClassList = this.classpathDigger.getLoadedClassList(); synchronized (doubletList) { for (Class clazz : loadedClassList) { if (doubletList.contains(clazz)) { continue; } try { if (this.isDoublet(clazz)) { doubletList.add(clazz); } } catch (NoSuchElementException nsee) { LOG.trace("{} not found -> ignored:", clazz, nsee); } } sortDoubletList(); } return Collections.unmodifiableList(doubletList); } /** * On Java 6 you may get *
     * java.lang.UnsupportedOperationException
     *     at java.util.concurrent.CopyOnWriteArrayList$COWIterator.set(CopyOnWriteArrayList.java:1013)
     *     at java.util.Collections.sort(Collections.java:161)
     * 
* as exception. So we provide a fallback here for this situation. */ private void sortDoubletList() { try { Collections.sort(doubletList, new ObjectComparator()); } catch (UnsupportedOperationException ex) { LOG.debug("Will sort doubletList with fallback because Collections.sort(..) failed:", ex); sortList(doubletList); } } private static void sortList(List> list) { List> sorted = new ArrayList>(list.size()); Collections.sort(sorted, new ObjectComparator()); list.clear(); list.addAll(sorted); } /** * To string. * * @return the string * @see java.lang.Object#toString() */ @Override public String toString() { return this.getClass().getSimpleName() + " for " + this.cloader; } ///// M U L T I T H R E A D I N G S E C T I O N /////////////////// /** The multi threading enabled. */ private boolean multiThreadingEnabled = getMultiThreadingEnabled(); /** * Multi threading is enabled if more than one processor will be found * or if property "multiThreadingEnabled=true" is set. *

* TODO: extract it to a (not yet existing) Config class *

* * @return true on multi core processors */ private static boolean getMultiThreadingEnabled() { boolean enabled = Boolean.getBoolean(System.getProperty("multiThreadingEnabled", "false")); if (enabled) { LOG.debug("Multi threading is enabled."); return true; } int n = Runtime.getRuntime().availableProcessors(); enabled = (n > 1); LOG.debug("{} processors found, multi threading is {}enabled.", n, enabled ? "" : "not "); return enabled; } /** * Is multi threading enabled? * Multi threading is automatically enabled if more than one processor * is found. Otherwise you can use set via the system property * "multiThreadingEnabled=true" to activate it. * * @return true if multi threading is enabled for this class. * @since 0.9.7 */ public boolean isMultiThreadingEnabled() { return multiThreadingEnabled; } /** * Here you can enable or disable the (experimental) multi threading mode to * see if it is really faster on a mult-core machine. * * @param enabled the enabled * @since 0.9.7 */ public void setMultiThreadingEnabled(final boolean enabled) { multiThreadingEnabled = enabled; } /** * This was an experiment in v0.9.7: can we tune getDoubleList() by using * thread techniques like consumer-/producer-pattern? Or is the * synchronization overhead to big? *

* Multi threading is automatically activated if property * "multiThreadingEnabled" is set to true or if more than one available * processor is found. *

*

* The method is protetected for testing reasons. *

* * @return a list of doublets */ @SuppressWarnings("unchecked") protected List> getDoubletListParallel() { List> loadedClassList = this.classpathDigger.getLoadedClassList(); if (loadedClassList.isEmpty()) { return loadedClassList; } LOG.trace("Creating queue with {} elements for synchronisation.", loadedClassList.size()); Queue> queue = new ConcurrentLinkedQueue>(loadedClassList); Class lastClass = loadedClassList.get(loadedClassList.size() - 1); if (this.isDoublet(lastClass)) { doubletList.add(lastClass); } LOG.trace("Creating multiple threads."); int n = 2; List>[] l = new List[n]; DoubletDiggerRunner[] d = new DoubletDiggerRunner[n]; Thread[] t = new Thread[n]; for (int i = 0; i < n; i++) { l[i] = new ArrayList>(); d[i] = new DoubletDiggerRunner(queue, lastClass, l[i]); t[i] = new Thread(d[i]); t[i].start(); } LOG.debug("Starting {} threads...", n); for (int i = 0; i < n; i++) { try { LOG.trace("{} started...", t[i]); t[i].join(); LOG.trace("{} finished.", t[i]); } catch (InterruptedException ie) { LOG.info("Waiting for {} threads are interrupted:", n, ie); Thread.currentThread().interrupt(); } } LOG.debug("Starting {} threads finished - will add results to doublet list.", n); for (int i = 0; i < n; i++) { doubletList.addAll(l[i]); } sortDoubletList(); LOG.trace("Result of {} threads was added to doublet list and sorted.", n); return Collections.unmodifiableList(doubletList); } /** * This class is needed to realize multi threading. */ class DoubletDiggerRunner implements Runnable { private final Queue> queue; private final Class lastClass; private final List> newDoublets; /** * Instantiates a new doublet digger. * * @param queue the queue * @param lastClass the last class * @param newDoublets the new doublets */ public DoubletDiggerRunner(final Queue> queue, final Class lastClass, final List> newDoublets) { this.queue = queue; this.lastClass = lastClass; this.newDoublets = newDoublets; } /** * Run. * * @see java.lang.Runnable#run() */ public void run() { LOG.debug("Running {}...", this); while (!queue.isEmpty()) { try { Class clazz = queue.remove(); if (clazz.equals(lastClass)) { LOG.trace("Last {} reached.", clazz); queue.add(clazz); break; } if (!doubletList.contains(clazz) && isDoublet(clazz)) { newDoublets.add(clazz); } } catch (NoSuchElementException nsee) { LOG.debug("No element from {} found:", queue, nsee); } } LOG.debug("Running {} finished with {} doublets found.", this, this.newDoublets.size()); } /** * To string. * * @return the string * @see java.lang.Object#toString() */ @Override public String toString() { return "DoubletDigger-" + Thread.currentThread().getId(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy