2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.util.Platform;
25 import java.lang.reflect.Constructor;
26 import java.lang.reflect.InvocationTargetException;
27 import java.util.HashMap;
31 * A class to hold singleton objects, whose scope (context) is
33 * <li>the Java runtime (JVM) when running as Java</li>
34 * <li>one 'applet', when running as JalviewJS</li>
36 * This allows separation of multiple JS applets running on the same browser
37 * page, each with their own 'singleton' instances.
39 * Instance objects are held in a separate Map (keyed by Class) for each
40 * context. For Java, this is just a single static Map. For SwingJS, the map is
41 * stored as a field {@code _swingjsSingletons} of
42 * {@code Thread.currentThread.getThreadGroup()}, as a proxy for the applet.
44 * Note that when an applet is stopped, its ThreadGroup is removed, allowing any
45 * singleton references to be garbage collected.
49 public class ApplicationSingletonProvider
52 * A tagging interface to mark classes whose singleton instances may be served
53 * by {@code ApplicationSingletonProvider}, giving a distinct instance per JS
56 * A class whose singleton should have global scope (be shared across all
57 * applets on a page) should <em>not</em> use this mechanism, but just provide
58 * a single instance (class static member) in the normal way.
60 public interface ApplicationSingletonI
65 * Map used to hold singletons in JVM context
67 private static Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> singletons = new HashMap<>();
70 * private constructor for non-instantiable class
72 private ApplicationSingletonProvider()
77 * Returns the singletons map for the current context (JVM for Java,
78 * ThreadGroup for JS), creating the map on the first request for each JS
83 private static Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> getContextMap()
85 @SuppressWarnings("unused")
86 ThreadGroup g = (Platform.isJS()
87 ? Thread.currentThread().getThreadGroup()
89 Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> map = singletons;
90 /** @j2sNative map = g._swingjsSingletons; */
93 map = new HashMap<>();
94 /** @j2sNative g._swingjsSingletons = map; */
101 * Answers the singleton instance of the given class for the current context
102 * (JVM or SwingJS 'applet'). If no instance yet exists, one is created, by
103 * calling the class's no-argument constructor. Answers null if any error
104 * occurs (or occurred previously for the same class).
109 @SuppressWarnings("unchecked")
110 public static <T extends ApplicationSingletonI> T getInstance(Class<T> c)
112 Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> map = getContextMap();
113 if (map.containsKey(c))
116 * singleton already created _or_ creation failed (null value stored)
118 return (T) map.get(c);
122 * create and save the singleton
127 Constructor<T> con = c.getDeclaredConstructor();
128 con.setAccessible(true);
129 o = con.newInstance();
130 } catch (IllegalAccessException | InstantiationException
131 | IllegalArgumentException | InvocationTargetException
132 | NoSuchMethodException | SecurityException e)
134 Console.error("Failed to create singleton for " + c.toString()
135 + ", error was: " + e.toString());
140 * store the new singleton; note that a
141 * null value is saved if construction failed
143 getContextMap().put(c, o);
148 * Removes the current singleton instance of the given class from the current
149 * application context. This has the effect of ensuring that a new instance is
150 * created the next time one is requested.
154 public static void removeInstance(
155 Class<? extends ApplicationSingletonI> c)
157 Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> map = getContextMap();