Index: admin/rest/src/test/java/org/glassfish/admin/rest/PropertiesBagTest.java =================================================================== --- admin/rest/src/test/java/org/glassfish/admin/rest/PropertiesBagTest.java (revision 44630) +++ admin/rest/src/test/java/org/glassfish/admin/rest/PropertiesBagTest.java (working copy) @@ -62,6 +62,7 @@ protected static final String URL_DOMAIN_PROPERTIES = "/domain/property"; protected static final String URL_JAVA_CONFIG_PROPERTIES = "/domain/configs/config/default-config/java-config/property"; protected static final String URL_SERVER_PROPERTIES = "/domain/servers/server/server/property"; + protected static final String URL_DERBYPOOL_PROPERTIES = "/domain/resources/jdbc-connection-pool/DerbyPool/property"; private static final String REQUEST_FORMAT = MediaType.APPLICATION_JSON; @Test @@ -81,6 +82,50 @@ public void serverProperties() { createAndDeleteProperties(URL_SERVER_PROPERTIES); } + + @Test + public void testOptimizedPropertyHandling() { + // First, test changing one property and adding a new + List> properties = new ArrayList>(); + properties.add(createProperty("PortNumber","1527")); + properties.add(createProperty("Password","APP")); + properties.add(createProperty("User","APP")); + properties.add(createProperty("serverName","localhost")); + properties.add(createProperty("DatabaseName","sun-appserv-samples")); + properties.add(createProperty("connectionAttributes",";create=false")); + properties.add(createProperty("foo","bar","test")); + createProperties(URL_DERBYPOOL_PROPERTIES, properties); + + List> newProperties = getProperties(get(URL_DERBYPOOL_PROPERTIES)); + for (Map property : newProperties) { + if (property.get("name").equals("connectionAttributes")) { + assertEquals(";create=false", property.get("value")); + } else if (property.get("name").equals("foo")) { + assertEquals("bar", property.get("value")); + assertEquals("test", property.get("description")); + } + } + + // Now test changing that property back and deleting the new one + properties = new ArrayList>(); + properties.add(createProperty("PortNumber","1527")); + properties.add(createProperty("Password","APP")); + properties.add(createProperty("User","APP")); + properties.add(createProperty("serverName","localhost")); + properties.add(createProperty("DatabaseName","sun-appserv-samples")); + properties.add(createProperty("connectionAttributes",";create=true")); + + createProperties(URL_DERBYPOOL_PROPERTIES, properties); + + newProperties = getProperties(get(URL_DERBYPOOL_PROPERTIES)); + for (Map property : newProperties) { + if (property.get("name").equals("connectionAttributes")) { + assertEquals(";create=true", property.get("value")); + } else if (property.get("name").equals("foo")) { + fail("The property was not deleted as expected."); + } + } + } protected void createAndDeleteProperties(String endpoint) { ClientResponse response = get(endpoint); @@ -90,17 +135,24 @@ List> properties = new ArrayList>(); for(int i = 0, max = generateRandomNumber(16); i < max; i++) { - properties.add(new HashMap() {{ - put ("name", "property_" + generateRandomString()); - put ("value", generateRandomString()); - put ("description", generateRandomString()); - }}); + properties.add(createProperty("property_" + generateRandomString(), generateRandomString(), generateRandomString())); } createProperties(endpoint, properties); response = delete(endpoint); checkStatusForSuccess(response); } + + protected Map createProperty(final String name, final String value) { + return createProperty(name, value, ""); + } + protected Map createProperty(final String name, final String value, final String description) { + return new HashMap() {{ + put ("name", name); + put ("value", value); + put ("description", description); + }}; + } protected void createProperties(String endpoint, List> properties) { final String payload = buildPayload(properties); Index: admin/rest/src/main/java/org/glassfish/admin/rest/Util.java =================================================================== --- admin/rest/src/main/java/org/glassfish/admin/rest/Util.java (revision 44630) +++ admin/rest/src/main/java/org/glassfish/admin/rest/Util.java (working copy) @@ -45,10 +45,13 @@ import javax.ws.rs.core.UriInfo; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.ws.rs.core.PathSegment; +import org.glassfish.admin.rest.utils.xml.RestActionReporter; +import org.glassfish.api.ActionReport.MessagePart; import org.glassfish.api.admin.ParameterMap; import org.jvnet.hk2.component.Habitat; @@ -280,16 +283,44 @@ candidatePathSegment = pathSegments; } - StringBuilder setBasePath = new StringBuilder(); + final StringBuilder sb = new StringBuilder(); for(PathSegment pathSegment : candidatePathSegment) { - setBasePath.append(pathSegment.getPath()); - setBasePath.append('.'); + sb.append(pathSegment.getPath()); + sb.append('.'); } + String setBasePath = sb.toString(); ParameterMap parameters = new ParameterMap(); + Map currentValues = getCurrentValues(setBasePath.toString(), habitat); + for (Map.Entry entry : data.entrySet()) { - StringBuilder setExpression = new StringBuilder(setBasePath); - parameters.add("DEFAULT", setExpression.append(entry.getKey()).append('=').append(entry.getValue()).toString()); + String currentValue = currentValues.get(setBasePath + entry.getKey()); + if ((currentValue == null) || (!currentValue.equals(entry.getValue()))) { + parameters.add("DEFAULT", setBasePath + entry.getKey() + "=" + entry.getValue()); + } } - return ResourceUtil.runCommand("set", parameters, habitat, ""); //TODO The last parameter is resultType and is not used. Refactor the called method to remove it + if (!parameters.entrySet().isEmpty()) { + return ResourceUtil.runCommand("set", parameters, habitat, ""); //TODO The last parameter is resultType and is not used. Refactor the called method to remove it + } else { + return new RestActionReporter(); // noop + } } + + private static Map getCurrentValues(String basePath, Habitat habitat) { + Map values = new HashMap(); + final String path = (basePath.endsWith(".")) ? basePath.substring(0, basePath.length()-1) : basePath; + RestActionReporter gr = ResourceUtil.runCommand("get", new ParameterMap() {{ + add ("DEFAULT", path); + }}, habitat, ""); + + MessagePart top = gr.getTopMessagePart(); + for (MessagePart child : top.getChildren()) { + String message = child.getMessage(); + if (message.contains("=")) { + String[] parts = message.split("="); + values.put(parts[0], (parts.length > 1) ? parts[1] : ""); + } + } + + return values; + } } \ No newline at end of file Index: admin/rest/src/main/java/org/glassfish/admin/rest/resources/PropertiesBagResource.java =================================================================== --- admin/rest/src/main/java/org/glassfish/admin/rest/resources/PropertiesBagResource.java (revision 44630) +++ admin/rest/src/main/java/org/glassfish/admin/rest/resources/PropertiesBagResource.java (working copy) @@ -66,9 +66,9 @@ import org.glassfish.admin.rest.results.OptionsResult; import org.glassfish.admin.rest.utils.xml.RestActionReporter; import org.glassfish.api.ActionReport; -import org.jvnet.hk2.config.ConfigBean; import org.jvnet.hk2.config.Dom; import org.jvnet.hk2.component.Habitat; +import org.jvnet.hk2.config.types.Property; import org.jvnet.hk2.config.TransactionFailure; /** @@ -156,7 +156,9 @@ @Produces({"text/html;qs=2",MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public Response delete() { try { - deleteExistingProperties(); + Map existing = getExistingProperties(); + deleteMissingProperties(existing, null); + String successMessage = localStrings.getLocalString("rest.resource.delete.message", "\"{0}\" deleted successfully.", new Object[]{uriInfo.getAbsolutePath()}); return ResourceUtil.getResponse(200, successMessage, requestHeaders, uriInfo); @@ -180,7 +182,8 @@ RestActionReporter ar = new RestActionReporter(); ar.setActionDescription("Property"); try { - deleteExistingProperties(); + Map existing = getExistingProperties(); + deleteMissingProperties(existing, properties); Map data = new LinkedHashMap(); for (Map property : properties) { String escapedName = getEscapedPropertyName(property.get("name")); @@ -214,13 +217,32 @@ return new ActionReportResult("properties", ar, new OptionsResult(Util.getResourceName(uriInfo))); } + + protected Map getExistingProperties() { + Map properties = new HashMap(); + for (Dom child : parent.nodeElements(tagName)) { + Property property = child.createProxy(); + properties.put(property.getName(), property); + } + return properties; + } - protected void deleteExistingProperties() throws TransactionFailure { + protected void deleteMissingProperties(Map existing, List> properties) throws TransactionFailure { + Set propNames = new HashSet(); + if (properties != null) { + for (Map property : properties) { + propNames.add(property.get("name")); + } + } + HashMap data = new HashMap(); - for (final Dom existingProp : parent.nodeElements(tagName)) { - data.clear(); - String escapedName = getEscapedPropertyName(((ConfigBean) existingProp).attribute("name")); - data.put (escapedName, ""); + for (final Property existingProp : existing.values()) { + if (!propNames.contains(existingProp.getName())) { + String escapedName = getEscapedPropertyName(existingProp.getName()); + data.put (escapedName, ""); + } + } + if (!data.isEmpty()) { Util.applyChanges(data, uriInfo, habitat); } }