So I really need some helpful hints with an issue I am having with my GWTP application, perhaps one of you have ran into this problem before. So I am using GWTP 1.1 with:
com.gwtplatform.mvp.Mvp (Not w/ entry point)
com.gwtplatform.dispatch.Dispatch with
com.google.gwt.uibinder.UiBinder
The issue I am having is that when ever I have a client sided error (can be almost anywhere in my client side, not sure where the boundary is) I receive a very cryptic message to do with GIN rather than a message that will help me resolve the issue. Seems like something to do with GWTP Proxying, here is the typical message that I get: http://pastebin.com/YgxPbkru the real issue isn't what the error message is presenting to me. The OpenIcidentPresenter extends IncidentPresenter which is a Presenter I have made to manage lifecycles of an Incident (in this case) this extends another presenter that I have made called a RequestPresenter, this is a Presenter that allows the user to Request PresenterWidget classes into it (into any given slot for that presenters lifecycle), this extends another presenter called RichPresenter, which just has things that almost all my Presenters require like page load indication and certain page locking etc, etc. Here are these classes:
OpenIncidentPresenter
public class OpenIncidentPresenter extends IncidentPresenter<OpenIncidentPresenter.MyView,
OpenIncidentPresenter.MyProxy> implements ViewUiHandlers, HasRequestedWidgets, NewIncidentHandler,
ChangeSectionHandler, ConfigureHandler {
public interface MyView extends View, HasUiHandlers<ViewUiHandlers> {
... Snip ...
}
#ProxyCodeSplit
#NameToken(NameTokens.open)
//#UseGatekeeper(LoginGatekeeper.class)
public interface MyProxy extends ProxyPlace<OpenIncidentPresenter> {
}
private static Logger logger = Logger.getLogger(OpenIncidentPresenter.class.getName());
Process process;
SectionTuple currentSection = new SectionTuple();
Map<Integer, SectionTuple> sections = new HashMap<Integer, SectionTuple>();
List<AccordionSection> sectionWigets = new ArrayList<AccordionSection>();
List<Activity> cachedActivities = new ArrayList<Activity>();
List<Authority> cachedAuthorities = new ArrayList<Authority>();
List<Severity> cachedSeverities = new ArrayList<Severity>();
List<Location> cachedLocations = new ArrayList<Location>();
List<Site> cachedSites = new ArrayList<Site>();
List<Area> cachedAreas = new ArrayList<Area>();
Severity currentSeverity;
boolean configured = false;
boolean changedConsequences = false;
final ImageResources imageResources;
RegisteredRequestWidget<ActionBarPresenterWidget> actionBarReg;
RegisteredRequestWidget<ProgressBarPresenterWidget> progressBarReg;
#Inject
public OpenIncidentPresenter(final EventBus eventBus, final MyView view,
final MyProxy proxy, final DispatchAsync dispatch,
final PlaceManager placeManager, ImageResources imageResources) {
super(eventBus, view, proxy, dispatch, placeManager);
this.imageResources = imageResources;
getView().setUiHandlers(this);
logger.log(Level.INFO, "Constructed OpenIncidentPresenter");
}
#Override
protected void revealInParent() {
RevealContentEvent.fire(this, ApplicationPresenter.SLOT_MIDDLE, this);
}
... Snip ...
}
IncidentPresenter
public abstract class IncidentPresenter<T extends View,
H extends Proxy<?>> extends RequestPresenter<T, H> implements HasIncident {
Logger logger = Logger.getLogger(IncidentPresenter.class.getName());
public static final String INCIDENT_COOKIE = "incidentId";
public interface LoadCallback {
void onFinished();
}
protected Incident incident;
protected DispatchAsync dispatch;
private boolean loaded = false;
public IncidentPresenter(EventBus eventBus, T view, H proxy,
DispatchAsync dispatch, PlaceManager placeManager) {
super(eventBus, view, proxy, placeManager, true);
this.dispatch = dispatch;
}
public abstract void onFailureToLoadIncident(Integer incidentId);
public abstract void onLoadedIncident(Incident incident);
#Override
public void loadIncident(final Integer id) {
loadIncident(id, null);
}
#Override
public void loadIncident(final Integer id, final LoadCallback callback) {
.. Snip ..
}
/**
* Process the incident dependencies
*/
protected void loadDependencies(final Incident incident, final LoadCallback callback) {
.. Snip ..
}
#Override
public Incident getIncident() {
return incident;
}
#Override
public void setIncident(Incident incident) {
this.incident = incident;
if(hasIncident()) {
String incidentCookie = Cookies.getCookie("incidentId");
if(incidentCookie == null || !incidentCookie.equals(String.valueOf(
incident.getId()))) {
// Set the cookie to the new incident id
Cookies.setCookie("incidentId", String.valueOf(incident.getId()));
logger.log(Level.INFO, "Set incident " + String.valueOf(incident.getId())
+ " to the cookie session");
}
} else {
Cookies.removeCookie("incidentId");
logger.log(Level.INFO, "Set incident to null incident object, cleared " +
"the cookie session");
}
}
#Override
public boolean isIncidentLoaded() {
return hasIncident() && loaded;
}
#Override
public boolean hasIncident() {
return IncidentUtils.isValid(incident);
}
#Override
public void resetIncident() {
setIncident(null);
loaded = false;
}
#Override
public DispatchAsync getDispatch() {
return dispatch;
}
public String getIncidentCookie() {
return Cookies.getCookie(INCIDENT_COOKIE);
}
}
RequestPresenter
public abstract class RequestPresenter<T extends View, H extends Proxy<?>>
extends RichPresenter<T, H> implements HasRequestedWidgets {
RequestWidgetManager requestManager = new RequestWidgetManager(this);
public RequestPresenter(EventBus eventBus, T view, H proxy,
PlaceManager placeManager) {
this(eventBus, view, proxy, placeManager, false);
}
public RequestPresenter(EventBus eventBus, T view, H proxy,
PlaceManager placeManager, boolean leaveConfirmation) {
super(eventBus, view, proxy, placeManager, leaveConfirmation);
}
#Override
public void prepareFromRequest(PlaceRequest request) {
prepareFromRequest(request, null);
}
/**
* Alternative to {#link RequestPresenter#prepareFromRequest(PlaceRequest)} that
* will allow you to register the {#link FinalCallback} in case you have dependency
* on the request widgets.
* #param request
* #param callback
*/
public void prepareFromRequest(PlaceRequest request, FinalCallback callback) {
super.prepareFromRequest(request);
executeAfterRequesting(callback);
requestWidgets();
}
#Override
protected void onBind() {
super.onBind();
registerRequestWidgets();
}
#Override
protected void onUnbind() {
super.onUnbind();
unregisterRequestWidgets();
}
#Override
protected void onHide() {
super.onHide();
dismissWidgets();
}
#Override
protected void onReveal() {
super.onReveal();
requestWidgets();
}
private void requestWidgets() {
requestManager.requestAll();
onRequestWidgets();
}
private void dismissWidgets() {
requestManager.dismissAll();
onDismissWidgets();
}
public void unregisterRequestWidgets() {
requestManager.unregisterAllWidgets();
}
#Override
public abstract void registerRequestWidgets();
public void onRequestWidgets() {
// Do nothing by default
}
public void onDismissWidgets() {
// Do nothing by default
}
protected RequestWidgetManager getRequestManager() {
return requestManager;
}
/**
* This will execute the callback method when the final request is made on loading.<br>
* This must be set before super.onReveal or super.prepareFromRequest are called.
* Or use {#link RequestPresenter#prepareFromRequest(PlaceRequest, FinalCallback)} to
* set the final callback.
* #param callback
*/
public void executeAfterRequesting(FinalCallback callback) {
requestManager.setFinalCallback(callback);
}
public <P extends PresenterWidget<?>> RequestedWidget<P> getRequestedWidget(
RegisteredRequestWidget<P> registry) {
return requestManager.get(registry);
}
public <P extends PresenterWidget<?>> RegisteredRequestWidget<P> registerRequestWidget(
HasHandlers handler, Class<P> clazz, Object slot, boolean clearSlot) {
return registerRequestWidget(handler, clazz, slot, clearSlot, null);
}
public <P extends PresenterWidget<?>> RegisteredRequestWidget<P> registerRequestWidget(
HasHandlers handler, Class<P> clazz, Object slot, boolean clearSlot,
RequestWidgetEvent.Callback<P> callback) {
return requestManager.registerWidget(handler, clazz, slot, clearSlot, callback);
}
}
RichPresenter
public abstract class RichPresenter<T extends View,
H extends Proxy<?>> extends Presenter<T, H> {
protected final PlaceManager placeManager;
private boolean leaveConfirmation;
private String defaultLeaveMessage = "Any unsaved work will be lost when " +
"leaving this page, are you sure you would like to leave?";
public RichPresenter(EventBus eventBus, T view, H proxy,
PlaceManager placeManager) {
this(eventBus, view, proxy, placeManager, false);
}
public RichPresenter(EventBus eventBus, T view, H proxy,
PlaceManager placeManager, boolean leaveConfirmation) {
super(eventBus, view, proxy);
this.placeManager = placeManager;
this.leaveConfirmation = leaveConfirmation;
}
/**
* Setup component control handlers for the UI
*/
protected void setupHandlers(final T view) {
// Do nothing by default
}
#Override
public void prepareFromRequest(PlaceRequest request) {
super.prepareFromRequest(request);
// Start Load Indicator
LoadingIndicatorEvent.fire(this, true);
// Attempt to set leave confirmation
setLeaveConfirmation(leaveConfirmation);
}
#Override
protected void onBind() {
super.onBind();
setupHandlers(getView());
}
#Override
protected void onUnbind() {
super.onUnbind();
// Remove all the event handlers
for(HandlerRegistration reg : handlerRegistrations) {
reg.removeHandler();
}
handlerRegistrations.clear();
}
#Override
protected void onReveal() {
super.onReveal();
// Stop Load Indicator
LoadingIndicatorEvent.fire(this, true);
// Attempt to set leave confirmation
setLeaveConfirmation(leaveConfirmation);
}
#Override
protected void onReset() {
super.onReset();
// Stop Load Indicator
LoadingIndicatorEvent.fire(this, false);
}
/**
* Set the page leave confirmation.
* #param leaveConfirmation
*/
public void setLeaveConfirmation(boolean leaveConfirmation) {
this.leaveConfirmation = leaveConfirmation;
if(leaveConfirmation && !BrowserUtils.isIEBrowser()) {
placeManager.setOnLeaveConfirmation(defaultLeaveMessage);
} else {
placeManager.setOnLeaveConfirmation(null);
}
}
public boolean isConfirmOnLeave() {
return leaveConfirmation;
}
public String getDefaultLeaveMessage() {
return defaultLeaveMessage;
}
public void setDefaultLeaveMessage(String message) {
this.defaultLeaveMessage = message;
}
}
I feel like this could be a contributing factor here. I have a large chain of presenters that I could be implementing wrong.
This is making it crazy hard for me to identify issues in my client side code. I have to go through my changes, reverting them until I no longer get this message. Which is just ridiculous. If you can see that I am doing something wrong or need more information let me know please! Would be so greatly appreciated.
Cheers! Ben
Related
I try to make sample login page with two fields (username, password) and save button with android architecture component, using android data binding, validating the data in viewmodel and from view model I make call to repository for remote server call as mentioned in official doc, remote server return me userid with success so how can I start new fragment from view model using this success? I learn something about singleLiveEvent and EventObserver, but I'm not able to find there clear usage example:
LoginViewModel
private MutableLiveData<String> snackbarStringSingleLiveEvent= new MutableLiveData<>();
#Inject
public LoginViewModel(#NonNull AppDatabase appDatabase,
#NonNull JobPortalApplication application,
#NonNull MyApiEndpointInterface myApiEndpointInterface) {
super(application);
loginRepository = new LoginRepository(application, appDatabase, myApiEndpointInterface);
snackbarStringSingleLiveEvent = loginRepository.getLogin(username.get(), password.get(), type.get());
}
public MutableLiveData<String> getSnackbarStringSingleLiveEvent() {
return snackbarStringSingleLiveEvent;
}
Repository
public SingleLiveEvent<String> getLogin(String name, String password, String type) {
SingleLiveEvent<String> mutableLiveData = new SingleLiveEvent<>();
apiEndpointInterface.getlogin(name, password, type).enqueue(new Callback<GenericResponse>() {
#Override
public void onResponse(Call<GenericResponse> call, Response<GenericResponse> response) {
mutableLiveData.setValue(response.body().getMessage());
}
#Override
public void onFailure(Call<GenericResponse> responseCall, Throwable t) {
mutableLiveData.setValue(Constant.FAILED);
}
});
return mutableLiveData;
}
Login Fragment
private void observeViewModel(final LoginViewModel viewModel) {
// Observe project data
viewModel.getSnackbarStringSingleLiveEvent().observe(this, new Observer<String>() {
#Override
public void onChanged(String s) {
}
});
}
How can I use EventObserver in above case? Any practical example?
Check out below example about how you can create single LiveEvent to observe only one time as LiveData :
Create a class called Event as below that will provide our data once and acts as child of LiveData wrapper :
public class Event<T> {
private boolean hasBeenHandled = false;
private T content;
public Event(T content) {
this.content = content;
}
public T getContentIfNotHandled() {
if (hasBeenHandled) {
return null;
} else {
hasBeenHandled = true;
return content;
}
}
public boolean isHandled() {
return hasBeenHandled;
}
}
Then declare this EventObserver class like below so that we don't end up placing condition for checking about Event handled every time, everywhere :
public class EventObserver<T> implements Observer<Event<T>> {
private OnEventChanged onEventChanged;
public EventObserver(OnEventChanged onEventChanged) {
this.onEventChanged = onEventChanged;
}
#Override
public void onChanged(#Nullable Event<T> tEvent) {
if (tEvent != null && tEvent.getContentIfNotHandled() != null && onEventChanged != null)
onEventChanged.onUnhandledContent(tEvent.getContentIfNotHandled());
}
interface OnEventChanged<T> {
void onUnhandledContent(T data);
}
}
And How you can implement it :
MutableLiveData<Event<String>> data = new MutableLiveData<>();
// And observe like below
data.observe(lifecycleOwner, new EventObserver<String>(data -> {
// your unhandled data would be here for one time.
}));
// And this is how you add data as event to LiveData
data.setValue(new Event(""));
Refer here for details.
Edit for O.P.:
Yes, data.setValue(new Event("")); is meant for repository when you've got response from API (Remember to return same LiveData type you've taken in VM instead of SingleLiveEvent class though).
So, let's say you've created LiveData in ViewModel like below :
private MutableLiveData<Event<String>> snackbarStringSingleLiveEvent= new MutableLiveData<>();
You provide value to this livedata as Single Event from repository like below :
#Override
public void onResponse(Call<GenericResponse> call, Response<GenericResponse> response) {
mutableLiveData.setValue(new Event(response.body().getMessage())); // we set it as Event wrapper class.
}
And observe it on UI (Fragment) like below :
viewModel.getSnackbarStringSingleLiveEvent().observe(this, new EventObserver<String>(data -> {
// your unhandled data would be here for one time.
}));
Event.java
public class Event<T> {
private T content;
private boolean hasBeenHandled = false;
public Event(T content) {
this.content = content;
}
/**
* Returns the content and prevents its use again.
*/
public T getContentIfNotHandled() {
if (hasBeenHandled) {
return null;
} else {
hasBeenHandled = true;
return content;
}
}
/**
* Returns the content, even if it's already been handled.
*/
public T peekContent() {
return content;
}
}
EventObserver.java
public class EventObserver<T> implements Observer<Event<? extends T>> {
public interface EventUnhandledContent<T> {
void onEventUnhandledContent(T t);
}
private EventUnhandledContent<T> content;
public EventObserver(EventUnhandledContent<T> content) {
this.content = content;
}
#Override
public void onChanged(Event<? extends T> event) {
if (event != null) {
T result = event.getContentIfNotHandled();
if (result != null && content != null) {
content.onEventUnhandledContent(result);
}
}
}
}
Example, In ViewModel Class
public class LoginViewModel extends BaseViewModel {
private MutableLiveData<Event<Boolean>> _isProgressEnabled = new MutableLiveData<>();
LiveData<Event<Boolean>> isProgressEnabled = _isProgressEnabled;
private AppService appService;
private SchedulerProvider schedulerProvider;
private SharedPreferences preferences;
#Inject
LoginViewModel(
AppService appService,
SchedulerProvider schedulerProvider,
SharedPreferences preferences
) {
this.appService = appService;
this.schedulerProvider = schedulerProvider;
this.preferences = preferences;
}
public void login(){
appService.login("username", "password")
.subscribeOn(schedulerProvider.executorIo())
.observeOn(schedulerProvider.ui())
.subscribe(_userLoginDetails::setValue,
_userLoginDetailsError::setValue,
() -> _isProgressEnabled.setValue(new Event<>(false)),
d -> _isProgressEnabled.setValue(new Event<>(true))
)
}
}
In Login Fragment,
viewModel.isProgressEnabled.observe(this, new EventObserver<>(hasEnabled -> {
if (hasEnabled) {
// showProgress
} else {
// hideProgress
}
}));
Using Event and EventObserver class we can achieve the same like SingleLiveEvent class but if you are thinking a lot of boilerplate code just avoid this method. I hope it would help you and give some idea about why we are using SingleEvent in LiveData.
I understand that Google gives the guidelines to use LiveData between the ViewModel and UI but there are edge cases where using LiveData as a SingleLiveEvent is like reinventing the wheel. For single time messaging between the view model and user interface we can use the delegate design pattern. When initializing the view model in the activity we just have to set the activity as the implementer of the interface. Then throughout our view model we can call the delegate method.
Interface
public interface Snackable:
void showSnackbarMessage(String message);
UI
public class MyActivity extends AppCompatActivity implements Snackable {
private MyViewModel myViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_layout);
this.myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
this.myViewModel.setListener(this);
}
#Override
public void showSnackbarMessage(String message) {
Toast.makeText(this, "message", Toast.LENGTH_LONG).show();
}
}
View Model
public class MyViewModel extends AndroidViewModel {
private Snackable listener;
public MyViewModel(#NonNull Application application) {
super(application);
}
public void setListener(MyActivity activity){
this.listener = activity;
}
private void sendSnackbarMessage(String message){
if(listener != null){
listener.showSnackbarMessage(message);
}
}
private void anyFunctionInTheViewModel(){
sendSnackbarMessage("Hey I've got a message for the UI!");
}
}
I'm creating an user event system using JDK 9 Flow API, so I have a room (which extends the UserSubscriver class above), it may have many users and each user can offer (dispatch) updates at any time.
public abstract class UserSubscriver implements Flow.Subscriber<Notification> {
private Flow.Subscription subscription;
#Override
public void onSubscribe(final Flow.Subscription subscription) {
this.subscription = subscription;
subscription.request(1);
}
#Override
public void onError(final Throwable throwable) {
// ...
}
#Override
public void onNext(final Notification notification) {
// ...
subscription.request(1);
}
#Override
public void onComplete() {
// How can I know who was the publisher of this?
}
}
User class:
public class User extends SubmissionPublisher<Notification> {
....
public int offer(Notification item) {
return super.offer(item, (sub, msg) -> false);
}
}
On the onUpdate I can receive any args, so I can receive the publisher of the update, but there are no args on onComplete.
How can I know who was the publisher of an onComplete event?
I use Grizzly to setup websocket server in java. My problem is that I can't set broadcaster to websocket to use optimized one by overriding WebSocketApplication. I followed the way the documentation describe. But I got that error.
The method setBroadcaster(Broadcaster) from the type SimpleWebSocket
is not visible.
Here is my code
public class BroadcastApplication extends WebSocketApplication {
private final Broadcaster broadcaster;
public BroadcastApplication(Broadcaster broadcaster) {
this.broadcaster = broadcaster;
}
#Override public WebSocket createSocket(ProtocolHandler handler,
HttpRequestPacket requestPacket, WebSocketListener... listeners) {
final DefaultWebSocket ws = (DefaultWebSocket) super.createSocket(handler, requestPacket, listeners);
ws.setBroadcaster(broadcaster); // Got error here
return ws;
}
#Override public void onMessage(WebSocket socket, String data) {
socket.broadcast(getWebSockets(), data);
}
}
My grizzly-websockets version is 2.3.22. Any suggestion is welcome.
Something like this should work:
public class BroadcastApplication extends WebSocketApplication {
private final Broadcaster broadcaster;
public BroadcastApplication(Broadcaster broadcaster) {
this.broadcaster = broadcaster;
}
#Override
public WebSocket createSocket(ProtocolHandler handler,
HttpRequestPacket requestPacket, WebSocketListener... listeners) {
return new BroadcastWebSocket(broadcaster, handler, requestPacket, listeners);
}
#Override
public void onMessage(WebSocket socket, String data) {
socket.broadcast(getWebSockets(), data);
}
private static class BroadcastWebSocket extends DefaultWebSocket {
public BroadcastWebSocket(Broadcaster broadcaster,
ProtocolHandler protocolHandler,
HttpRequestPacket request,
WebSocketListener... listeners) {
super(protocolHandler, request, listeners);
this.broadcaster = broadcaster;
}
}
}
I have some methods in a class like this:
#Override
public void sendRemoteRecord(String token, int channelId, int eventId, final ServiceCallback<RemoteRecordResponse> callback) {
epgServicesApiManager.sendRemoteRecord(token, channelId, eventId)
.observeOn(scheduler)
.subscribe(new Action1<RemoteRecordResponse>() {
#Override
public void call(RemoteRecordResponse model) {
if (callback != null)
callback.onSuccess(model);
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
if (callback != null)
callback.onError();
}
});
}
#Override
public void activateRemoteRecord(String token, String cardNumber, final ServiceCallback<RemoteRecordActivateResponse> callback) {
epgServicesApiManager.activateRemoteRecord(token, cardNumber)
.observeOn(scheduler)
.subscribe(new Action1<RemoteRecordActivateResponse>() {
#Override
public void call(RemoteRecordActivateResponse remoteRecordActivateResponse) {
if (callback != null)
callback.onSuccess(remoteRecordActivateResponse);
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
if (callback != null)
callback.onError();
}
});
}
Is it possible to remove the duplication around the code after the observeOn() line?
The annoying part is making sure I do the null check on the callback before using it.
At present, I know of seven distinct methods I need in this class and possibly more.
Unfortunately, in Java 1.7 there is no way to fix this without increasing the amount of code. You can reduce the amount of code needed locally, by introducing some helper classes.
One solution is to move your anonymous inner classes to top-level classes. From there you can introduce a dummy callback and some null-checking work an an abstract class.
It may end up looking something like this (horizontal rules are used to highlight that these classes are in separate files).
This is a dummy callback class, it does exactly nothing, but is safe to call against. This will replace the null values.
public class NullServiceCallBack<T> implements ServiceCallBack<T> {
#Override
public void onSuccess(T target) {}
#Override
public void onError() {}
}
This is an abstract class that handles the validation, converting null values to instances of NullServiceCallback:
public abstract class CallBackAction<T> implements Action1<T> {
private final ServiceCallBack<T> Callback;
public CallBackAction(ServiceCallBack<T> callback) {
this.Callback = (null != callback) ? callback : new NullServiceCallBack<>();
}
protected ServiceCallBack<T> getCallback() {
return Callback;
}
}
This is the concrete class you'll use for success.
public class SuccessCallbackAction<T> extends CallBackAction<T> {
public SuccessCallbackAction(ServiceCallBack<T> callback) {
super(callback);
}
#Override
public void call(T target) {
getCallback().onSuccess(target);
}
}
This is the concrete class for errors. This doesn't do anything with the arguments to call, so we can make this implement for Object once and be done with it.
public class ErrorCallbackAction extends CallBackAction<Object> {
public ErrorCallbackAction(ServiceCallBack<Object> callback) {
super(callback);
}
#Override
public void call(Throwable target) {
getCallback().onError();
}
}
So in the end, your example above should look something like this:
#Override
public void sendRemoteRecord(String token, int channelId, int eventId, final ServiceCallback<RemoteRecordResponse> callback) {
epgServicesApiManager.sendRemoteRecord(token, channelId, eventId)
.observeOn(scheduler)
.subscribe(new SuccessCallbackAction<RemoteRecordResponse>(callback),
new ErrorCallbackAction(callback));
}
#Override
public void activateRemoteRecord(String token, String cardNumber, final ServiceCallback<RemoteRecordActivateResponse> callback) {
epgServicesApiManager.activateRemoteRecord(token, cardNumber)
.observeOn(scheduler)
.subscribe(new SuccessCallbackAction<RemoteRecordActivateResponse>(callback),
new ErrorCallbackAction(callback));
}
Locally, we've reduced the amount of code, and made the intent a little more clear. Globally, we've increased the complexity with the addition of 4 new classes. Whether this is worth it depends on the context your code lives in, and is your call.
Introduce a dummy callback that does nothing, then do safeCallback().onSuccess() or safeCallback().onError()
Also, you can do this:
class SuccessCallback<T> extends Action1<T>() {
#Override
public void call(T value) {
safeCallback().onSuccess(value);
}
}
class ErrorCallback extends Action1<Throwable>() {
#Override
public void call(T value) {
safeCallback().onError();
}
}
then...
subscribe(new SuccessCallback<RemoteRecordActivateResponse>(), new ErrorCallback());
Does this work?
Is there any way to detect change in an integer? Such as creating a listener to listen to the integer to detect and change in value it has. I know this is possible with booleans with a few tricks but I cannot seem to adapt this to an int value. Does anyone have any idea how this could be done? I need to know how to do this in the Java language. Below is code that I found online that allows for a boolean listener. How can I convert this to an integer listener?
import java.util.List;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.EventObject;
import java.awt.EventQueue;
//can u see this austin? can u see this i typed this at 9:33 my time
/**
* This class uses the EventQueue to process its events, but you should only
* really do this if the changes you make have an impact on part of a GUI
* eg. adding a button to a JFrame.
*
* Otherwise, you should create your own event dispatch thread that can handle
* change events
*/
public class test1 implements BooleanChangeDispatcher {
public static void main(String[] args) {
BooleanChangeListener listener = new BooleanChangeListener() { // add this to the class
#Override
public void stateChanged(BooleanChangeEvent event) {
System.out.println("Detected change to: "
+ event.getDispatcher().getFlag()
+ " -- event: " + event);
}
};
test1 test = new test1(false);
test.addBooleanChangeListener(listener);
// test.setFlag(false); // no change, no event dispatch
// test.setFlag(true); // changed to true -- event dispatched
}
private boolean flag;
private List<BooleanChangeListener> listeners;
public test1(boolean initialFlagState) {
flag = initialFlagState;
listeners = new ArrayList<BooleanChangeListener>();
}
#Override
public void addBooleanChangeListener(BooleanChangeListener listener) {
listeners.add(listener);
}
#Override
public void setFlag(boolean flag) {
if (this.flag != flag) {
this.flag = flag;
dispatchEvent();
}
}
#Override
public boolean getFlag() {
return flag;
}
private void dispatchEvent() {
final BooleanChangeEvent event = new BooleanChangeEvent(this);
for (BooleanChangeListener l : listeners) {
dispatchRunnableOnEventQueue(l, event);
}
}
private void dispatchRunnableOnEventQueue(
final BooleanChangeListener listener,
final BooleanChangeEvent event) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
listener.stateChanged(event);
}
});
}
}
interface BooleanChangeDispatcher {
public void addBooleanChangeListener(BooleanChangeListener listener);
public boolean getFlag();
public void setFlag(boolean flag);
}
/**
* Listener interface for classes interested in knowing about a boolean
* flag change.
*/
interface BooleanChangeListener extends EventListener {
public void stateChanged(BooleanChangeEvent event);
}
/**
* This class lets the listener know when the change occured and what
* object was changed.
*/
class BooleanChangeEvent extends EventObject {
private final BooleanChangeDispatcher dispatcher;
public BooleanChangeEvent(BooleanChangeDispatcher dispatcher) {
super(dispatcher);
this.dispatcher = dispatcher;
}
// type safe way to get source (as opposed to getSource of EventObject
public BooleanChangeDispatcher getDispatcher() {
return dispatcher;
}
}
I would create a class capable of registering listeners. Below is a mocked up example. It might even compile as is (assuming you write the corresponding VocalIntegerListener interface exists and is implemented somehow... it's pretty simple).
class VocalInteger {
private int value;
private final Object lock = new Object();
Set<VocalIntegerListener> listeners; // assume interface exists - it's easy
public VocalInteger() {
this(0);
}
public VocalInteger(int value) {
this.value = value;
listeners = new HashSet<VocalIntegerListener>();
}
public int getValue() {
return value;
}
public void setValue(int value) {
synchronized(lock) {
int oldValue = this.value;
this.value = value;
for(VocalIntegerListener listener : listeners) {
listener.fireChangedEvent(oldvalue, value); // assume exists
}
}
}
public void registerListener(VocalIntegerListener listener) {
synchronized(lock) {
listeners.add(listener);
}
}
}
Have a look at "Java Beans" and "bound properties" for the standard approach how to listen for property changed events:
http://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html
http://docs.oracle.com/javase/tutorial/javabeans/