Rx : Issue with Exception handling stream - system.reactive

I am trying to create a small rx Exception handling chunk of code, where I am expecting to get the same exception shown under a particular situation.
The actual use case is that I have a button that attempts to get data from the Clipboard, that the user may or may not have access to, which should result in a SecurityException being raised.
There is also the possibility that some other Exception may occur, which I would also like to handle (albeit showing a different error message to user).
There is also the possibility that something goes wrong with the stream itself, which I want to deal with in the subscribers OnError method.
Here is what I have so far (note I am using a TextBox to simulate the text that would be on the clipboard, and a checkbox to simulate whether a SecurityException should be raised or not)
private Subject<bool> simulatedPasteCommandSubject = new Subject<bool>();
private readonly CompositeDisposable disposables = new CompositeDisposable();
public Form1()
{
InitializeComponent();
WireUpSimulatedPasteCommandSubject();
}
private Func<IObservable<string>> ObserverableFactory
{
get
{
return () =>
{
return Observable.Defer(() =>
{
return simulatedPasteCommandSubject
.Select(_ => this.GetText())
.Where(x => !string.IsNullOrWhiteSpace(x))
.TrySelect<string, string>(this.TrySelectClipboardData);
});
};
}
}
private void WireUpSimulatedPasteCommandSubject()
{
var clipboardStream = ObserverableFactory();
this.disposables.Add(clipboardStream
.Catch((SecurityException ex) =>
{
this.ShowError("SecurityException");
return ObserverableFactory();
})
.Catch((Exception ex) =>
{
this.ShowError("Exception");
return ObserverableFactory();
})
.Do(data => LogData(data))
.Subscribe(
data => this.ImportTheClipboardData(data),
ex => this.ShowError("Something very bad happened")));
}
private void ImportTheClipboardData(string data)
{
MessageBox.Show(string.Format("Importing Clipboard data\r\n {0}", data));
}
private string GetText()
{
if (chkShouldThrow.Checked)
throw new SecurityException("SecurityException");
// simulate text coming from Clipboard
return textBox1.Text;
}
private void button1_Click(object sender, EventArgs e)
{
simulatedPasteCommandSubject.OnNext(true);
}
private bool TrySelectClipboardData(string dataIn, out string dataOut)
{
dataOut = string.Join("", dataIn.Reverse());
return true;
}
private void LogData(string data)
{
string dataLog = string.Format("Data : {0}", data);
Debug.WriteLine(dataLog);
}
private void ShowError(string ex)
{
string error = string.Format("Error : {0}", ex);
MessageBox.Show(error);
}
Sorry for the largish listing. Thing is if I type some text into the TextBox (to simulate the ClipBoard) and then click the button (which is simulating what the user would do, which makes the clipboard stream yield (OnNext)), I get what I expect. In this case a simple reversed string.
If however I click my CheckBox that should simulate a SecurityException being raised,and then click the button (which is simulating what the user would do, which makes the clipboard stream yield (OnNext)), I do indeed Catch the SecurityException, and see that logged. All good so far
Problem comes when I try and click the button again with the CheckBox that should simulate a SecurityException being raised still checked. I then get the following
"Exception"
Then when I try and click the button again with the CheckBox that should simulate a SecurityException being raised still checked. I then get the following
"Something very bad happened"
When what I am expecting to see is "SecurityException" being caught EVERY TIME, as it was the 1st time.
I have a feeling this is to do with the same stream being returned which was previously in the catch of the "SecurityException"

If I understand your code properly, You want all SecurityExcetions to be swallowed and the sequence to reconnect transparently to the user. ie If the user tries to access the clipboard multiple times they get the error message multiple times.
If this is the case, your problem lies with your lack of a loop. You get the sequence initially from the factory. If this sequence errors, then you get a second sequence. However if the 2nd sequence errors, you have no catch on that.
I think what you want to do is to catch the expected exceptions, show the message box, and then continue the sequence with the error. This will then allow you to log the error and also leverage the Retry() operator.
Maybe something like this will help
clipboardStream
.Catch((SecurityException ex) =>
{
this.ShowError("SecurityException");
return Observable.Throw<string>(ex);
})
.Catch((Exception ex) =>
{
this.ShowError("Exception");
return Observable.Throw<string>(ex);
})
.Do(data => LogData(data))
.Retry()
.Subscribe(
data => this.ImportTheClipboardData(data),
ex => this.ShowError("Wont get here (I dont think!)"));
Attempt 2
clipboardStream
.Do(data => LogData(data))
.Catch((SecurityException ex) =>
{
this.ShowError("SecurityException");
return Observable.Throw<string>(ex);
})
.Retry()
.Catch((Exception ex) =>
{
this.ShowError("Exception");
return Observable.Throw<string>(ex);
})
.Retry()
.Subscribe(
data => this.ImportTheClipboardData(data),
ex => this.ShowError("Wont get here (I dont think!)"));

Ok, So Lee Campbell Attempt 2 works a treat and gives me what I wanted. Here is full reworked solution (Thanks Lee)
public partial class Form1 : Form
{
private Subject<bool> simulatedPasteCommandSubject = new Subject<bool>();
private readonly CompositeDisposable disposables = new CompositeDisposable();
public Form1()
{
InitializeComponent();
WireUpSimulatedPasteCommandSubject();
}
private Func<IObservable<string>> ObserverableFactory
{
get
{
return () =>
{
return Observable.Defer(() =>
{
return simulatedPasteCommandSubject
.Select(_ => this.GetText())
.Where(x => !string.IsNullOrWhiteSpace(x))
.TrySelect<string, string>(this.TrySelectClipboardData);
});
};
}
}
private void WireUpSimulatedPasteCommandSubject()
{
var clipboardStream = ObserverableFactory();
this.disposables.Add(clipboardStream
.Do(data => LogData(data))
.Catch((SecurityException ex) =>
{
this.ShowError("SecurityException");
return Observable.Throw<string>(ex);
})
.Retry()
.Catch((Exception ex) =>
{
this.ShowError("Exception");
return Observable.Throw<string>(ex);
})
.Retry()
.Subscribe(
data => this.ImportTheClipboardData(data),
ex => this.ShowError("Wont get here (I dont think!)")));
}
private void ImportTheClipboardData(string data)
{
MessageBox.Show(string.Format("Importing Clipboard data\r\n {0}", data));
}
private string GetText()
{
if (chkShouldThrow.Checked)
throw new SecurityException("SecurityException");
// simulate text coming from Clipboard
return textBox1.Text;
}
private void button1_Click(object sender, EventArgs e)
{
simulatedPasteCommandSubject.OnNext(true);
}
private bool TrySelectClipboardData(string dataIn, out string dataOut)
{
dataOut = string.Join("", dataIn.Reverse());
return true;
}
private void LogData(string data)
{
string dataLog = string.Format("Data : {0}", data);
Debug.WriteLine(dataLog);
}
private void ShowError(string ex)
{
string error = string.Format("Error : {0}", ex);
MessageBox.Show(error);
}
}

Related

RxJava Problem with reading a file with Observable and take operator

My working environment is JDK 1.6 and RxJava 2
I want to make an Observable which emits an item that is a file line string read via BufferedReader as follows:
...
Observable<String> fileLineObservable = Observable.defer(new Callable<String>(){
return new ObservableSource<String> call() throws Exception {
return new ObservableSource<String>() {
public void subscribe(Observer<String> observer) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(filePath));
String line = null;
while ((line = reader.readLine()) != null) {
observer.onNext(line);
}
observer.onComplete();
... catching exception and close reader
}
}
}
}
});
I also want to make an Observer that observes the above Observable with one take(count) operator as follows:
fileLineObservable.take(2)
.subscribe(new Consumer<String>() {
public void onNext(String line) {
... do something with the file line string
}
});
I meet NullPointerException when executing the above code and I know why. The NPE is caused by that the second call of onNext leads to execute onComplete on the TakeObserver instance and inside the onComplete method, upstream.dispose that is not set(null) is called. The upstream variable of TakeObserver is supposed to be set with onSubscribe(Disposable disposable) when it subscribes an Observable.
How can I solve this problem? Should I implement my own Disposable class to set the upstream of TakeObserver?
What about this solution?
Observable<String> observableFile2(Path path) {
return Observable.using(
() -> Files.newBufferedReader(path),
reader -> {
return Observable.fromIterable(() -> {
return new Iterator<>() {
private String nextLine = null;
#Override
public boolean hasNext() {
try {
nextLine = reader.readLine();
return nextLine != null;
} catch (Exception ex) {
return false;
}
}
#Override
public String next() {
if (nextLine != null) {
return nextLine;
}
throw new IllegalStateException("nextLine can not be null.");
}
};
});
},
BufferedReader::close
);
}
Observable#using makes sure, that the BufferedReader is closed properly on disposable / onError
Observable#fromIterable wraps the readLine calls and handles onComplete for us.
Testing
testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.2")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.6.2")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.2")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.6.2")
testImplementation("com.google.jimfs:jimfs:1.1")
Tests
#Test
void name() {
observableFile2(hello).take(2)
.test()
.assertValues("line0", "line1")
.assertComplete();
}
#Test
void name2() {
observableFile2(hello).take(10)
.test()
.assertValues("line0", "line1", "line2", "line3")
.assertComplete();
}
#Test
void name3() {
observableFile2(hello2)
.test()
.assertComplete();
}

Can we throw an exception in fallback or fallbackFactory of #FeignClient

I'm use the #FeignClient and want to do some logic(like record the exception information) when Feign throw Exception and then reply the result to front end.
I noticed Feign will throw FeignException when connection fail or http status not expect.
So I defined a #ExceptionHandler to caught FeignException after the callback method was invoked.
#ExceptionHandler(value = FeignException.class)
#ResponseBody
public ResponseResult feignException(FeignException exception){
String message = exception.getMessage();
byte[] content = exception.content();
int status = exception.status();
if(content!=null){
String response=new String(content);
message=String.format("%s response message : %s",message,response);
}
log.warn("{} : {} , cause by : {}",exception.getClass().getSimpleName(),message,exception.getCause());
return ResponseResult.fail(HttpStatus.valueOf(status),String.format("9%s00",status),message);
But it can't caught when I set the callback or callbackFactory of #FeignClient.
#FeignClient(url = "${onboardingcase.uri}",name = "OnBoardingCaseService",
fallbackFactory = OnBoardingCaseServiceFallBack.class)
#Component
#Slf4j
public class OnBoardingCaseServiceFallBack implements FallbackFactory<OnBoardingCaseService> {
#Override
public OnBoardingCaseService create(Throwable throwable) {
return new OnBoardingCaseService() {
#Override
public OnBoardingCaseVo query(String coid) {
if(throwable instanceof FeignException){
throw (FeignException)throwable;
}
return null;
}
};
}
}
I noticed because hystrix took over this method.And will catch exception in HystrixInvocationHandler.
try {
Object fallback = HystrixInvocationHandler.this.fallbackFactory.create(this.getExecutionException());
Object result = ((Method)HystrixInvocationHandler.this.fallbackMethodMap.get(method)).invoke(fallback, args);
if (HystrixInvocationHandler.this.isReturnsHystrixCommand(method)) {
return ((HystrixCommand)result).execute();
} else if (HystrixInvocationHandler.this.isReturnsObservable(method)) {
return ((Observable)result).toBlocking().first();
} else if (HystrixInvocationHandler.this.isReturnsSingle(method)) {
return ((Single)result).toObservable().toBlocking().first();
} else if (HystrixInvocationHandler.this.isReturnsCompletable(method)) {
((Completable)result).await();
return null;
} else {
return HystrixInvocationHandler.this.isReturnsCompletableFuture(method) ? ((Future)result).get() : result;
}
} catch (IllegalAccessException var3) {
throw new AssertionError(var3);
} catch (ExecutionException | InvocationTargetException var4) {
throw new AssertionError(var4.getCause());
} catch (InterruptedException var5) {
Thread.currentThread().interrupt();
throw new AssertionError(var5.getCause());
}
So I want to know how can I throw an exception when I using callback / callbackFactory or there is another way to instead callbackFactory to do the "call back"?
Many Thanks
I found a solution to this problem.
public class OnBoardingCaseServiceFallBack implements FallbackFactory<OnBoardingCaseService> {
#Override
public OnBoardingCaseService create(Throwable throwable) {
return new OnBoardingCaseService() {
#Override
public OnBoardingCaseVo query(String coid) {
log.error("OnBoardingCaseService#query fallback , exception",throwable);
if(throwable instanceof FeignException){
throw (FeignException)throwable;
}
return null;
}
};
}
}
And then caught the HystrixRuntimeException and get the cause of exception in ExceptionHandler for get the realException that was wrapped by Hystrix.
#ExceptionHandler(value = HystrixRuntimeException.class)
#ResponseBody
public ResponseResult hystrixRuntimeException(HystrixRuntimeException exception){
Throwable fallbackException = exception.getFallbackException();
Throwable assertError = fallbackException.getCause();
Throwable realException = assertError.getCause();
if(realException instanceof FeignException){
FeignException feignException= (FeignException) realException;
String message = feignException.getMessage();
byte[] content = feignException.content();
int status = feignException.status();
if(content!=null){
String response=new String(content);
message=String.format("%s response message : %s",message,response);
}
return ResponseResult.fail(HttpStatus.valueOf(status),String.format("9%s00",status),message);
}
String message = exception.getMessage();
log.warn("{} : {} , cause by : {}",exception.getClass().getSimpleName(),message,exception.getCause());
return ResponseResult.fail(ResultCode.FAIL.httpStatus(),ResultCode.FAIL.code(),message);
}
But I don't think that's a good way~
I have never done this in fallback, I have implemented custom error decoder(“CustomFeignErrorDecoder”) class and extended feign.codec.ErrorDecoder, every time an error occurs it comes to this class.
In decode function throw a custom exception and catch it in the controller or service layer to show your message to the frontend.
Example:
#Component
public class CustomFeignErrorDecoder implements ErrorDecoder {
#Override
public Exception decode(String methodKey, Response response) {
throw new CustomFeignErrorDecoderException(methodKey +" response status "+ response.status() +" request "+ response.request()+ " method "+ response.request().httpMethod());
}
}

How to perform an action after the subscriber on next callback in Rx?

I'd like to be able to add an extension method like this:
IObservable<T> AfterSubscriber(this IObservable<T> observable, Action<T> action)
Where:
var subj = new Subject<int>();
subj.
AfterSubscriber(i => Console.WriteLine("Second")).
Subscribe(i => Console.WriteLine("First"));
subj.OnNext(1);
Produces the output:
First
Second
Any suggestions on how to implement the above method? Are there any built in methods I can use? (Assumption: synchronous/immediate subscription)
How about this implementation?
public static IObservable<T> AfterSubscriber<T>(
this IObservable<T> #this, Action<T> after)
{
if (#this == null) throw new ArgumentNullException("#this");
if (after == null) throw new ArgumentNullException("after");
return Observable.Create<T>(o =>
{
return #this.Subscribe(t =>
{
o.OnNext(t);
try
{
after(t);
}
catch (Exception exception)
{
o.OnError(exception);
}
}, o.OnError, o.OnCompleted);
});
}

Show previous instance of RCP application

I had an rcp application which runs for only first run, when a user attempts to re-execute the application, second instance behaves as a client which encodes and sends its arguments over the socket to the first instance which acts as a server and then exits silently. The first instance receives and decodes that message, then behaves as if it had been invoked with those arguments.
so far so good i made internal protocol specification for passing arguments between two instances.
I could not bring the first instance(RCP application) to front. It is in minimized state only,
this is in continuation to my previous question
the change i made to previous post is start method of application class
public Object start(IApplicationContext context) throws Exception {
if (!ApplicationInstanceManager.registerInstance()) {
return IApplication.EXIT_OK;
}
ApplicationInstanceManager
.setApplicationInstanceListener(new ApplicationInstanceListener() {
public void newInstanceCreated() {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
System.out.println("New instance detected...");
//Display.getCurrent().getActiveShell()
.forceActive();// this gives null
// pointer exception
// hence commented
}
});
}
});
Display display = PlatformUI.createDisplay();
try {
int returnCode = PlatformUI.createAndRunWorkbench(display,
new ApplicationWorkbenchAdvisor());
if (returnCode == PlatformUI.RETURN_RESTART)
return IApplication.EXIT_RESTART;
else
return IApplication.EXIT_OK;
} finally {
display.dispose();
}
}
below line is stopping me to bring Application to front
Display.getCurrent().getActiveShell().forceActive();
generates null pointer exception at getActiveShell()
how can i maximize the previous instance or bring it to front
I wrote an instance manager to restrict my RCP to a single instance.
Here's the code that goes in Application.java, in the start method:
if (!ApplicationInstanceManager.registerInstance()) {
return IApplication.EXIT_OK;
}
ApplicationInstanceManager
.setApplicationInstanceListener(new ApplicationInstanceListener() {
public void newInstanceCreated() {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (DEBUG)
System.out.println("New instance detected...");
Display.getCurrent().getActiveShell().forceActive();
}
});
}
});
Here's the listener interface:
public interface ApplicationInstanceListener {
public void newInstanceCreated();
}
And here's the Manager class:
public class ApplicationInstanceManager {
private static final boolean DEBUG = true;
private static ApplicationInstanceListener subListener;
/** Randomly chosen, but static, high socket number */
public static final int SINGLE_INSTANCE_NETWORK_SOCKET = 44331;
/** Must end with newline */
public static final String SINGLE_INSTANCE_SHARED_KEY = "$$RabidNewInstance$$\n";
/**
* Registers this instance of the application.
*
* #return true if first instance, false if not.
*/
public static boolean registerInstance() {
// returnValueOnError should be true if lenient (allows app to run on
// network error) or false if strict.
boolean returnValueOnError = true;
// try to open network socket
// if success, listen to socket for new instance message, return true
// if unable to open, connect to existing and send new instance message,
// return false
try {
final ServerSocket socket = new ServerSocket(
SINGLE_INSTANCE_NETWORK_SOCKET, 10, InetAddress
.getLocalHost());
if (DEBUG)
System.out
.println("Listening for application instances on socket "
+ SINGLE_INSTANCE_NETWORK_SOCKET);
Thread instanceListenerThread = new InstanceListenerThread(socket);
instanceListenerThread.start();
// listen
} catch (UnknownHostException e) {
EclipseLogging.logError(RabidPlugin.getDefault(),
RabidPlugin.PLUGIN_ID, e);
return returnValueOnError;
} catch (IOException e) {
return portTaken(returnValueOnError, e);
}
return true;
}
private static boolean portTaken(boolean returnValueOnError, IOException e) {
if (DEBUG)
System.out.println("Port is already taken. "
+ "Notifying first instance.");
try {
Socket clientSocket = new Socket(InetAddress.getLocalHost(),
SINGLE_INSTANCE_NETWORK_SOCKET);
OutputStream out = clientSocket.getOutputStream();
out.write(SINGLE_INSTANCE_SHARED_KEY.getBytes());
out.close();
clientSocket.close();
System.out.println("Successfully notified first instance.");
return false;
} catch (UnknownHostException e1) {
EclipseLogging.logError(RabidPlugin.getDefault(),
RabidPlugin.PLUGIN_ID, e);
return returnValueOnError;
} catch (IOException e1) {
EclipseLogging
.logError(
RabidPlugin.getDefault(),
RabidPlugin.PLUGIN_ID,
"Error connecting to local port for single instance notification",
e);
return returnValueOnError;
}
}
public static void setApplicationInstanceListener(
ApplicationInstanceListener listener) {
subListener = listener;
}
private static void fireNewInstance() {
if (subListener != null) {
subListener.newInstanceCreated();
}
}
public static void main(String[] args) {
if (!ApplicationInstanceManager.registerInstance()) {
// instance already running.
System.out.println("Another instance of this application "
+ "is already running. Exiting.");
System.exit(0);
}
ApplicationInstanceManager
.setApplicationInstanceListener(new ApplicationInstanceListener() {
public void newInstanceCreated() {
System.out.println("New instance detected...");
// this is where your handler code goes...
}
});
}
public static class InstanceListenerThread extends Thread {
private ServerSocket socket;
public InstanceListenerThread(ServerSocket socket) {
this.socket = socket;
}
#Override
public void run() {
boolean socketClosed = false;
while (!socketClosed) {
if (socket.isClosed()) {
socketClosed = true;
} else {
try {
Socket client = socket.accept();
BufferedReader in = new BufferedReader(
new InputStreamReader(client.getInputStream()));
String message = in.readLine();
if (SINGLE_INSTANCE_SHARED_KEY.trim().equals(
message.trim())) {
if (DEBUG)
System.out.println("Shared key matched - "
+ "new application instance found");
fireNewInstance();
}
in.close();
client.close();
} catch (IOException e) {
socketClosed = true;
}
}
}
}
}
}
After your IApplication start up, you can also check and lock the OSGi instance location using org.eclipse.osgi.service.datalocation.Location.isSet() and org.eclipse.osgi.service.datalocation.Location.lock()
The location is usually retrieved from your Activator using code like:
public Location getInstanceLocation() {
if (locationTracker == null) {
Filter filter = null;
try {
filter = context.createFilter(Location.INSTANCE_FILTER);
} catch (InvalidSyntaxException e) {
// ignore this. It should never happen as we have tested the
// above format.
}
locationTracker = new ServiceTracker(context, filter, null);
locationTracker.open();
}
return (Location) locationTracker.getService();
}

GWT client "throws Exception" cause compling problem

I try to use get result from a api called j-calais, and then out put the result on a web page, i write all the code in client, but it cant compile right, dont know why??? please help. the source code like below:
there is no obvious error arise, but it cant be compile successfully..... thanks a lot:
public void onModuleLoad() {
// Create table for stock data.
stocksFlexTable.setText(0, 0, "Type");
stocksFlexTable.setText(0, 1, "Name");
// Assemble Add Stock panel.
addPanel.add(newSymbolTextBox);
addPanel.add(addStockButton);
// Assemble Main panel.
mainPanel.add(stocksFlexTable);
mainPanel.add(addPanel);
mainPanel.add(lastUpdatedLabel);
// Associate the Main panel with the HTML host page.
RootPanel.get("stockList").add(mainPanel);
// Move cursor focus to the input box.
newSymbolTextBox.setFocus(true);
// Listen for mouse events on the Add button.
addStockButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
try {
addStock();
} catch (Exception e) {
e.printStackTrace();
}
}
});
// Listen for keyboard events in the input box.
newSymbolTextBox.addKeyPressHandler(new KeyPressHandler() {
public void onKeyPress(KeyPressEvent event) {
if (event.getCharCode() == KeyCodes.KEY_ENTER) {
try {
addStock();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
private void addStock() throws Exception {
final String url_s = newSymbolTextBox.getText().toUpperCase().trim();
newSymbolTextBox.setFocus(true);
newSymbolTextBox.setText("");
int row = stocksFlexTable.getRowCount();
CalaisClient client = new CalaisRestClient("ysw5rx69jkvdnzqf6sgjduqj");
System.out.print("read success...\n");
URL url = new URL(url_s);
CalaisResponse response = client.analyze(url);
for (CalaisObject entity : response.getEntities()) {
System.out.println(entity.getField("_type") + ":"
+ entity.getField("name"));
stocks.add(entity.getField("_type"));
stocksFlexTable.setText(row, 0, entity.getField("_type"));
stocksFlexTable.setText(row, 1, entity.getField("name"));
}
for (CalaisObject topic : response.getTopics()) {
System.out.println(topic.getField("categoryName"));
}
}
}
GWT only handles unchecked exceptions so you can throw Runtime Exceptions
or write your own Exception that extends from Runtime Exception then it will not cause any compile time problem
void f() throws NullPointerException // will not cause any problem because it is Runtime exception so unchecked
void f() throws IllegalAccessException // it is checked exception so there will be problem at compile time