Hi, i'am thinking this is the bug or some kind of unexpected behaviour.
JForex version: 3.6.39
JForex API version: 2.13.98
Reproducing:1. Save this code as jforex terminal plugin with file name as TestPlugin.java:
package jforex;
import com.dukascopy.api.*;
import com.dukascopy.api.plugins.IPluginContext;
import com.dukascopy.api.plugins.Plugin;
import com.dukascopy.api.system.ClientFactory;
import com.dukascopy.api.system.IClient;
import com.dukascopy.api.system.IStrategyExceptionHandler;
@RequiresFullAccess // don't know is this needed here
public class TestPlugin extends Plugin {
@Override
public void onStart(IPluginContext context) throws JFException {
try {
final IClient client = ClientFactory.getDefaultInstance();
if (client == null) {
context.getConsole().getErr().println("null client instance");
} else {
final DeadSimpleStrategy strategy = new DeadSimpleStrategy();
context.getConsole().getOut().println(client);
client.startStrategy(strategy, strategy);
}
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
context.getConsole().getErr().println(e.getLocalizedMessage());
}
}
@Override
public void onStop() throws JFException {
super.onStop();
}
public static class DeadSimpleStrategy implements IStrategy, IStrategyExceptionHandler {
@JFXInject
IContext ctx;
@JFXInject
IConsole console;
@Override
public void onStart(IContext context) throws JFException {
console.getOut().println("Strategy started");
}
@Override
public void onTick(Instrument instrument, ITick tick) throws JFException {
}
@Override
public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
}
@Override
public void onMessage(IMessage message) throws JFException {
}
@Override
public void onAccount(IAccount account) throws JFException {
}
@Override
public void onStop() throws JFException {
console.getOut().println("Strategy stopped!");
}
@Override
public void onException(long strategyId, Source source, Throwable t) {
console.getErr().printf("Strategy: %d Error: %s%n", strategyId, t.toString());
this.ctx.stop();
}
}
}
2. Compile plugin.
3. Activate plugin.
4. Open forex terminal error log and find something like this:
java.lang.NullPointerException @ com.dukascopy.api.impl.connect.DCClientImpl.a(L:1248)
null: java.lang.NullPointerException
at com.dukascopy.api.impl.connect.DCClientImpl.a(L:1248)
at com.dukascopy.api.impl.connect.DCClientImpl.startStrategy(L:1226)
at jforex.TestPlugin.onStart(TestPlugin.java:22)
at com.dukascopy.api.plugins.Plugin.onStart(Plugin.java:9)
at com.dukascopy.api.impl.execution.s.call(L:30)
at com.dukascopy.api.impl.execution.s.call(L:15)
at com.dukascopy.api.impl.connect.at.a(L:140)
at com.dukascopy.api.impl.connect.JForexTaskManager.a(L:887)
at com.dukascopy.api.impl.execution.j.call(L:34)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at com.dukascopy.api.impl.execution.f$a.e(L:904)
at com.dukascopy.api.impl.execution.f$a.run(L:926)
at java.lang.Thread.run(Thread.java:748)
Expected:No NullPointerException
Explanation:I assume I wrote correct code from java and forex api perspective.
Inside the plugin onStart method i'am trying to create instance of IClient class on line 16, because of its singleton nature, I assume that I will get same instance that is auto create when i'am log in into the terminal application. Checking null condition successfully print to the console IClient instance (line 21), but executing startStrategy method (line 22) resulting in NullPointerException in error log. I also tried with DeadSimpleStrategy implementation from separate file (not as nested class) - same behaviour.
If I change IClient to ITesterClient (line 16 + appropriate import):
final ITesterClient client = TesterFactory.getDefaultInstance();
- everything is fine, but of course strategy will not started because client not connected (Connection error).
So, in my opinion this is incorrect behaviour, if usage of IClient inside plugin or maybe totally inside terminal app - is not supported it would be better if it would be more transparent or explicit to the client, for example: ClientFactory.getDefaultInstance() call could return InstantiationException when in terminal context - this is bad, I understand, i'am not experienced in jforex sdk internals, but just as idea. Also improved java docs maybe enough, just expose some restrictions of IClient interface in docs.
Offtopic: Conceptually, I'am trying to create plugin that continuously working as server (zmq, http, grpc or whatever, doesn't matter) and spawn new strategies per client (or several per client, could be complex logic). I'am trying started new strategies via IClient and ITesterClient instances (not via IStrategyManager) because I want to eliminate needs to compile strategy in runtime or precompiled on artifact (jar) building stage. Is i'am on the right way or maybe somebody could help me to get better direction?
Thanks.