Skip to content

Commit

Permalink
New function console:dump: prints all variables in the local variable…
Browse files Browse the repository at this point in the history
… stack, which are visible at the point the statement appears in the code.
  • Loading branch information
wolfgangmm committed Oct 26, 2014
1 parent 071d823 commit 668386b
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 28 deletions.
8 changes: 4 additions & 4 deletions console.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<div xmlns="http://www.w3.org/1999/xhtml" data-template="templates:surround" data-template-with="templates/page.html" data-template-at="content">
<!-- Content Header (Page header) -->
<section class="content-header">
Expand Down Expand Up @@ -43,7 +42,7 @@ <h1>
<th class="hidden-xs">Source</th>
<th class="hidden-xs">Line/Column</th>
<th>Message</th>
<th/>
<th></th>
</thead>
<tbody id="console">
<tr>
Expand All @@ -57,8 +56,9 @@ <h3 class="box-title">Note</h3>
</div><!-- /.box-header -->
<div class="box-body">
<p>To use this feature you need a version of eXist build after April 10th, 2014. Older versions may work in part.</p>
<p>You have to restart eXist once after installing this app to enable the servlet on the web server.
Afterwards use the console:log functions in your XQuery to send messages to the remote console. For example:</p>
<p>If you upgraded this app from an older version, please restart eXist once to refresh the library
on the web server.</p>
<p>Use the console:log functions in your XQuery to send messages to the remote console. For example:</p>
<pre>import module namespace console="http://exist-db.org/xquery/console";
console:log("Hello world!")</pre>
</div>
Expand Down
19 changes: 18 additions & 1 deletion resources/scripts/console.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,24 @@ var RemoteConsole = (function() {

td = document.createElement("td");
td.className = "message";
td.appendChild(document.createTextNode(data.message));
if (data.json) {
var json = JSON.parse(data.message);
var dl = document.createElement("dl");
dl.className = "dl-horizontal";
for (var key in json) {
var name = document.createElement("dt");
// name.className = "var";
name.appendChild(document.createTextNode("$" + key));
dl.appendChild(name);
var value = document.createElement("dd");
value.appendChild(document.createTextNode(json[key]));
dl.appendChild(value);
// table.appendChild(row);
}
td.appendChild(dl);
} else {
td.appendChild(document.createTextNode(data.message));
}
tr.appendChild(td);

td = document.createElement("td");
Expand Down
4 changes: 4 additions & 0 deletions src/org/exist/console/ConsoleAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ public interface ConsoleAdapter {

public void log(String channel, String message);

public void log(String channel, boolean json, String message);

public void log(String channel, String source, int line, int column, String message);

public void log(String channel, String source, int line, int column, boolean json, String message);

public void send(String channel, String message);
}
15 changes: 13 additions & 2 deletions src/org/exist/console/xquery/ConsoleModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public class ConsoleModule extends AbstractInternalModule {
new FunctionDef(Log.signatures[0], Log.class),
new FunctionDef(Log.signatures[1], Log.class),
new FunctionDef(Log.signatures[2], Log.class),
new FunctionDef(Log.signatures[3], Log.class),
new FunctionDef(Log.signatures[4], Log.class),
new FunctionDef(Log.signatures[5], Log.class),
new FunctionDef(JMXToken.signature, JMXToken.class)
};

Expand All @@ -28,14 +31,22 @@ public class ConsoleModule extends AbstractInternalModule {
private static final Logger LOG = Logger.getLogger(ConsoleModule.class);

public static void log(String channel, String message) {
log(channel, false, message);
}

public static void log(String channel, boolean json, String message) {
if (adapter != null) {
adapter.log(channel, message);
adapter.log(channel, json, message);
}
}

public static void log(String channel, String source, int line, int column, String message) {
log(channel, source, line, column, false, message);
}

public static void log(String channel, String source, int line, int column, boolean json, String message) {
if (adapter != null) {
adapter.log(channel, source, line, column, message);
adapter.log(channel, source, line, column, json, message);
}
}

Expand Down
122 changes: 101 additions & 21 deletions src/org/exist/console/xquery/Log.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@

import org.exist.dom.QName;
import org.exist.storage.serializers.Serializer;
import org.exist.util.serializer.json.JSONWriter;
import org.exist.xquery.*;
import org.exist.xquery.value.*;
import org.xml.sax.SAXException;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.TransformerException;
import java.io.StringWriter;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
* Functions for logging to a console.
Expand Down Expand Up @@ -46,6 +52,36 @@ public class Log extends BasicFunction {
"Value to be sent. Will be transformed into JSON.")
},
new FunctionReturnSequenceType(Type.EMPTY, Cardinality.EMPTY, "Empty")
),
new FunctionSignature(
new QName("dump", ConsoleModule.NAMESPACE_URI, ConsoleModule.PREFIX),
"Dump the local variable stack to the console, including all variables which are visible at the " +
"point the statement appears in the code. Only the variables matching one of the names given in parameter " +
"$vars are dumped. All others are ignored. Use with care: might produce lots of output.",
new SequenceType[] {
new FunctionParameterSequenceType("channel", Type.STRING, Cardinality.EXACTLY_ONE,
"The channel to print to."),
new FunctionParameterSequenceType("vars", Type.STRING, Cardinality.ONE_OR_MORE,
"The names of the variables to dump."),
},
new FunctionReturnSequenceType(Type.EMPTY, Cardinality.EMPTY, "Empty")
),
new FunctionSignature(
new QName("dump", ConsoleModule.NAMESPACE_URI, ConsoleModule.PREFIX),
"Dump the local variable stack to the console, including all variables which are visible at the " +
"point the statement appears in the code. Use with care: might produce lots of output.",
new SequenceType[] {
new FunctionParameterSequenceType("channel", Type.STRING, Cardinality.EXACTLY_ONE,
"The channel to print to.")
},
new FunctionReturnSequenceType(Type.EMPTY, Cardinality.EMPTY, "Empty")
),
new FunctionSignature(
new QName("dump", ConsoleModule.NAMESPACE_URI, ConsoleModule.PREFIX),
"Dump the local variable stack to the console, including all variables which are visible at the " +
"point the statement appears in the code. Use with care: might produce lots of output.",
null,
new FunctionReturnSequenceType(Type.EMPTY, Cardinality.EMPTY, "Empty")
)
};

Expand All @@ -69,23 +105,78 @@ public void analyze(AnalyzeContextInfo contextInfo) throws XPathException {

@Override
public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException {
final String channel = getArgumentCount() == 1 ? "default" : args[0].getStringValue();
final Sequence params = getArgumentCount() == 1 ? args[0] : args[1];
final StringBuilder out = new StringBuilder();
final Properties outputProperties = new Properties(SERIALIZATION_PROPERTIES);
final boolean jsonFormat = isCalledAs("send");
if (jsonFormat) {
outputProperties.setProperty(OutputKeys.METHOD, "json");
if (isCalledAs("dump")) {
final String channel = getArgumentCount() == 0 ? "default" : args[0].getStringValue();
Set<String> varsToPrint = null;
if (getArgumentCount() == 2) {
varsToPrint = new HashSet<String>();
for (SequenceIterator i = args[1].iterate(); i.hasNext(); ) {
varsToPrint.add(i.nextItem().getStringValue());
}
}
StringWriter writer = new StringWriter();
JSONWriter jsonWriter = new JSONWriter(writer);
try {
jsonWriter.startDocument();
jsonWriter.startElement("", "result", "result");

Map<QName, Variable> vars = context.getLocalVariables();
for (Map.Entry<QName, Variable> var: vars.entrySet()) {
String name = var.getKey().toString();
if (varsToPrint == null || varsToPrint.contains(name)) {
jsonWriter.startElement("", name, name);
StringBuilder value = new StringBuilder();
printItems(value, outputProperties, false, var.getValue().getValue());
jsonWriter.characters(value);
jsonWriter.endElement("", name, name);
}
}
jsonWriter.endElement("", "result", "result");
jsonWriter.endDocument();

final String msg = writer.toString();
if (parent == null) {
ConsoleModule.log(channel, true, msg);
} else {
ConsoleModule.log(channel, parent.getSource().getKey(), parent.getLine(), parent.getColumn(), true, msg);
}
} catch (TransformerException e) {
e.printStackTrace();
}

} else {
final String channel = getArgumentCount() == 1 ? "default" : args[0].getStringValue();
final Sequence params = getArgumentCount() == 1 ? args[0] : args[1];
final StringBuilder out = new StringBuilder();
final boolean jsonFormat = isCalledAs("send");
if (jsonFormat) {
outputProperties.setProperty(OutputKeys.METHOD, "json");
}
printItems(out, outputProperties, jsonFormat, params);
final String msg = out.toString();
if (isCalledAs("send")) {
ConsoleModule.send(channel, msg);
} else {
if (parent == null) {
ConsoleModule.log(channel, msg);
} else {
ConsoleModule.log(channel, parent.getSource().getKey(), parent.getLine(), parent.getColumn(), msg);
}
}
}
int j = 0;
for (SequenceIterator i = params.iterate(); i.hasNext(); j++) {
return Sequence.EMPTY_SEQUENCE;
}

private void printItems(StringBuilder out, Properties outputProperties, boolean jsonFormat, Sequence sequence) throws XPathException {
for (SequenceIterator i = sequence.iterate(); i.hasNext(); ) {
final Item item = i.nextItem();
if (Type.subTypeOf(item.getType(), Type.NODE)) {
Serializer serializer = context.getBroker().getSerializer();
final Serializer serializer = context.getBroker().getSerializer();
serializer.reset();
try {
serializer.setProperties(outputProperties);
out.append(serializer.serialize((NodeValue)item));
out.append(serializer.serialize((NodeValue) item));
} catch (SAXException e) {
out.append(e.getMessage());
}
Expand All @@ -95,16 +186,5 @@ public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathExce
out.append(item.getStringValue());
}
}
final String msg = out.toString();
if (isCalledAs("send")) {
ConsoleModule.send(channel, msg);
} else {
if (parent == null) {
ConsoleModule.log(channel, msg);
} else {
ConsoleModule.log(channel, parent.getSource().getKey(), parent.getLine(), parent.getColumn(), msg);
}
}
return Sequence.EMPTY_SEQUENCE;
}
}
12 changes: 12 additions & 0 deletions src/org/exist/remoteconsole/RemoteConsoleAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ public RemoteConsoleAdapter(RemoteConsoleServlet servlet) {

@Override
public void log(String channel, String message) {
log(channel, false, message);
}

@Override
public void log(String channel, boolean json, String message) {
final Map<String, Object> data = new HashMap<String, Object>();
data.put("json", json);
data.put("message", message);
data.put("timestamp", new DateTimeValue(new Date()));

Expand All @@ -26,10 +32,16 @@ public void log(String channel, String message) {

@Override
public void log(String channel, String source, int line, int column, String message) {
log(channel, source, line, column, false, message);
}

@Override
public void log(String channel, String source, int line, int column, boolean json, String message) {
final Map<String, Object> data = new HashMap<String, Object>();
data.put("source", source);
data.put("line", line);
data.put("column", column);
data.put("json", json);
data.put("message", message);
data.put("timestamp", new DateTimeValue(new Date()));

Expand Down

0 comments on commit 668386b

Please sign in to comment.