Skip to content

Commit 2e378f0

Browse files
dmealingclaude
andcommitted
Decouple Maven dependencies from metadata package - implement LoaderConfigurable pattern
BREAKING CHANGE: Replace MojoSupport with LoaderConfigurable abstraction ## Architecture Changes: - Remove MojoSupport interface from metadata package - Add LoaderConfigurable interface for generic build tool integration - Add LoaderConfigurationBuilder with fluent API for type-safe configuration - Add MavenLoaderConfiguration bridge in maven-plugin module ## Benefits: - Zero Maven dependencies in core metadata package - Extensible design for other build tools (Gradle, SBT, etc.) - Better separation of concerns and cleaner dependencies - Maintains all existing Maven plugin functionality ## Files Changed: - **NEW**: LoaderConfigurable, LoaderConfigurationBuilder, LoaderConfigurationConstants - **NEW**: MavenLoaderConfiguration (Maven-specific bridge) - **UPDATED**: MetaDataLoader, SimpleLoader, FileMetaDataLoader (use new pattern) - **UPDATED**: AbstractMetaDataMojo (use LoaderConfigurable instead of MojoSupport) - **REMOVED**: MojoSupport interface and mojo package - **NEW**: Comprehensive unit tests for abstraction layer ## Future Extensibility: Ready for C#, TypeScript, and other build system integrations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 13bc865 commit 2e378f0

12 files changed

Lines changed: 501 additions & 138 deletions

File tree

.vscode/settings.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
{
2-
"java.compile.nullAnalysis.mode": "automatic",
3-
"java.configuration.updateBuildConfiguration": "automatic"
2+
"java.compile.nullAnalysis.mode": "automatic"
43
}

core/src/main/java/com/draagon/meta/loader/file/FileMetaDataLoader.java

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,47 +62,47 @@ protected ClassLoader getDefaultMetaDataClassLoader() {
6262
// MOJO Support Methods
6363

6464
@Override
65-
protected void mojoProcessSources( String sourceDir, List<String> rawSources ) {
66-
67-
if ( rawSources == null ) throw new IllegalArgumentException(
65+
protected void processSources(String sourceDir, List<String> rawSources) {
66+
if (rawSources == null) throw new IllegalArgumentException(
6867
"sourceURIList was null on setURIList for Loader: " + toString());
6968

7069
List<String> localSourceList = new ArrayList<>();
7170
List<URI> uriSourceList = new ArrayList<>();
7271

7372
// See if the raw input is a URI or not and add to appropriate list
74-
for ( String raw: rawSources ) {
75-
if ( raw.indexOf(":") > 0) uriSourceList.add(URIHelper.toURI(raw));
73+
for (String raw : rawSources) {
74+
if (raw.indexOf(":") > 0) uriSourceList.add(URIHelper.toURI(raw));
7675
else localSourceList.add(raw);
7776
}
7877

79-
// Set URI Souces
78+
// Set URI Sources
8079
if (!uriSourceList.isEmpty()) {
8180
URIFileMetaDataSources uriSources = new URIFileMetaDataSources(uriSourceList);
8281
getLoaderOptions().addSources(uriSources);
83-
uriSources.setLoaderClassLoader( getMetaDataClassLoader() );
82+
uriSources.setLoaderClassLoader(getMetaDataClassLoader());
8483
}
8584

8685
// Set Local Sources
8786
if (!localSourceList.isEmpty()) {
8887
LocalFileMetaDataSources localSources = null;
89-
if ( sourceDir != null ) localSources = new LocalFileMetaDataSources(sourceDir,localSourceList);
88+
if (sourceDir != null) localSources = new LocalFileMetaDataSources(sourceDir, localSourceList);
9089
else localSources = new LocalFileMetaDataSources(localSourceList);
9190
getLoaderOptions().addSources(localSources);
92-
localSources.setLoaderClassLoader( getMetaDataClassLoader() );
91+
localSources.setLoaderClassLoader(getMetaDataClassLoader());
9392
}
9493
}
9594

9695
@Override
97-
public void mojoInit( Map<String, String> args ) {
98-
99-
mojoInitArgs( args );
100-
96+
public void configure(LoaderConfiguration config) {
97+
// Process configuration arguments first to set up parsers
98+
processArguments(config.getArguments());
99+
101100
FileLoaderOptions options = getLoaderOptions()
102-
.addParser( XML_EXTENSION, XMLMetaDataParser.class)
103-
.addParser( JSON_EXTENSION, JsonMetaDataParser.class);
101+
.addParser(XML_EXTENSION, XMLMetaDataParser.class)
102+
.addParser(JSON_EXTENSION, JsonMetaDataParser.class);
104103

105-
init();
104+
// Call parent to handle the rest of the configuration
105+
super.configure(config);
106106
}
107107

108108
////////////////////////////////////////////////////////////////////////////////////////////

maven-plugin/src/main/java/com/draagon/meta/mojo/AbstractMetaDataMojo.java

Lines changed: 36 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import com.draagon.meta.MetaDataException;
44
import com.draagon.meta.generator.Generator;
55
import com.draagon.meta.loader.MetaDataLoader;
6-
import com.draagon.meta.loader.mojo.MojoSupport;
6+
import com.draagon.meta.loader.LoaderConfigurable;
77
import com.draagon.meta.loader.simple.SimpleLoader;
88
import org.apache.maven.plugin.AbstractMojo;
99
import org.apache.maven.plugin.MojoExecution;
@@ -128,81 +128,78 @@ public Map<String, String> mergeAndOverwriteArgs(GeneratorParam g) {
128128

129129
protected MetaDataLoader createLoader(ClassLoader projectClassLoader) {
130130

131-
MojoSupport mojoSupport = null;
131+
LoaderConfigurable configurable = null;
132132
String loaderClass = loaderConfig.getClassname();
133133
String loaderName = loaderConfig.getName();
134134

135135
if (loaderClass != null) {
136-
mojoSupport = getConfiguredLoader( projectClassLoader, loaderClass, loaderName);
137-
}
138-
else {
139-
mojoSupport = new SimpleLoader( loaderName );
136+
configurable = getConfiguredLoader(projectClassLoader, loaderClass, loaderName);
137+
} else {
138+
configurable = new SimpleLoader(loaderName);
140139
}
141140

142-
// Get the Source Directory if it is specified
141+
// Configure the loader using the new pattern
142+
String sourceDir = null;
143143
File srcDir = getSourceDir();
144-
if ( srcDir != null ) mojoSupport.mojoSetSourceDir( loaderConfig.getSourceDir() );
145-
//getLog().info( "Setting MojoClassLoader: " + getProjectClassLoader() );
146-
mojoSupport.mojoSetClassLoader( projectClassLoader );
147-
mojoSupport.mojoSetSources( loaderConfig.getSources() );
148-
mojoSupport.mojoInit( getGlobals() );
149-
150-
MetaDataLoader loader = mojoSupport.getLoader();
151-
//getLog().info("projectClassLoader: " + projectClassLoader);
152-
//getLog().info("loader.getClass(): " + mojoSupport.getClass().getClassLoader());
153-
//getLog().info("loader.getMDCP(): " + mojoSupport.getLoader().getMetaDataClassLoader());
154-
//getLog().info("loader.Options(): " + mojoSupport.getLoader().getLoaderOptions().getClass().getClassLoader());
144+
if (srcDir != null) {
145+
sourceDir = loaderConfig.getSourceDir();
146+
}
147+
148+
MavenLoaderConfiguration.configure(configurable, sourceDir, projectClassLoader,
149+
loaderConfig.getSources(), getGlobals());
150+
151+
MetaDataLoader loader = configurable.getLoader();
155152

156153
getLog().info("MetaData Mojo > Create Loader: " + loader.toString());
157154

158155
return loader;
159156
}
160157

161-
private MojoSupport getConfiguredLoader(ClassLoader projectClassLoader, String loaderClass, String loaderName) {
158+
private LoaderConfigurable getConfiguredLoader(ClassLoader projectClassLoader, String loaderClass, String loaderName) {
162159

163-
MojoSupport mojoSupport;
160+
LoaderConfigurable configurable;
164161
try {
165162
// Attempt to load the loader by classname
166163
Class c;
167164
try {
168165
c = projectClassLoader.loadClass(loaderClass);
169166
}
170-
catch ( ClassNotFoundException ex ) {
171-
throw new MetaDataException( "Could not create MetaDataLoader("+loaderName+") with class "+
172-
"[" + loaderClass + "] as it was not found on the Project ClassLoader" );
167+
catch (ClassNotFoundException ex) {
168+
throw new MetaDataException("Could not create MetaDataLoader(" + loaderName + ") with class " +
169+
"[" + loaderClass + "] as it was not found on the Project ClassLoader");
173170
}
174171

175172
// See if it's an interface
176-
if ( c.isInterface() ) {
177-
throw new MetaDataException( "Could not create MetaDataLoader("+loaderName+") with class "+
178-
"[" + loaderClass + "] as it is an interface" );
173+
if (c.isInterface()) {
174+
throw new MetaDataException("Could not create MetaDataLoader(" + loaderName + ") with class " +
175+
"[" + loaderClass + "] as it is an interface");
179176
}
180177

181-
// See if it implements MojoSupport
182-
if (!MojoSupport.class.isAssignableFrom( c )) {
183-
throw new MetaDataException( "Could not create MetaDataLoader("+loaderName+") with class "+
184-
"[" + loaderClass + "] as it does not implement MojoSupport" );
178+
// See if it implements LoaderConfigurable
179+
if (!LoaderConfigurable.class.isAssignableFrom(c)) {
180+
throw new MetaDataException("Could not create MetaDataLoader(" + loaderName + ") with class " +
181+
"[" + loaderClass + "] as it does not implement LoaderConfigurable");
185182
}
186183

187184
// Try for a constructor with a String for the loaderName
188185
Constructor cc = null;
189186
try {
190187
cc = c.getDeclaredConstructor(String.class);
191188
}
192-
catch( NoSuchMethodException | SecurityException ex ) {
193-
throw new MetaDataException( "Could not create MetaDataLoader("+loaderName+") with class "+
194-
"[" + loaderClass + "] as the Constructor was not found or had security issues: "+
195-
ex.getMessage(), ex );
189+
catch (NoSuchMethodException | SecurityException ex) {
190+
throw new MetaDataException("Could not create MetaDataLoader(" + loaderName + ") with class " +
191+
"[" + loaderClass + "] as the Constructor was not found or had security issues: " +
192+
ex.getMessage(), ex);
196193
}
197194

198-
mojoSupport = (MojoSupport) cc.newInstance( loaderName );
195+
configurable = (LoaderConfigurable) cc.newInstance(loaderName);
199196
}
200-
catch ( InstantiationException | IllegalAccessException | InvocationTargetException ex) {
201-
throw new MetaDataException( "Could not instantiate MetaDataLoader("+loaderName+") with class "+
202-
"[" + loaderConfig.getClassname() + "]: " + ex.getMessage(), ex );
197+
catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
198+
throw new MetaDataException("Could not instantiate MetaDataLoader(" + loaderName + ") with class " +
199+
"[" + loaderConfig.getClassname() + "]: " + ex.getMessage(), ex);
203200
}
204201

205-
return mojoSupport;
202+
return configurable;
206203
}
207204

208205
protected ClassLoader createProjectClassLoader()
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2003 Draagon Software LLC. All Rights Reserved.
3+
*
4+
* This software is the proprietary information of Draagon Software LLC.
5+
* Use is subject to license terms.
6+
*/
7+
package com.draagon.meta.mojo;
8+
9+
import com.draagon.meta.loader.LoaderConfigurable;
10+
import com.draagon.meta.loader.LoaderConfigurationBuilder;
11+
12+
import java.util.List;
13+
import java.util.Map;
14+
15+
/**
16+
* Maven-specific implementation of LoaderConfiguration.
17+
* This bridges Maven Mojo configuration to the generic LoaderConfigurable interface.
18+
*/
19+
public class MavenLoaderConfiguration {
20+
21+
/**
22+
* Configure a LoaderConfigurable instance using Maven-specific configuration
23+
*
24+
* @param configurable The loader to configure
25+
* @param sourceDir The Maven source directory
26+
* @param classLoader The Maven project class loader
27+
* @param sources The list of source files
28+
* @param globals The global arguments map
29+
*/
30+
public static void configure(LoaderConfigurable configurable,
31+
String sourceDir,
32+
ClassLoader classLoader,
33+
List<String> sources,
34+
Map<String, String> globals) {
35+
36+
LoaderConfigurable.LoaderConfiguration config = new LoaderConfigurationBuilder()
37+
.sourceDir(sourceDir)
38+
.classLoader(classLoader)
39+
.sources(sources)
40+
.arguments(globals)
41+
.build();
42+
43+
configurable.configure(config);
44+
}
45+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package com.draagon.meta.mojo;
2+
3+
import com.draagon.meta.loader.LoaderConfigurable;
4+
import com.draagon.meta.loader.LoaderConfigurationConstants;
5+
import com.draagon.meta.loader.MetaDataLoader;
6+
import org.junit.Before;
7+
import org.junit.Test;
8+
9+
import java.util.Arrays;
10+
import java.util.HashMap;
11+
import java.util.List;
12+
import java.util.Map;
13+
14+
import static org.junit.Assert.*;
15+
16+
/**
17+
* Unit tests for MavenLoaderConfiguration
18+
*/
19+
public class MavenLoaderConfigurationTest {
20+
21+
private TestLoaderConfigurable testLoader;
22+
private ClassLoader testClassLoader;
23+
private List<String> testSources;
24+
private Map<String, String> testGlobals;
25+
26+
@Before
27+
public void setUp() {
28+
testLoader = new TestLoaderConfigurable();
29+
testClassLoader = getClass().getClassLoader();
30+
testSources = Arrays.asList("source1.json", "source2.json");
31+
testGlobals = new HashMap<>();
32+
testGlobals.put(LoaderConfigurationConstants.ARG_REGISTER, "true");
33+
testGlobals.put(LoaderConfigurationConstants.ARG_VERBOSE, "false");
34+
testGlobals.put("customArg", "customValue");
35+
}
36+
37+
@Test
38+
public void testConfigureWithAllParameters() {
39+
String sourceDir = "/test/source";
40+
41+
MavenLoaderConfiguration.configure(testLoader, sourceDir, testClassLoader, testSources, testGlobals);
42+
43+
LoaderConfigurable.LoaderConfiguration config = testLoader.getReceivedConfiguration();
44+
assertNotNull("Configuration should have been received", config);
45+
46+
assertEquals(sourceDir, config.getSourceDir());
47+
assertEquals(testClassLoader, config.getClassLoader());
48+
assertEquals(testSources, config.getSources());
49+
assertEquals(testGlobals, config.getArguments());
50+
}
51+
52+
@Test
53+
public void testConfigureWithNulls() {
54+
MavenLoaderConfiguration.configure(testLoader, null, null, null, null);
55+
56+
LoaderConfigurable.LoaderConfiguration config = testLoader.getReceivedConfiguration();
57+
assertNotNull("Configuration should have been received", config);
58+
59+
assertNull(config.getSourceDir());
60+
assertNull(config.getClassLoader());
61+
assertTrue(config.getSources().isEmpty());
62+
assertTrue(config.getArguments().isEmpty());
63+
}
64+
65+
@Test
66+
public void testConfigureWithEmptyCollections() {
67+
List<String> emptySources = Arrays.asList();
68+
Map<String, String> emptyGlobals = new HashMap<>();
69+
70+
MavenLoaderConfiguration.configure(testLoader, "/test", testClassLoader, emptySources, emptyGlobals);
71+
72+
LoaderConfigurable.LoaderConfiguration config = testLoader.getReceivedConfiguration();
73+
assertNotNull("Configuration should have been received", config);
74+
75+
assertEquals("/test", config.getSourceDir());
76+
assertEquals(testClassLoader, config.getClassLoader());
77+
assertTrue(config.getSources().isEmpty());
78+
assertTrue(config.getArguments().isEmpty());
79+
}
80+
81+
/**
82+
* Test implementation of LoaderConfigurable for testing purposes
83+
*/
84+
private static class TestLoaderConfigurable implements LoaderConfigurable {
85+
private LoaderConfiguration receivedConfiguration;
86+
87+
@Override
88+
public void configure(LoaderConfiguration config) {
89+
this.receivedConfiguration = config;
90+
}
91+
92+
@Override
93+
public MetaDataLoader getLoader() {
94+
return null; // Not needed for this test
95+
}
96+
97+
public LoaderConfiguration getReceivedConfiguration() {
98+
return receivedConfiguration;
99+
}
100+
}
101+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2003 Draagon Software LLC. All Rights Reserved.
3+
*
4+
* This software is the proprietary information of Draagon Software LLC.
5+
* Use is subject to license terms.
6+
*/
7+
package com.draagon.meta.loader;
8+
9+
import java.util.List;
10+
import java.util.Map;
11+
12+
/**
13+
* Generic interface for configuring MetaDataLoaders from external build tools.
14+
* This abstraction keeps build-tool-specific dependencies out of the core metadata package.
15+
*
16+
* Implementation Pattern:
17+
* - Core metadata classes implement this interface
18+
* - Build tools (Maven, Gradle, etc.) create specific configuration builders
19+
* - Configuration is applied through this generic interface
20+
*/
21+
public interface LoaderConfigurable {
22+
23+
/**
24+
* Configure the loader with a configuration object
25+
* @param config The configuration to apply
26+
*/
27+
void configure(LoaderConfiguration config);
28+
29+
/**
30+
* Get the configured MetaDataLoader instance
31+
* @return The configured loader
32+
*/
33+
MetaDataLoader getLoader();
34+
35+
/**
36+
* Configuration data holder
37+
*/
38+
interface LoaderConfiguration {
39+
String getSourceDir();
40+
ClassLoader getClassLoader();
41+
List<String> getSources();
42+
Map<String, String> getArguments();
43+
}
44+
}

0 commit comments

Comments
 (0)