Skip to content

Commit

Permalink
Add XPath probe
Browse files Browse the repository at this point in the history
  • Loading branch information
bertfrees committed Feb 26, 2015
1 parent 1a70f07 commit d27742d
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 68 deletions.
17 changes: 17 additions & 0 deletions yourkit-probes/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.daisy.pipeline</groupId>
<artifactId>saxon-adapter</artifactId>
<version>1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.daisy.pipeline</groupId>
<artifactId>pax-exam-helper</artifactId>
Expand Down Expand Up @@ -60,6 +66,17 @@
<classifier>linux</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.daisy.pipeline</groupId>
<artifactId>saxon-adapter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.daisy.pipeline</groupId>
<artifactId>pax-exam-helper</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package org.daisy.pipeline.yourkit.probes;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import com.yourkit.probes.ObjectRowIndexMap;
import com.yourkit.probes.OnEnterResult;
import com.yourkit.probes.ReturnValue;
import com.yourkit.probes.StringColumn;
import com.yourkit.probes.Table;

import com.yourkit.probes.MethodPattern;
import com.yourkit.probes.Param;
import com.yourkit.probes.This;

public final class XPath {

@MethodPattern("net.sf.saxon.lib.ExtensionFunctionDefinition:<init>()")
public static final class ExtensionFunctionProbe {
public static void onReturn(@This Object definition) {
String qName = invoke(definition, "net.sf.saxon.lib.ExtensionFunctionDefinition", "getFunctionQName").toString();
EXTENSION_FUNCTIONS.createRow(definition, qName);
}
}

@MethodPattern("*:makeCallExpression()")
public static final class ExtensionFunctionCallExpressionProbe {
public static void onReturn(@This Object definition,
@ReturnValue Object call) {
if (isInstanceOf(definition, "net.sf.saxon.lib.ExtensionFunctionDefinition"))
EXTENSION_FUNCTIONS.assocCall(definition, call);
}
}

@MethodPattern("*:call(*)")
public static final class ExtensionFunctionCallProbe {
public static int onEnter(@This Object call,
@Param(1) Object context,
@Param(2) Object arguments) {
if (isInstanceOf(call, "net.sf.saxon.lib.ExtensionFunctionCall"))
return EXTENSION_FUNCTION_CALLS.createRow(call, Arrays.toString((Object[])arguments));
else
return Table.NO_ROW;
}
public static void onReturn(@OnEnterResult int row) {
if (row != Table.NO_ROW)
EXTENSION_FUNCTION_CALLS.closeRow(row);
}
}

private static final ExtensionFunctionTable EXTENSION_FUNCTIONS = new ExtensionFunctionTable();
private static class ExtensionFunctionTable extends Table {
public ExtensionFunctionTable() {
super("XPath Extension Functions", Table.MASK_FOR_GENERIC_LASTING_EVENTS);
}
public final StringColumn QNAME = new StringColumn("QName");
private final ObjectRowIndexMap rows = new ObjectRowIndexMap();
public void createRow(Object definition, String qName) {
int row = createRow();
rows.put(definition, row);
QNAME.setValue(row, qName);
}
public int getRow(Object definition) {
return rows.get(definition);
}
private final Map callToDefinition = new HashMap();
public void assocCall(Object definition, Object call) {
callToDefinition.put(call, definition);
}
public int getRowForCall(Object call) {
return getRow(callToDefinition.get(call));
}
}

private static final ExtensionFunctionCallTable EXTENSION_FUNCTION_CALLS = new ExtensionFunctionCallTable();
private static class ExtensionFunctionCallTable extends Table {
public ExtensionFunctionCallTable() {
super(EXTENSION_FUNCTIONS, "Calls", Table.MASK_FOR_SINGLE_METHOD_CALL_LASTING_EVENTS);
}
private final StringColumn INPUT = new StringColumn("Input");
public int createRow(Object call, String input) {
int row = createRow(EXTENSION_FUNCTIONS.getRowForCall(call));
INPUT.setValue(row, input);
return row;
}
}

private static Object invoke(Object obj, String className, String methodName, Object... params) {
Class c = obj.getClass();
while (!c.getName().equals(className))
c = c.getSuperclass();
Method method;
Class[] paramTypes = new Class[params.length];
int i = 0;
for (Object o : params)
paramTypes[i++] = o.getClass();
try {
method = c.getMethod(methodName, paramTypes);
return method.invoke(obj, params); }
catch (Exception e) {
throw new RuntimeException(e); }
}

private static boolean isInstanceOf(Object obj, String className) {
Class c = obj.getClass();
while (c != null) {
if (c.getName().equals(className))
return true;
c = c.getSuperclass(); }
return false;
}
}
68 changes: 0 additions & 68 deletions yourkit-probes/src/test/java/LiblouisTest.java

This file was deleted.

138 changes: 138 additions & 0 deletions yourkit-probes/src/test/java/ProbesTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import javax.inject.Inject;

import java.io.File;

import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;

import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.ExtensionFunctionCall;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.SequenceType;
import net.sf.saxon.value.StringValue;

import org.daisy.pipeline.braille.liblouis.Liblouis;

import static org.daisy.pipeline.pax.exam.Options.brailleModule;
import static org.daisy.pipeline.pax.exam.Options.felixDeclarativeServices;
import static org.daisy.pipeline.pax.exam.Options.forThisPlatform;
import static org.daisy.pipeline.pax.exam.Options.logbackBundles;
import static org.daisy.pipeline.pax.exam.Options.logbackConfigFile;

import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.ops4j.pax.exam.Configuration;
import org.ops4j.pax.exam.junit.PaxExam;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
import org.ops4j.pax.exam.spi.reactors.PerClass;
import org.ops4j.pax.exam.util.PathUtils;

import static org.ops4j.pax.exam.CoreOptions.bootDelegationPackage;
import static org.ops4j.pax.exam.CoreOptions.junitBundles;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.options;
import static org.ops4j.pax.exam.CoreOptions.vmOption;

import org.osgi.framework.BundleContext;

import org.xml.sax.InputSource;

@RunWith(PaxExam.class)
@ExamReactorStrategy(PerClass.class)
public class ProbesTest {

@Inject
Liblouis liblouis;

@Test
public void testLiblouisTranslation() {
assertEquals("foobar",
liblouis.translate("file:" + PathUtils.getBaseDir() + "/target/test-classes/foobar.cti",
"foobar", false, null));
}

@Inject
BundleContext context;

@Test
public void testCustomXPathFunction() throws Exception {
context.registerService(
ExtensionFunctionDefinition.class.getName(),
new ExtensionFunctionDefinition() {
public StructuredQName getFunctionQName() {
return new StructuredQName("ex", "http://example.net/ns", "foo");
}
public SequenceType[] getArgumentTypes() {
return new SequenceType[] {};
}
public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
return SequenceType.SINGLE_STRING;
}
public ExtensionFunctionCall makeCallExpression() {
return new ExtensionFunctionCall() {
public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
return new StringValue("foobar");
}
};
}
},
new Hashtable<String,Object>());

// Don't use @Inject here because the ExtensionFunctionDefinition binding on
// org.daisy.common.xpath.saxon.XPathFactoryImpl is static, which means that the injected xpathFactory
// would not have the custom function registered.
XPathFactory xpathFactory = (XPathFactory)context.getService(context.getServiceReference(XPathFactory.class.getName()));
XPath xpath = xpathFactory.newXPath();
xpath.setNamespaceContext(
new NamespaceContext() {
public String getNamespaceURI(String prefix) {
return "ex".equals(prefix) ? "http://example.net/ns" : null; }
public String getPrefix(String namespaceURI) {
return null; }
public Iterator<String> getPrefixes(String namespaceURI) {
return Collections.emptyIterator(); }
}
);
assertEquals("foobar", xpath.compile("ex:foo()").evaluate(null, XPathConstants.STRING));
}

@Configuration
public Option[] config() {
File probeClasspath = new File(PathUtils.getBaseDir() + "/target/classes");
return options(
vmOption("-agentpath:${yourkit.home}/bin/mac/libyjpagent.jnilib="
+ "dir=" + PathUtils.getBaseDir() + "/target/yourkit"
+ ",onexit=memory"
+ ",probeclasspath=" + probeClasspath
+ ",probe=org.daisy.pipeline.yourkit.probes.Liblouis"
+ ",probe=org.daisy.pipeline.yourkit.probes.XPath"
),
vmOption("-Xbootclasspath/a:" + probeClasspath),
bootDelegationPackage("org.daisy.pipeline.yourkit.probes"),
logbackConfigFile(),
logbackBundles(),
felixDeclarativeServices(),
mavenBundle().groupId("com.google.guava").artifactId("guava").versionAsInProject(),
mavenBundle().groupId("net.java.dev.jna").artifactId("jna").versionAsInProject(),
mavenBundle().groupId("org.liblouis").artifactId("liblouis-java").versionAsInProject(),
brailleModule("common-java"),
brailleModule("liblouis-core"),
forThisPlatform(brailleModule("liblouis-native")),
mavenBundle().groupId("org.daisy.libs").artifactId("saxon-he").versionAsInProject(),
mavenBundle().groupId("org.daisy.pipeline").artifactId("saxon-adapter").versionAsInProject(),
junitBundles()
);
}
}

0 comments on commit d27742d

Please sign in to comment.