From 6f2a6ce80c414b43934056fa7651e1b3cc0e4568 Mon Sep 17 00:00:00 2001 From: Ron Anderson Date: Tue, 23 Apr 2013 14:50:34 -0500 Subject: [PATCH] Issue #1939 Fix deadlock at CAVE start up. Change-Id: Ic3a0f527cdae7ee1abc32662c90d9594686d4bab Former-commit-id: b38ba1c92e4e176364dc3967d11b98fbd66478cd --- .../jobs/NotificationManagerJob.java | 17 ++- .../thinclient/cave/ThinClientComponent.java | 9 +- .../awips/AbstractCAVEComponent.java | 44 ++++-- edexOsgi/build.edex/esb/conf/wrapper.conf | 1 + ...f.common.serialization.ISerializableObject | 1 - .../serialization/SerializableManager.java | 136 +++++++++++------- 6 files changed, 142 insertions(+), 66 deletions(-) diff --git a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/notification/jobs/NotificationManagerJob.java b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/notification/jobs/NotificationManagerJob.java index b671c659b6..09e372328f 100644 --- a/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/notification/jobs/NotificationManagerJob.java +++ b/cave/com.raytheon.uf.viz.core/src/com/raytheon/uf/viz/core/notification/jobs/NotificationManagerJob.java @@ -69,6 +69,8 @@ import com.raytheon.uf.viz.core.notification.NotificationMessage; * ------------ ---------- ----------- -------------------------- * 05/08/08 1127 randerso Initial Creation * 09/03/08 1448 chammack Refactored notification observer interface + * 04/23/13 1939 randerso Add separate connect method to allow application + * to complete initialization before connecting to JMS * * * @author randerso @@ -184,7 +186,6 @@ public class NotificationManagerJob implements ExceptionListener, IDisposable { */ protected NotificationManagerJob() { this.listeners = new HashMap(); - connect(true); Activator.getDefault().registerDisposable(this); } @@ -411,6 +412,20 @@ public class NotificationManagerJob implements ExceptionListener, IDisposable { } } + /** + * Connect to JMS + */ + public static void connect() { + getInstance().connect(true); + } + + /** + * Disconnect from JMS + */ + public static void disconnect() { + getInstance().disconnect(true); + } + private static class NotificationListener implements MessageListener { private Type type; diff --git a/cave/com.raytheon.uf.viz.thinclient.cave/src/com/raytheon/uf/viz/thinclient/cave/ThinClientComponent.java b/cave/com.raytheon.uf.viz.thinclient.cave/src/com/raytheon/uf/viz/thinclient/cave/ThinClientComponent.java index f2a2ff1134..d3790c5aba 100644 --- a/cave/com.raytheon.uf.viz.thinclient.cave/src/com/raytheon/uf/viz/thinclient/cave/ThinClientComponent.java +++ b/cave/com.raytheon.uf.viz.thinclient.cave/src/com/raytheon/uf/viz/thinclient/cave/ThinClientComponent.java @@ -27,6 +27,7 @@ import java.util.List; import javax.xml.bind.JAXBException; +import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.ui.application.WorkbenchAdvisor; import org.osgi.framework.Bundle; @@ -67,7 +68,8 @@ import com.raytheon.viz.ui.personalities.awips.CAVE; * * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- - * Aug 4, 2011 njensen Initial creation + * Aug 4, 2011 njensen Initial creation + * Apr 23, 2013 1939 randerso Return null from initializeSerialization * * * @@ -192,6 +194,7 @@ public class ThinClientComponent extends CAVE implements IThinClientComponent { } } + @Override public void stopComponent() { // Persist caches cacheManager.storeCaches(); @@ -201,13 +204,15 @@ public class ThinClientComponent extends CAVE implements IThinClientComponent { } @Override - protected void initializeSerialization() { + protected Job initializeSerialization() { try { SerializationUtil.getJaxbContext(); } catch (JAXBException e) { statusHandler.handle(Priority.CRITICAL, "An error occured initializing Serialization", e); } + + return null; } } diff --git a/cave/com.raytheon.viz.ui.personalities.awips/src/com/raytheon/viz/ui/personalities/awips/AbstractCAVEComponent.java b/cave/com.raytheon.viz.ui.personalities.awips/src/com/raytheon/viz/ui/personalities/awips/AbstractCAVEComponent.java index 72fdcb03bc..6fed3c1335 100644 --- a/cave/com.raytheon.viz.ui.personalities.awips/src/com/raytheon/viz/ui/personalities/awips/AbstractCAVEComponent.java +++ b/cave/com.raytheon.viz.ui.personalities.awips/src/com/raytheon/viz/ui/personalities/awips/AbstractCAVEComponent.java @@ -50,6 +50,8 @@ import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus.Priority; import com.raytheon.uf.common.time.SimulatedTime; +import com.raytheon.uf.common.time.util.ITimer; +import com.raytheon.uf.common.time.util.TimeUtil; import com.raytheon.uf.viz.alertviz.SystemStatusHandler; import com.raytheon.uf.viz.alertviz.ui.dialogs.AlertVisualization; import com.raytheon.uf.viz.application.ProgramArguments; @@ -60,6 +62,7 @@ import com.raytheon.uf.viz.core.localization.CAVELocalizationNotificationObserve import com.raytheon.uf.viz.core.localization.LocalizationConstants; import com.raytheon.uf.viz.core.localization.LocalizationInitializer; import com.raytheon.uf.viz.core.localization.LocalizationManager; +import com.raytheon.uf.viz.core.notification.jobs.NotificationManagerJob; import com.raytheon.uf.viz.core.status.VizStatusHandlerFactory; import com.raytheon.viz.alerts.jobs.AutoUpdater; import com.raytheon.viz.alerts.jobs.MenuUpdater; @@ -90,6 +93,8 @@ import com.raytheon.viz.core.units.UnitRegistrar; * mode is off. * Jan 09, 2013 #1442 rferrel Changes to notify SimultedTime listeners. * Apr 17, 2013 1786 mpduff startComponent now sets StatusHandlerFactory + * Apr 23, 2013 #1939 randerso Allow serialization to complete initialization + * before connecting to JMS to avoid deadlock * * * @@ -139,7 +144,8 @@ public abstract class AbstractCAVEComponent implements IStandaloneComponent { UnitRegistrar.registerUnits(); CAVEMode.performStartupDuties(); - long t0 = System.currentTimeMillis(); + ITimer timer = TimeUtil.getTimer(); + timer.start(); Display display = null; int modes = getRuntimeModes(); @@ -166,7 +172,7 @@ public abstract class AbstractCAVEComponent implements IStandaloneComponent { } UFStatus.setHandlerFactory(new VizStatusHandlerFactory()); - initializeSerialization(); + Job serializationJob = initializeSerialization(); initializeDataStoreFactory(); initializeObservers(); @@ -204,12 +210,22 @@ public abstract class AbstractCAVEComponent implements IStandaloneComponent { WorkbenchAdvisor workbenchAdvisor = null; // A component was passed as command line arg // launch cave normally, should cave be registered as component? - long t1 = System.currentTimeMillis(); - System.out.println("Localization time: " + (t1 - t0) + "ms"); - try { initializeSimulatedTime(); + // wait for serialization initialization to complete before + // opening JMS connection to avoid deadlock in class loaders + if (serializationJob != null) { + serializationJob.join(); + } + + // open JMS connection to allow alerts to be received + NotificationManagerJob.connect(); + + timer.stop(); + System.out.println("Initialization time: " + timer.getElapsedTime() + + "ms"); + if (cave) { workbenchAdvisor = getWorkbenchAdvisor(); } else if (!nonui) { @@ -220,6 +236,7 @@ public abstract class AbstractCAVEComponent implements IStandaloneComponent { if (workbenchAdvisor instanceof HiddenWorkbenchAdvisor == false) { startInternal(componentName); } + if (workbenchAdvisor != null) { returnCode = PlatformUI.createAndRunWorkbench(display, workbenchAdvisor); @@ -239,6 +256,15 @@ public abstract class AbstractCAVEComponent implements IStandaloneComponent { // catch any exceptions to ensure rest of finally block // executes } + + try { + // disconnect from JMS + NotificationManagerJob.disconnect(); + } catch (RuntimeException e) { + // catch any exceptions to ensure rest of finally block + // executes + } + if (av != null) { av.dispose(); } @@ -368,8 +394,8 @@ public abstract class AbstractCAVEComponent implements IStandaloneComponent { !LocalizationManager.internalAlertServer).run(); } - protected void initializeSerialization() { - new Job("Loading Serialization") { + protected Job initializeSerialization() { + Job job = new Job("Loading Serialization") { @Override protected IStatus run(IProgressMonitor monitor) { @@ -382,7 +408,9 @@ public abstract class AbstractCAVEComponent implements IStandaloneComponent { return Status.OK_STATUS; } - }.schedule(); + }; + job.schedule(); + return job; } /** diff --git a/edexOsgi/build.edex/esb/conf/wrapper.conf b/edexOsgi/build.edex/esb/conf/wrapper.conf index a86819ea2d..601cb7f131 100644 --- a/edexOsgi/build.edex/esb/conf/wrapper.conf +++ b/edexOsgi/build.edex/esb/conf/wrapper.conf @@ -147,6 +147,7 @@ wrapper.java.additional.48=-Dhttp.port=${HTTP_PORT} wrapper.java.additional.49=-Dedex.arch=${EDEX_BITS}-bit wrapper.java.additional.50=-Dedex.tmp=${TEMP_DIR} wrapper.java.additional.51=-Dncf.bandwidth.manager.service=${NCF_BANDWIDTH_MANAGER_SERVICE} +wrapper.java.additional.52=-DinitializeHibernatables=true # Initial Java Heap Size (in MB) wrapper.java.initmemory=${INIT_MEM} diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/META-INF/services/com.raytheon.uf.common.serialization.ISerializableObject b/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/META-INF/services/com.raytheon.uf.common.serialization.ISerializableObject index 087b58f697..d2ad90380f 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/META-INF/services/com.raytheon.uf.common.serialization.ISerializableObject +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/META-INF/services/com.raytheon.uf.common.serialization.ISerializableObject @@ -3,7 +3,6 @@ com.raytheon.uf.common.dataplugin.gfe.db.objects.DatabaseID com.raytheon.uf.common.dataplugin.gfe.db.objects.GFERecord com.raytheon.uf.common.dataplugin.gfe.db.objects.GridLocation com.raytheon.uf.common.dataplugin.gfe.db.objects.ParmID -com.raytheon.uf.common.dataplugin.gfe.db.objects.GridLocation com.raytheon.uf.common.dataplugin.gfe.reference.ReferenceData com.raytheon.uf.common.dataplugin.gfe.sample.SampleData com.raytheon.uf.common.dataplugin.gfe.server.lock.Lock diff --git a/edexOsgi/com.raytheon.uf.common.serialization/src/com/raytheon/uf/common/serialization/SerializableManager.java b/edexOsgi/com.raytheon.uf.common.serialization/src/com/raytheon/uf/common/serialization/SerializableManager.java index aa4f702402..06b9ff95a2 100644 --- a/edexOsgi/com.raytheon.uf.common.serialization/src/com/raytheon/uf/common/serialization/SerializableManager.java +++ b/edexOsgi/com.raytheon.uf.common.serialization/src/com/raytheon/uf/common/serialization/SerializableManager.java @@ -14,7 +14,7 @@ * Omaha, NE 68106 * 402.291.0100 * - * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * See the AWIPS II Master Rights File ("Master Rights File.p df") for * further licensing information. **/ package com.raytheon.uf.common.serialization; @@ -31,6 +31,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.ServiceConfigurationError; import java.util.Set; @@ -56,6 +57,9 @@ import com.raytheon.uf.common.serialization.jaxb.JaxbDummyObject; * Aug 11, 2008 njensen Initial creation * Aug 31, 2009 2924 rjpeter Added Embeddable. * Feb 07, 2013 1543 djohnson Implement IJaxbableClassesLocator. + * Apr 24, 2013 1939 randerso Clean up code and attempt to improve speed. + * Added initializeHibernatables flag to disable + * processing hibernatables on CAVE * * * @author njensen @@ -82,8 +86,6 @@ public class SerializableManager implements IJaxbableClassesLocator { */ @SuppressWarnings(value = { "unchecked" }) private synchronized void initialize() { - ClassLoader cl = getClass().getClassLoader(); - long t0 = System.currentTimeMillis(), total = 0; // this is here in case in the future we want to re-initialize the lists // during runtime, i.e. hot deploy of a new plugin hibernatables.clear(); @@ -96,23 +98,37 @@ public class SerializableManager implements IJaxbableClassesLocator { .getResources( "META-INF/services/" + ISerializableObject.class.getName()); + + // doHibernate will be false in CAVE since they are not needed + boolean doHibernate = Boolean.getBoolean("initializeHibernatables"); + // In testing 1 thread is slowest, 2 threads cuts the time down // about 50% and 3 threads cuts down another 5% or so, 4 threads // shows no benefit over 2. These results are system specific. int numThreads = 3; - Thread[] threads = new Thread[numThreads]; - for (int i = 1; i < numThreads; i++) { - threads[i] = new LoadSerializableClassesThread(urls, clazzSet, - hibernatables); + LoadSerializableClassesThread[] threads = new LoadSerializableClassesThread[numThreads]; + for (int i = 0; i < numThreads; i++) { + threads[i] = new LoadSerializableClassesThread(urls, + doHibernate); threads[i].start(); } - threads[0] = new LoadSerializableClassesThread(urls, clazzSet, - hibernatables); - // Run this one on the current thread since its not doing anything - // but waiting and this avoids overhead of starting a new thread - threads[0].run(); - for (int i = 1; i < numThreads; i++) { - threads[i].join(); + + for (LoadSerializableClassesThread thread : threads) { + thread.join(); + clazzSet.addAll(thread.getClazzList()); + + if (doHibernate) { + for (Entry>> entry : thread + .getHibernatables().entrySet()) { + List> list = hibernatables + .get(entry.getKey()); + if (list == null) { + list = new ArrayList>(); + hibernatables.put(entry.getKey(), list); + } + list.addAll(entry.getValue()); + } + } } } catch (Throwable e) { e.printStackTrace(); @@ -127,6 +143,7 @@ public class SerializableManager implements IJaxbableClassesLocator { System.out.println("Total time spent loading classes: " + (System.currentTimeMillis() - realStartTime) + "ms"); + } private static void fail(Class service, String msg, Throwable cause) @@ -153,25 +170,28 @@ public class SerializableManager implements IJaxbableClassesLocator { return -1; } int ci = ln.indexOf('#'); - if (ci >= 0) + if (ci >= 0) { ln = ln.substring(0, ci); + } ln = ln.trim(); int n = ln.length(); if (n != 0) { - if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0)) + if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0)) { fail(service, u, lc, "Illegal configuration-file syntax"); + } int cp = ln.codePointAt(0); - if (!Character.isJavaIdentifierStart(cp)) + if (!Character.isJavaIdentifierStart(cp)) { fail(service, u, lc, "Illegal provider-class name: " + ln); + } for (int i = Character.charCount(cp); i < n; i += Character .charCount(cp)) { cp = ln.codePointAt(i); - if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) + if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) { fail(service, u, lc, "Illegal provider-class name: " + ln); + } } - if (!names.contains(ln)) - names.add(ln); + names.add(ln); } return lc + 1; } @@ -233,30 +253,42 @@ public class SerializableManager implements IJaxbableClassesLocator { private final Enumeration urls; - private final Set> clazzSet; + private final boolean doHibernate; + + private final List> clazzList; private final Map>> hibernatables; public LoadSerializableClassesThread(Enumeration urls, - Set> clazzSet, - Map>> hibernatables) { + boolean doHibernate) { this.urls = urls; - this.clazzSet = clazzSet; - this.hibernatables = hibernatables; + this.doHibernate = doHibernate; + this.clazzList = new ArrayList>(500); + this.hibernatables = new HashMap>>(); + } + + public List> getClazzList() { + return clazzList; + } + + public Map>> getHibernatables() { + return hibernatables; } @Override public void run() { try { ClassLoader cl = getClass().getClassLoader(); - Set> pluginHibernateSet = new HashSet>(); + Set> pluginHibernateSet = null; + if (doHibernate) { + pluginHibernateSet = new HashSet>(); + } List names = new ArrayList(); URL u = getNextUrl(); while (u != null) { InputStream in = null; BufferedReader r = null; names.clear(); - pluginHibernateSet.clear(); String path = u.getPath(); int endIndex = path.indexOf(".jar"); if (endIndex < 0) { @@ -276,24 +308,31 @@ public class SerializableManager implements IJaxbableClassesLocator { "utf-8")); int lc = 1; while ((lc = parseLine(ISerializableObject.class, u, r, - lc, names)) >= 0) + lc, names)) >= 0) { ; + } } catch (IOException x) { fail(ISerializableObject.class, "Error reading configuration file", x); } finally { try { - if (r != null) + if (r != null) { r.close(); - if (in != null) + } + if (in != null) { in.close(); + } } catch (IOException y) { fail(ISerializableObject.class, "Error closing configuration file", y); } } - Iterator iter = names.iterator(); + if (doHibernate) { + pluginHibernateSet.clear(); + } + + Iterator iter = names.iterator(); while (iter.hasNext()) { String clazz = iter.next(); try { @@ -303,18 +342,20 @@ public class SerializableManager implements IJaxbableClassesLocator { boolean added = false; if (c.getAnnotation(XmlAccessorType.class) != null || c.getAnnotation(XmlRegistry.class) != null) { - addToClazzSet(c); + clazzList.add(c); added = true; } - if (c.getAnnotation(Entity.class) != null - || c.getAnnotation(Embeddable.class) != null) { - pluginHibernateSet.add(c); - added = true; + if (doHibernate) { + if (c.getAnnotation(Entity.class) != null + || c.getAnnotation(Embeddable.class) != null) { + pluginHibernateSet.add(c); + added = true; + } } long time = (System.currentTimeMillis() - t0); - if (!added) { + if (doHibernate && !added) { System.out .println("Class: " + clazz @@ -329,8 +370,10 @@ public class SerializableManager implements IJaxbableClassesLocator { } } - if (pluginHibernateSet.size() > 0) { - addToHibernatables(pluginFQN, pluginHibernateSet); + if (doHibernate && pluginHibernateSet.size() > 0) { + hibernatables.put(pluginFQN, + new ArrayList>( + pluginHibernateSet)); } u = getNextUrl(); } @@ -347,20 +390,5 @@ public class SerializableManager implements IJaxbableClassesLocator { return null; } } - - private void addToClazzSet(Class clazz) { - synchronized (clazzSet) { - clazzSet.add(clazz); - } - } - - private void addToHibernatables(String pluginFQN, - Set> pluginHibernateSet) { - synchronized (hibernatables) { - hibernatables.put(pluginFQN, - new ArrayList>( - pluginHibernateSet)); - } - } } }