I've been reading about the new Architecture Components that were introduced to Android and I cannot figure out how this works:
ViewModelProviders.of(Activity).get(Class)
Initially I thought that it calls the default constructor and returns a ViewModel object that you then instantiate with eg. an init() method as per
public class UserProfileViewModel extends ViewModel {
private String userId;
private User user;
public void init(String userId) {
this.userId = userId;
}
public User getUser() {
return user;
}
}
Snippet taken from the guide: https://developer.android.com/topic/libraries/architecture/guide.html
However, later on in the guide there is this snippet:
public class UserProfileViewModel extends ViewModel {
private LiveData<User> user;
private UserRepository userRepo;
#Inject // UserRepository parameter is provided by Dagger 2
public UserProfileViewModel(UserRepository userRepo) {
this.userRepo = userRepo;
}
public void init(String userId) {
if (this.user != null) {
// ViewModel is created per Fragment so
// we know the userId won't change
return;
}
user = userRepo.getUser(userId);
}
So how does the ViewModelProvider know to call the provided constructor? Or it sees that there is only 1 constructor and calls that? For example if there were 2 constructors what would happen?
I tried digging through the code and what I found was:
#Override
public <T extends ViewModel> T create(Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
return super.create(modelClass);
}
Inside of the DefaultFactory class inside ViewModelProviders.java. However, this confused me even further. How does getConstructor(Application.class) even work when ViewModel objects do not have a constructor taking an Application as an argument?
In the snippet there is a condition that checks if modelClass is of type AndroidViewModel (inherits ViewModel) which constructor takes Application parameter. This is more like exclusive case that spares the Factory of looking up for constructor matching specific parameters.
This provider looks up for constructor matching provider arguments when creating it:
public class ViewModelParameterizedProvider {
private AtomicBoolean set = new AtomicBoolean(false);
private ViewModelStore viewModelStore = null;
static ViewModelParameterizedProvider getProvider() {
return new ViewModelParameterizedProvider();
}
#MainThread
public static ViewModelProvider ofSupportFragment(Fragment fragment, Object... params) {
return getProvider().of(fragment).with(params);
}
#MainThread
public static ViewModelProvider ofActivity(FragmentActivity fragmentActivity, Object... params) {
return getProvider().of(fragmentActivity).with(params);
}
#MainThread
public static ViewModelProvider ofFragment(android.app.Fragment fragment, Object... params) {
return getProvider().of(fragment).with(params);
}
private ViewModelParameterizedProvider of(Fragment fragment) {
checkForPreviousTargetsAndSet();
viewModelStore = ViewModelStores.of(fragment);
return this;
}
private ViewModelParameterizedProvider of(android.app.Fragment fragment) {
FragmentActivity fragAct = (FragmentActivity) fragment.getActivity();
return of(fragAct);
}
private ViewModelParameterizedProvider of(FragmentActivity activity) {
checkForPreviousTargetsAndSet();
viewModelStore = ViewModelStores.of(activity);
return this;
}
private ViewModelProvider with(Object... constructorParams) {
return new ViewModelProvider(viewModelStore, parametrizedFactory(constructorParams));
}
private void checkForPreviousTargetsAndSet() {
if (set.get()) {
throw new IllegalArgumentException("ViewModelStore already has been set. Create new instance.");
}
set.set(true);
}
private ViewModelProvider.Factory parametrizedFactory(Object... constructorParams) {
return new ParametrizedFactory(constructorParams);
}
private final class ParametrizedFactory implements ViewModelProvider.Factory {
private final Object[] mConstructorParams;
ParametrizedFactory(Object... constructorParams) {
mConstructorParams = constructorParams;
}
#Override
public <T extends ViewModel> T create(Class<T> modelClass) {
if (modelClass == null) {
throw new IllegalArgumentException("Target ViewModel class can not be null")
}
Log.w("ParametrizedFactory", "Don't use callbacks or Context parameters in order to avoid leaks!!")
try {
if (mConstructorParams == null || mConstructorParams.length == 0) {
return modelClass.newInstance();
} else {
Class<?>[] classes = new Class<?>[mConstructorParams.length];
for (int i = 0; i < mConstructorParams.length; i++) {
classes[i] = mConstructorParams[i].getClass();
}
return modelClass.getConstructor(classes).newInstance(mConstructorParams);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
}
Here is kotlin version.
Here is more read on the subject
Related
I have a java jar file contain interface named IOperator like this :
public interface IOperator {
String execute(Map<String, String> parameters);
boolean isSuccess();
}
now in groovy script (in single file):
public class ClassA {
}
public class ClassB {
}
public class classC implements IOperator {
#Override
String execute(Map<String, String> parameters) {
ClassA classA = new ClassA();
ClassB classB = new ClassB();
return null
}
#Override
boolean isSuccess() {
return false
}
}
Is there any way scan this groovy script to find that specific class implemented IOperator and invoke execute method ?
Note: I want do it in Java code.
Finally i found answer :
public class OperatorManager {
public static void run(File scriptFile) {
try {
GroovyScriptEngineImpl engine = (GroovyScriptEngineImpl) new GroovyScriptEngineFactory().getScriptEngine();
GroovyClassLoader classLoader = new GroovyClassLoader();
classLoader.parseClass(new GroovyCodeSource(scriptFile));
engine.setClassLoader(classLoader);
Class<?>[] loadedClasses = engine.getClassLoader().getLoadedClasses();
Class<?> operatorImplClass = Arrays.stream(loadedClasses)
.filter(IOperator.class::isAssignableFrom)
.findFirst().orElse(null);
if (operatorImplClass != null) {
String result = invokeMethod(operatorImplClass);
System.out.println(result);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static String invokeMethod(Class<?> item) {
try {
Object instance = item.getConstructor().newInstance();
Method execute = item.getDeclaredMethod("execute", Map.class);
return (String) execute.invoke(instance, new Object[]{null});
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
From the start, sorry for bad English, I am working on it.
My goal is to create http methods in vert.x. Each method consists of steps, which can be blocked by other steps. For simplifying one step can be blocked by exactly one another.
I decided to create an AsyncMethodHandler which inside of handle method call, create exemplars of AsyncStepHandlers. Method handler also creates a map of steps futures, and try to create a compose handler for them to join.
here's the code AsyncMethodHandler:
public abstract class AsyncMethodHandler<T extends BaseChannelResponse> implements Handler<RoutingContext> {
private static final String CONTENT_TYPE_JSON = "application/json; charset=utf-8";
private final List<Class<? extends AsyncStepHandler>> steplist;
private final HttpMethod methodType;
private final String endpointName;
private final HttpEndpointName endpoint;
private String responseEndpoint;
public AsyncMethodHandler(HttpEndpointName endpoint, String endpointName, HttpMethod methodType, List<Class<? extends AsyncStepHandler>> steplist) {
this.steplist = steplist;
this.endpoint = endpoint;
this.endpointName = endpointName;
this.methodType = methodType;
}
#Override
public void handle(RoutingContext event) {
try {
Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution = new ConcurrentHashMap<>(steplist.size());
List<AsyncStepHandler> handlers = new ArrayList<>(steplist.size());
for (Class<? extends AsyncStepHandler> stepClass : this.steplist) {
AsyncStepHandler stepHandler = stepClass.getConstructor(RoutingContext.class).newInstance(event);
mapOfExecution.put(stepClass, stepHandler.getStepFuture());
handlers.add(stepHandler);
}
for (AsyncStepHandler stepHandler : handlers) {
stepHandler.before(mapOfExecution).setHandler(stepHandler.makeHandler(mapOfExecution));
}
CompositeFuture.join(new ArrayList<>(mapOfExecution.values())).setHandler(handleResult(event, mapOfExecution));
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
private Handler<AsyncResult<CompositeFuture>> handleResult(RoutingContext event, Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution) {
return result -> {
if (result.succeeded()) {
succeeded(event.response(), generateResponse(mapOfExecution));
} else {
ChannelAPIException error = ChannelAPIException.createFrom(result.cause());
errored(event.response(), error.getCode(), error.getMessage());
}
};
}
protected abstract T generateResponse(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution);
private void errored(HttpServerResponse response, int code, String message) {
response.putHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE_JSON)
.setStatusCode(code)
.end(message);
CAPIMetricFactory.incBotResponseError(this.responseEndpoint, code);
}
private void succeeded(HttpServerResponse response, T result) {
response.putHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE_JSON)
.setStatusCode(200)
.end(Serializer.toPrettyJson(result));
CAPIMetricFactory.incBotResponse(this.responseEndpoint);
}
public String getEndpointName() {
return endpointName;
}
public HttpMethod getMethodType() {
return methodType;
}
public HttpEndpointName getEndpoint() {
return endpoint;
}
public void setResponseEndpoint(String responseEndpoint) {
this.responseEndpoint = responseEndpoint;
}
}
here's the code AsyncStepHandlers:
public abstract class AsyncStepHandler<T> {
private final Future stepFuture;
private final RoutingContext context;
private final Class<? extends AsyncStepHandler> before;
public AsyncStepHandler(RoutingContext context) {
this(Future.future(), context, null);
}
public AsyncStepHandler(RoutingContext context, Class<? extends AsyncStepHandler> before) {
this(Future.future(), context, before);
}
private AsyncStepHandler(Future stepFuture, RoutingContext context, Class<? extends AsyncStepHandler> before) {
this.stepFuture = stepFuture;
this.context = context;
this.before = before;
}
public static <T> T getResultFromMap(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution, Class<? extends AsyncStepHandler> key) {
return (T) mapOfExecution.get(key).result();
}
public final Future getStepFuture() {
return stepFuture;
}
public RoutingContext getContext() {
return context;
}
public Buffer getContextBody() {
return context.getBody();
}
public String getContextBodyAsString() {
return context.getBodyAsString();
}
public Future before(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution) {
if (before != null) {
return mapOfExecution.get(before);
} else {
return Future.succeededFuture();
}
}
public abstract Future<T> execute(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution);
public Handler<AsyncResult> makeHandler(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution) {
return result -> {
if (result.succeeded()) {
this.execute(mapOfExecution).setHandler(this.finish());
} else {
stepFuture.fail(result.cause());
}
};
}
private Handler<AsyncResult<T>> finish() {
return result -> {
if (result.succeeded()) {
stepFuture.complete(result.result());
} else {
stepFuture.fail(result.cause());
}
};
}
}
So then, I try to create some actual methods and steps. For example:
create parameters object from the request body
from created earlier parameters get token and try to authorize
from an authorized object from the previous step consider validating the status of the request.
So here's the code:
public class SimpleTestMethod extends AsyncMethodHandler<TestData> {
public SimpleTestMethod(String endpoint) {
super(
CHANNEL_API_SEND_TEXT,
endpoint,
POST,
new ArrayList<Class<? extends AsyncStepHandler>>(){{
add(ParametersStep.class);
}{
add(AuthorizationStep.class);
}{
add(ValidateStep.class);
}}
);
}
#Override
protected TestData generateResponse(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution) {
System.out.println("End");
SendMessageParameters response = (SendMessageParameters) mapOfExecution.get(ParametersStep.class).result();
ValidationResult validationResult = (ValidationResult) mapOfExecution.get(ValidateStep.class).result();
return new TestData(response.toString(),0l);
}
}
First if for example steps will be like this:
public class ParametersStep extends AsyncStepHandler<SendMessageParameters> {
public ParametersStep(RoutingContext context) {
super(context);
}
#Override
public Future<SendMessageParameters> execute(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution) {
System.out.println("ParametersStep");
SendMessageParameters parameters = parseJson(this.getContextBodyAsString(), SendMessageParameters.class);
return Future.succeededFuture(parameters);
}
}
Execution will be expectable. But if I will add some additional awaiting for some step, then the next after that step will never start.
For example:
public class AuthorizationStep extends AsyncStepHandler<AuthResponse> {
public AuthorizationStep(RoutingContext context) {
super(context, ParametersStep.class);
}
#Override
public Future<AuthResponse> execute(Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution) {
System.out.println("AuthorizationStep");
final Future<AuthResponse> authorization = Future.future();
SendMessageParameters parameters = getResultFromMap(mapOfExecution, ParametersStep.class);
AuthResponse response = new AuthResponse(new ChannelTokenData(0l,parameters.getToken(),true,0l,0l,null));
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
authorization.complete(response);
}
});
t.start();
return authorization;
}
}
Then no steps, that awaits of authorization step ending will be called. I reading the official doc, and tried to find some information about this case, but did not succeed. I tried different technics setHandler, compose but get's the same result.
Can anybody help me with understanding why next step won't start and solving this issue because the next part is to use CompositeFuture =)
P.S.:
What is the most interesting, if for example AuthorizationStep is second in 3 steps method - execution will stop on second step. But if I do this:
#Override
public void handle(RoutingContext event) {
try {
Map<Class<? extends AsyncStepHandler>, Future> mapOfExecution = new ConcurrentHashMap<>(steplist.size());
List<AsyncStepHandler> handlers = new ArrayList<>(steplist.size());
CountDownLatch latch = new CountDownLatch(steplist.size());
for (Class<? extends AsyncStepHandler> stepClass : this.steplist) {
AsyncStepHandler stepHandler = stepClass.getConstructor(RoutingContext.class).newInstance(event);
mapOfExecution.put(stepClass, stepHandler.getStepFuture());
handlers.add(stepHandler);
stepHandler.setLatch(latch);
}
for (AsyncStepHandler stepHandler : handlers) {
stepHandler.before(mapOfExecution).setHandler(stepHandler.makeHandler(mapOfExecution));
}
latch.await();
CompositeFuture.join(new ArrayList<>(mapOfExecution.values())).setHandler(handleResult(event, mapOfExecution));
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
And this in AsyncStepHandler:
private Handler<AsyncResult<T>> finish() {
return result -> {
System.out.println("finish");
if (result.succeeded()) {
latch.countDown();
stepFuture.complete(result.result());
} else {
stepFuture.fail(result.cause());
}
};
}
Everything starts to work. If I add countdown latch, and add await before Composite future join, all will be fine.
I'm sending more than 1 request to a web service, below there is an example of that requests. Its important for my application to get the answer from the web service so if there is an exception application will try couple of times to get the answer.
Because of that getting something simple like
deviceList = serviceAdapter.getDevices(); is turn into below code.
boolean flag = true;
int counter = 1;
List<Device> deviceList = null;
while (flag) {
try {
deviceList = serviceAdapter.getDevices();
flag = false;
} catch (Exception e) {
try {
if (counter == 5) {
System.out.println("Timeout Occured!");
flag = false;
} else {
Thread.sleep(1000 * counter);
counter++;
}
} catch (InterruptedException e1) {
}
}
}
And in my application i have lots of requests which means there will be more ugly codes. Is there a way where i will call my request methods as parameter for another method something like this:
deviceList = wrapperMethod(serviceAdapter.getDevices());
Problem is there will be different type of requests, so they will return different type objects (list,array,string,int) and their paramaters will change. Is there a suitable solution in java for this problem?
You can pass a Supplier<T> to the wrapperMethod:
public static <T> T wrapperMethod (Supplier<T> supp) {
boolean flag = true;
int counter = 1;
T value = null;
while (flag) {
try {
value = supp.get();
flag = false;
} catch (Exception e) {
try {
if (counter == 5) {
System.out.println("Timeout Occured!");
flag = false;
} else {
Thread.sleep(1000 * counter);
counter++;
}
} catch (InterruptedException e1) {
}
}
}
}
And call it with:
List<Device> deviceList = wrapperMethod (() -> serviceAdapter.getDevices());
I'm afraid, though, that it will limit the methods you call within the lambda expression to throw only RuntimeExceptions.
You can use some command implementation to execute some specific codes :
Here is a simple example of a command
interface Command{
void run();
}
And a couple of implementations :
class SayHello implements Command{
#Override
public void run() {System.out.println("Hello World");}
}
class KillMe implements Command{
public void run() { throw new RuntimeException();};
}
All we have to do to execute those method is to receive an instance of Command and run the method :
public static void execCommand(Command cmd) {
cmd.run();
}
And to use this
public static void main(String[] args) {
execCommand(new SayHello());
execCommand(new KillMe());
}
Hello World
Exception in thread "main" java.lang.RuntimeException
It also accepts lambda expression :
execCommand(() -> System.out.println("Say goodbye"));
And method reference :
public class Test{
public static void testMe() {
System.out.println("I work");
}
}
execCommand(Test::testMe);
Note that I didn't specify that this could throw Exception so I am limited to unchecked exception like RuntimeException but of course void run() throws Exception could be a solution. That way you can do what ever you want.
Full example (with exceptions) :
public class Test {
public static void main(String[] args) {
try {
execCommand(new SayHello());
execCommand(() -> System.out.println("Say goodbye"));
execCommand(Test::testMe);
execCommand(new KillMe());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void testMe() throws IOException{
System.out.println("I work");
}
public static void execCommand(Command cmd) throws Exception {
cmd.run();
}
}
interface Command{
void run() throws Exception;
}
class SayHello implements Command{
#Override
public void run() {System.out.println("Hello World");}
}
class KillMe implements Command{
public void run() { throw new RuntimeException();};
}
Output:
Hello World
Say goodbye
I work
Exception in thread "main" java.lang.RuntimeException
at main.KillMe.run(Test.java:39)
at main.Test.execCommand(Test.java:25)
at main.Test.main(Test.java:17)
You can use #RetryOnFailure annotation from jcabi-aspects
Create a wrapper method then annotate it to enable auto retry upon Exception
As an example:
#RetryOnFailure(attempts = 5)
List<Device> retryWhenFailed(ServiceAdapter serviceAdapter) throws Exception {
return serviceAdapter.getDevices();
}
This solution uses Generics to be able to handle different Object with most of the same code and a Runnable to execute the fetching.
With this solution, you would need only to write the different adapters extending from ServiceAdapter<T extends Fetchable> to implement the logic to fetch the data for each different class (which would have to implement Fetchable).
Define an interface that abtracts the objects that can be fetched by the different services.
package so50488682;
public interface Fetchable {
}
The ojbect that are to be retrieved implement this interface so you can use the same code for different classes.
package so50488682;
public class Device implements Fetchable{
private String id;
public Device(String id) {
this.id = id;
}
public String toString() {
return "I am device " + id;
}
}
Define an abstract ServiceAdapter that the different service adapters will extend to implement the logic for each kind of object to be retrieved. We add throws Exception to the get() method so this method cand just delegate the exception handling to the FetcherService and decide if it should retry or fail.
package so50488682;
import java.util.List;
public abstract class ServiceAdapter<T extends Fetchable> {
public abstract List<T> get() throws Exception;
}
This is an example of an implementation done to get objects of class Device.
package so50488682;
import java.util.ArrayList;
import java.util.List;
public class DeviceServiceAdapter extends ServiceAdapter<Device>{
#Override
public List<Device> get() throws Exception{
List<Device> rtn = new ArrayList<>();
// fetch the data and put it into rtn, this is a mock
Device d = new Device("1");
rtn.add(d);
d = new Device("2");
rtn.add(d);
d = new Device("3");
rtn.add(d);
//
return rtn;
}
}
Finally this is a generic solution to run the different service adapters.
public class FetcherService<T extends Fetchable> implements Runnable{
List<T> result = new ArrayList<>();
ServiceAdapter<T> serviceAdapter;
#Override
public void run() {
boolean flag = true;
int counter = 1;
while (flag) {
try {
result = serviceAdapter.get();
flag = false;
} catch (Exception e) {
try {
if (counter == 5) {
System.out.println("Timeout Occured!");
flag = false;
} else {
Thread.sleep(1000 * counter);
counter++;
}
} catch (InterruptedException e1) {
throw new RuntimeException("Got Interrupted in sleep", e);
}
}
}
}
public List<T> getResult() {
return result;
}
public void setResult(List<T> result) {
this.result = result;
}
public void setAdapter(ServiceAdapter<T> adapter) {
this.serviceAdapter = adapter;
}
}
From the main or calling program it work like this:
package so50488682;
import java.util.List;
public class SO50488682 {
public static void main(String args[]) {
try {
DeviceServiceAdapter deviceServiceAdapter = new DeviceServiceAdapter();
FetcherService<Device> deviceFetcherService = new FetcherService<>();
deviceFetcherService.setAdapter(deviceServiceAdapter);
deviceFetcherService.run();
List<Device> devices = deviceFetcherService.getResult();
for(Device device : devices) {
System.out.println(device.toString());
}
}catch(Exception e) {
System.out.println("Exception after retrying a couple of times");
e.printStackTrace();
}
}
}
I am new to Vertx.
I am playing with the API and I am trying to write a FileSizeHandler. I don't know if it is the correct way to do it but I would like to have your opinions.
In my code I would like to use the handler like this :
public class MyVerticle extends AbstractVerticle {
#Override
public void start() throws Exception {
getFileSize("./my_file.txt", event -> {
if(event.succeeded()){
Long result = event.result();
System.out.println("FileSize is " + result);
} else {
System.out.println(event.cause().getLocalizedMessage());
}
});
}
private void getFileSize(String filepath, Handler<AsyncResult<Long>> resultHandler){
resultHandler.handle(new FileSizeHandler(filepath));
}
}
Here is my FileSizeHandler class :
public class FileSizeHandler implements AsyncResult<Long> {
private boolean isSuccess;
private Throwable cause;
private Long result;
public FileSizeHandler(String filePath){
cause = null;
isSuccess = false;
result = 0L;
try {
result = Files.size(Paths.get(filePath));
isSuccess = !isSuccess;
} catch (IOException e) {
cause = e;
}
}
#Override
public Long result() {
return result;
}
#Override
public Throwable cause() {
return cause;
}
#Override
public boolean succeeded() {
return isSuccess;
}
#Override
public boolean failed() {
return !isSuccess;
}
}
What bothers me in the handler, is that I have to do it in the constructor of the class. Is there a better way to do it?
First of all, you called your class FileHandler, but it's not. It's a result.
You declare handler in Vert.x like that:
public class MyHandler implements Handler<AsyncResult<Long>> {
#Override
public void handle(AsyncResult<Long> event) {
// Do some async code here
}
}
Now, for what you do, there's vertx.fileSystem():
public class MyVerticle extends AbstractVerticle {
#Override
public void start() throws Exception {
vertx.fileSystem().readFile("./my_file.txt", (f) -> {
if (f.succeeded()) {
System.out.println(f.result().length());
}
else {
f.cause().printStackTrace();
}
});
}
}
I have an abstract superclass AsyncActor which extends the Actor class. This superclass contains two abstract methods, notifySuccess and notifyFailure. notifyFailure is overriden in AsyncActor, but notifySuccess isn't because it's implementation changes from subclass to subclass. I have two actors, CreateObjActor and DeleteObjActor. Each extends AsyncActor and overrides notifySuccess. The execute method located in AsyncTask calls notifySuccess, but nothing happens.
public class AsyncActor extends Action<String> {
private Future future;
private final Action action;
private final ExecutorService executor = Executors.newSingleThreadExecutor();
public AsyncActor(Action action) {
this.action = action;
}
public Action getAction() {
return action;
}
public Future getFuture() {
return future;
}
public void execute() {
if(action.execute()) {
executeAsynchronously();
}
}
public void executeAsynchronously() {
ServiceFw.log.debug("Writing directory to file...");
Callable<String> asyncTask = () -> {
try {
ServiceFw.entryManager.writeBack();
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
ServiceFw.log.debug("Exception thrown during asynchronous wait");
e.printStackTrace();
} catch (BusinessException e) {
notifyFailure();
}
return "write back operation";
};
future = executor.submit(asyncTask);
Runnable poll = () -> {
if(future!=null) {
notifySuccess();
} else {
error.setSeverity(ExtendedError.Severity.Warning);
notifyFailure(error);
}
};
poll.run();
}
#Override
public void notifySuccess() {
}
#Override
public void notifyFailure() {
ServiceFw.log.error("Error with asynchronous processing of write back" );
ServiceFw.entryManager.deleteActor(this);
}
}
public class CreateObjActor extends AsyncActor {
public CreateObjActor(Action action) {
super(action);
}
#Override
public void notifySuccess() {
try {
ServiceFw.log.debug("Finished asynchronous operation: " + getFuture().get());
Entry entry = getAction().getEntryModel();
if (getAction().isNotify()) {
Notification notification = new Notification(entry);
ServiceFw.notificationDispatcher.dispatchNotification(notification);
try {
ServiceFw.eventFramework.dispatchEvent("entry-added");
} catch (BusinessException e) {
ServiceFw.log.error("Event could not be dispatched for newly added entry: " + entry.toString());
}
}
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
ServiceFw.entryManager.deleteActor(this);
}
}
}
I've tried making AsyncActor not abstract and providing an empty implementation of notifySuccess, but this didn't work either. Is there a way I can call the method from the superclass? The execute method contains a lot of code (not shown here) that would result in a lot of duplicate code if it had to be placed in each subclass.