Skip to content

Commit

Permalink
* Quote MBean names if special characters (,:="\n) are used in property
Browse files Browse the repository at this point in the history
values

- Add test for the quoting
- Upgrade .classpath to Eclipse Photon
  • Loading branch information
weissreto committed Aug 14, 2018
1 parent ca29d14 commit 712f4a0
Show file tree
Hide file tree
Showing 5 changed files with 306 additions and 42 deletions.
77 changes: 39 additions & 38 deletions .classpath
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>
4 changes: 2 additions & 2 deletions src/main/java/com/axonivy/jmx/internal/Instruction.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
* }
* }
* -----
* Instruction.parseInstruction(manager, MyBean.class "Hello #{name}").execute(new MyBean()) -> "Hello Reto"
* Instruction.parseInstruction(manager, MyBean.class "Hello #{application.description}").execute(new MyBean()) -> "Hello Weiss"
* Instruction.parseInstruction(manager, MyBean.class, "Hello #{name}").execute(new MyBean()) -> "Hello Reto"
* Instruction.parseInstruction(manager, MyBean.class, "Hello #{application.description}").execute(new MyBean()) -> "Hello Weiss"
* </pre>
*
*
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/axonivy/jmx/internal/MBeanType.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class MBeanType
private MBeanManager manager;
private Class<?> mBeanClass;
private Instruction descriptionInstruction;
private Instruction nameInstruction;
private NameInstruction nameInstruction;
private List<MCompositionReferenceInfo> compositionReferenceInfos;

MBeanType(MBeanManager manager, Class<?> mBeanClass)
Expand Down Expand Up @@ -53,7 +53,7 @@ String evaluateName(Object mBean)
{
if (nameInstruction == null)
{
nameInstruction = Instruction.parseInstruction(manager, mBeanClass, annotation.value());
nameInstruction = NameInstruction.parseInstruction(manager, mBeanClass, annotation.value());
}
return nameInstruction.execute(mBean);
}
Expand Down
163 changes: 163 additions & 0 deletions src/main/java/com/axonivy/jmx/internal/NameInstruction.java
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 src/test/java/com/axonivy/jmx/TestMBeanNameQuoting.java
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));
}
}

0 comments on commit 712f4a0

Please sign in to comment.