Skip to content

Commit f421b3c

Browse files
ggiannolamgaffigan
authored andcommitted
Use a Set for serverPlugins instead of a de-duplicating adder
Addresses review feedback on #397. Rather than routing the eight registrations through a helper that scans the List, serverPlugins is now a Set so the de-duplication is the natural behaviour of the collection and the registration calls stay as they were. LinkedHashSet rather than HashSet: initPlugins loads plugins in a deliberate order (descending plugin weight), and that order is preserved when starting and stopping them. getServerPlugins still returns a List so the ExtensionController signature is unchanged for extensions. The unit tests added in the previous commit are removed. They exercised the helper directly, and with a Set the invariant is enforced by the collection type rather than by logic of our own. Signed-off-by: Giovanni Giannola <giogiannola@globalesm.com>
1 parent aac9558 commit f421b3c

2 files changed

Lines changed: 18 additions & 134 deletions

File tree

‎server/src/main/java/com/mirth/connect/server/controllers/DefaultExtensionController.java‎

Lines changed: 18 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,14 @@ public class DefaultExtensionController extends ExtensionController {
8484

8585
// these are plugins for specific extension points, keyed by plugin name
8686
// (not path)
87-
private List<ServerPlugin> serverPlugins = new ArrayList<ServerPlugin>();
87+
/*
88+
* A plugin class may implement several of the plugin type interfaces, in which case it is
89+
* registered once for each interface it implements. A Set holds a single entry per plugin,
90+
* so start() and stop() are invoked once per plugin rather than once per interface.
91+
* LinkedHashSet because initPlugins loads plugins in a deliberate order (by plugin weight)
92+
* and that order is preserved when they are started and stopped.
93+
*/
94+
private Set<ServerPlugin> serverPlugins = new LinkedHashSet<ServerPlugin>();
8895
private Map<String, ServicePlugin> servicePlugins = new LinkedHashMap<String, ServicePlugin>();
8996
private Map<String, ChannelPlugin> channelPlugins = new LinkedHashMap<String, ChannelPlugin>();
9097
private Map<String, CodeTemplateServerPlugin> codeTemplateServerPlugins = new LinkedHashMap<String, CodeTemplateServerPlugin>();
@@ -240,42 +247,42 @@ public void initPlugins() {
240247
*/
241248
servicePlugin.init(currentProperties);
242249
servicePlugins.put(servicePlugin.getPluginPointName(), servicePlugin);
243-
addServerPlugin(servicePlugin);
250+
serverPlugins.add(servicePlugin);
244251
logger.debug("sucessfully loaded server plugin: " + serverPlugin.getPluginPointName());
245252
}
246253

247254
if (serverPlugin instanceof ChannelPlugin) {
248255
ChannelPlugin channelPlugin = (ChannelPlugin) serverPlugin;
249256
channelPlugins.put(channelPlugin.getPluginPointName(), channelPlugin);
250-
addServerPlugin(channelPlugin);
257+
serverPlugins.add(channelPlugin);
251258
logger.debug("sucessfully loaded server channel plugin: " + serverPlugin.getPluginPointName());
252259
}
253260

254261
if (serverPlugin instanceof CodeTemplateServerPlugin) {
255262
CodeTemplateServerPlugin codeTemplateServerPlugin = (CodeTemplateServerPlugin) serverPlugin;
256263
codeTemplateServerPlugins.put(codeTemplateServerPlugin.getPluginPointName(), codeTemplateServerPlugin);
257-
addServerPlugin(codeTemplateServerPlugin);
264+
serverPlugins.add(codeTemplateServerPlugin);
258265
logger.debug("sucessfully loaded server code template plugin: " + serverPlugin.getPluginPointName());
259266
}
260267

261268
if (serverPlugin instanceof DataTypeServerPlugin) {
262269
DataTypeServerPlugin dataTypePlugin = (DataTypeServerPlugin) serverPlugin;
263270
dataTypePlugins.put(dataTypePlugin.getPluginPointName(), dataTypePlugin);
264-
addServerPlugin(dataTypePlugin);
271+
serverPlugins.add(dataTypePlugin);
265272
logger.debug("sucessfully loaded server data type plugin: " + serverPlugin.getPluginPointName());
266273
}
267274

268275
if (serverPlugin instanceof ResourcePlugin) {
269276
ResourcePlugin resourcePlugin = (ResourcePlugin) serverPlugin;
270277
resourcePlugins.put(resourcePlugin.getPluginPointName(), resourcePlugin);
271-
addServerPlugin(resourcePlugin);
278+
serverPlugins.add(resourcePlugin);
272279
logger.debug("Successfully loaded resource plugin: " + resourcePlugin.getPluginPointName());
273280
}
274281

275282
if (serverPlugin instanceof TransmissionModeProvider) {
276283
TransmissionModeProvider transmissionModeProvider = (TransmissionModeProvider) serverPlugin;
277284
transmissionModeProviders.put(transmissionModeProvider.getPluginPointName(), transmissionModeProvider);
278-
addServerPlugin(transmissionModeProvider);
285+
serverPlugins.add(transmissionModeProvider);
279286
logger.debug("Successfully loaded transmission mode provider plugin: " + transmissionModeProvider.getPluginPointName());
280287
}
281288

@@ -287,7 +294,7 @@ public void initPlugins() {
287294
}
288295

289296
this.authorizationPlugin = authorizationPlugin;
290-
addServerPlugin(authorizationPlugin);
297+
serverPlugins.add(authorizationPlugin);
291298
logger.debug("sucessfully loaded server authorization plugin: " + serverPlugin.getPluginPointName());
292299
}
293300

@@ -299,7 +306,7 @@ public void initPlugins() {
299306
}
300307

301308
this.multiFactorAuthenticationPlugin = multiFactorAuthenticationPlugin;
302-
addServerPlugin(multiFactorAuthenticationPlugin);
309+
serverPlugins.add(multiFactorAuthenticationPlugin);
303310
logger.debug("sucessfully loaded server multi-factor authentication plugin: " + serverPlugin.getPluginPointName());
304311
}
305312
} catch (Exception e) {
@@ -309,32 +316,6 @@ public void initPlugins() {
309316
}
310317
}
311318

312-
/**
313-
* Registers a plugin in the list used to start and stop all server plugins.
314-
* <p>
315-
* A single plugin class may implement more than one of the plugin type interfaces (for example
316-
* both {@link ServicePlugin} and {@link ChannelPlugin}). Such a plugin is registered against
317-
* each type it implements, so this method guards against adding the same instance more than
318-
* once. Without the guard, {@link #startPlugins()} and {@link #stopPlugins()} would invoke
319-
* {@link ServerPlugin#start()} and {@link ServerPlugin#stop()} once per implemented interface
320-
* rather than once per plugin.
321-
* <p>
322-
* Instances are compared by identity on purpose. Two distinct plugin instances must both be
323-
* registered even if the plugin class considers them equal.
324-
* <p>
325-
* Package private so it can be exercised directly by unit tests without going through the full
326-
* extension loading process.
327-
*/
328-
void addServerPlugin(ServerPlugin serverPlugin) {
329-
for (ServerPlugin registeredPlugin : serverPlugins) {
330-
if (registeredPlugin == serverPlugin) {
331-
return;
332-
}
333-
}
334-
335-
serverPlugins.add(serverPlugin);
336-
}
337-
338319
/* These are the maps for the different types of plugins */
339320
/* ********************************************************************** */
340321

@@ -742,7 +723,8 @@ public List<String> getClientLibraries() {
742723
}
743724

744725
public List<ServerPlugin> getServerPlugins() {
745-
return serverPlugins;
726+
// Copied into a List so the ExtensionController signature stays unchanged for extensions.
727+
return new ArrayList<ServerPlugin>(serverPlugins);
746728
}
747729

748730
void extractZipEntry(ZipEntry entry, File installTempDir, ZipFile zipFile) throws IOException {

‎server/src/test/java/com/mirth/connect/server/controllers/DefaultExtensionControllerTest.java‎

Lines changed: 0 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,9 @@
99

1010
package com.mirth.connect.server.controllers;
1111

12-
import static org.junit.Assert.assertEquals;
13-
import static org.junit.Assert.assertSame;
1412
import static org.junit.Assert.assertTrue;
1513

1614
import java.io.File;
17-
import java.util.List;
1815
import java.util.zip.ZipEntry;
1916
import java.util.zip.ZipException;
2017
import java.util.zip.ZipFile;
@@ -23,7 +20,6 @@
2320
import org.junit.Before;
2421
import org.junit.Test;
2522

26-
import com.mirth.connect.plugins.ServerPlugin;
2723
import com.mirth.connect.util.ZipTestUtils;
2824

2925
public class DefaultExtensionControllerTest {
@@ -76,98 +72,4 @@ public void cleanupTestFolder() {
7672
private ZipFile createTempZipFile(String fileName) throws Exception {
7773
return new ZipFile(ZipTestUtils.createTempZipFile(fileName));
7874
}
79-
80-
/*
81-
* A plugin class may implement several of the plugin type interfaces (ServicePlugin,
82-
* ChannelPlugin, and so on). initPlugins registers the instance once per interface it
83-
* implements, so registration must ignore an instance that is already registered. Otherwise
84-
* start() and stop() get invoked once per implemented interface instead of once per plugin.
85-
*/
86-
@Test
87-
public void testAddServerPluginIgnoresDuplicateRegistrationOfSameInstance() {
88-
DefaultExtensionController extensionController = new DefaultExtensionController();
89-
CountingServerPlugin plugin = new CountingServerPlugin("multi-type plugin");
90-
91-
extensionController.addServerPlugin(plugin);
92-
extensionController.addServerPlugin(plugin);
93-
extensionController.addServerPlugin(plugin);
94-
95-
List<ServerPlugin> registeredPlugins = extensionController.getServerPlugins();
96-
assertEquals(1, registeredPlugins.size());
97-
assertSame(plugin, registeredPlugins.get(0));
98-
}
99-
100-
@Test
101-
public void testStopPluginsStopsPluginRegisteredForMultipleTypesOnce() {
102-
DefaultExtensionController extensionController = new DefaultExtensionController();
103-
CountingServerPlugin plugin = new CountingServerPlugin("multi-type plugin");
104-
105-
extensionController.addServerPlugin(plugin);
106-
extensionController.addServerPlugin(plugin);
107-
108-
extensionController.stopPlugins();
109-
110-
assertEquals(1, plugin.stopCount);
111-
}
112-
113-
/*
114-
* Registration deliberately compares instances by identity, so two separate plugin instances
115-
* are both registered and both stopped even when the plugin class reports them as equal.
116-
*/
117-
@Test
118-
public void testAddServerPluginRegistersDistinctButEqualInstancesSeparately() {
119-
DefaultExtensionController extensionController = new DefaultExtensionController();
120-
AlwaysEqualServerPlugin firstPlugin = new AlwaysEqualServerPlugin();
121-
AlwaysEqualServerPlugin secondPlugin = new AlwaysEqualServerPlugin();
122-
123-
extensionController.addServerPlugin(firstPlugin);
124-
extensionController.addServerPlugin(secondPlugin);
125-
126-
assertEquals(2, extensionController.getServerPlugins().size());
127-
128-
extensionController.stopPlugins();
129-
130-
assertEquals(1, firstPlugin.stopCount);
131-
assertEquals(1, secondPlugin.stopCount);
132-
}
133-
134-
private static class CountingServerPlugin implements ServerPlugin {
135-
private final String pluginPointName;
136-
int stopCount;
137-
138-
private CountingServerPlugin(String pluginPointName) {
139-
this.pluginPointName = pluginPointName;
140-
}
141-
142-
@Override
143-
public String getPluginPointName() {
144-
return pluginPointName;
145-
}
146-
147-
@Override
148-
public void start() {
149-
// Not exercised here: startPlugins() also reaches into ControllerFactory.
150-
}
151-
152-
@Override
153-
public void stop() {
154-
stopCount++;
155-
}
156-
}
157-
158-
private static class AlwaysEqualServerPlugin extends CountingServerPlugin {
159-
private AlwaysEqualServerPlugin() {
160-
super("always equal plugin");
161-
}
162-
163-
@Override
164-
public boolean equals(Object other) {
165-
return other instanceof AlwaysEqualServerPlugin;
166-
}
167-
168-
@Override
169-
public int hashCode() {
170-
return AlwaysEqualServerPlugin.class.hashCode();
171-
}
172-
}
17375
}

0 commit comments

Comments
 (0)