Skip to content

Commit c13b9bd

Browse files
Merge pull request #6045 from peterrinehart/BACKLOG-46631
[BACKLOG-46631] Ensured that the list of plugin directories is sorted
2 parents b743622 + 3c734c9 commit c13b9bd

File tree

2 files changed

+54
-1
lines changed

2 files changed

+54
-1
lines changed

extensions/src/main/java/org/pentaho/platform/plugin/services/pluginmgr/SystemPathXmlPluginProvider.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.dom4j.Document;
2222
import org.dom4j.Element;
2323
import org.dom4j.Node;
24+
import org.slf4j.LoggerFactory;
2425
import org.pentaho.platform.api.engine.IContentGeneratorInfo;
2526
import org.pentaho.platform.api.engine.IPentahoSession;
2627
import org.pentaho.platform.api.engine.IPlatformPlugin;
@@ -48,6 +49,7 @@
4849
import java.io.File;
4950
import java.io.FilenameFilter;
5051
import java.util.ArrayList;
52+
import java.util.Arrays;
5153
import java.util.Collection;
5254
import java.util.Collections;
5355
import java.util.List;
@@ -64,6 +66,7 @@ public class SystemPathXmlPluginProvider implements IPluginProvider {
6466

6567
public static final String CLASS_PROPERRTY = "class";
6668
private static final Pattern PLUGIN_DATE_STAMP_REGEX = Pattern.compile( "([\\w\\-]+)-2\\d{3}-[\\d\\-]+" );
69+
private static final org.slf4j.Logger log = LoggerFactory.getLogger( SystemPathXmlPluginProvider.class );
6770

6871
/**
6972
* Gets the list of plugins that this provider class has discovered.
@@ -83,6 +86,7 @@ public List<IPlatformPlugin> getPlugins( IPentahoSession session ) throws Platfo
8386
"PluginManager.ERROR_0004_CANNOT_FIND_SYSTEM_FOLDER" ) ); //$NON-NLS-1$
8487
}
8588
File[] kids = systemDir.listFiles();
89+
Arrays.sort( kids ); // default sort is by name, which is what we want
8690
// look at each child to see if it is a folder
8791
for ( File kid : kids ) {
8892
if ( kid.isDirectory() ) {
@@ -102,8 +106,11 @@ public List<IPlatformPlugin> getPlugins( IPentahoSession session ) throws Platfo
102106
return Collections.unmodifiableList( plugins );
103107
}
104108

109+
@SuppressWarnings( "squid:S3776" ) // cannot break down into smaller methods without reducing readability
105110
protected void processDirectory( List<IPlatformPlugin> plugins, File folder, IPentahoSession session )
106111
throws PlatformPluginRegistrationException {
112+
113+
log.debug( "Processing plugin directory: {}", folder.getAbsolutePath() );
107114
// see if there is a plugin.xml file
108115
FilenameFilter filter = new NameFileFilter( "plugin.xml", IOCase.SENSITIVE ); //$NON-NLS-1$
109116
File[] kids = folder.listFiles( filter );
@@ -114,13 +121,15 @@ protected void processDirectory( List<IPlatformPlugin> plugins, File folder, IPe
114121
FilenameFilter deleteFilter = new NameFileFilter( ".plugin-manager-delete", IOCase.SENSITIVE ); //$NON-NLS-1$
115122
kids = folder.listFiles( deleteFilter );
116123
if ( kids != null && kids.length > 0 ) {
124+
log.debug( "Deleting plugin directory marked for deletion: {}", folder.getAbsolutePath() );
117125
deleteFolder( folder );
118126
return;
119127
}
120128
// see if we should ignore this plugin because it is marked to be ignored
121129
FilenameFilter ignoreFilter = new NameFileFilter( ".kettle-ignore", IOCase.SENSITIVE ); //$NON-NLS-1$
122130
kids = folder.listFiles( ignoreFilter );
123131
if ( kids != null && kids.length > 0 ) {
132+
log.debug( "Ignoring plugin directory marked to be ignored: {}", folder.getAbsolutePath() );
124133
return;
125134
}
126135

@@ -144,7 +153,19 @@ protected void processDirectory( List<IPlatformPlugin> plugins, File folder, IPe
144153
// XML document can't be read. We'll just return a null document.
145154
}
146155
if ( doc != null ) {
147-
plugins.add( createPlugin( doc, session, folder.getName(), hasLib ) );
156+
PlatformPlugin newPlugin = createPlugin( doc, session, folder.getName(), hasLib );
157+
// make sure we don't already have a plugin with this id
158+
if ( plugins.stream().anyMatch( p -> p.getId().equals( newPlugin.getId() ) ) ) {
159+
String msg =
160+
Messages.getInstance().getErrorString(
161+
"PluginManager.ERROR_0024_PLUGIN_ALREADY_LOADED_BY_SAME_NAME", newPlugin.getId(), folder //$NON-NLS-1$
162+
.getAbsolutePath() );
163+
Logger.error( getClass().toString(), msg );
164+
PluginMessageLogger.add( msg );
165+
log.warn( "Plugin with id {} already loaded, skipping plugin in folder {}", newPlugin.getId(), path );
166+
} else {
167+
plugins.add( newPlugin );
168+
}
148169
}
149170
} catch ( Exception e ) {
150171
throw new PlatformPluginRegistrationException( Messages.getInstance().getErrorString(
@@ -163,6 +184,7 @@ protected void processDirectory( List<IPlatformPlugin> plugins, File folder, IPe
163184
*/
164185
private File cleanUpUninstalledPlugins( File folder, FilenameFilter deleteFilter, FilenameFilter ignoreFilter ) {
165186
// strip off any datestamp left from the plugin manager to leave only the base folder name
187+
log.debug( "Checking for datestamp on plugin folder: {}", folder.getAbsolutePath() );
166188
String newFolderName = stripDateStampFromFolderName( folder.getName() );
167189
if ( !folder.getName().equals( newFolderName ) ) {
168190
// The plugin folder had a date stamp on the end of the name which was removed.
@@ -178,6 +200,7 @@ private File cleanUpUninstalledPlugins( File folder, FilenameFilter deleteFilter
178200
File[] deleteFiles = oldPluginFolder.listFiles( deleteFilter );
179201
if ( null != ignoreFiles && ignoreFiles.length == 1 && null != deleteFiles && deleteFiles.length == 1 ) {
180202
// we can and should delete this folder before we rename the other one
203+
log.debug( "Deleting old plugin folder marked for deletion: {}", oldPluginFolder.getAbsolutePath() );
181204
deleteFolder( oldPluginFolder );
182205
canRenameFolder = true;
183206
} else {
@@ -208,6 +231,7 @@ private void deleteFolder( File folder ) {
208231
// delete the folder and its contents
209232
try {
210233
FileUtils.deleteDirectory( folder );
234+
log.debug( "Deleted plugin directory: {}", folder.getAbsolutePath() );
211235
String msg = Messages.getInstance().getString(
212236
"PluginManager.PLUGIN_FOLDER_DELETED", folder.getAbsolutePath() );
213237
Logger.info( getClass().toString(), msg );

extensions/src/test/java/org/pentaho/platform/plugin/services/pluginmgr/SystemPathXmlPluginProviderTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,4 +325,33 @@ public void testProcessDirectory_renameFolderWithDateStamp() throws Exception {
325325
assertEquals( 1, plugins.size() );
326326
}
327327
}
328+
329+
/*
330+
* This test forces the condition where a plugin with a given ID already exists in the plugins list.
331+
* It verifies that the plugin manager does not add a duplicate plugin and logs an appropriate message.
332+
*/
333+
@Test
334+
public void testProcessDirectory_duplicatePluginId() throws Exception {
335+
File pluginDir = tempFolder.newFolder( "duplicatePlugin" );
336+
File pluginXml = new File( pluginDir, "plugin.xml" );
337+
pluginXml.createNewFile();
338+
List<IPlatformPlugin> plugins = new ArrayList<>();
339+
IPentahoSession session = Mockito.mock( IPentahoSession.class );
340+
// Add a plugin with the same ID to the list
341+
IPlatformPlugin existingPlugin = Mockito.mock( IPlatformPlugin.class );
342+
Mockito.when( existingPlugin.getId() ).thenReturn( "duplicatePlugin" );
343+
plugins.add( existingPlugin );
344+
// statically mock the ActionSequenceResource to always return an inputStream to the plugin.xml file
345+
try ( MockedStatic<ActionSequenceResource> mockedStatic =
346+
Mockito.mockStatic( ActionSequenceResource.class ) ) {
347+
String pluginXmlText = "<plugin title=\"duplicatePlugin\" name=\"duplicatePlugin\"></plugin>";
348+
// create an input stream from the pluginXmlText
349+
mockedStatic.when( () -> ActionSequenceResource.getInputStream( anyString(), any() ) )
350+
.thenReturn( new ByteArrayInputStream( pluginXmlText.getBytes() ) );
351+
systemPathXmlPluginProvider.processDirectory( plugins, pluginDir, session );
352+
// Verify that the plugin was not added again
353+
assertEquals( 1, plugins.size() );
354+
assertTrue( PluginMessageLogger.getAll().stream().anyMatch( msg -> msg.contains( "A plugin has already been registered by name duplicatePlugin" ) ) );
355+
}
356+
}
328357
}

0 commit comments

Comments
 (0)