How to create another "version" of process() - java

I'm using MessageConsole.java to redirect standard output stream to a text pane. After finishing it, I thought it would be nice to redirect the error stream as well. To do so, I added another buffered reader to a modified version of this answer. Next is where my problem is - I need another "version" of the process() method which prints to System.err instead of System.out. I tried Googling it, but my results were nill. How would I add in another version of a Overridden method that requires specific arguments? The code might look something like the second example.
My current code
class ConsoleThread extends SwingWorker<Void, String> {
String command;
ConsoleThread(String cmd) {
command = cmd;
}
#Override
protected Void doInBackground() throws Exception {
Process ps = Runtime.getRuntime().exec(command);
BufferedReader is = new BufferedReader(new InputStreamReader(ps.getInputStream()));
BufferedReader es = new BufferedReader(new InputStreamReader(ps.getErrorStream()));
String outputLine;
String errorLine;
while ((outputLine = is.readLine()) != null) {
publish(outputLine);
}
while ((errorLine = es.readLine()) != null) {
publish(errorLine);
}
is.close();
return null;
}
#Override
protected void process(List<String> chunk) {
for (String string : chunk) {
System.out.println(string);
}
}
}
What the answer might look like (a snippet of code is worth a thousand words)
class ConsoleThread extends SwingWorker<Void, String> {
String command;
ConsoleThread(String cmd) {
command = cmd;
}
#Override
protected Void doInBackground() throws Exception {
Process ps = Runtime.getRuntime().exec(command);
BufferedReader is = new BufferedReader(new InputStreamReader(ps.getInputStream()));
BufferedReader es = new BufferedReader(new InputStreamReader(ps.getErrorStream()));
String outputLine;
String errorLine;
while ((outputLine = is.readLine()) != null) {
publish(outputLine);
}
while ((errorLine = es.readLine()) != null) {
publish2(errorLine);
}
is.close();
return null;
}
#Override
protected void process(List<String> chunk) {
for (String string : chunk) {
System.out.println(string);
}
}
#Override
protected void process2(List<String> chunk) {
for (String string : chunk) {
System.err.println(string);
}
}
}
Where process2() would be treated like the original process().
To be clear, the current code works but sends any error messages to the output stream rather than the error stream. (See this)

You don't need "another version of Process" at all. All you need are two new threads, one for each Stream, both the InputStream and the ErrorStream. Create two Runnables, put the while loops in those Runnables, pass your Streams into them, and run the Runnables in their own threads.
You could wrap the messages that you wish to publish in a wrapper object that identifies the stream origin of the method, allowing you to use the same publish/process pair, or you could use other notification methods such as PropertyChangeListeners and PropertyChangeSupport.
For what it's worth, I've used this code in previous attempts to read error and output streams:
enum GobblerType.java
public enum GobblerType {
ERROR, OUTPUT
}
class StreamGobbler.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
public class StreamGobbler implements Runnable {
private InputStream is;
private GobblerType type;
private OutputStream os;
public StreamGobbler(InputStream is, GobblerType type) {
this(is, type, null);
}
public GobblerType getType() {
return type;
}
public StreamGobbler(InputStream is, GobblerType type, OutputStream redirect) {
this.is = is;
this.type = type;
this.os = redirect;
}
public void run() {
try {
PrintWriter pw = null;
if (os != null) {
pw = new PrintWriter(os, true);
}
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
if (pw != null) {
pw.println(line);
}
// System.out.println(type + "> " + line);
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
class TextAreaOutputStream.java -- I have most doubts about this one
import java.io.IOException;
import java.io.OutputStream;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class TextAreaOutputStream extends OutputStream {
private final JTextArea textArea;
private final StringBuilder sb = new StringBuilder();
private String title;
public TextAreaOutputStream(final JTextArea textArea, String title) {
this.textArea = textArea;
this.title = title;
sb.append(title + "> ");
}
#Override
public void flush() {
}
#Override
public void close() {
}
#Override
public void write(int b) throws IOException {
if (b == '\r')
return;
if (b == '\n') {
final String text = sb.toString() + "\n";
SwingUtilities.invokeLater(new Runnable() {
public void run() {
textArea.append(text);
}
});
sb.setLength(0);
sb.append(title + "> ");
return;
}
sb.append((char) b);
}
}
None of this is professional code, just junk I've played with.

Related

Getting error "This method must return a result of type java.lang.String"

Suppose that file.txt only contains "Hello". When I compile the Java code, it shows
Error: This method must return a result of type java.lang.String in line5.
When I print in readTxt function, that works, it can show "Hello".
I already check the result is correctly String type, but it also shows compiler error. How can I make the return value to the main function?
import java.io.*;
import java.lang.String;
public class ReadTxtFile {
public static String readTxt(String filePath) {
try {
File file = new File(filePath);
if(file.isFile() && file.exists()) {
InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "utf-8");
BufferedReader br = new BufferedReader(isr);
String lineTxt = null;
lineTxt = br.readLine();
//System.out.println(lineTxt);
br.close();
return lineTxt;
} else {
}
} catch (Exception e) {
}
}
public static void main(String[] args) {
String filePath = "C:/file.txt";
String fileword = readTxt(filePath);
System.out.println(fileword);
}
}
You promised to return a String from your method, so you now have to do that. The only way around that promise is to throw an exception.
public static String readTxt(String filePath) { // Here you promise to return a String
try {
...
if(file.isFile() && file.exists()) {
...
return lineTxt; // Here you return a String as promised
} else {
// Here you're missing either return or throw
}
} catch (Exception e) {
// Here you're missing either return or throw
}
}
This is fundamentally a design problem - what should your method do if it fails to read the file for some reason? Return a special string like "Error"? Return null? Fail and throw and exception? Something else?
Answer that to yourself and it will be clear to you how to fix the code.
There are several best practices you should follow that will prevent future error. I have tried to cover them. Not saying mine is the perfect one, but you will get the idea.
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class StackOverFlow {
public static void main(String[] args) {
try {
String sText = getFileText("C:/file.txt");
System.out.println("Text is: " + sText);
} catch (FileNotFoundException e) {
System.out.println("File not Found");
} catch (IOException e) {
System.out.println("#Error while reading text: " + e.getMessage());
}
}
private static String getFileText(String filePath) throws FileNotFoundException, IOException {
File file = new File(filePath);
String line = null;
StringBuilder stringBuilder = new StringBuilder();
String ls = System.getProperty("line.separator");
BufferedReader reader = null;
try{
reader = new BufferedReader(new FileReader(file));
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
stringBuilder.append(ls);
}
reader.close();
}finally {
reader.close();
}
return new String(stringBuilder);
}
}

Java ProcessBuilder Output to String

How to redirect or get the system output to String?
ProcessBuilder pb = new ProcessBuilder().inheritIO();
...
for (...){
pb.command(...);
pb.start();
//here >>> assign output string to variable
}
Here is an opinion on how to capture the standard output of a system command process into a string container.
Adapted from the web:
try {
ProcessBuilder pb = new ProcessBuilder("echo", "dummy io");
final Process p=pb.start();
BufferedReader br=new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
StringBuilder sb = new StringBuilder();
while((line=br.readLine())!=null) sb.append(line);
}
System.out.println(sb.toString());
In congruence with my original comment on what would be a good example of Basic I/O. I hacked out some code, with a few more features than basic.
Extras
An environment shell for variables and
A working directory
These features add "profile-style" execution to your System commands.
Foundational Work
Java Threading and Joining by Oracle.
Code
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* Created by triston on 11/2/17.
*/
public class Commander {
private Commander(){} // no construction
public static class StreamHandler implements Runnable {
Object source;
Object destination;
StreamHandler(Object source, Object oDestination) {
this.source = source; this.destination = oDestination;
}
public void run() {
if (source instanceof InputStream) {
BufferedReader br = new BufferedReader(new InputStreamReader((InputStream) source));
String line;
try {
while ((line = br.readLine()) != null) ((StringBuilder) destination).append(line + '\n');
} catch (IOException oE) {
}
} else {
PrintWriter pw = new PrintWriter((OutputStream)destination);
pw.print((String)source);
pw.flush(); pw.close();
}
}
public static Thread read(InputStream source, StringBuilder dest) {
Thread thread = new Thread(new StreamHandler(source, dest));
(thread).start();
return thread;
}
public static Thread write(String source, OutputStream dest) {
Thread thread = new Thread(new StreamHandler(source, dest));
(thread).start();
return thread;
}
}
static Map<String, String> environment = loadEnvironment();
static String workingDirectory = ".";
static Map<String, String> loadEnvironment() {
ProcessBuilder x = new ProcessBuilder();
return x.environment();
}
static public void resetEnvironment() {
environment = loadEnvironment();
workingDirectory = ".";
}
static public void loadEnvirons(HashMap input) {
environment.putAll(input);
}
static public String getEnviron(String name) {
return environment.get(name);
}
static public void setEnviron(String name, String value) {
environment.put(name, value);
}
static public boolean clearEnviron(String name) {
return environment.remove(name) != null;
}
static public boolean setWorkingDirectory(String path) {
File test = new File(path);
if (!test.isDirectory()) return false;
workingDirectory = path;
return true;
}
static public String getWorkingDirectory() {
return workingDirectory;
}
static public class Command {
ProcessBuilder processBuilder = new ProcessBuilder();
Process process;
public Command(String... parameters) {
processBuilder.environment().putAll(environment);
processBuilder.directory(new File(workingDirectory));
processBuilder.command(parameters);
}
public int start(String input, StringBuilder output, StringBuilder error) throws IOException {
// start the process
process = processBuilder.start();
// start the error reader
Thread errorBranch = StreamHandler.read(process.getErrorStream(), error);
// start the output reader
Thread outputBranch = StreamHandler.read(process.getInputStream(), output);
// start the input
Thread inputBranch = StreamHandler.write(input, process.getOutputStream());
int rValue = 254;
try {
inputBranch.join(); rValue--;
outputBranch.join(); rValue--;
errorBranch.join(); rValue--;
return process.waitFor();
} catch (InterruptedException oE) {
oE.printStackTrace();
return rValue;
}
}
}
Testing
#Test public void foo() {
Command cmd = new Command("sh", "--");
StringBuilder output = new StringBuilder();
StringBuilder error = new StringBuilder();
int pValue = 127;
try {
pValue = cmd.start("echo well done > /dev/stderr\n\necho oh, wow; false", output, error);
} catch (IOException oE) {
}
System.out.println("output: "+output.toString());
System.out.println("error: "+error.toString());
System.out.println("\nExit code: "+pValue);
System.exit(pValue);
}
Bring your own package and JUnit annotations. This sample code demonstrates return value, command input, command standard output, and command error output.
My original design, called for the main thread to perform the standard output processing.
Have a great day.

How to Organize Commands/Handlers on Socket Game Server?

I am working on a socket server for a MMORPG I am creating, and I am unsure how to organize commands and handlers for readability.
I will be getting the command from data[0], such as ban, kick, identify, etc. I have read that you can use a HashMap and have an Interface Command, and create a new class for each handler/command that implements Command. You'd then create a HashMap and go from there. I have also read that you can reflection. I want to do what an experienced programmer would do.
Client Class:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Client implements Runnable {
private Socket socket;
private Server server;
private boolean isConnected;
private BufferedReader in;
private PrintWriter out;
public Client(Socket socket, Server server) {
this.socket = socket;
this.server = server;
this.isConnected = true;
this.in = null;
this.out = null;
}
#Override
public void run() {
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
while (isConnected) {
String recv = in.readLine().trim();
if (recv != null) {
String[] data = recv.split("%");
}
}
} catch (IOException e) {}
}
public synchronized void send(String data) {
out.println(data);
}
}
What you want is to use the Command Pattern.
Here is how I would use the Command Pattern for handling commands from a client.
/* Command Exception. */
public class ClientCommandException extends Exception {
public ClientCommandException(String msg) {
super(msg);
}
}
/* The ClientCommand interface */
public interface ClientCommand {
void execute(Client client, String[] params) throws ClientCommandException;
}
import java.util.HashMap;
import java.util.Arrays;
/* Container for commands */
public class Commands implements ClientCommand {
private HashMap<String, ClientCommand> cmds;
public Commands() {
cmds = new HashMap<String, ClientCommand>();
}
public void addCommand(ClientCommand cmd, String name) {
cmds.put(name, cmd);
}
public void execute(Client client, String[] params) throws ClientCommandException {
ClientCommand cmd = cmds.get(params[0]);
if(cmd != null) {
cmd.execute(client, Arrays.copyOfRange(params, 1, params.length));
} else {
throw new ClientCommandException("Unknown Command: " + params[0]);
}
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class Client implements Runnable {
private boolean isConnected;
private Commands cmds;
private BufferedReader in;
private PrintWriter out;
private class EchoCommand implements ClientCommand {
public void execute(Client client, String[] params) throws ClientCommandException {
StringBuilder b = new StringBuilder("Echo back:");
int len = params.length;
for(int i = 0; i < len; i++) {
b.append(' ');
b.append(params[i]);
}
client.send(b.toString());
}
}
private class DisconnectCommand implements ClientCommand {
public void execute(Client client, String[] params) throws ClientCommandException {
client.close();
}
}
public Client() {
cmds = new Commands();
cmds.addCommand(new EchoCommand(), "echo");
cmds.addCommand(new DisconnectCommand(), "disconnect");
/* sub-commands */
Commands server = new Commands();
server.addCommand(new EchoCommand(), "print");
cmds.addCommand(server, "server");
isConnected = true;
}
public void addCommand(ClientCommand cmd, String name) {
cmds.addCommand(cmd, name);
}
public void close() {
isConnected = false;
}
#Override
public void run() {
try {
in = new BufferedReader(new InputStreamReader(System.in));
out = new PrintWriter(System.out, true);
while (isConnected) {
String recv = in.readLine().trim();
if (recv != null) {
String[] data = recv.split("%");
try {
cmds.execute(this, data);
} catch(ClientCommandException e) {
/* Return some error back to the client. */
out.println(e.toString());
}
}
}
} catch (IOException e) {}
}
public synchronized void send(String data) {
out.println(data);
}
public static void main(String[] args){
Client client = new Client();
System.out.println("Start Client.");
client.run();
}
}

wants to execute a java class(which contains main) dynamically from a struts2 web page

I wanted to create a web page in Struts2.0 which contains a textarea, a property field and submit button. User will enter a java code in this text area and my code will compile it and execute it and will give the result of this code on my property field... the above code works fine in a standalone application. but it does not shows any thing in my web application. plz any one can address it... thanks in advance.
package org.controller;
import com.opensymphony.xwork2.ActionSupport;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
public class JCompilerAction extends ActionSupport
{
String program;
String MSg;
public JCompilerAction() {
}
public String getProgram() {
return program;
}
public void setProgram(String program) {
this.program = program;
}
public String getMSg() {
return MSg;
}
public void setMSg(String MSg) {
this.MSg = MSg;
}
public String Compile() {
try {
byte[] bFile = program.getBytes();
File f = new File("D:/nullprog.java");
FileOutputStream fileOuputStream = new FileOutputStream(f);
fileOuputStream.write(bFile);
fileOuputStream.close();
Process p1 = Runtime.getRuntime().exec("javac D:/nullprog.java");
BufferedReader in = new BufferedReader(new InputStreamReader(p1.getErrorStream()));
String line = null;
boolean isError = false;
while ((line = in.readLine()) != null) {
MSg = line;
isError = true;
return SUCCESS;
}
p1.waitFor();
if (!isError)
{
Process p2 = Runtime.getRuntime().exec("cmd /c start nullprog");
BufferedReader in1 = new BufferedReader(new InputStreamReader(p2.getInputStream()));
String line1 = null;
while ((line1 = in1.readLine()) != null) {
MSg += line1;
}
return SUCCESS;
}
} catch (Exception e) {
e.printStackTrace();
}
return SUCCESS;
}
public String execute() {
return SUCCESS;
}
}
Below is a simple program that first compiles a code and then executes it:
Executer.java
import java.lang.reflect.Method;
public class Executer {
public static void main(String args[]) {
try{
java.lang.Process p1 = Runtime.getRuntime().exec("javac MyClass.java");
p1.waitFor();
Class<?> c = Class.forName("MyClass");
Object obj = c.newInstance();
Method[] mArr = obj.getClass().getMethods();
for(Method m : mArr){
if(m.getName().equalsIgnoreCase("main")){
m.invoke(obj, new Object[]{new String[]{}});
}
}
} catch (Exception e){
e.printStackTrace();
}
}
}
MyClass.java
import javax.swing.JOptionPane;
public class MyClass {
public static void main(String[] args) {
JOptionPane.showMessageDialog(null, "Hii");
}
}
For simplicity both the classes are in same package. When I execute Executer class, it first compiles MyClass.java and then runs the main method.
If your requirement is to have the Java files in some folder outside of the project, the to load the compiler class file, you need to follow as mentioned in this answer.

How To Modify The Raw XML message of an Outbound CXF Request?

I would like to modify an outgoing SOAP Request.
I would like to remove 2 xml nodes from the Envelope's body.
I managed to set up an Interceptor and get the generated String value of the message set to the endpoint.
However, the following code does not seem to work as the outgoing message is not edited as expected. Does anyone have some code or ideas on how to do this?
public class MyOutInterceptor extends AbstractSoapInterceptor {
public MyOutInterceptor() {
super(Phase.SEND);
}
public void handleMessage(SoapMessage message) throws Fault {
// Get message content for dirty editing...
StringWriter writer = new StringWriter();
CachedOutputStream cos = (CachedOutputStream)message.getContent(OutputStream.class);
InputStream inputStream = cos.getInputStream();
IOUtils.copy(inputStream, writer, "UTF-8");
String content = writer.toString();
// remove the substrings from envelope...
content = content.replace("<idJustification>0</idJustification>", "");
content = content.replace("<indicRdv>false</indicRdv>", "");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(content.getBytes(Charset.forName("UTF-8")));
message.setContent(OutputStream.class, outputStream);
}
Based on the first comment, I created an abstract class which can easily be used to change the whole soap envelope.
Just in case someone wants a ready-to-use code part.
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.log4j.Logger;
/**
* http://www.mastertheboss.com/jboss-web-services/apache-cxf-interceptors
* http://stackoverflow.com/questions/6915428/how-to-modify-the-raw-xml-message-of-an-outbound-cxf-request
*
*/
public abstract class MessageChangeInterceptor extends AbstractPhaseInterceptor<Message> {
public MessageChangeInterceptor() {
super(Phase.PRE_STREAM);
addBefore(SoapPreProtocolOutInterceptor.class.getName());
}
protected abstract Logger getLogger();
protected abstract String changeOutboundMessage(String currentEnvelope);
protected abstract String changeInboundMessage(String currentEnvelope);
public void handleMessage(Message message) {
boolean isOutbound = false;
isOutbound = message == message.getExchange().getOutMessage()
|| message == message.getExchange().getOutFaultMessage();
if (isOutbound) {
OutputStream os = message.getContent(OutputStream.class);
CachedStream cs = new CachedStream();
message.setContent(OutputStream.class, cs);
message.getInterceptorChain().doIntercept(message);
try {
cs.flush();
IOUtils.closeQuietly(cs);
CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class);
String currentEnvelopeMessage = IOUtils.toString(csnew.getInputStream(), "UTF-8");
csnew.flush();
IOUtils.closeQuietly(csnew);
if (getLogger().isDebugEnabled()) {
getLogger().debug("Outbound message: " + currentEnvelopeMessage);
}
String res = changeOutboundMessage(currentEnvelopeMessage);
if (res != null) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Outbound message has been changed: " + res);
}
}
res = res != null ? res : currentEnvelopeMessage;
InputStream replaceInStream = IOUtils.toInputStream(res, "UTF-8");
IOUtils.copy(replaceInStream, os);
replaceInStream.close();
IOUtils.closeQuietly(replaceInStream);
os.flush();
message.setContent(OutputStream.class, os);
IOUtils.closeQuietly(os);
} catch (IOException ioe) {
getLogger().warn("Unable to perform change.", ioe);
throw new RuntimeException(ioe);
}
} else {
try {
InputStream is = message.getContent(InputStream.class);
String currentEnvelopeMessage = IOUtils.toString(is, "UTF-8");
IOUtils.closeQuietly(is);
if (getLogger().isDebugEnabled()) {
getLogger().debug("Inbound message: " + currentEnvelopeMessage);
}
String res = changeInboundMessage(currentEnvelopeMessage);
if (res != null) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Inbound message has been changed: " + res);
}
}
res = res != null ? res : currentEnvelopeMessage;
is = IOUtils.toInputStream(res, "UTF-8");
message.setContent(InputStream.class, is);
IOUtils.closeQuietly(is);
} catch (IOException ioe) {
getLogger().warn("Unable to perform change.", ioe);
throw new RuntimeException(ioe);
}
}
}
public void handleFault(Message message) {
}
private class CachedStream extends CachedOutputStream {
public CachedStream() {
super();
}
protected void doFlush() throws IOException {
currentStream.flush();
}
protected void doClose() throws IOException {
}
protected void onWrite() throws IOException {
}
}
}
I had this problem as well today. After much weeping and gnashing of teeth, I was able to alter the StreamInterceptor class in the configuration_interceptor demo that comes with the CXF source:
OutputStream os = message.getContent(OutputStream.class);
CachedStream cs = new CachedStream();
message.setContent(OutputStream.class, cs);
message.getInterceptorChain().doIntercept(message);
try {
cs.flush();
CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class);
String soapMessage = IOUtils.toString(csnew.getInputStream());
...
The soapMessage variable will contain the complete SOAP message. You should be able to manipulate the soap message, flush it to an output stream and do a message.setContent(OutputStream.class... call to put your modifications on the message. This comes with no warranty, since I'm pretty new to CXF myself!
Note: CachedStream is a private class in the StreamInterceptor class. Don't forget to configure your interceptor to run in the PRE_STREAM phase so that the SOAP interceptors have a chance to write the SOAP message.
Following is able to bubble up server side exceptions. Use of os.close() instead of IOUtils.closeQuietly(os) in previous solution is also able to bubble up exceptions.
public class OutInterceptor extends AbstractPhaseInterceptor<Message> {
public OutInterceptor() {
super(Phase.PRE_STREAM);
addBefore(StaxOutInterceptor.class.getName());
}
public void handleMessage(Message message) {
OutputStream os = message.getContent(OutputStream.class);
CachedOutputStream cos = new CachedOutputStream();
message.setContent(OutputStream.class, cos);
message.getInterceptorChain.aad(new PDWSOutMessageChangingInterceptor(os));
}
}
public class OutMessageChangingInterceptor extends AbstractPhaseInterceptor<Message> {
private OutputStream os;
public OutMessageChangingInterceptor(OutputStream os){
super(Phase.PRE_STREAM_ENDING);
addAfter(StaxOutEndingInterceptor.class.getName());
this.os = os;
}
public void handleMessage(Message message) {
try {
CachedOutputStream csnew = (CachedOutputStream) message .getContent(OutputStream.class);
String currentEnvelopeMessage = IOUtils.toString( csnew.getInputStream(), (String) message.get(Message.ENCODING));
csnew.flush();
IOUtils.closeQuietly(csnew);
String res = changeOutboundMessage(currentEnvelopeMessage);
res = res != null ? res : currentEnvelopeMessage;
InputStream replaceInStream = IOUtils.tolnputStream(res, (String) message.get(Message.ENCODING));
IOUtils.copy(replaceInStream, os);
replaceInStream.close();
IOUtils.closeQuietly(replaceInStream);
message.setContent(OutputStream.class, os);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
}
Good example for replacing outbound soap content based on this
package kz.bee.bip;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
public class SOAPOutboundInterceptor extends AbstractPhaseInterceptor<Message> {
public SOAPOutboundInterceptor() {
super(Phase.PRE_STREAM);
addBefore(SoapPreProtocolOutInterceptor.class.getName());
}
public void handleMessage(Message message) {
boolean isOutbound = false;
isOutbound = message == message.getExchange().getOutMessage()
|| message == message.getExchange().getOutFaultMessage();
if (isOutbound) {
OutputStream os = message.getContent(OutputStream.class);
CachedStream cs = new CachedStream();
message.setContent(OutputStream.class, cs);
message.getInterceptorChain().doIntercept(message);
try {
cs.flush();
IOUtils.closeQuietly(cs);
CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class);
String currentEnvelopeMessage = IOUtils.toString(csnew.getInputStream(), "UTF-8");
csnew.flush();
IOUtils.closeQuietly(csnew);
/* here we can set new data instead of currentEnvelopeMessage*/
InputStream replaceInStream = IOUtils.toInputStream(currentEnvelopeMessage, "UTF-8");
IOUtils.copy(replaceInStream, os);
replaceInStream.close();
IOUtils.closeQuietly(replaceInStream);
os.flush();
message.setContent(OutputStream.class, os);
IOUtils.closeQuietly(os);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
public void handleFault(Message message) {
}
private static class CachedStream extends CachedOutputStream {
public CachedStream() {
super();
}
protected void doFlush() throws IOException {
currentStream.flush();
}
protected void doClose() throws IOException {
}
protected void onWrite() throws IOException {
}
}
}
a better way would be to modify the message using the DOM interface, you need to add the SAAJOutInterceptor first (this might have a performance hit for big requests) and then your custom interceptor that is executed in phase USER_PROTOCOL
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Node;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
abstract public class SoapNodeModifierInterceptor extends AbstractSoapInterceptor {
SoapNodeModifierInterceptor() { super(Phase.USER_PROTOCOL); }
#Override public void handleMessage(SoapMessage message) throws Fault {
try {
if (message == null) {
return;
}
SOAPMessage sm = message.getContent(SOAPMessage.class);
if (sm == null) {
throw new RuntimeException("You must add the SAAJOutInterceptor to the chain");
}
modifyNodes(sm.getSOAPBody());
} catch (SOAPException e) {
throw new RuntimeException(e);
}
}
abstract void modifyNodes(Node node);
}
this one's working for me. It's based on StreamInterceptor class from configuration_interceptor example in Apache CXF samples.
It's in Scala instead of Java but the conversion is straightforward.
I tried to add comments to explain what's happening (as far as I understand).
import java.io.OutputStream
import org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor
import org.apache.cxf.helpers.IOUtils
import org.apache.cxf.io.CachedOutputStream
import org.apache.cxf.message.Message
import org.apache.cxf.phase.AbstractPhaseInterceptor
import org.apache.cxf.phase.Phase
// java note: base constructor call is hidden at the end of class declaration
class StreamInterceptor() extends AbstractPhaseInterceptor[Message](Phase.PRE_STREAM) {
// java note: put this into the constructor after calling super(Phase.PRE_STREAM);
addBefore(classOf[SoapPreProtocolOutInterceptor].getName)
override def handleMessage(message: Message) = {
// get original output stream
val osOrig = message.getContent(classOf[OutputStream])
// our output stream
val osNew = new CachedOutputStream
// replace it with ours
message.setContent(classOf[OutputStream], osNew)
// fills the osNew instead of osOrig
message.getInterceptorChain.doIntercept(message)
// flush before getting content
osNew.flush()
// get filled content
val content = IOUtils.toString(osNew.getInputStream, "UTF-8")
// we got the content, we may close our output stream now
osNew.close()
// modified content
val modifiedContent = content.replace("a-string", "another-string")
// fill original output stream
osOrig.write(modifiedContent.getBytes("UTF-8"))
// flush before set
osOrig.flush()
// replace with original output stream filled with our modified content
message.setContent(classOf[OutputStream], osOrig)
}
}

Categories

Resources