Skip to content
Commits on Source (4)
......@@ -34,7 +34,7 @@
<parent>
<groupId>org.jboss.byteman</groupId>
<artifactId>byteman-root</artifactId>
<version>4.0.8</version>
<version>4.0.10</version>
</parent>
<properties>
......@@ -1196,6 +1196,22 @@
<argLine>-javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestBindNull.btm</argLine>
</configuration>
</execution>
<execution>
<id>bugfixes.TestTraceOpenAndWrite</id>
<phase>integration-test</phase>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<forkCount>1</forkCount>
<reuseForks>true</reuseForks>
<includes>
<include>org/jboss/byteman/tests/bugfixes/TestTraceOpenAndWrite.class</include>
</includes>
<argLine>-javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestTraceOpenAndWrite.btm</argLine>
</configuration>
</execution>
<execution>
<id>javaops.TestInnerClasses</id>
<phase>integration-test</phase>
......@@ -2230,6 +2246,22 @@
<argLine>-Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestConstructorArgUpcast.btm</argLine>
</configuration>
</execution>
<execution>
<id>bugfixes.TestTraceOpenAndWrite.compiled</id>
<phase>integration-test</phase>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<forkCount>1</forkCount>
<reuseForks>true</reuseForks>
<includes>
<include>org/jboss/byteman/tests/bugfixes/TestTraceOpenAndWrite.class</include>
</includes>
<argLine>-Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-agent-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/bugfixes/TestTraceOpenAndWrite.btm</argLine>
</configuration>
</execution>
<execution>
<id>javaops.TestInnerClasses.compiled</id>
<phase>integration-test</phase>
......@@ -2395,7 +2427,7 @@
<relocations>
<relocation>
<pattern>org.objectweb.asm</pattern>
<shadedPattern>org.pobjectweb.asm</shadedPattern>
<shadedPattern>org.objectweb.asm</shadedPattern>
</relocation>
<relocation>
<pattern>java_cup.runtime</pattern>
......
......@@ -182,7 +182,7 @@ public class Retransformer extends Transformer {
if(newTransformSet == null || newTransformSet.isInstalled()) {
if(oldTransformSet.isInstalled()) {
// we need to run an uninstall for the old transform set
oldTransformSet.getInstalledRule().uninstalled();
oldTransformSet.getRule().uninstalled();
}
} else {
// copy across the rule used for the prior
......@@ -191,9 +191,9 @@ public class Retransformer extends Transformer {
// if any of the newly injected rules pass
// ensureTypeCheckCompiled
if(newTransformSet != null) {
newTransformSet.setInstalled(oldTransformSet.getInstalledRule());
newTransformSet.setInstalled(oldTransformSet.getRule());
} else {
newRuleScript.ensureTransformSet(oldTransformSet.getLoader(), oldTransformSet.getTriggerClass(), oldTransformSet.getInstalledRule());
newRuleScript.ensureTransformSet(oldTransformSet.getLoader(), oldTransformSet.getTriggerClass(), oldTransformSet.getRule());
}
}
}
......@@ -345,6 +345,9 @@ public class Retransformer extends Transformer {
// the rule key
for (RuleScript oldRuleScript : toBeRemoved) {
// mark the script as deleted so it doesn't get run any more
oldRuleScript.setDeleted();
// now deal with uninstall, allowing for possible reinstall
RuleScript newRuleScript = scriptRepository.scriptForRuleName(oldRuleScript.getName());
// new script may not exist!
if (newRuleScript != null) {
......@@ -354,20 +357,19 @@ public class Retransformer extends Transformer {
TransformSet newTransformSet = newRuleScript.lookupTransformSet(oldTransformSet.getLoader(), oldTransformSet.getTriggerClass());
if(newTransformSet == null || newTransformSet.isInstalled()) {
if(oldTransformSet.isInstalled()) {
// we need to run an uninstall for the old transform set
oldTransformSet.getInstalledRule().uninstalled();
// new rule has already been installed so we need
// to run an uninstall for the old transform set
oldTransformSet.getRule().uninstalled();
}
} else {
// copy across the rule used for the prior
// install so we can use it for a later uninstall
// it will be replaced with a new instance
// if any of the newly injected rules pass
// ensureTypeCheckCompiled
if(newTransformSet != null) {
newTransformSet.setInstalled(oldTransformSet.getInstalledRule());
} else {
newRuleScript.ensureTransformSet(oldTransformSet.getLoader(), oldTransformSet.getTriggerClass(), oldTransformSet.getInstalledRule());
}
// new rule is not yet installed so we can elide
// the uninstalled + installed lifecycle events
// we have to copy across the rule used for the prior
// install so we can use it as the default argument
// for a later uninstall. it will be replaced with a
// new instance if any of the newly injected rules
// pass ensureTypeCheckCompiled
newTransformSet.setInstalled(oldTransformSet.getRule());
}
}
}
......@@ -375,12 +377,13 @@ public class Retransformer extends Transformer {
for (TransformSet oldTransformSet : oldRuleScript.getTransformSets()) {
if(oldTransformSet.isInstalled()) {
// we need to run an uninstall for the old transform set
oldTransformSet.getInstalledRule().uninstalled();
oldTransformSet.getRule().uninstalled();
}
}
out.println("uninstall RULE " + oldRuleScript.getName());
}
out.println("uninstall RULE " + oldRuleScript.getName());
}
// now purge the rules for the old script
}
public void appendJarFile(PrintWriter out, JarFile jarfile, boolean isBoot) throws Exception
......
......@@ -27,6 +27,7 @@ import org.jboss.byteman.rule.Rule;
import org.jboss.byteman.rule.type.TypeHelper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.io.StringWriter;
import java.io.PrintWriter;
......@@ -39,6 +40,20 @@ import java.io.PrintWriter;
public class RuleScript
{
/**
* a counter used to ensure rule identifiers are unique
*/
private static int nextId = 0;
/**
* a method to return the next available counter for use in constructing a key for a rule
* @return the next id
*/
private synchronized static int nextId()
{
return nextId++;
}
/**
* the name of the rule from which this script is derived
*/
......@@ -99,6 +114,24 @@ public class RuleScript
* true if this rule should be compiled to bytecode otherwise false
*/
private final boolean compileToBytecode;
/**
* hash map used to lookup a key used at injection time to identify a
* rule cloned from this script for injection into a specific trigger
* method. the map translates a string constructed from the trigger class
* name, method name, method descriptor and class loader hash to a unique
* key based on the rule name. This ensures that concurrent attempts to inject
* the rule into the same trigger method will employ the same key and hence
* perform exactly the same transformation. That way it does not matter which
* of the transformations are accepted or dropped by the JVM when defining a
* newly loaded class. Any transform result for a given key is as valid as
* any other.
*/
private final HashMap<String, String> keySet;
/**
* base string from which to construct rule injection keys
*/
private final String key_base;
/**
* a list of records identifying transforms associated with a specific class.
* each set is identified by the name of a trigger class and the class's
......@@ -146,6 +179,8 @@ public class RuleScript
this.file = file;
this.compileToBytecode = compileToBytecode;
this.transformSets = new ArrayList<TransformSet>();
this.keySet = new HashMap<String, String>();
this.key_base = name + "_" + nextId();
}
public String getName() {
......@@ -196,6 +231,20 @@ public class RuleScript
public boolean isCompileToBytecode() { return compileToBytecode; }
public synchronized String getRuleKey(String triggerClassName, String triggerMethodName, String triggerMethodDescriptor, ClassLoader loader) {
if (triggerMethodName == null) {
// this can happen when we get errors ???
return key_base;
}
String lookup = triggerClassName + "." + triggerMethodName + TypeHelper.internalizeDescriptor(triggerMethodDescriptor) + "_" + loader.hashCode();
String result = keySet.get(lookup);
if (result == null) {
result = key_base + ":" + keySet.size();
keySet.put(lookup, result);
}
return result;
}
/**
* getter for list of transforms applied for this script. must be called synchronized on the script.
* @return the list of transforms
......@@ -260,23 +309,8 @@ public class RuleScript
}
/**
* record the fact that a trigger call has been successfully installed into bytecode associated with a specific
* class and loader and a corresponding rule instance been installed
* @param loader the loader of the class for which injection was attempted
* @param internalClassName the internal Java name of the class
* @param triggerMethodName the name of the method injected into
* @param desc the descriptor of the method injected into
* @param rule the rule which was injected
* @return true if the successful injection was recorded false if not
*/
public synchronized boolean recordMethodTransform(ClassLoader loader, String internalClassName, String triggerMethodName, String desc, Rule rule)
{
return recordTransform(loader, internalClassName, triggerMethodName, desc, rule, null);
}
/**
* record the fact that a trigger call has failed to install into bytecode associated with a specific
* class and loader
* record the fact that a trigger call has succeeded or else failed to install into bytecode
* associated with a specific class and loader
* @param loader the loader of the class for which injection was attempted
* @param internalClassName the internal Java name of the class
* @param triggerMethodName the name of the method injected into
......@@ -345,7 +379,9 @@ public class RuleScript
// find an existing transform set or create a new one
TransformSet transformSet = ensureTransformSet(loader, triggerClass, null);
for (Transform transform : transformSet.getTransforms()) {
if(transform.getRule() == rule) {
// transform may not employ the same rule
// but it may have the same key.
if(transform.getRule().getKey() == rule.getKey()) {
transform.setCompiled(successful, detail);
boolean isInstalled = transformSet.isInstalled();
// record this as the latest rule to be installed
......
......@@ -33,7 +33,13 @@ import java.io.PrintWriter;
public class Transform
{
private ClassLoader loader;
/**
* fully qualified internal name of class
*/
private String internalClassName;
/**
* full method name including descriptor
*/
private String triggerMethodName;
private Rule rule;
private Throwable throwable;
......@@ -42,10 +48,6 @@ public class Transform
private boolean successful;
private String detail;
public Transform(ClassLoader loader, String internalClassName, Rule rule) {
this(loader, internalClassName, null, rule, null);
}
public Transform(ClassLoader loader, String internalClassName, String triggerMethodName, Rule rule, Throwable th) {
this.loader = loader;
this.internalClassName = internalClassName;
......
......@@ -82,7 +82,7 @@ public class TransformContext
// associated with this rule's script
// which specify the same class and loader
ruleScript.purge(loader, triggerClassName);
// ruleScript.purge(loader, triggerClassName);
try {
parseRule();
......@@ -332,7 +332,7 @@ public class TransformContext
if (failed) {
// we had an injection failure so purge all successfully
// injected rules
purgeRules();
// purgeRules();
return false;
} else {
......@@ -343,7 +343,7 @@ public class TransformContext
Rule rule = ruleMap.get(key);
if(!ruleScript.recordTransform(loader, triggerClassName, triggerMethodName, triggerMethodDescriptor, rule, null)) {
// rule script must have been deleted so purge rules and avoid installing the transformed code
purgeRules();
// purgeRules();
return false;
}
......
......@@ -25,30 +25,41 @@
package org.jboss.byteman.agent;
import org.jboss.byteman.rule.Rule;
import org.jboss.byteman.rule.helper.Helper;
import java.util.ArrayList;
import java.util.List;
/**
* A TransformSet groups together a set of Transform records which
* share a common classloader and trigger class name. This grouping
* ensures that all transforms arising from a specific retransform
* operation can be managed as a unit. In particular this is needed
* in order to allow installation and uninstallation of a rule to
* be performed consistently.
* share a common classloader, trigger class name (and RuleScript).
* The set includes details of successful or failed transforms.
*
* This grouping ensures that all transforms arising from a specific
* retransform operation for a new, modified or deleted script can
* be managed as a unit. In particular this is needed in order to
* allow installation and uninstallation of a rule to be performed
* consistently.
*
* Note that although the loader and trigger class name uniquely
* identify a single trigger class a transform set may still
* contain more than one successful transform. That is possible
* because the RuleScript may omit a descriptor and hence may match
* multiple overloaded variants of the method named in the rule's
* METHOD clause.
*/
public class TransformSet
{
private ClassLoader loader;
private String triggerClass;
private List<Transform> transforms;
private Rule installedRule;
private Rule rule;
public TransformSet(ClassLoader loader, String triggerClass)
{
this.loader = loader;
this.triggerClass = triggerClass;
this.transforms = new ArrayList<Transform>();
this.installedRule = null;
this.rule = null;
}
public boolean isFor(ClassLoader loader, String triggerClass)
......@@ -58,7 +69,25 @@ public class TransformSet
public void add(Transform transform)
{
if (transform.getThrowable() == null) {
// transform was successful. see if we have another
// transform for the same method and if so just stick
// with the existing one as both are equivalent
String key = transform.getRule().getKey();
for (Transform current : transforms) {
if (current.getRule() != null && key.equals(current.getRule().getKey())) {
// the other transform should not have resulted in an error
if (transform.getThrowable() != null && Transformer.isVerbose()) {
Helper.verbose("TransformSet.add : mismatch between successful and failed transforms with key " + key);
Helper.verboseTraceException(transform.getThrowable());
}
return;
}
}
}
// add the new transform to the list
transforms.add(transform);
}
public ClassLoader getLoader()
......@@ -75,17 +104,17 @@ public class TransformSet
*/
public boolean isInstalled()
{
return getInstalledRule() != null;
return getRule() != null;
}
public void setInstalled(Rule rule)
public void setInstalled(Rule key)
{
installedRule = rule;
this.rule = key;
}
public Rule getInstalledRule()
public Rule getRule()
{
return installedRule;
return rule;
}
public List<Transform> getTransforms()
......
......@@ -1447,6 +1447,11 @@ public class Transformer implements ClassFileTransformer {
private void setAgentVersion() throws Exception
{
if (inst == null) {
// we are just doing rule checks so no
// need to bother with this
return;
}
String version = System.getProperty(AGENT_VERSION);
if (version != null && !version.equals("")) {
throw new Exception("Transformer.setAgentVersion: Byteman agent version already set!");
......
......@@ -122,10 +122,14 @@ public class InvokeTriggerAdapter extends RuleTriggerAdapter
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
if ((count == 0 || visitedCount < count) && matchCall(owner, name, desc)) {
// a relevant invocation occurs in the called method
visitedCount++;
if (!latched && (count == 0 || visitedCount == count)) {
injectTriggerPoint();
// avoid matching calls to println which can be injected into
// Byteman catch handlers when verbose mode is enabled
if (!inBytemanHandler()) {
// a relevant invocation occurs in the called method
visitedCount++;
if(!latched && (count == 0 || visitedCount == count)) {
injectTriggerPoint();
}
}
}
if (!whenComplete) {
......
......@@ -873,7 +873,7 @@ public class RuleTriggerMethodAdapter extends RuleGeneratorAdapter
// we don't need a return or throw handler if we are in the scope of a monitor enter/exit pair
// all the handler regions end here so we use the same end label for all of them
Label end = new Label();
Label executeHandler = newLabel();
Label executeHandler = new Label();
if (!cfg.inOpenMonitor()) {
// ok this one needs the full set of handlers and the subtype handlers need to come first
......@@ -970,11 +970,6 @@ public class RuleTriggerMethodAdapter extends RuleGeneratorAdapter
// saved exception then protect it with a try catch block and update
// the trigger details so that it is the target of this block
// generate a rethrow handler for each exception type
Label newStart = newLabel();
Label newEnd = newLabel();
// if we get here the return and throw handlers labels should be null
if (details.getEarlyReturnHandler() != null || details.getThrowHandler() != null) {
Helper.err("unexpected : trigger region with open monitorenters has subtype handler!");
......@@ -982,12 +977,12 @@ public class RuleTriggerMethodAdapter extends RuleGeneratorAdapter
// generate rethrow code and mark the handler as a try catch block for all
// three exception types
newStart = newLabel();
newEnd = newLabel();
Label newStart = newLabel();
Label newEnd = newLabel();
Label newEarlyReturn = newLabel();
Label newThrow = newLabel();
visitLabel(details.getExecuteHandler());
Label newExecute = newLabel();
visitLabel(details.getExecuteHandler());
visitLabel(newStart);
while (openEnters.hasNext()) {
CodeLocation enterLocation = openEnters.next();
......
......@@ -79,9 +79,9 @@ public class Rule
*/
private String name;
/**
* the class loader for the target class
* the class loader for the trigger class
*/
private ClassLoader targetLoader;
private ClassLoader triggerLoader;
/**
* the class loader for the help adapter
*/
......@@ -201,7 +201,7 @@ public class Rule
this.ruleScript = ruleScript;
this.helperClass = null;
this.targetLoader = loader;
this.triggerLoader = loader;
bindings = new Bindings();
checked = false;
......@@ -255,7 +255,7 @@ public class Rule
// We always need to load the helper via the module system if we're compiling to byte code
// so that the compiler can use it.
String helperToUse = (helperName != null) ? helperName : Helper.class.getName();
this.helperLoader = getModuleSystem().createLoader(targetLoader, imports);
this.helperLoader = getModuleSystem().createLoader(triggerLoader, imports);
this.helperToUse = helperToUse;
// initialise helper class lazily so it doesn't happen under
// a Transform, invalidating ruel injection
......@@ -268,7 +268,7 @@ public class Rule
this.helperLoader = null;
this.helperToUse = Helper.class.getName();
helperClass = Helper.class;
typeGroup = new TypeGroup(targetLoader);
typeGroup = new TypeGroup(triggerLoader);
}
ParseNode eventTree = (ParseNode)ruleTree.getChild(0);
......@@ -371,7 +371,7 @@ public class Rule
*/
public ClassLoader getLoader()
{
return targetLoader;
return triggerLoader;
}
/**
......@@ -548,7 +548,7 @@ public class Rule
}
// this uses the original class loader for matching
boolean runInstall = ruleScript.recordCompile(this, triggerClass, targetLoader, !checkFailed, detail);
boolean runInstall = ruleScript.recordCompile(this, triggerClass, triggerLoader, !checkFailed, detail);
if (runInstall) {
installed();
}
......@@ -856,22 +856,11 @@ public class Rule
return key;
}
String key = getName() + "_" + nextId();
this.key = key;
key = ruleScript.getRuleKey(triggerClass, triggerMethod, triggerDescriptor, triggerLoader);
ruleKeyMap.put(key, this);
return key;
}
/**
* return the key under which this rule has been indexed in the rule key map
* @return the key
*/
public String lookupKey()
{
return key;
}
/**
* delete any reference to the rule from the rule map
*/
......
......@@ -414,20 +414,21 @@ public class Helper
{
PrintStream ps = traceMap.get(identifier);
if (ps == null) {
synchronized(traceMap) {
if (doTraceOpen(identifier, null)) {
synchronized (traceMap) {
if(doTraceOpen(identifier, null)) {
ps = traceMap.get(identifier);
} else {
if (identifier.equals("err")) {
if(identifier.equals("err")) {
ps = System.err;
} else {
ps = System.out;
}
}
}
ps.print(message);
ps.flush();
}
ps.print(message);
ps.flush();
return true;
}
......@@ -445,20 +446,21 @@ public class Helper
{
PrintStream ps = traceMap.get(identifier);
if (ps == null) {
synchronized(traceMap) {
if (doTraceOpen(identifier, null)) {
synchronized (traceMap) {
if(doTraceOpen(identifier, null)) {
ps = traceMap.get(identifier);
} else {
if (identifier.equals("err")) {
if(identifier.equals("err")) {
ps = System.err;
} else {
ps = System.out;
}
}
}
ps.println(message);
ps.flush();
}
ps.println(message);
ps.flush();
return true;
}
......
/*
* JBoss, Home of Professional Open Source
* Copyright 2019, Red Hat and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
* @authors Andrew Dinn
*/
package org.jboss.byteman.tests.bugfixes;
import org.jboss.byteman.tests.Test;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
public class TestTraceOpenAndWrite extends Test
{
public TestTraceOpenAndWrite()
{
super(TestTraceOpenAndWrite.class.getCanonicalName());
}
public void test()
{
// make sure we have no existing output file
File file = new File("target/out.txt");
if (file.exists()) {
file.delete();
}
file.deleteOnExit();
triggerMethod();
checkOutput(false);
assertTrue(file.exists());
try {
FileInputStream fis = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
assertTrue(br.ready());
String text = br.readLine();
assertTrue("this is a trace message".equals(text));
assertFalse(br.ready());
} catch (FileNotFoundException e) {
e.printStackTrace();
fail("unexpected exception " + e);
} catch (IOException e) {
e.printStackTrace();
fail("unexpected exception " + e);
}
}
public void triggerMethod() {
}
@Override
public String getExpected() {
logExpected("opened trace file");
logExpected("written trace message");
return super.getExpected();
}
}
##############################################################################
# JBoss, Home of Professional Open Source
# Copyright 2019, Red Hat and individual contributors
# by the @authors tag. See the copyright.txt in the distribution for a
# full listing of individual contributors.
#
# This is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 2.1 of
# the License, or (at your option) any later version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this software; if not, write to the Free
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
#
# @authors Andrew Dinn
#
# Test for BYTEMAN-387. Ensure that trace operations to
# an explicitly opened file work correctly
RULE test trace open and write
CLASS org.jboss.byteman.tests.bugfixes.TestTraceOpenAndWrite
METHOD triggerMethod
HELPER org.jboss.byteman.tests.helpers.Default
AT ENTRY
IF TRUE
DO traceOpen("log", "target/out.txt");
$this.log("opened trace file");
trace("log", "this is ");
trace("log", "a trace message");
$this.log("written trace message");
ENDRULE
......@@ -7,7 +7,7 @@
<parent>
<groupId>org.jboss.byteman</groupId>
<artifactId>byteman-root</artifactId>
<version>4.0.8</version>
<version>4.0.10</version>
</parent>
<description>
......
......@@ -32,7 +32,7 @@
<parent>
<groupId>org.jboss.byteman</groupId>
<artifactId>byteman-root</artifactId>
<version>4.0.8</version>
<version>4.0.10</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<build>
......
......@@ -32,7 +32,7 @@
<parent>
<groupId>org.jboss.byteman</groupId>
<artifactId>byteman-root</artifactId>
<version>4.0.8</version>
<version>4.0.10</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<build>
......
......@@ -33,7 +33,7 @@
<parent>
<groupId>org.jboss.byteman</groupId>
<artifactId>byteman-root</artifactId>
<version>4.0.8</version>
<version>4.0.10</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<dependencies>
......
......@@ -28,7 +28,7 @@
<parent>
<groupId>org.jboss.byteman</groupId>
<artifactId>byteman-jboss-modules</artifactId>
<version>4.0.8</version>
<version>4.0.10</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
......@@ -28,7 +28,7 @@
<parent>
<groupId>org.jboss.byteman</groupId>
<artifactId>byteman-root</artifactId>
<version>4.0.8</version>
<version>4.0.10</version>
<relativePath>../../pom.xml</relativePath>
</parent>
......
......@@ -19,7 +19,7 @@
<parent>
<groupId>org.jboss.byteman</groupId>
<artifactId>byteman-jboss-modules</artifactId>
<version>4.0.8</version>
<version>4.0.10</version>
<relativePath>../pom.xml</relativePath>
</parent>
......