add support to read multiple line value when loading agent jar
git-svn-id: https://svn.apache.org/repos/asf/harmony/enhanced/classlib/trunk@889056 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/modules/instrument/src/main/native/instrument/shared/inst_agt.c b/modules/instrument/src/main/native/instrument/shared/inst_agt.c
index 4155a0c..11f1b9e 100644
--- a/modules/instrument/src/main/native/instrument/shared/inst_agt.c
+++ b/modules/instrument/src/main/native/instrument/shared/inst_agt.c
@@ -206,7 +206,7 @@
/* read bytes */
size = zipEntry.uncompressedSize;
- result = (char *)hymem_allocate_memory(size*sizeof(char));
+ result = (char *)hymem_allocate_memory(size*sizeof(char) + 1);
#ifndef HY_ZIP_API
retval = zip_getZipEntryData(privatePortLibrary, &zipFile, &zipEntry, (unsigned char*)result, size);
#else /* HY_ZIP_API */
@@ -223,6 +223,7 @@
return NULL;
}
+ result[size] = '\0';
/* free resource */
#ifndef HY_ZIP_API
zip_freeZipEntry(privatePortLibrary, &zipEntry);
@@ -243,6 +244,7 @@
char *pos;
char *end;
char *value;
+ char *tmp;
int length;
PORT_ACCESS_FROM_JAVAVM(vm);
@@ -253,18 +255,46 @@
pos = manifest+ (strstr(lwrmanifest,target) - lwrmanifest);
pos += strlen(target)+2;//": "
end = strchr(pos, '\n');
+
+ while (end != NULL && *(end + 1) == ' ') {
+ end = strchr(end + 1, '\n');
+ }
+
if(NULL == end){
end = manifest + strlen(manifest);
}
- /* in windows, has '\r\n' in the end of line, omit '\r' */
- if (*(end - 1) == '\r'){
- end--;
- }
+
length = end - pos;
value = (char *)hymem_allocate_memory(sizeof(char)*(length+1));
- strncpy(value, pos, length);
- *(value+length) = '\0';
+ tmp = value;
+
+ end = strchr(pos, '\n');
+ while (end != NULL && *(end + 1) == ' ') {
+ /* in windows, has '\r\n' in the end of line, omit '\r' */
+ if (*(end - 1) == '\r') {
+ strncpy(tmp, pos, end - 1 - pos);
+ tmp += end - 1 - pos;
+ pos = end + 2;
+ } else {
+ strncpy(tmp, pos, end - pos);
+ tmp += end - pos;
+ pos = end + 2;
+ }
+ end = strchr(end + 1, '\n');
+ }
+
+ if (NULL == end) {
+ strcpy(tmp, pos);
+ } else {
+ /* in windows, has '\r\n' in the end of line, omit '\r' */
+ if (*(end - 1) == '\r') {
+ end--;
+ }
+ strncpy(tmp, pos, end - pos);
+ *(tmp + (end - pos)) = '\0';
+ }
+
return value;
}
@@ -316,7 +346,11 @@
check_jvmti_error(env, (*jvmti)->GetSystemProperty(jvmti,"java.class.path",&classpath),"Failed to get classpath.");
classpath_cpy = (char *)hymem_allocate_memory((sizeof(char)*(strlen(classpath)+strlen(jar_name)+2)));
strcpy(classpath_cpy,classpath);
+#if defined(WIN32) || defined(WIN64)
strcat(classpath_cpy,";");
+#else
+ strcat(classpath_cpy,":");
+#endif
strcat(classpath_cpy,jar_name);
check_jvmti_error(env, (*jvmti)->SetSystemProperty(jvmti, "java.class.path",classpath_cpy),"Failed to set classpath.");
hymem_free_memory(classpath_cpy);
diff --git a/modules/instrument/src/test/java/org/apache/harmony/tests/java/lang/instrument/HelloWorldTest.java b/modules/instrument/src/test/java/org/apache/harmony/tests/java/lang/instrument/HelloWorldTest.java
new file mode 100644
index 0000000..13dde0b
--- /dev/null
+++ b/modules/instrument/src/test/java/org/apache/harmony/tests/java/lang/instrument/HelloWorldTest.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.tests.java.lang.instrument;
+
+import static org.apache.harmony.tests.java.lang.instrument.InstrumentTestHelper.LINE_SEPARATOR;
+import static org.apache.harmony.tests.java.lang.instrument.InstrumentTestHelper.PREMAIN_CLASS;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.harmony.tests.java.lang.instrument.agents.HelloWorldAgent;
+
+public class HelloWorldTest extends TestCase {
+ private InstrumentTestHelper helper = null;
+
+ private static String JAR_PREFIX = "HelloWorldTest";
+
+ @Override
+ public void setUp() {
+ helper = new InstrumentTestHelper();
+ }
+
+ @Override
+ public void tearDown() {
+ helper.clean();
+ }
+
+ public void testHelloWorld() throws Exception {
+ helper.addManifestAttributes(PREMAIN_CLASS, HelloWorldAgent.class
+ .getName());
+ helper.setJarName(JAR_PREFIX + ".testHelloWorld.jar");
+ helper.setMainClass(TestMain.class);
+ List<Class> classes = new ArrayList<Class>();
+ classes.add(HelloWorldAgent.class);
+ helper.addClasses(classes);
+
+ helper.run();
+
+ assertEquals(0, helper.getExitCode());
+ assertEquals("Hello World" + LINE_SEPARATOR, helper.getStdOut());
+ assertEquals("", helper.getStdErr());
+ }
+
+ public void testMultiLineValue() throws Exception {
+ StringBuilder name = new StringBuilder(HelloWorldAgent.class.getName());
+ // line separator + one whitespace is allowed
+ name.insert(5, "\n ");
+ helper.addManifestAttributes(PREMAIN_CLASS, name.toString());
+
+ helper.setJarName(JAR_PREFIX + ".testMultiLineValue.jar");
+ helper.setMainClass(TestMain.class);
+ List<Class> classes = new ArrayList<Class>();
+ classes.add(HelloWorldAgent.class);
+ helper.addClasses(classes);
+
+ helper.run();
+
+ assertEquals(0, helper.getExitCode());
+ assertEquals("Hello World" + LINE_SEPARATOR, helper.getStdOut());
+ assertEquals("", helper.getStdErr());
+ }
+
+ public void testInvalidMultiLineValue() throws Exception {
+ StringBuilder name = new StringBuilder(HelloWorldAgent.class.getName());
+ // line separator + two whitespaces is not allowed
+ name.insert(5, "\n ");
+ helper.addManifestAttributes("Premain-Class", name.toString());
+
+ helper.setJarName(JAR_PREFIX + ".testInvalidMultiLineValue.jar");
+ helper.setMainClass(TestMain.class);
+ List<Class> classes = new ArrayList<Class>();
+ classes.add(HelloWorldAgent.class);
+ helper.addClasses(classes);
+
+ helper.run();
+
+ assertTrue(0 != helper.getExitCode());
+ assertTrue(helper.getStdErr().contains(
+ ClassNotFoundException.class.getName()));
+ }
+}
diff --git a/modules/instrument/src/test/java/org/apache/harmony/tests/java/lang/instrument/InstrumentTestHelper.java b/modules/instrument/src/test/java/org/apache/harmony/tests/java/lang/instrument/InstrumentTestHelper.java
new file mode 100644
index 0000000..8e50c4f
--- /dev/null
+++ b/modules/instrument/src/test/java/org/apache/harmony/tests/java/lang/instrument/InstrumentTestHelper.java
@@ -0,0 +1,373 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.tests.java.lang.instrument;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+public class InstrumentTestHelper {
+ private Manifest manifest;
+
+ private List<Class> classes = new ArrayList<Class>();
+
+ private String jarName;
+
+ private Class mainClass;
+
+ private String commandAgent;
+
+ private String commandAgentOptions;
+
+ private List<String> classpath = new ArrayList<String>();
+
+ private StringBuilder stdOut = new StringBuilder();
+
+ private StringBuilder stdErr = new StringBuilder();
+
+ private int exitCode;
+
+ private String mainJarName;
+
+ private List<File> toBeCleaned = new ArrayList<File>();
+
+ public static final String PREMAIN_CLASS = "Premain-Class";
+
+ public static final String AGENT_CLASS = "Agent-Class";
+
+ public static final String BOOT_CLASS_PATH = "Boot-Class-Path";
+
+ public static final String CAN_REDEFINE_CLASSES = "Can-Redefine-Classes";
+
+ public static final String CAN_RETRANSFORM_CLASSES = "Can-Retransform-Classes";
+
+ public static final String CAN_SET_NATIVE_METHOD_PREFIX = "Can-Set-Native-Method-Prefix";
+
+ public static final String LINE_SEPARATOR = System
+ .getProperty("line.separator");
+
+ /*
+ * This variable is just for debugging. set to false, generated jar won't be
+ * deleted after testing, so you can verify if the jar was generated as you
+ * expected.
+ */
+ private boolean isDeleteJarOnExit = true;
+
+ public void clean() {
+ for (File file : toBeCleaned) {
+ file.delete();
+ }
+ }
+
+ public InstrumentTestHelper() {
+ manifest = new Manifest();
+ manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
+ }
+
+ public void addClasses(List<Class> added) {
+ classes.addAll(added);
+ }
+
+ public void addManifestAttributes(String key, String value) {
+ manifest.getMainAttributes().putValue(key, value);
+ }
+
+ public String getCommandAgent() {
+ return commandAgent;
+ }
+
+ public void setCommandAgent(String commandAgent) {
+ this.commandAgent = commandAgent;
+ }
+
+ public String getCommandAgentOptions() {
+ return commandAgentOptions;
+ }
+
+ public void setCommandAgentOptions(String commandAgentOptions) {
+ this.commandAgentOptions = commandAgentOptions;
+ }
+
+ public String getJarName() {
+ return jarName;
+ }
+
+ public void setJarName(String jarName) {
+ if (jarName.endsWith(".jar")) {
+ this.jarName = jarName;
+ mainJarName = jarName.substring(0, jarName.length() - 4)
+ + ".main.jar";
+ } else {
+ this.jarName = jarName + ".jar";
+ mainJarName = jarName + ".main.jar";
+ }
+ }
+
+ public Class getMainClass() {
+ return mainClass;
+ }
+
+ public void setMainClass(Class mainClass) {
+ this.mainClass = mainClass;
+ }
+
+ public void run() throws Exception {
+ generateJars();
+ runAgentTest();
+ }
+
+ private void runAgentTest() throws IOException, InterruptedException {
+ String[] args = new String[2];
+ args[0] = "-javaagent:" + commandAgent;
+ if (commandAgentOptions != null
+ && commandAgentOptions.trim().length() != 0) {
+ args[0] += "=" + commandAgentOptions;
+ }
+
+ args[1] = mainClass.getName();
+
+ Process process = execJava(args, getClasspath());
+ process.waitFor();
+
+ exitCode = process.exitValue();
+ }
+
+ private Process execJava(String[] args, String[] classpath)
+ throws IOException {
+ // this function returns the resulting process from the exec
+ StringBuilder command;
+ String testVMArgs;
+ StringTokenizer st;
+
+ List<String> execArgs = new ArrayList<String>(3 + args.length);
+
+ // construct the name of executable file
+ String executable = System.getProperty("java.home");
+ if (!executable.endsWith(File.separator)) {
+ executable += File.separator;
+ }
+ executable += "bin" + File.separator + "java";
+ execArgs.add(executable);
+
+ // add classpath string
+ StringBuilder classPathString = new StringBuilder();
+ if (classpath != null && classpath.length > 0) {
+ boolean isFirst = true;
+ for (String element : classpath) {
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ classPathString.append(File.pathSeparator);
+ }
+ classPathString.append(element);
+ }
+ }
+
+ if (classPathString.toString().length() != 0) {
+ execArgs.add("-cp");
+ execArgs.add(classPathString.toString());
+ }
+
+ // parse hy.test.vmargs if was given
+ testVMArgs = System.getProperty("hy.test.vmargs");
+ if (testVMArgs != null) {
+ st = new StringTokenizer(testVMArgs, " ");
+ while (st.hasMoreTokens()) {
+ execArgs.add(st.nextToken());
+ }
+ }
+
+ // add custom args given as parameter
+ for (String arg : args) {
+ execArgs.add(arg);
+ }
+
+ // construct command line string and print it to stdout
+ command = new StringBuilder(execArgs.get(0));
+ for (int i = 1; i < execArgs.size(); i++) {
+ command.append(" ");
+ command.append(execArgs.get(i));
+ }
+ System.out.println("Exec: " + command.toString());
+ System.out.println();
+
+ // execute java process
+ final Process proc = Runtime.getRuntime().exec(
+ execArgs.toArray(new String[execArgs.size()]));
+
+ final String lineSeparator = System.getProperty("line.separator");
+ Thread errReader = new Thread(new Runnable() {
+ public void run() {
+ BufferedReader reader = new BufferedReader(
+ new InputStreamReader(proc.getErrorStream()));
+ String line = null;
+ try {
+ while ((line = reader.readLine()) != null) {
+ stdErr.append(line).append(lineSeparator);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ errReader.start();
+
+ Thread outReader = new Thread(new Runnable() {
+ public void run() {
+ BufferedReader reader = new BufferedReader(
+ new InputStreamReader(proc.getInputStream()));
+ String line = null;
+ try {
+ while ((line = reader.readLine()) != null) {
+ stdOut.append(line).append(lineSeparator);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ outReader.start();
+
+ return proc;
+ }
+
+ private void generateJars() throws FileNotFoundException, IOException,
+ URISyntaxException {
+
+ File agentJar = generateJar(jarName, manifest, classes,
+ isDeleteJarOnExit);
+ toBeCleaned.add(agentJar);
+ commandAgent = agentJar.getAbsolutePath();
+
+ List<Class> list = new ArrayList<Class>();
+ list.add(mainClass);
+
+ File mainJarFile = generateJar(mainJarName, null, list,
+ isDeleteJarOnExit);
+ toBeCleaned.add(mainJarFile);
+
+ classpath.add(mainJarFile.getAbsolutePath());
+ }
+
+ private static File calculateJarDir() throws MalformedURLException,
+ URISyntaxException {
+ URL location = ClassLoader.getSystemResource(InstrumentTestHelper.class
+ .getName().replace(".", "/")
+ + ".class");
+ String locationStr = location.toString();
+
+ // calculate jarDir
+ File jarDir = null;
+ if ("jar".equals(location.getProtocol())) {
+ URL jar = null;
+ int index = locationStr.indexOf(".jar!") + 4;
+ if (locationStr.startsWith("jar:")) {
+ jar = new URL(locationStr.substring(4, index));
+ } else {
+ jar = new URL(locationStr.substring(0, index));
+ }
+ jarDir = new File(jar.toURI()).getParentFile();
+ } else {
+ int index = locationStr.lastIndexOf(InstrumentTestHelper.class
+ .getName().replace(".", "/"));
+ URL jar = new URL(locationStr.substring(0, index));
+ jarDir = new File(jar.toURI());
+ }
+ return jarDir;
+ }
+
+ public String getStdOut() {
+ return stdOut.toString();
+ }
+
+ public String getStdErr() {
+ return stdErr.toString();
+ }
+
+ public String[] getClasspath() {
+ return classpath.toArray(new String[0]);
+ }
+
+ public void setClasspath(String[] pathes) {
+ classpath.addAll(Arrays.asList(pathes));
+ }
+
+ public int getExitCode() {
+ return exitCode;
+ }
+
+ public boolean isDeleteJarOnExit() {
+ return isDeleteJarOnExit;
+ }
+
+ public void setDeleteJarOnExit(boolean isDeleteJarOnExit) {
+ this.isDeleteJarOnExit = isDeleteJarOnExit;
+ }
+
+ public static File generateJar(String jarName, Manifest manifest,
+ List<Class> classes, boolean isDeleteOnExit)
+ throws FileNotFoundException, IOException, URISyntaxException {
+ File jarDir = calculateJarDir();
+
+ File jarFile = new File(jarDir, jarName);
+
+ JarOutputStream out = null;
+ if (manifest == null) {
+ out = new JarOutputStream(new FileOutputStream(jarFile));
+ } else {
+ out = new JarOutputStream(new FileOutputStream(jarFile), manifest);
+ }
+
+ for (Iterator<Class> iter = classes.iterator(); iter.hasNext();) {
+ Class element = iter.next();
+ String name = element.getName().replace(".", "/") + ".class";
+ InputStream in = ClassLoader.getSystemResourceAsStream(name);
+ byte[] bs = new byte[1024];
+ int count = 0;
+ ZipEntry entry = new ZipEntry(name);
+ out.putNextEntry(entry);
+ while ((count = in.read(bs)) != -1) {
+ out.write(bs, 0, count);
+ }
+ in.close();
+ }
+ out.close();
+
+ if (isDeleteOnExit) {
+ jarFile.deleteOnExit();
+ }
+
+ return jarFile;
+ }
+}
diff --git a/modules/instrument/src/test/java/org/apache/harmony/tests/java/lang/instrument/agents/HelloWorldAgent.java b/modules/instrument/src/test/java/org/apache/harmony/tests/java/lang/instrument/agents/HelloWorldAgent.java
new file mode 100644
index 0000000..24ca671
--- /dev/null
+++ b/modules/instrument/src/test/java/org/apache/harmony/tests/java/lang/instrument/agents/HelloWorldAgent.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.tests.java.lang.instrument.agents;
+
+import java.lang.instrument.Instrumentation;
+
+public class HelloWorldAgent {
+ public static void premain(String agentArgs, Instrumentation inst) {
+ System.out.println("Hello World");
+ }
+}