-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Quote MBean names if special characters (,:="\n) are used in property
values - Add test for the quoting - Upgrade .classpath to Eclipse Photon
- Loading branch information
Showing
5 changed files
with
306 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,38 +1,39 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<classpath> | ||
<classpathentry kind="src" output="target/classes" path="src/main/java"> | ||
<attributes> | ||
<attribute name="optional" value="true"/> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"> | ||
<attributes> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry kind="src" output="target/test-classes" path="src/test/java"> | ||
<attributes> | ||
<attribute name="optional" value="true"/> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"> | ||
<attributes> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry kind="src" path="src/example/java"/> | ||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"> | ||
<attributes> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> | ||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"> | ||
<attributes> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry kind="output" path="target/classes"/> | ||
</classpath> | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<classpath> | ||
<classpathentry kind="src" path="src/main/java"> | ||
<attributes> | ||
<attribute name="optional" value="true"/> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry excluding="**" kind="src" path="src/main/resources"> | ||
<attributes> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry kind="src" output="target/test-classes" path="src/test/java"> | ||
<attributes> | ||
<attribute name="test" value="true"/> | ||
<attribute name="optional" value="true"/> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry excluding="**" kind="src" path="src/test/resources"> | ||
<attributes> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry kind="src" path="src/example/java"/> | ||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"> | ||
<attributes> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> | ||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"> | ||
<attributes> | ||
<attribute name="maven.pomderived" value="true"/> | ||
</attributes> | ||
</classpathentry> | ||
<classpathentry kind="output" path="target/classes"/> | ||
</classpath> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
163 changes: 163 additions & 0 deletions
163
src/main/java/com/axonivy/jmx/internal/NameInstruction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
package com.axonivy.jmx.internal; | ||
|
||
import java.util.Collections; | ||
import java.util.Comparator; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
import javax.management.MalformedObjectNameException; | ||
import javax.management.ObjectName; | ||
|
||
import org.apache.commons.lang3.StringUtils; | ||
|
||
class NameInstruction | ||
{ | ||
private ObjectName template; | ||
private boolean isRelative; | ||
private List<Property<Instruction>> valueInstructions; | ||
|
||
private NameInstruction(MBeanManager manager, Class<?> mBeanClass, String instruction) throws MalformedObjectNameException | ||
{ | ||
isRelative = isRelative(instruction); | ||
if (isRelative) | ||
{ | ||
instruction = ":"+instruction; | ||
} | ||
template = new ObjectName(instruction); | ||
List<Property<String>> properties = getProperties(); | ||
valueInstructions = properties | ||
.stream() | ||
.map(property -> new Property<>(property.getKey(), Instruction.parseInstruction(manager, mBeanClass, property.getValue()))) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
static NameInstruction parseInstruction(MBeanManager manager, Class<?> mBeanClass, String value) | ||
{ | ||
try | ||
{ | ||
return new NameInstruction(manager, mBeanClass, value); | ||
} | ||
catch(MalformedObjectNameException ex) | ||
{ | ||
throw new IllegalArgumentException("Object name of MBean '"+value+"'is malformed", ex); | ||
} | ||
} | ||
|
||
String execute(Object mBean) | ||
{ | ||
List<Property<String>> evaluatedValues = evaluateAndQuoteValues(mBean); | ||
String name = buildName(evaluatedValues); | ||
return name; | ||
} | ||
|
||
private boolean isRelative(String instruction) | ||
{ | ||
int firstColon = StringUtils.indexOf(instruction, ":"); | ||
if (firstColon < 0) | ||
{ | ||
return true; | ||
} | ||
int firstEqual = StringUtils.indexOf(instruction, "="); | ||
return firstEqual<firstColon; // colon appears in quoted value | ||
} | ||
|
||
private List<Property<String>> getProperties() | ||
{ | ||
List<Property<String>> properties = template | ||
.getKeyPropertyList() | ||
.entrySet() | ||
.stream() | ||
.map(entry -> new Property<>(entry.getKey(), entry.getValue())) | ||
.collect(Collectors.toList()); | ||
String strName = template.getKeyPropertyListString(); | ||
Collections.sort(properties, new PropertyComparator(strName)); | ||
return properties; | ||
} | ||
|
||
private List<Property<String>> evaluateAndQuoteValues(Object mBean) | ||
{ | ||
List<Property<String>> evaluatedValues = valueInstructions | ||
.stream() | ||
.map(property -> new Property<>(property.getKey(), property.getValue().execute(mBean))) | ||
.map(this::quoteIfNecessary) | ||
.collect(Collectors.toList()); | ||
return evaluatedValues; | ||
} | ||
|
||
private String buildName(List<Property<String>> evaluatedValues) | ||
{ | ||
StringBuilder name = new StringBuilder(256); | ||
if (!isRelative) | ||
{ | ||
name.append(template.getDomain()); | ||
name.append(':'); | ||
} | ||
String properties = evaluatedValues | ||
.stream() | ||
.map(Property::toString) | ||
.collect(Collectors.joining(",")); | ||
name.append(properties); | ||
return name.toString(); | ||
} | ||
|
||
private Property<String> quoteIfNecessary(Property<String> property) | ||
{ | ||
if (needsQuoting(property.getValue())) | ||
{ | ||
return new Property<String>(property.getKey(), ObjectName.quote(property.getValue())); | ||
} | ||
return property; | ||
} | ||
|
||
private boolean needsQuoting(String value) | ||
{ | ||
boolean isQuoted = StringUtils.startsWith(value, "\"") && StringUtils.endsWith(value, "\""); | ||
return !isQuoted && StringUtils.containsAny(value, ',',':','=','\n', '\"'); | ||
} | ||
|
||
private static final class Property<T> | ||
{ | ||
private String key; | ||
private T value; | ||
|
||
private Property(String key, T value) | ||
{ | ||
this.key = key; | ||
this.value = value; | ||
} | ||
|
||
private T getValue() | ||
{ | ||
return value; | ||
} | ||
|
||
private String getKey() | ||
{ | ||
return key; | ||
} | ||
|
||
@Override | ||
public String toString() | ||
{ | ||
return key+"="+value; | ||
} | ||
} | ||
|
||
private static final class PropertyComparator implements Comparator<Property<String>> | ||
{ | ||
private String strName; | ||
|
||
private PropertyComparator(String strName) | ||
{ | ||
this.strName = strName; | ||
} | ||
|
||
@Override | ||
public int compare(Property<String> property1, Property<String> property2) | ||
{ | ||
int positionProperty1 = StringUtils.indexOf(strName, property1.getKey()+"="); | ||
int positionProperty2 = StringUtils.indexOf(strName, property2.getKey()+"="); | ||
return Integer.compare(positionProperty1, positionProperty2); | ||
} | ||
} | ||
} |
100 changes: 100 additions & 0 deletions
100
src/test/java/com/axonivy/jmx/TestMBeanNameQuoting.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package com.axonivy.jmx; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import javax.management.MalformedObjectNameException; | ||
import javax.management.ObjectName; | ||
|
||
import org.junit.Test; | ||
|
||
public class TestMBeanNameQuoting | ||
{ | ||
@Test | ||
public void quotedBean() throws MalformedObjectNameException | ||
{ | ||
assertThat(isRegistered("Test:type=\",=:\\n \\\" hi\"")).isFalse(); | ||
MBeans.registerMBeanFor(new QuotedMBean()); | ||
assertThat(isRegistered("Test:type=\",=:\\n \\\" hi\"")).isTrue(); | ||
} | ||
|
||
@Test | ||
public void notQuotedBeanFromElExpression() throws MalformedObjectNameException | ||
{ | ||
assertNameNotQuoted("hello world"); | ||
} | ||
|
||
@Test | ||
public void quotedBeanFromElExpressionWithComma() throws MalformedObjectNameException | ||
{ | ||
assertNameQuoted("hello,world"); | ||
} | ||
|
||
@Test | ||
public void quotedBeanFromElExpressionWithEqual() throws MalformedObjectNameException | ||
{ | ||
assertNameQuoted("hello=world"); | ||
} | ||
|
||
@Test | ||
public void quotedBeanFromElExpressionWithCollon() throws MalformedObjectNameException | ||
{ | ||
assertNameQuoted("hello:world"); | ||
} | ||
|
||
@Test | ||
public void quotedBeanFromElExpressionWithNewLine() throws MalformedObjectNameException | ||
{ | ||
assertNameQuoted("hello\nworld"); | ||
} | ||
|
||
@Test | ||
public void quotedBeanFromElExpressionWithQuote() throws MalformedObjectNameException | ||
{ | ||
assertNameQuoted("hello\"world"); | ||
} | ||
|
||
@Test | ||
public void notQuotedBeanFromElExpressionThatIsAlreadyQuoted() throws MalformedObjectNameException | ||
{ | ||
assertNameNotQuoted("\"hello,=:\\n\\\"world\""); | ||
} | ||
|
||
private static void assertNameNotQuoted(String name) throws MalformedObjectNameException | ||
{ | ||
assertName(name, name); | ||
} | ||
|
||
private void assertNameQuoted(String name) throws MalformedObjectNameException | ||
{ | ||
assertName(name, ObjectName.quote(name)); | ||
} | ||
|
||
private static void assertName(String name, String mBeanName) throws MalformedObjectNameException | ||
{ | ||
assertThat(isRegistered("Test:name="+mBeanName)).isFalse(); | ||
MBeans.registerMBeanFor(new NameMBean(name)); | ||
assertThat(isRegistered("Test:name="+mBeanName)).isTrue(); | ||
} | ||
|
||
@MBean(value="Test:type=\",=:\\n \\\" hi\"") | ||
private static final class QuotedMBean | ||
{ | ||
} | ||
|
||
@MBean(value="Test:name=#{name}") | ||
private static final class NameMBean | ||
{ | ||
@SuppressWarnings("unused") | ||
private String name; | ||
|
||
private NameMBean(String name) | ||
{ | ||
this.name = name; | ||
} | ||
} | ||
|
||
private static boolean isRegistered(String name) throws MalformedObjectNameException | ||
{ | ||
return MBeans.getMBeanServer().isRegistered(new ObjectName(name)); | ||
} | ||
} |