Skip to content

Commit 276f9e7

Browse files
authored
Refactor WrapperProperties template to remove caching and simplify implementation (#2436)
* Refactor WrapperProperties template to remove caching and simplify implementation This commit refactors the WrapperProperties template used for Maven model properties: TEMPLATE CHANGES (src/mdo/java/WrapperProperties.java): - Removed caching mechanism (orderedProps field and ensureInitialized() method) - Simplified read operations to directly delegate to getter.get() - Introduced writeOperation() pattern for all write operations that: * Creates a fresh OrderedProperties from current state * Performs the operation on the copy * Only calls setter if changes were made - All write operations now use synchronized writeOperation() wrapper - Maintains insertion order through OrderedProperties inner class - Supports multiple wrapper instances sharing the same backend storage The new approach eliminates cache invalidation complexity while ensuring: - Thread safety through synchronized write operations - Insertion order preservation - Immediate visibility of changes across wrapper instances - Proper delegation to underlying storage SIDE EFFECTS: - Consolidated redundant test classes into single PropertiesTest - Removed PropertiesOrderTest and WrapperPropertiesOrderTest (functionality covered by PropertiesTest) - Cleaned up duplicate test methods in PropertiesTest This refactoring simplifies the WrapperProperties implementation while maintaining all required functionality for Maven model property management. * Fix after review
1 parent 780a5cb commit 276f9e7

File tree

4 files changed

+304
-235
lines changed

4 files changed

+304
-235
lines changed

impl/maven-core/src/test/java/org/apache/maven/model/PropertiesOrderTest.java

Lines changed: 0 additions & 97 deletions
This file was deleted.
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.model;
20+
21+
import java.util.ArrayList;
22+
import java.util.LinkedHashMap;
23+
import java.util.List;
24+
import java.util.Map;
25+
import java.util.Properties;
26+
27+
import org.junit.jupiter.api.Nested;
28+
import org.junit.jupiter.api.Test;
29+
30+
import static org.junit.jupiter.api.Assertions.assertEquals;
31+
import static org.junit.jupiter.api.Assertions.assertNotNull;
32+
33+
/**
34+
* Comprehensive test suite for Properties behavior in Maven models.
35+
* Tests order preservation, caching behavior, and WrapperProperties functionality.
36+
*/
37+
class PropertiesTest {
38+
39+
@Nested
40+
class OrderPreservationTests {
41+
42+
@Test
43+
void testPropertiesOrderPreservedInImmutableModel() {
44+
// Create properties with specific insertion order using LinkedHashMap
45+
Map<String, String> orderedMap = new LinkedHashMap<>();
46+
orderedMap.put("third", "3");
47+
orderedMap.put("first", "1");
48+
orderedMap.put("second", "2");
49+
50+
// Create model and set properties
51+
Model model = new Model();
52+
Properties props = model.getProperties();
53+
54+
// Create properties and populate from map to maintain order
55+
orderedMap.forEach(props::setProperty);
56+
57+
// Get the immutable delegate (v4 API model is already immutable)
58+
org.apache.maven.api.model.Model immutable = model.getDelegate();
59+
60+
// Verify order is preserved
61+
Map<String, String> resultProps = immutable.getProperties();
62+
assertNotNull(resultProps);
63+
64+
// Check order by collecting keys in iteration order
65+
List<String> keys = new ArrayList<>(resultProps.keySet());
66+
67+
// Verify the original insertion order is maintained
68+
assertEquals(3, keys.size());
69+
assertEquals("third", keys.get(0));
70+
assertEquals("first", keys.get(1));
71+
assertEquals("second", keys.get(2));
72+
}
73+
74+
@Test
75+
void testPropertiesOrderPreservedInMutableModel() {
76+
// Create ordered map to simulate properties with specific order
77+
Map<String, String> orderedMap = new LinkedHashMap<>();
78+
orderedMap.put("z-property", "z");
79+
orderedMap.put("a-property", "a");
80+
orderedMap.put("m-property", "m");
81+
82+
// Create and populate model
83+
Model model = new Model();
84+
Properties props = model.getProperties();
85+
86+
// Create properties and populate from map to maintain order
87+
orderedMap.forEach(props::setProperty);
88+
89+
// Get properties back and verify order
90+
Properties resultProps = model.getProperties();
91+
92+
// Check order by collecting keys in iteration order
93+
List<String> keys = new ArrayList<>();
94+
resultProps.keySet().forEach(k -> keys.add(k.toString()));
95+
96+
// Verify the original insertion order is maintained
97+
assertEquals(3, keys.size());
98+
assertEquals("z-property", keys.get(0));
99+
assertEquals("a-property", keys.get(1));
100+
assertEquals("m-property", keys.get(2));
101+
}
102+
103+
@Test
104+
void testOrderPreservationAfterModification() {
105+
// Create a model with properties
106+
Model model = new Model();
107+
Properties modelProps = model.getProperties();
108+
109+
// Add properties in specific order
110+
modelProps.setProperty("first", "1");
111+
modelProps.setProperty("second", "2");
112+
113+
// Modify existing property
114+
modelProps.setProperty("first", "modified");
115+
116+
// Add new property
117+
modelProps.setProperty("third", "3");
118+
119+
// Collect keys in iteration order
120+
List<String> keys = new ArrayList<>();
121+
modelProps.keySet().forEach(k -> keys.add(k.toString()));
122+
123+
// Verify order is preserved (first should still be first since it was modified, not re-added)
124+
assertEquals(3, keys.size());
125+
assertEquals("first", keys.get(0));
126+
assertEquals("second", keys.get(1));
127+
assertEquals("third", keys.get(2));
128+
129+
// Verify value was updated
130+
assertEquals("modified", modelProps.getProperty("first"));
131+
}
132+
}
133+
134+
@Nested
135+
class WrapperPropertiesBehaviorTests {
136+
137+
@Test
138+
void testWriteOperationBehavior() {
139+
// Create a Model with initial properties
140+
Model model = new Model();
141+
142+
// Set initial properties using setProperties to establish the backend
143+
Properties initialProps = new Properties();
144+
initialProps.setProperty("initial.key", "initial.value");
145+
model.setProperties(initialProps);
146+
147+
// Get the WrapperProperties instance
148+
Properties wrapperProps = model.getProperties();
149+
150+
// First read - should initialize cache
151+
assertEquals("initial.value", wrapperProps.getProperty("initial.key"));
152+
153+
// Simulate external change by directly calling setProperties (another WrapperProperties instance)
154+
Properties externalProps = new Properties();
155+
externalProps.setProperty("initial.key", "externally.modified");
156+
externalProps.setProperty("external.key", "external.value");
157+
model.setProperties(externalProps);
158+
159+
// Read again - should return fresh value (no caching in current implementation)
160+
assertEquals("externally.modified", wrapperProps.getProperty("initial.key"));
161+
162+
// Now perform a write operation
163+
wrapperProps.setProperty("new.key", "new.value");
164+
165+
// Read the initial key again - should return the current value
166+
assertEquals("externally.modified", wrapperProps.getProperty("initial.key"));
167+
168+
// Read the external key that was set before the write operation
169+
assertEquals("external.value", wrapperProps.getProperty("external.key"));
170+
171+
// Read the new key that was just set
172+
assertEquals("new.value", wrapperProps.getProperty("new.key"));
173+
}
174+
175+
@Test
176+
void testMultipleWrapperPropertiesShareSameBackend() {
177+
// Create a Model with initial properties
178+
Model model = new Model();
179+
180+
Properties initialProps = new Properties();
181+
initialProps.setProperty("shared.key", "initial.value");
182+
model.setProperties(initialProps);
183+
184+
// Get two WrapperProperties instances from the same Model
185+
Properties wrapper1 = model.getProperties();
186+
Properties wrapper2 = model.getProperties();
187+
188+
// Both wrappers should read the same initial value
189+
assertEquals("initial.value", wrapper1.getProperty("shared.key"));
190+
assertEquals("initial.value", wrapper2.getProperty("shared.key"));
191+
192+
// Write through wrapper1
193+
wrapper1.setProperty("from.wrapper1", "value1");
194+
195+
// wrapper2 should see the changes immediately (no caching)
196+
assertEquals("value1", wrapper2.getProperty("from.wrapper1"));
197+
assertEquals("initial.value", wrapper2.getProperty("shared.key"));
198+
199+
// Now wrapper2 performs a write operation
200+
wrapper2.setProperty("from.wrapper2", "value2");
201+
202+
// Both wrappers should see all changes immediately
203+
assertEquals("value1", wrapper1.getProperty("from.wrapper1"));
204+
assertEquals("value2", wrapper1.getProperty("from.wrapper2"));
205+
assertEquals("value1", wrapper2.getProperty("from.wrapper1"));
206+
assertEquals("value2", wrapper2.getProperty("from.wrapper2"));
207+
208+
// Add another property through wrapper1
209+
wrapper1.setProperty("another.key", "another.value");
210+
assertEquals("another.value", wrapper1.getProperty("another.key"));
211+
assertEquals("another.value", wrapper2.getProperty("another.key"));
212+
}
213+
214+
@Test
215+
void testVariousWriteOperations() {
216+
// Create a Model with initial properties
217+
Model model = new Model();
218+
219+
Properties initialProps = new Properties();
220+
initialProps.setProperty("key1", "value1");
221+
model.setProperties(initialProps);
222+
223+
Properties wrapper = model.getProperties();
224+
225+
// Initial read
226+
assertEquals("value1", wrapper.getProperty("key1"));
227+
228+
// Test put() method
229+
wrapper.put("key2", "value2");
230+
assertEquals("value2", wrapper.getProperty("key2"));
231+
232+
// Simulate external change
233+
Properties externalProps1 = new Properties();
234+
externalProps1.setProperty("key1", "modified_after_put");
235+
externalProps1.setProperty("key2", "value2");
236+
externalProps1.setProperty("external.key", "external.value");
237+
model.setProperties(externalProps1);
238+
assertEquals("modified_after_put", wrapper.getProperty("key1"));
239+
240+
// Test remove() method
241+
wrapper.remove("key2");
242+
assertEquals(null, wrapper.getProperty("key2"));
243+
244+
// Simulate external change
245+
Properties externalProps2 = new Properties();
246+
externalProps2.setProperty("key1", "modified_after_remove");
247+
externalProps2.setProperty("external.key", "external.value");
248+
model.setProperties(externalProps2);
249+
assertEquals("modified_after_remove", wrapper.getProperty("key1"));
250+
251+
// Test putAll() method
252+
Properties newProps = new Properties();
253+
newProps.setProperty("putall.key1", "putall.value1");
254+
newProps.setProperty("putall.key2", "putall.value2");
255+
wrapper.putAll(newProps);
256+
assertEquals("putall.value1", wrapper.getProperty("putall.key1"));
257+
assertEquals("putall.value2", wrapper.getProperty("putall.key2"));
258+
}
259+
}
260+
}

0 commit comments

Comments
 (0)