In java, I feed very confused on observer pattern or JMS. I want to use the event notification like below. Let us forget about those JMS or Observer, do you think it is possible or doable ? if yes, how to complete it ?
newSalesOrder = new SalesOrder();
newSalesOrder.notified("new SalesOrder order ID="+orderId);
EventRegister.bindEvent(SalesOrder.class, Delivery.class);
EventRegister.bindEvent(SalesOrder.class, Warehouse.class);
////////////
Delivery delivery = new Delivery();
delivery.listerning(new Event(source){
if(source == SalesOrder.class){
}
});
//////////
Warehouse warehouse = new Warehouse();
warehouse.listerning(new Event(source){
if(source == SalesOrder.class){
}
});
///////////
EventRegister{
static bindEvent(Class source, Class destination){
//???
}
}
You need to register (bind) objects not classes. You can keep registration list static at EventRegister but I think it's better to keep them as instance at SalesOrder. So it would be:
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
SalesOrder mySalesOrder = new SalesOrder();
Warehouse myWarehouse = new Warehouse();
mySalesOrder.addListener(myWarehouse);
Delivery myDelivery = new Delivery();
mySalesOrder.addListener(myDelivery);
Event myEvent = new Event();
// Now 'myDelivery' and 'myWarehouse' objects will receive 'myEvent'
// object on their 'onEvent(Event event)' method
System.out.println("Event to be published: " + myEvent);
mySalesOrder.publishEvent(myEvent);
}
}
interface Listener {
public void onEvent(Event event);
}
class Event {
// Add reqired detail here!
}
class SalesOrder {
private List<Listener> listeners = new ArrayList<Listener>();
public void addListener(Listener listener) {
listeners.add(listener);
}
public void removeListener(Listener listener) {
listeners.remove(listener);
}
// Use proper access modifier
public void publishEvent(Event event) {
System.out.println(this + " is goint to publish " + event
+ " to " + listeners + " listeners.");
for (Listener listener : listeners) {
listener.onEvent(event);
}
}
// ...
}
class Warehouse implements Listener {
public void onEvent(Event event) {
// Do something when event received
System.out.println(event + " received at " + this);
}
// ...
}
class Delivery implements Listener {
public void onEvent(Event event) {
// Do something when event received
System.out.println(event + " received at " + this);
}
// ...
}
If you run it it will print something like:
Event to be published: Event#190d11
SalesOrder#a90653 is goint to publish Event#190d11 to [Warehouse#de6ced, Delivery#c17164] listeners.
Event#190d11 received at Warehouse#de6ced
Event#190d11 received at Delivery#c17164
This is a trivial sample, in real life use cases you may consider using a thread-safe implementation and refactoring registration out of SalesOrder using composition or inheritance.
Related
#EventHandler
public void playerInteraction(PlayerInteractEvent event)
{
Action action = event.getAction();
Player player = event.getPlayer();
Block block = event.getClickedBlock();
if (action.equals(Action.RIGHT_CLICK_BLOCK))
{
if (block.getType().equals(Material.NETHER_WART_BLOCK))
{
player.setHealth(player.getHealth() -1);
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_HURT, 10, 1);
}
else if (block.getType().equals(Material.DIAMOND_BLOCK))
{
player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 1000, 2));
player.playSound(player.getLocation(), Sound.ENTITY_SPLASH_POTION_BREAK, 10, 1);
}
else if(block.getType().equals(Material.EMERALD_BLOCK))
{
if (player.getHealth() != 20)
{
player.setHealth(player.getHealth() + 1);
player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 10, 1);;
}
if (player.getHealth() == 20)
{
player.sendMessage(ChatColor.DARK_RED + "You are already at full health!");
}
}
}
}
For some reason, all of these things happen twice whenever I right click the designated blocks. Anyone know why? I have posted the entire method, it's a player interaction event.
Thanks :)
First of all, make sure yo haven't registered the Listener class containing the event handler twice.
If that's not the case, according to this thread on the spigot forums, since Mojang added the left hand slot to Minecraft some events like PlayerInteractEvent or InventoryClickEvent will be called twice (once for each hand).
One possible fix is to "disable" the left hand on the event handler:
#EventHandler
public void onPlayerInteraction(PlayerInteractEvent event) {
if(event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getHand() == EquipmentSlot.HAND) {
//Do something once
}
}
If you require that both hands could be used to trigger the event you could do the following:
First time the code gets executed you add the player to a list.
Before executing the code you check if the player is in the list. If it's in the list it means the code was executed once so you can skip it.
Schedule a task to remove the player from the list some ticks later.
The code could be as follows:
public class Foo implements Listener {
//An instance of the main plugin class
private MainClass plugin;
private List<UUID> playerBlacklist = new ArrayList<>();
#EventHandler
public void onPlayerInteractEvent(PlayerInteractEvent event) {
if(playerBlacklist.contains(event.getPlayer().getUniqueId)) {
return;
} else {
blacklistPlayer(event.getPlayer());
}
//Do something
}
private void blacklistPlayer(UUID uuid) {
playerBlacklist.add(uuid);
BukkitRunnable runnable = new BukkitRunnable(){
#Override
public void run() {
playerBlacklist.remove(uuid);
}
}
runnable.runTaskLaterAsynchronously(plugin, 5L);
}
}
Let me know if this solved your issue.
I have a simple Java program which should listen for changes of GPIO status.
I'm using a button to change the status of a GPIO and from terminal I can see it works:
Despite this, the event listener is never triggered.
Here is the code:
public class GpioHandler
{
private static final GpioController gpioController = GpioFactory.getInstance();
public static ButtonsHandler buttons;
public GpioHandler()
{
buttons = new ButtonsHandler(gpioController, RaspiPin.GPIO_05);
buttons.listener();
}
}
public class ButtonsHandler
{
private static HashMap<String, GpioPinDigitalOutput> buttons = new HashMap<String, GpioPinDigitalOutput>();
public ButtonsHandler(GpioController gpioController, Pin... pins)
{
for(int c = 0; c < pins.length; c++)
{
Integer index = c + 1;
buttons.put(index.toString(), gpioController.provisionDigitalOutputPin(pins[c]));
}
}
public void listener()
{
for(HashMap.Entry<String, GpioPinDigitalOutput> pin : buttons.entrySet())
{
pin.getValue().addListener(new GpioPinListenerDigital() {
#Override
public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event)
{
System.out.println(" --> GPIO PIN STATE CHANGE: " + pin.getKey() + " = " + event.getState());
}
});
}
}
}
I'm using a RaspberryPi 4 and the last version of Pi4j (1.2).
Any suggestion?
Okay, apparently it was just me being stupid.
The mistake is that I was using the class GpioPinDigitalOutput instead of GpioPinDigitalInput.
I changed it and also modified this line
buttons.put(index.toString(), gpioController.provisionDigitalOutputPin(pins[c]));
into
buttons.put(index.toString(), gpioController.provisionDigitalInputPin(pins[c], PinPullResistance.PULL_DOWN));
to prevent the value from floating.
Now everything works just fine.
I'm working on a simple translation app as part of a university project. For the translation process, I query MyMemory's Translate API using Retrofit and I retrieve the translated output as a String. This is working fine for the most part, but it's causing some issues in other areas of my program.
When I make a call to retrieve a translation from the library, subsequent methods begin to run before the translation is fully recieved - this then prevents those methods from working fully since they rely on the received translation.
Here are some relevant snippets of my code that might explain the question better:
TranslateAPI: (Interface that i use to retrieve a translation)
public class TranslateAPI {
private static final String ENDPOINT = "http://api.mymemory.translated.net";
public final static String FRENCH = "FR";
public final static String ENGLISH = "EN";
public final static String ITALIAN = "IT";
public final static String GREEK = "EL";
public final static String SPANISH = "ES";
private final TranslateService mService;
String translation = "";
public interface TranslateService {
#GET("/get")
Call<TranslatedData> getTranslation(
#Query("q") String textToTranslate,
#Query(value = "langpair", encoded = true)
String languagePair);
}
public TranslateAPI() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.build();
mService = retrofit.create(TranslateService.class);
}
public String translate(final String textToTranslate, final String fromLanguage, final String toLanguage) {
mService.getTranslation(textToTranslate, URLEncoder.encode(fromLanguage + "|" + toLanguage))
.enqueue(new Callback<TranslatedData>() {
#Override
public void onResponse(Response<TranslatedData> response, Retrofit retrofit) {
String output =
String.format(response.body().responseData.translatedText);
String.format("Translation of: %s, %s->%s = %s", textToTranslate,
fromLanguage, toLanguage, response.body().responseData.translatedText);
System.out.println("Result: " + output);
translation = output;
System.out.println("The result of the field translation is: " + translation);
}
#Override
public void onFailure(Throwable t) {
System.out.println("[DEBUG]" + " RestApi onFailure - " + "");
}
});
return translation;
}
}
In the code above, the translate(final String textToTranslate, final String fromLanguage, final String toLanguage) method successfully returns the translated output as a string.
Now, to demonstrate exactly what goes wrong, assume the following code snippet for my main activity:
private void runChatBot() {
translateOutput(input, targetLanguage); //calls the translate method of the TranslateAPI class
System.out.println("translatedOutput value in MainActivity: " + translatedOutput);
//Use translated output here
}
What happens here is that the print statement in runChatbot() executes before the call to the translation API. This is not the desired behaviour, as I would like the translateOutput() method to execute fully before any following instructions.
Any help is much appreciated. Thanks in advance :)
UPDATE: Current code after initial answers
TranslateAPI - Declarations
public interface Callbacks {
void onTranslationReceived(String result);
void onTranslationFailed();
}
TranslateAPI - translate()
public void translate(final String textToTranslate, final String fromLanguage, final String toLanguage) {
mService.getTranslation(textToTranslate, URLEncoder.encode(fromLanguage + "|" + toLanguage))
.enqueue(new Callback<TranslatedData>() {
#Override
public void onResponse(Response<TranslatedData> response, Retrofit retrofit) {
String output =
String.format(response.body().responseData.translatedText);
String.format("Translation of: %s, %s->%s = %s", textToTranslate,
fromLanguage, toLanguage, response.body().responseData.translatedText);
System.out.println("Result: " + output);
translation = output;
System.out.println("The result of the field translation is: " + translation);
}
#Override
public void onFailure(Throwable t) {
System.out.println("[DEBUG]" + " RestApi onFailure - " + "");
}
});
}
MainActivity:
#Override
public void onTranslationReceived(String result) {
runChatBot();
}
#Override
public void onTranslationFailed() {
//Handle failure here
}
public void runChatBot() {
translatedOutput = translateAPI.getTranslation();
System.out.println("translatedOutput value in MainActivity: " + translatedOutput);
userOutputView.setText(translatedOutput);
ttsResponse(translatedOutput, TTSLanguage);
setVisualCue(chatBot.getVisualMatch());
chatBot.clearResults();
}
Since your translate() method is asynchronous, you should define a callback in TranslateAPI to send the result back to your Activity when the result is received. By doing this, you would then only perform work on the translation result once you know you've received a response from TranslateAPI.
So in TranslateAPI you would define a callback interface:
public interface Callbacks {
void onTranslationReceived(String result);
}
Then you would have your Activity implement TranslateAPI.Callbacks and implement the callback like this:
public void onTranslationReceived(String result) {
//do something with the result
runChatBot(result); //or something similar
}
Then, once you receive the response in the callback, you do whatever it is you have to do with the translation result. This way, you know you will never be executing anything on the translated result until the translation is complete.
EDIT IN RESPONSE TO COMMENTS
So in order to actually send the response to your Activity once the translate response is received, you need to pass a reference to your Activity into TranslateAPI. Since your Activity implements the callbacks, you can simply pass this in: TranslateAPI translateApi = new TranslateAPI(this);
Then in your TranslateAPI, you'll need to take this reference and use it as the "listener" of your callbacks. So in TranslateAPI you'll want to define a variable like this private Callbacks listener; and you'll assign this in your TranslateAPI constructor the value that's passed in from the Activity. So your TranslateAPI constructor might look like this:
public TranslateAPI(Callbacks listener) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.build();
mService = retrofit.create(TranslateService.class);
//this is the line you would add...
this.listener = listener;
}
And then in your onResponse() callback in TranslateAPI, you simply pass the value to the listener, which passes it back to the implemented method in your Activity. Like this:
#Override
public void onResponse(Response<TranslatedData> response, Retrofit retrofit) {
String output = String.format(response.body().responseData.translatedText);
String.format("Translation of: %s, %s->%s = %s", textToTranslate, fromLanguage, toLanguage, response.body().responseData.translatedText);
System.out.println("Result: " + output);
translation = output;
System.out.println("The result of the field translation is: " + translation);
//this is the line you would add...
listener.onTranslateReceived(translation);
}
Hope this helps clarify things. Let me know if you have any more questions!
this happens because code is executed asynchronous. Your retrofit network request takes some time to complete, so by default, java will execute the next line of code before it concludes. To solve this you must use the retrofit callback onResponse and onFailure.
I sugest you to create a new interface and pass it on constructor os method of your TranslateApiCode. Something like:
public interface OnTranslate {
void onSuccess(); // here you can pass any object you need later
void onError(); // here you can pass any object you need later
}
public String translate(final String textToTranslate, final String fromLanguage, final String toLanguage) {
mService.getTranslation(textToTranslate, URLEncoder.encode(fromLanguage + "|" + toLanguage))
.enqueue(new Callback<TranslatedData>() {
#Override
public void onResponse(Response<TranslatedData> response, Retrofit retrofit) {
String output =
String.format(response.body().responseData.translatedText);
String.format("Translation of: %s, %s->%s = %s", textToTranslate,
fromLanguage, toLanguage, response.body().responseData.translatedText);
System.out.println("Result: " + output);
translation = output;
System.out.println("The result of the field translation is: " + translation);
myOnTranslateVariable.onSuccess();
}
#Override
public void onFailure(Throwable t) {
System.out.println("[DEBUG]" + " RestApi onFailure - " + "");
myOnTranslateVariable.onError();
}
});
return translation;
}
private void runChatBot() {
translateOutput(input, targetLanguage, new OnTranslate() {
void onSucess() {
System.out.println("translatedOutput value in MainActivity: " + translatedOutput);
}
void onError() {
System.out.println("some error happened");
}
}); //calls the translate method of the TranslateAPI class
//Use translated output here
}
Try Using Handler to notify when translation is completed and then perform the required operations. Thanks
My ContentObserver for observing the history in the browser is not being called. I don't understand why it isn't. I'm not doing anything different or bizarre, I'm following the API specs exactly, but to no avail! Below is my code:
In my service:
public class MonitorService extends Service {
//some global variables declared here
private ContentObserver historyObserver, searchObserver, chromeObserver;
public void onCreate() {
super.onCreate();
isRunning = false;
this.preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
//this.historyObserver = new HistoryObserver();
this.historyObserver = new HistoryObserver(new Handler());
this.searchObserver = new HistoryObserver(new Handler());
this.chromeObserver = new HistoryObserver(new Handler());
getApplicationContext().getContentResolver().registerContentObserver(Uri.parse("content://com.android.chrome.browser/history"), false, this.chromeObserver);
getApplicationContext().getContentResolver().registerContentObserver(android.provider.Browser.BOOKMARKS_URI, false, this.historyObserver);
getApplicationContext().getContentResolver().registerContentObserver(android.provider.Browser.SEARCHES_URI, false, this.searchObserver);
}
//Other required methods in class
}//end of class
Then in my HistoryObserver Class we have:
public class HistoryObserver extends ContentObserver {
public final String TAG = "HistoryObserver";
public HistoryObserver(Handler handler) {
super(handler);
Log.d(TAG, "Creating new HistoryObserver");
}
public HistoryObserver() {
super(null);
Log.d(TAG, "Creating a new HistoryObserver without a Handler");
}
#Override
public boolean deliverSelfNotifications() {
Log.d(TAG, "delivering self notifications");
return true;
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.d(TAG, "onChange without uri: " + selfChange);
//onChange(selfChange, null);
}
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
Log.d(TAG, "onChange: " + selfChange + "\t " + uri.toString());
}
}
Like I said there is nothing special or unique about this implementation. Yet, when I go go to a new website or search for something in Chrome, the onChange method is never fired.
I figured out the problem. The /history content provider isn't an observable. The observables come through the /bookmark uri. Once I discovered that, things got working very quickly and very well.
Use Case: Collect lower level Motion events, correlate them to remove duplicates (e.g., a person walked around a house passes in front of one camera then another), then report the correlated Detection event.
Approach: (see pic) I initiate Motion Events from the video analytics and other sensors which are received and correlated by the AwarenessAnalytics component, which then raises a Detection Event to the Home Automation Main. It is similar to Chain-of-Responsibility pattern, though in reverse with events.
I have defined two completely separate event interfaces in separate files in the same package;
public interface MotionEventListener {
public void motionDetected(MotionEvent event);
public void motionLocation (MotionLocation location);
public void motionCeased(MotionEvent event);
public void eventVideoComplete(String eventId);
}
public interface DetectionEventListener {
public void motionIsDetected(DetectionEvent event);
public void motionAtLocation (MotionLocation location);
public void motionHasCeased(DetectionEvent event);
public void eventVideoNowComplete(String eventId);
}
I create the Motion Events in the VideoAnalytic thread;
private synchronized void fireDetectedEvent() {
Object source = new Object();
alertStartTime = getDateTime();
eventId++;
System.out.println("*** Motion Detected! ***" + alertStartTime + ", eventId = " +
eventId);
// Send alert to listener
String details ="";
MotionEvent event = new MotionEvent(source, alertActive, eventId,
Calendar.getInstance(), cameraId, Classification.unknown, details, alertStartTime);
Iterator i = listeners.iterator();
if (alertActive) {
while(i.hasNext()) {
((MotionEventListener) i.next()).motionDetected(event);
}
} else {
while(i.hasNext()) {
((MotionEventListener) i.next()).motionCeased(event);
}
resetVideoStreamEventCounter = 0;// keeps track of how many video resets occur from one
//event to another
}
}
The Motion events are successfully caught in the AwarenessAnalytic layer, where I createa new Detection Event if there is not already an ongoing event;
public void motionDetected(MotionEvent e) {
System.out.println("Motion Detected Listener activated " + e.getCameraId());
if (alertCounter == 0) {
Object source = new Object();
System.out.println("*** Motion Detected! ***" );
// Send alert to listener
alertCounter++;
alertId++;
alertActive = true;
DetectionEvent event = new DetectionEvent(
source,
alertActive,
alertId,
e.getEventDateTime(),
e.getCameraId(),
e.getEventType(),
e.getKnownDetails(),
e.getEventSubdirectory());
Iterator i = listeners.iterator();
if (alertActive) {
while(i.hasNext()) {
((DetectionEventListener) i.next()).motionDetected(event);
}
} else {
alertCounter++;
}
}
System.out.println("Motion Detected event received by AA from " + e.getCameraId());
}
Design pictorial:
Problem:
I've tried to catch the events in Home Automation Main as follows;
AwarenessAnalytics awarenessAnalytic = new AwarenessAnalytics();
// establish the listener set
awarenessAnalytic.addListener(this);
However, this results in a compilation error "Cannot use this in a static context"
Do I need a separate listener class for this? Or something else?
#Kon provided the clues needed to solve this problem (he is the one who deserves the credit). I created a separated DetectionListener class that implemented DetectionEventListener;
public class DetectionListener implements DetectionEventListener {
public DetectionListener() {
super();
}
public void motionIsDetected(DetectionEvent e) {
System.out.println("Motion Detected Awareness Listener test driver activated " +
e.getCameraId());
}
public void motionAtLocation (MotionLocation e) {
System.out.println("Test driver Motion location = " + e.getX() + ", " + e.getY());
}
public void motionHasCeased(DetectionEvent e) {
System.out.println("Motion Ceased Listener test driver activated from " +
e.getCameraId());
}
public void eventVideoNowComplete (String eventId) {
System.out.println("Event Video test driver activated");
}
}
And then in the Home Automation Main set up the AwarenessAnalytics instance, the DetectionListener instance, and add it to the AwarenessAnalytics instance;
AwarenessAnalytics awarenessAnalytic = new AwarenessAnalytics();
// establish the listener set
DetectionEventListener Del = new DetectionListener();
awarenessAnalytic.addListener(Del);
Now I need to call the main from the DetectionListener to complete the circle and take action on the events.