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;
24 import jalview.bin.Console;
26 import java.lang.reflect.Constructor;
27 import java.lang.reflect.InvocationTargetException;
28 import java.util.HashMap;
32 * A class to hold singleton objects, whose scope (context) is
34 * <li>the Java runtime (JVM) when running as Java</li>
35 * <li>one 'applet', when running as JalviewJS</li>
37 * This allows separation of multiple JS applets running on the same browser
38 * page, each with their own 'singleton' instances.
40 * Instance objects are held in a separate Map (keyed by Class) for each
41 * context. For Java, this is just a single static Map. For SwingJS, the map is
42 * stored as a field {@code _swingjsSingletons} of
43 * {@code Thread.currentThread.getThreadGroup()}, as a proxy for the applet.
45 * Note that when an applet is stopped, its ThreadGroup is removed, allowing any
46 * singleton references to be garbage collected.
50 public class ApplicationSingletonProvider
53 * A tagging interface to mark classes whose singleton instances may be served
54 * by {@code ApplicationSingletonProvider}, giving a distinct instance per JS
57 * A class whose singleton should have global scope (be shared across all
58 * applets on a page) should <em>not</em> use this mechanism, but just provide
59 * a single instance (class static member) in the normal way.
61 public interface ApplicationSingletonI
66 * Map used to hold singletons in JVM context
68 private static Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> singletons = new HashMap<>();
71 * private constructor for non-instantiable class
73 private ApplicationSingletonProvider()
78 * Returns the singletons map for the current context (JVM for Java,
79 * ThreadGroup for JS), creating the map on the first request for each JS
84 private static Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> getContextMap()
86 @SuppressWarnings("unused")
87 ThreadGroup g = (Platform.isJS()
88 ? Thread.currentThread().getThreadGroup()
90 Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> map = singletons;
91 /** @j2sNative map = g._swingjsSingletons; */
94 map = new HashMap<>();
95 /** @j2sNative g._swingjsSingletons = map; */
102 * Answers the singleton instance of the given class for the current context
103 * (JVM or SwingJS 'applet'). If no instance yet exists, one is created, by
104 * calling the class's no-argument constructor. Answers null if any error
105 * occurs (or occurred previously for the same class).
110 public static ApplicationSingletonI getInstance(Class<? extends ApplicationSingletonI> c)
112 Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> map = getContextMap();
113 if (map.containsKey(c))
116 * singleton already created _or_ creation failed (null value stored)
122 * create and save the singleton
124 ApplicationSingletonI o = map.get(c);
127 Constructor<? extends ApplicationSingletonI> con = c
128 .getDeclaredConstructor();
129 con.setAccessible(true);
130 o = con.newInstance();
131 } catch (IllegalAccessException | InstantiationException
132 | IllegalArgumentException | InvocationTargetException
133 | NoSuchMethodException | SecurityException e)
135 Console.error("Failed to create singleton for " + c.toString()
136 + ", error was: " + e.toString());
141 * store the new singleton; note that a
142 * null value is saved if construction failed
144 getContextMap().put(c, o);
149 * Removes the current singleton instance of the given class from the current
150 * application context. This has the effect of ensuring that a new instance is
151 * created the next time one is requested.
155 public static void removeInstance(
156 Class<? extends ApplicationSingletonI> c)
158 Map<Class<? extends ApplicationSingletonI>, ApplicationSingletonI> map = getContextMap();