How to capture console output of Eclipse plugin with custom Launch Configuration? - java

I'm writing an Eclipse plugin with a custom launch configuration, i.e. a launch() method inside a subclass of LaunchConfigurationDelegate. This method essentially just calls Runtime.exec(), but when I write to System.out from within launch() it goes to the console of the Eclipse instance which is debugging the plugin, rather than to the console of the plugin instance itself. I've analysed the ILaunchConfiguration and ILaunch arguments to the method but cannot find anywhere that they specify any output/error streams I can write to.
As is recommended in the tutorials, I have 2 separate plugins running together; one which handles the UI stuff (LaunchConfigurationTab,LaunchConfigurationTabGroup,LaunchShortcut,) and the other which contains the LaunchConfigurationDelegate itself.
I created a console in my UI plugin using this code, and I can write to it fine from within the UI code. But I cannot figure out how to direct output generated in my non-UI plugin to the console created in my UI plugin.
I've read this post and this one, but they do not specify how to "get ahold" of the output which is generated within the launch() method in the first place.
Any pointers would be really welcome, I am stuck!

Well I finally managed to get something working as follows:
In my LaunchConfigurationDelegate I introduced the following static method:
public static void setConsole(PrintStream ps) {
System.setOut(ps);
System.setErr(ps);
}
Then when creating my console in my UI plugin's PerspectiveFactory I call it as follows:
private void createConsole() {
console = new MessageConsole("My Console", null);
console.activate();
ConsolePlugin.getDefault().getConsoleManager().addConsoles(new IConsole[]{ console });
MessageConsoleStream stream = console.newMessageStream();
MyLaunchConfigurationDelegate.setConsole(new PrintStream(stream));
}
This works, except everytime I close down Eclipse and restart it the console disappears. However when I reset my perspective, the console appears again. So obviously I need that code to be called on startup, not in the PerspectiveFactory itself.
Hope this helps someone.. and if anybody has some input for this last problem (or about my approach in general) please do comment!

Related

How to listen to (take a copy / split ) System.out messages

I have an integration test (ClientIT) which uses the logging output from a test helper Class (ClientBasic) to determine whether the test passes or fails. I have re-directed the System.out/System.err inside the ClientBasic class to provide a link back to ClientIT using an OutputStream as follows,
System.setOut(new PrintStream(ClientBasic.out));
System.setErr(new PrintStream(ClientBasic.err));
where,
static OutputStream out;
static OutputStream err;
In ClientIT I call Client basic as follows,
ClientBasic.process(clientArgs.getArguments(), out, err);
This works fine, exactly how I wanted except when I run it using the Maven Failsafe plugins as part of the Package/Verify goal I get the message,
[WARNING] Corrupted STDOUT by directly writing to native stream in
forked JVM 1. See FAQ web page and the dump file
/path-to-project/target/failsafe-reports/2019-07-17T13-33-44_769-jvmRun1.dumpstream
which has the effect of really corrupting the display output of any logging info when the ClientBasic runs as part of my integration test - run locally from the command line or in Jenkins (using mvn ...) - strangely it still works fine when I run it from inside the IDE - maybe its not calling the failsafe plug-in directly ?
Anyway the above effect is documented on the Maven site as Corrupted STDOUT
So to try and get around this problem what I would like to do is to simply hook onto the System.out/err stream and create a copy - i.e. a bit like a splitter, rather than re-directing the System.out stream.
Does anyone now if this is possible and if so how to do this ?
As the error does not immediately blame setOut/setErr, it should be possible to make ones own splitter extending PrintStream.
public class Splitter extends PrintStream {
public Splitter(PrintStream sysPS, PrintWriter myOut) { ... }
... override methods by code generation in IDE.
}
PrintStream myOut = ...;
Splitter outSplitter = new Splitter(System.out, myOut);
Splitter errSplitter = new Splitter(System.err, myOut);
System.setOut(outSplitter);
System.setErr(errSplitter);
...
// At end:
System.setOut(outSplitter.getSysPS());
System.setErr(errSplitter.getSysPS());
IDE code generation and a couple of smart regex replaces will give you a correct class.
Alternatively there probably exists such a splitter as a Logger.

Py4j launch_gateway not connecting properly

I am trying to use py4j to open up a gateway that I can use to pass objects from java into python. When I try to open a gateway with the py4j function launch_gateway it does not seem to properly connect to my Java class. However, when I launch my java class in the command line and then connect to it in python using JavaGateway everything works as expected. I would like to be able to use the built in method as I am sure that I am not accounting for things that have already been considered in the design of py4j, but I'm just not sure what I'm doing wrong.
Let's say I wanted to create a gateway to the class sandbox.demo.solver.UtilityReporterEntryPoint.class. In the command line I can do this by executing the following:
java -cp /Users/grr/anaconda/share/py4j/py4j0.10.4.jar: sandbox.demo.solver.UtilityReporterEntryPoint py4j.GatewayServer
This launches as expected and I can use the methods in my class from within python after connecting to the gateway. So far so good.
My understanding of the py4j documentation would lead me to believe I should do the following to launch the gateway in python:
port = launch_gateway(classpath='sandbox.demo.solver.UtilityReporterEntryPoint')
params = GatewayParameters(port=port)
gateway= JavaGateway(gateway_parameters=params)
I get no errors when executing these three lines, but when I try to access my java class methods with gateway.entry_point.someMethod() it fails with the following error:
Py4JError: An error occurred while calling t.getReport. Trace:
py4j.Py4JException: Target Object ID does not exist for this gateway :t
at py4j.Gateway.invoke(Gateway.java:277)
at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132)
at py4j.commands.CallCommand.execute(CallCommand.java:79)
at py4j.GatewayConnection.run(GatewayConnection.java:214)
at java.lang.Thread.run(Thread.java:745)
Obviously something is not getting called correctly within launch_gateway or I am feeding it the wrong information.
In the py4j source code for launch_gateway you can see that given the inputs you provide and those constructed by the function, a command is constructed that eventually gets called by subprocess.Popen. So given the input passed to launch_gateway above the command passed into Popen would be:
command = ['java', '-classpath', '/Users/grr/anaconda/share/py4j/py4j0.10.4.jar:sandbox.demo.solver.UtilityReporterEntryPoint', 'py4j.GatewayServer', '0']
Passing this command to Popen returns the listening port as expected. However, connecting to this listening port still does not allow access to my class methods.
Finally, passing the command as a single string to Popen without the final argument ('0'), properly launches a gateway which again operates as expected. Having taken a glance at the Java source code for py4j.GatewayServer.class this makes no sense as the main method seems to indicate that the class should exit with status 1 if the length of arguments is 0.
At this point I'm kind of at a loss. I can hack my way into a workable solution, but as I said I'm sure that ignores important aspects of the gateway behavior and I don't like hacky solutions. I'd love to tag #Barthelemy in this one, but hopefully he reads this. Thanks in advance for any help.
EDIT
For now I have been able to work around this issue with the following steps.
Package entire project including all external dependencies into a single jar file magABM-all.jar, with 'Main-Class' set to UtilityReporterEntryPoint.
Include if...else block regarding presence of --die-on-exit exactly like it is in GatewayServer.java
Use subprocess.Popen to call the command to run the project jar.
UtilityReporterEntryPoint.java
public static void main(String[] args) throws IOException {
GatewayServer server = new GatewayServer(new UtilityReporterEntryPoint());
System.out.println("Gateway Server Started");
server.start();
if (args[0].equals("--die-on-exit")) {
try {
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in, Charset.forName("UTF-8")));
stdin.readLine();
System.exit(0);
} catch (java.io.IOException e) {
System.exit(1);
}
}
}
app.py
def setup_gateway()
"""Launch a py4j gateway using UtilityReporterEntryPoint."""
process = subprocess.Popen('java -jar magABM-all.jar --die-on-exit', shell=True)
time.sleep(0.5)
gateway = JavaGateway()
return gateway
In this way I can still use gateway.shutdown if necessary and if the python process that starts the py4j gateway dies or is closed the gateway will be closed.
N.B I would by no means consider this a final solution as py4j was written by much smarter individuals with a clear purpose in mind and I am sure that there is a way to manage this exact workflow within the confines of py4j. This is just a stopgap solution.
There are a few issues:
The classpath parameter in launch_gateway should be a directory or a jar file, not a class name. For example, if you want to include additional Java libraries, you would add them to the classpath parameter.
The error you receive when you call gateway.entry_point.someMethod() means that you have no entry point. When you call launch_gateway, the JVM is started with GatewayServer.main, which launches a GatewayServer with no entry point: GatewayServer server = new GatewayServer(null, port). It is not possible currently to use launch_gateway and specify an entry point.
When you start the JVM with java -cp /Users/grr/anaconda/share/py4j/py4j0.10.4.jar: sandbox.demo.solver.UtilityReporterEntryPoint py4j.GatewayServer I believe the JVM uses UtilityReporterEntryPoint as the main class. Although you did not provide the code, I assume that this class has a main method and that it launches a GatewayServer with an instance of UtilityReporterEntryPoint as the entry point. Note that there is a whitespace between the colon and the class name so UtilityReporterEntryPoint is seen as the main class and not as being part of the classpath.

Programmatically refreshing a file in an eclipse Java application

I have the following program which adds a method to itself when run. But I have to refresh it every time using the F5 button or the refresh option.
Is there a way I could code the refresh in the program itself so that it refreshes itself after the modification? The project I am working on is a Java application and not an eclipse plugin so as far as I know the refreshLocal() method can't be used.
public class Demo {
public static void main(String[] args) throws IOException, CoreException {
File file = new File("/home/kishan/workspace/Roast/src/Demo.java");
if (file.exists()) {
JavaClassSource javaClass = Roaster.parse(JavaClassSource.class,
file);
javaClass.addMethod().setPublic().setStatic(true)
.setName("newMethod").setReturnTypeVoid()
.setBody("System.out.println(\"newMethod created\");")
.addParameter("String[]", "stringArray");
FileWriter writer = new FileWriter(file);
writer.write(javaClass.toString());
writer.flush();
writer.close();
}
}
}
I have tried using the refreshLocal() method defined in the eclipse JDT but since my project is a Java application the ResourcePlugin.getWorkspace() method does not work giving me a "workspace closed" error. Any suggestion is appreciated.
You see, eclipse runs your Java class within its own dedicated JVM. Thus there is no direct programmatic way of enforcing a refresh within eclipse.
You could check this older question; maybe that could lead to a reasonable workarounds.
On the other hand you might step back and ask yourself why exactly you want to achieve that. Your workflow simply doesn't make much sense when looking at it; as in: when generating code that way, shouldn't that generated code better go in its own specific place?
If you intend to "generate" code frequently to then continue to use it in eclipse; well, that somehow smells like a strange idea.
Eclipse has "Refresh using native hooks or polling" which might might help.
You can find it under Window > Prefrences > General > Workspace.
See On Eclipse, what does "Preferences -> General -> Workspace -> Refresh using native hooks or polling" do?

Execute a Scala action inside an Java Controller (PlayFramework)

I've create an app using the Playfrawork with Java.
And I'm using the SecureSocial plugin on it.
Inside my controller there is a method that one of the process of it is to execute the handle start reset password inside the SecureSocial.
But that method is written in Scala.
If I refer the method inside my Java source like this:
Registration.handleStartResetPassword();
Nothing happens! Same if I change to the following line:
Registration.handleStartResetPassword().apply();
Same as the following source code block:
play.api.libs.iteratee.Iteratee<byte[], SimpleResult> it = Registration.handleStartResetPassword().apply(ctx()._requestHeader());
Future<SimpleResult> future = it.run();
Option<Try<SimpleResult>> result = future.value();
SimpleResult res = result.get().get();
I removed all my code and just tried to execute the Secure Social code but nothing happens.
Here is my method:
public static Result resetPassword() {
Registration.handleStartResetPassword().apply();
return TODO;
}
My route is this one:
POST /reset controllers.ProfileController.resetPassword
Edited: Added another way that I've tried to run but just didm't got the method running.
When you call handleStartResetPassword() you get an instance of Action back, it doesn't actually do anything until you to feed it with a particular request by calling Action.apply(request) which will then actually run the logic for that controller action and return a Future<Result>.
Not entirely sure if you can bridge backwards and get a play Java async result out of that though.

How to initialize an application's java object from command line

I have an ear application (myApp) that runs on a Websphere Application Server (WAS). I have a jar (myJar) that is loaded into the classpath of myApp when the WAS server is started. myJar has a class (MyInitClass) that reads from a db and loads a set of data (myData) into memory. This data gets read many times by myApp. The point is to get myData into memory to prevent doing a db call every time this data is used. This part works great!
The solution I am trying to provide is a manual initialization of MyInitClass. myData gets changed from time to time and I would like to be able to reinitialize MyInitClass from a command line so I don't have to restart the application. Is this possible?
myApp calls a class (MyClass) that has something like this:
public static MyInitClass initClass;
public boolean doStuff()
{
if (initClass == null)
{
// this method loads the data into initClass.myData array
initClass.dataInitializer();
}
else
// no need to reload initClass.myData
}
I have created code similar to this in another class (MyManualInit):
public static void main(String[] args)
{
MyClass.initClass = new MyInitClass();
MyClass.initClass.dataInitializer();
}
When I run MyManualInit from command line it prints all the same debug info that gets printed during the initialization from myApp. But myApp does not recognize that MyInitClass has been reinitialized. I have printed System.out.println(System.getProperty("java.home")) from both processes to validate that I am using the same JRE to run both. Am I doing something obviously wrong here or does it just not work like that? I assumed if I ran MyManualInit on the same JRE it would work. MyClass, MyInitClass and MyManualInit are all in myJar.
Please let me know if you need more info.
You are mixing things. Websphere runs in an instance of the JVM and your command line program instantiates a new one, and objects do not communicate between different JVMs (at least without some effort, bringing up sockets, etc.)
Actually your code does a lazy initialization of your initClass object, and it should be enough without any command line interaction. Why is it not enough for you?

Categories