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();
}
});
}
}
Related
I have written a RxDownloader class to download items from a predefined list. The downloader is working fine when exporting as debug aar(sdk) file but getting crashed in relase build with following stacktrace:-
java.lang.AbstractMethodError: abstract method "void io.reactivex.rxjava3.core.Observer.onSubscribe(io.reactivex.rxjava3.disposables.Disposable)"
at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn.subscribeActual(ObservableSubscribeOn.java:34)
at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13176)
at io.reactivex.rxjava3.core.Observable.subscribeWith(Observable.java:13229)
Following is RxDownloader class code:-
public class RxDownloader {
private static final String TAG = RxDownloader.class.getSimpleName();
private static Scheduler mScheduler;
public static void initRxDownloader() {
mScheduler = Schedulers.newThread();
}
public void downloadFromList( ArrayList<DownloadItemEntity> downloadItemEntities, IDownloadListener iDownloadListener) {
Observable downloadObservable = Observable.fromIterable(downloadItemEntities);
Observer downloadObserver = new Observer() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onNext(Object o) {
DownloadItemEntity downloadItemEntity = (DownloadItemEntity) o;
downloadPictureFromURL(downloadItemEntity, iDownloadListener);
}
#Override
public void onError(#NonNull Throwable e) {
}
#Override
public void onComplete() {
try {
} catch (Exception e) {
e.printStackTrace();
}
}
};
downloadObservable.subscribeOn(mScheduler).subscribeWith(downloadObserver);
}}
Anyone knows how to resolve this error?
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 have a class:
public class EventListener<T extends Event> {
private final EventHandler<T> handler;
public EventListener(EventHandler<T> handler) {
if (handler == null) {
throw new NullPointerException();
}
this.handler = handler;
}
public boolean isApplicable(Event event) {
try {
this.getClass().getMethod("onEvent", event.getClass());
return true;
} catch (NoSuchMethodException | SecurityException ex) {
return false;
}
}
public void onEvent(T event) {
handler.handle(event);
}
}
Before an invocation of the onEvent(T event) method I need to call the isApplicable(Event event) method to check that the listener could handle this event.
I don't want to increase a number of the constructor's parameters in such way:
public class GenericClass<T> {
private final Class<T> type;
public GenericClass(Class<T> type) {
this.type = type;
}
public Class<T> getMyType() {
return this.type;
}
}
Listeners for different events will be stored in the same list. So I need some tool to check that the listener could handle the event from the list.
public class EventService extends Service {
private final ArrayList<EventListener> listeners = new ArrayList<>();
public EventService(ServiceContainer serviceContainer) {
super(serviceContainer);
}
public ArrayList<EventListener> getListeners() {
return listeners;
}
public <T extends Event> void trigger(T event) {
listeners.stream().forEach((listener) -> {
if (listener.isApplicable(event)) {
listener.onEvent(event);
}
});
}
}
Is there a more elegant code to put in this method:
public boolean isApplicable(Event event) {
try {
this.getClass().getMethod("onEvent", event.getClass());
return true;
} catch (NoSuchMethodException | SecurityException ex) {
return false;
}
}
As mentioned in the comments, your code should unless your event is an instance of Event and not of a class inheriting it...which is impossible if Event is an interface or an abstract class.
To keep the same kind of "philosophy", I would pass the expected class as constructor argument and check if the event is an instance of this class.
public class EventListener<T extends Event> {
private final EventHandler<T> handler;
private final Class<T> eventClass;
public EventListener(EventHandler<T> handler, Class<T> eventClass) {
if (handler == null) {
throw new NullPointerException();
}
if (eventClass == null) {
throw new NullPointerException();
}
this.handler = handler;
this.eventClass = eventClass;
}
public boolean isApplicable(Event event) {
return this.eventClass.isInstance(event);
}
public void onEvent(T event) {
handler.handle(event);
}
}
Finally:
public class EventService extends Service {
private final ArrayList<EventListener> listeners = new ArrayList<>();
public EventService(ServiceContainer serviceContainer) {
super(serviceContainer);
}
public ArrayList<EventListener> getListeners() {
return listeners;
}
public <T extends Event> void trigger(T event) {
listeners.stream().forEach((listener) -> {
listener.onEvent(event);
});
}
}
public class EventListener {
private final Consumer<Event> handler;
public EventListener(Consumer<Event> handler) {
if (handler == null) {
throw new NullPointerException();
}
this.handler = handler;
}
public void onEvent(Event event) {
handler.accept(event);
}
}
public class SpecifiedEventListener<T extends Event> extends EventListener {
private final Class<T> eventClass;
public SpecifiedEventListener(Class<T> eventClass, Consumer<T> handler) {
super((Event event) -> {
handler.accept((T) event);
});
if (eventClass == null
|| handler == null) {
throw new NullPointerException();
}
this.eventClass = eventClass;
}
#Override
public void onEvent(Event event) {
if (event.getClass().isAssignableFrom(eventClass)) {
super.onEvent(event);
}
}
public Class<T> getEventClass() {
return eventClass;
}
}
I'm using a multiplayer Game Client that's called AppWarp (http://appwarp.shephertz.com), where you can add event listeners to be called back when event's happen, let's assume we'll be talking about the Connection Listener, where you need to implement this interface:
public interface ConnectionRequestListener {
void onConnectDone(ConnectEvent var1);
void onDisconnectDone(ConnectEvent var1);
void onInitUDPDone(byte var1);
}
My goal here is to mainly create a Reactive version of this client to be used in my Apps Internally instead of using the Client itself directly (I'll also rely on interfaces later instead of just depending on the WarpClient itself as in the example, but that's not the important point, please read my question at the very end).
So what I did is as follows:
1) I introduced a new event, named it RxConnectionEvent (Which mainly groups Connection-Related events) as follows:
public class RxConnectionEvent {
// This is the original connection event from the source client
private final ConnectEvent connectEvent;
// this is to identify if it was Connection / Disconnection
private final int eventType;
public RxConnectionEvent(ConnectEvent connectEvent, int eventType) {
this.connectEvent = connectEvent;
this.eventType = eventType;
}
public ConnectEvent getConnectEvent() {
return connectEvent;
}
public int getEventType() {
return eventType;
}
}
2) Created some event types as follows:
public class RxEventType {
// Connection Events
public final static int CONNECTION_CONNECTED = 20;
public final static int CONNECTION_DISCONNECTED = 30;
}
3) Created the following observable which emits my new RxConnectionEvent
import com.shephertz.app42.gaming.multiplayer.client.WarpClient;
import com.shephertz.app42.gaming.multiplayer.client.events.ConnectEvent;
import rx.Observable;
import rx.Subscriber;
import rx.functions.Action0;
import rx.subscriptions.Subscriptions;
public class ConnectionObservable extends BaseObservable<RxConnectionEvent> {
private ConnectionRequestListener connectionListener;
// This is going to be called from my ReactiveWarpClient (Factory) Later.
public static Observable<RxConnectionEvent> createConnectionListener(WarpClient warpClient) {
return Observable.create(new ConnectionObservable(warpClient));
}
private ConnectionObservable(WarpClient warpClient) {
super(warpClient);
}
#Override
public void call(final Subscriber<? super RxConnectionEvent> subscriber) {
subscriber.onStart();
connectionListener = new ConnectionRequestListener() {
#Override
public void onConnectDone(ConnectEvent connectEvent) {
super.onConnectDone(connectEvent);
callback(new RxConnectionEvent(connectEvent, RxEventType.CONNECTION_CONNECTED));
}
#Override
public void onDisconnectDone(ConnectEvent connectEvent) {
super.onDisconnectDone(connectEvent);
callback(new RxConnectionEvent(connectEvent, RxEventType.CONNECTION_DISCONNECTED));
}
// not interested in this method (for now)
#Override
public void onInitUDPDone(byte var1) { }
private void callback(RxConnectionEvent rxConnectionEvent)
{
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(rxConnectionEvent);
} else {
warpClient.removeConnectionRequestListener(connectionListener);
}
}
};
warpClient.addConnectionRequestListener(connectionListener);
subscriber.add(Subscriptions.create(new Action0() {
#Override
public void call() {
onUnsubscribed(warpClient);
}
}));
}
#Override
protected void onUnsubscribed(WarpClient warpClient) {
warpClient.removeConnectionRequestListener(connectionListener);
}
}
4) and finally my BaseObservable looks like the following:
public abstract class BaseObservable<T> implements Observable.OnSubscribe<T> {
protected WarpClient warpClient;
protected BaseObservable (WarpClient warpClient)
{
this.warpClient = warpClient;
}
#Override
public abstract void call(Subscriber<? super T> subscriber);
protected abstract void onUnsubscribed(WarpClient warpClient);
}
My question is mainly: is my implementation above correct or should I instead create separate observable for each event, but if so, this client has more than 40-50 events do I have to create separate observable for each event?
I also use the code above as follows (used it in a simple "non-final" integration test):
public void testConnectDisconnect() {
connectionSubscription = reactiveWarpClient.createOnConnectObservable(client)
.subscribe(new Action1<RxConnectionEvent>() {
#Override
public void call(RxConnectionEvent rxEvent) {
assertEquals(WarpResponseResultCode.SUCCESS, rxEvent.getConnectEvent().getResult());
if (rxEvent.getEventType() == RxEventType.CONNECTION_CONNECTED) {
connectionStatus = connectionStatus | 0b0001;
client.disconnect();
} else {
connectionStatus = connectionStatus | 0b0010;
connectionSubscription.unsubscribe();
haltExecution = true;
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
fail("Unexpected error: " + throwable.getMessage());
haltExecution = true;
}
});
client.connectWithUserName("test user");
waitForSomeTime();
assertEquals(0b0011, connectionStatus);
assertEquals(true, connectionSubscription.isUnsubscribed());
}
I suggest you avoid extending the BaseObservable directly since it's very error prone. Instead, try using the tools Rx itself gives you to create your observable.
The easiest solution is using a PublishSubject, which is both an Observable and a Subscriber. The listener simply needs to invoke the subject's onNext, and the subject will emit the event. Here's a simplified working example:
public class PublishSubjectWarpperDemo {
public interface ConnectionRequestListener {
void onConnectDone();
void onDisconnectDone();
void onInitUDPDone();
}
public static class RxConnectionEvent {
private int type;
public RxConnectionEvent(int type) {
this.type = type;
}
public int getType() {
return type;
}
public String toString() {
return "Event of Type " + type;
}
}
public static class SimpleCallbackWrapper {
private final PublishSubject<RxConnectionEvent> subject = PublishSubject.create();
public ConnectionRequestListener getListener() {
return new ConnectionRequestListener() {
#Override
public void onConnectDone() {
subject.onNext(new RxConnectionEvent(1));
}
#Override
public void onDisconnectDone() {
subject.onNext(new RxConnectionEvent(2));
}
#Override
public void onInitUDPDone() {
subject.onNext(new RxConnectionEvent(3));
}
};
}
public Observable<RxConnectionEvent> getObservable() {
return subject;
}
}
public static void main(String[] args) throws IOException {
SimpleCallbackWrapper myWrapper = new SimpleCallbackWrapper();
ConnectionRequestListener listner = myWrapper.getListener();// Get the listener and attach it to the game here.
myWrapper.getObservable().observeOn(Schedulers.newThread()).subscribe(event -> System.out.println(event));
listner.onConnectDone(); // Call the listener a few times, the observable should print the event
listner.onDisconnectDone();
listner.onInitUDPDone();
System.in.read(); // Wait for enter
}
}
A more complex solution would be to use one of the onSubscribe implementations to create an observable using Observable.create(). For example AsyncOnSubscibe. This solution has the benefit of handling backperssure properly, so your event subscriber doesn't become overwhelmed with events. But in your case, that sounds like an unlikely scenario, so the added complexity is probably not worth it.
I have this Exception:
public class ErrorException extends Exception
{
private static final long serialVersionUID = 1L;
private String errorMessage = "";
private int errorCode = 0;
private String errorLevel = "";
private Window errorSource = null;
public String getErrorMessage()
{
return errorMessage;
}
public int getErrorCode()
{
return errorCode;
}
public String getErrorLevel()
{
return errorLevel;
}
public Window getErrorSource()
{
return errorSource;
}
public ErrorException(String message, int code, int level, Window source)
{
super();
errorMessage = message;
errorCode = code;
switch (level)
{
case 0:
{
errorLevel = "benignError";
}
case 1:
{
errorLevel = "criticalError";
}
case 2:
{
errorLevel = "terminalError";
}
}
errorSource = source;
}
}
And I have this method:
public static Element check(final Document document) throws ErrorException
{
try
{
chapter.resetLatch();
final SecondaryLoop loop = Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop();
new Thread()
{
#Override
public void run()
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
answer.getPreviousElement().takeFocus();
question.removeAnswer(answer);
question.rewriteLetters();
Utils.update(chapter);
loop.exit();
}
});
}
}.start();
loop.enter();
chapter.getLatch().await();
}
catch (InterruptedException e)
{
throw new ErrorException("blankElementDialogError", 8, 1, Main.getGui().getMasterWindow());
}
return new Element();
}
And I use it in this constructor code:
public ConfirmCloseDialog(final Document document, final int postOperation)
{
final CustomJButton doSave = new CustomJButton(Main.getString("doSave"), false);
doSave.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent arg0)
{
getConfirmCloseDialog().dispose();
new Thread()
{
#Override
public void run()
{
/*this method is the one above -->*/Element problem = BlankElementDialog.check(document);
if (problem == null)
{
new SaveChooser(document, postOperation);
}
else
{
new BlankElementDialog(problem);
}
}
}.start();
}
});
}
The code for the second part is not full, but there are no special constructs in the rest of the code (just some GUi objects being constructed and there is no try catch anywhere in the constructor).
However, Eclipse isn't forcing me to encapsulate the method call into try catch block, despite the fact that the method throws an Exception (ErorrException subclasses Exception).
And I know that Exception is checked exception, so it should force it, right?
Why?
What do I have to do so it would force it?
Even without any details Eclipse should notify, look at this:
Just restart the Eclipse should solve the issue.
public class TestClass {
public static void main(String[] args) {
method(2);//Notification here!
}
static void method(int a) throws myException {
}
}
class myException extends Exception {
}