I know that EventBus is deprecated in Reactor3.x, and the suggested solution is ReplayProcessor. I have read https://github.com/reactor/reactor-core/issues/375. But the code here is too draft. I created a demo project to prove the idea here. Can someone give some comments?
======== Application.java
package hello;
import org.reactivestreams.Subscription;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Flux;
import reactor.core.publisher.ReplayProcessor;
import reactor.core.publisher.BaseSubscriber;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application implements CommandLineRunner {
private static final int NUMBER_OF_QUOTES = 10;
#Bean
ReplayProcessor createReplayProcessor() {
ReplayProcessor<MyEvent> rp = ReplayProcessor.create();
Flux<MyEvent> interest1 = rp.filter(ev -> filterInterest1(ev));
Flux<MyEvent> interest2 = rp.filter(ev -> filterInterest2(ev));
interest1.subscribe(new BaseSubscriber<MyEvent>() {
#Override
protected void hookOnSubscribe(Subscription subscription) {
requestUnbounded();
}
#Override
protected void hookOnNext(MyEvent value) {
//todo: call service method
System.out.println("event 1 handler -> event name:" + value.getEventName());
}
});
interest2.subscribe(new BaseSubscriber<MyEvent>() {
#Override
protected void hookOnSubscribe(Subscription subscription) {
requestUnbounded();
}
#Override
protected void hookOnNext(MyEvent value) {
//todo: call service method
System.out.println("event2 handler -> event name:" + value.getEventName());
}
});
return rp;
}
public boolean filterInterest1(MyEvent myEvent) {
if (myEvent != null && myEvent.getEventName() != null
&& myEvent.getEventName().equalsIgnoreCase("event1")) {
return true;
}
return false;
}
public boolean filterInterest2(MyEvent myEvent) {
if (myEvent != null && myEvent.getEventName() != null
&& myEvent.getEventName().equalsIgnoreCase("event2")) {
return true;
}
return false;
}
#Autowired
private Publisher publisher;
#Bean
public CountDownLatch latch() {
return new CountDownLatch(NUMBER_OF_QUOTES);
}
#Override
public void run(String... args) throws Exception {
publisher.publishQuotes(NUMBER_OF_QUOTES);
}
public static void main(String[] args) throws InterruptedException {
ApplicationContext app = SpringApplication.run(Application.class, args);
app.getBean(CountDownLatch.class).await(10, TimeUnit.SECONDS);
}
}
==========MyEvent.java=============
package hello;
public class MyEvent {
private String eventName = "";
public String getEventName() {
return eventName;
}
public void setEventName(String eventName) {
this.eventName = eventName;
}
public MyEvent(String eventName) {
this.eventName = eventName;
}
public void filterInterest1(MyEvent myEvent) {
}
}
=============Publisher.java ===========
package hello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.ReplayProcessor;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
#Service
public class Publisher {
#Autowired
ReplayProcessor rp;
#Autowired
CountDownLatch latch;
public void publishQuotes(int numberOfQuotes) throws InterruptedException {
long start = System.currentTimeMillis();
rp.onNext(new MyEvent("event1"));
rp.onNext(new MyEvent("event2"));
rp.onNext(new MyEvent("event3"));
long elapsed = System.currentTimeMillis() - start;
System.out.println("Elapsed time: " + elapsed + "ms");
System.out.println("Average time per quote: " + elapsed / numberOfQuotes + "ms");
}
}
The whole code is https://github.com/yigubigu/reactor-start-sample.git
IMHO you can relay in Spring event handlers. Matt Raible and Josh Long use that in this couple of tutorials:
https://developer.okta.com/blog/2018/09/24/reactive-apis-with-spring-webflux
https://developer.okta.com/blog/2018/09/25/spring-webflux-websockets-react
Key takeaways:
#Component
class ProfileCreatedEventPublisher implements
ApplicationListener<ProfileCreatedEvent>,
Consumer<FluxSink<ProfileCreatedEvent>>
Uses an event loop to take events from a LinkedBlockingQueue.
#Override
public void onApplicationEvent(ProfileCreatedEvent event)
Queue the events that can be published anywhere within your app.
ProfileCreatedEventPublisher is used in ServerSentEventController to create a Flux of events (that can be chained with a filter), it transforms and sends them to a web client.
Related
When I create a custom app for SCDF I can, according to the reference define relevant properties that are visible through the dashboard when creating a new stream/task. I created a spring-configuration-metadata-whitelist.properties file like this:
configuration-properties.classes=com.example.MySourceProperties
configuration-properties.names=my.prop1,my.prop2
When I create a new stream definition through the dashboard all properties defined in com.example.MySourceProperties are displayed in the properties dialog, but my.prop1 and my.prop2 are not. Both properties aren't optional and must always be set by the user. How can I include them in the properties dialog?
This tells it which class to pull these properties from Task1 properties class
That we can use with "#EnableConfigurationProperties(Task1Properties.class) declaration
configuration-properties.classes=com.shifthunter.tasks.Task1Properties
Task1Properties.java
package com.shifthunter.tasks;
import org.springframework.boot.context.properties.ConfigurationProperties;
#ConfigurationProperties("pulldata-task")
public class Task1Properties {
/**
* The path to get the source doc from
*/
private String sourceFilePath;
/**
* The path to put the destination doc
*/
private String destinationFilePath;
/**
* Property to drive the exit code
*/
private String controlMessage;
public String getSourceFilePath() {
return sourceFilePath;
}
public void setSourceFilePath(String sourceFilePath) {
this.sourceFilePath = sourceFilePath;
}
public String getDestinationFilePath() {
return destinationFilePath;
}
public void setDestinationFilePath(String destinationFilePath) {
this.destinationFilePath = destinationFilePath;
}
public String getControlMessage() {
return controlMessage;
}
public void setControlMessage(String controlMessage) {
this.controlMessage = controlMessage;
}
}
ShiftHunterTaskPullDataApp.java
package com.shifthunter.tasks;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.task.configuration.EnableTask;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#EnableTask
#EnableConfigurationProperties(Task1Properties.class)
#SpringBootApplication
public class ShiftHunterTaskPullDataApp {
public static void main(String[] args) {
SpringApplication.run(ShiftHunterTaskPullDataApp.class, args);
}
#Bean
public Task1 task1() {
return new Task1();
}
public class Task1 implements CommandLineRunner {
#Autowired
private Task1Properties config;
#Override
public void run(String... strings) throws Exception {
System.out.println("source: " + config.getSourceFilePath());
System.out.println("destination: " + config.getDestinationFilePath());
System.out.println("control message: " + config.getControlMessage());
if(config.getControlMessage().equals("fail")) {
System.out.println("throwing an exception ...");
throw new Exception("I'm ANGRY");
}
System.out.println("pulldata-task complete!");
}
}
}
Sream Dataflow task-pull-data
app register --name task-pull-data --type task --uri maven://com.shifthunter.tasks:shifthunter-task-pulldata:jar:0.0.1-SNAPSHOT
task-pull-data - Details
I'm a student doing an Android Things project, which is about connecting Rasperi pi with an Accelerometer sensor to get acceleration data, I finished the coding part in Android Studio and the sensor is working fine, then I tried to connect my project to the cloud by following this tutorial: http://blog.blundellapps.co.uk/tut-google-cloud-iot-core-mqtt-on-android/
I think I have to publish the accelerometer data to the cloud by calling this method below in MainActivity class, I did that but still have an error, see the update I did at end of this question please.
#Override
public void onSensorChanged(SensorEvent event) {
Log.d(TAG, "Accel X " + event.values[0]);
Log.d(TAG, "Accel Y " + event.values[1]);
Log.d(TAG, "Accel Z " + event.values[2]);
}
Also, do I have to do something in the cloud platform to receive the data?, I already set up the communication with my Google IoT Core details as shown in the below code:
communicator = new IotCoreCommunicator.Builder()
.withContext(this)
.withCloudRegion("us-central1")
.withProjectId("my-first-project-198704")
.withRegistryId("vibration")
.withDeviceId("my-device")
.withPrivateKeyRawFileId(R.raw.rsa_private)
.build();
This is what I'm seeing in the cloud:
And this is the acceleration data I have in Android Studio:
My project consists of two modules but here I will attach only the second module because the question is limited to 3000 characters only and the second module is what I need to edit I think.
sample module:
AccelerometerActivity class:
package com.cacaosd.sample;
import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import com.cacaosd.adxl345.ADXL345SensorDriver;
import com.cacaosd.sample.BoardDefaults;
import java.io.IOException;
public class AccelerometerActivity extends Activity implements SensorEventListener {
private static final String TAG = AccelerometerActivity.class.getSimpleName();
private ADXL345SensorDriver mSensorDriver;
private SensorManager mSensorManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mSensorManager.registerDynamicSensorCallback(new SensorManager.DynamicSensorCallback() {
#Override
public void onDynamicSensorConnected(Sensor sensor) {
if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
mSensorManager.registerListener(AccelerometerActivity.this,
sensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}
});
try {
mSensorDriver = new ADXL345SensorDriver(BoardDefaults.getI2CPort());
mSensorDriver.registerAccelerometerSensor();
} catch (IOException e) {
Log.e(TAG, "Error configuring sensor", e);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "Closing sensor");
if (mSensorDriver != null) {
mSensorManager.unregisterListener(this);
try {
mSensorDriver.close();
} catch (IOException e) {
Log.e(TAG, "Error closing sensor", e);
} catch (Exception e) {
e.printStackTrace();
} finally {
mSensorDriver = null;
}
}
}
#Override
public void onSensorChanged(SensorEvent event) {
Log.d(TAG, "Accel X " + event.values[0]);
Log.d(TAG, "Accel Y " + event.values[1]);
Log.d(TAG, "Accel Z " + event.values[2]);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
Log.i(TAG, "sensor accuracy changed: " + accuracy);
}
}
BoardDefaults class:
package com.cacaosd.sample;
import android.os.Build;
import com.google.android.things.pio.PeripheralManagerService;
import java.util.List;
/**
* Created by cagdas on 20.12.2016.
*/
#SuppressWarnings("WeakerAccess")
public class BoardDefaults {
private static final String DEVICE_EDISON_ARDUINO = "edison_arduino";
private static final String DEVICE_EDISON = "edison";
private static final String DEVICE_RPI3 = "rpi3";
private static final String DEVICE_NXP = "imx6ul";
private static String sBoardVariant = "";
/**
* Return the preferred I2C port for each board.
*/
public static String getI2CPort() {
switch (getBoardVariant()) {
case DEVICE_EDISON_ARDUINO:
return "I2C6";
case DEVICE_EDISON:
return "I2C1";
case DEVICE_RPI3:
return "I2C1";
case DEVICE_NXP:
return "I2C2";
default:
throw new IllegalStateException("Unknown Build.DEVICE " + Build.DEVICE);
}
}
private static String getBoardVariant() {
if (!sBoardVariant.isEmpty()) {
return sBoardVariant;
}
sBoardVariant = Build.DEVICE;
// For the edison check the pin prefix
// to always return Edison Breakout pin name when applicable.
if (sBoardVariant.equals(DEVICE_EDISON)) {
PeripheralManagerService pioService = new PeripheralManagerService();
List<String> gpioList = pioService.getGpioList();
if (gpioList.size() != 0) {
String pin = gpioList.get(0);
if (pin.startsWith("IO")) {
sBoardVariant = DEVICE_EDISON_ARDUINO;
}
}
}
return sBoardVariant;
}
}
IotCoreCommunicator class:
package com.cacaosd.sample1;
import android.content.Context;
import android.util.Log;
import java.util.concurrent.TimeUnit;
import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
public class IotCoreCommunicator {
private static final String SERVER_URI = "ssl://mqtt.googleapis.com:8883";
public static class Builder {
private Context context;
private String projectId;
private String cloudRegion;
private String registryId;
private String deviceId;
private int privateKeyRawFileId;
public Builder withContext(Context context) {
this.context = context;
return this;
}
public Builder withProjectId(String projectId) {
this.projectId = projectId;
return this;
}
public Builder withCloudRegion(String cloudRegion) {
this.cloudRegion = cloudRegion;
return this;
}
public Builder withRegistryId(String registryId) {
this.registryId = registryId;
return this;
}
public Builder withDeviceId(String deviceId) {
this.deviceId = deviceId;
return this;
}
public Builder withPrivateKeyRawFileId(int privateKeyRawFileId) {
this.privateKeyRawFileId = privateKeyRawFileId;
return this;
}
public IotCoreCommunicator build() {
if (context == null) {
throw new IllegalStateException("context must not be null");
}
if (projectId == null) {
throw new IllegalStateException("projectId must not be null");
}
if (cloudRegion == null) {
throw new IllegalStateException("cloudRegion must not be null");
}
if (registryId == null) {
throw new IllegalStateException("registryId must not be null");
}
if (deviceId == null) {
throw new IllegalStateException("deviceId must not be null");
}
String clientId = "projects/" + projectId + "/locations/" + cloudRegion + "/registries/" + registryId + "/devices/" + deviceId;
if (privateKeyRawFileId == 0) {
throw new IllegalStateException("privateKeyRawFileId must not be 0");
}
MqttAndroidClient client = new MqttAndroidClient(context, SERVER_URI, clientId);
IotCorePasswordGenerator passwordGenerator = new IotCorePasswordGenerator(projectId, context.getResources(), privateKeyRawFileId);
return new IotCoreCommunicator(client, deviceId, passwordGenerator);
}
}
private final MqttAndroidClient client;
private final String deviceId;
private final IotCorePasswordGenerator passwordGenerator;
IotCoreCommunicator(MqttAndroidClient client, String deviceId, IotCorePasswordGenerator passwordGenerator) {
this.client = client;
this.deviceId = deviceId;
this.passwordGenerator = passwordGenerator;
}
public void connect() {
monitorConnection();
clientConnect();
subscribeToConfigChanges();
}
private void monitorConnection() {
client.setCallback(new MqttCallback() {
#Override
public void connectionLost(Throwable cause) {
Log.e("TUT", "connection lost", cause);
}
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
Log.d("TUT", "message arrived " + topic + " MSG " + message);
// You need to do something with messages when they arrive
}
#Override
public void deliveryComplete(IMqttDeliveryToken token) {
Log.d("TUT", "delivery complete " + token);
}
});
}
private void clientConnect() {
try {
MqttConnectOptions connectOptions = new MqttConnectOptions();
// Note that the the Google Cloud IoT Core only supports MQTT 3.1.1, and Paho requires that we explicitly set this.
// If you don't, the server will immediately close its connection to your device.
connectOptions.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
// With Google Cloud IoT Core, the username field is ignored, however it must be set for the
// Paho client library to send the password field. The password field is used to transmit a JWT to authorize the device.
connectOptions.setUserName("unused-but-necessary");
connectOptions.setPassword(passwordGenerator.createJwtRsaPassword());
IMqttToken iMqttToken = client.connect(connectOptions);
iMqttToken.setActionCallback(new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
Log.d("TUT", "success, connected");
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Log.e("TUT", "failure, not connected", exception);
}
});
iMqttToken.waitForCompletion(TimeUnit.SECONDS.toMillis(30));
Log.d("TUT", "IoT Core connection established.");
} catch (MqttException e) {
throw new IllegalStateException(e);
}
}
/**
* Configuration is managed and sent from the IoT Core Platform
*/
private void subscribeToConfigChanges() {
try {
client.subscribe("/devices/" + deviceId + "/config", 1);
} catch (MqttException e) {
throw new IllegalStateException(e);
}
}
public void publishMessage(String subtopic, String message) {
String topic = "/devices/" + deviceId + "/" + subtopic;
String payload = "{msg:\"" + message + "\"}";
MqttMessage mqttMessage = new MqttMessage(payload.getBytes());
mqttMessage.setQos(1);
try {
client.publish(topic, mqttMessage);
Log.d("TUT", "IoT Core message published. To topic: " + topic);
} catch (MqttException e) {
throw new IllegalStateException(e);
}
}
public void disconnect() {
try {
Log.d("TUT", "IoT Core connection disconnected.");
client.disconnect();
} catch (MqttException e) {
throw new IllegalStateException(e);
}
}
}
IotCorePasswordGenerator class:
package com.cacaosd.sample1;
import android.content.res.Resources;
import android.util.Base64;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
class IotCorePasswordGenerator {
private final String projectId;
private final Resources resources;
private final int privateKeyRawFileId;
IotCorePasswordGenerator(String projectId, Resources resources, int privateKeyRawFileId) {
this.projectId = projectId;
this.resources = resources;
this.privateKeyRawFileId = privateKeyRawFileId;
}
char[] createJwtRsaPassword() {
try {
byte[] privateKeyBytes = decodePrivateKey(resources, privateKeyRawFileId);
return createJwtRsaPassword(projectId, privateKeyBytes).toCharArray();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Algorithm not supported. (developer error)", e);
} catch (InvalidKeySpecException e) {
throw new IllegalStateException("Invalid Key spec. (developer error)", e);
} catch (IOException e) {
throw new IllegalStateException("Cannot read private key file.", e);
}
}
private static byte[] decodePrivateKey(Resources resources, int privateKeyRawFileId) throws IOException {
try(InputStream inStream = resources.openRawResource(privateKeyRawFileId)) {
return Base64.decode(inputToString(inStream), Base64.DEFAULT);
}
}
private static String inputToString(InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
private static String createJwtRsaPassword(String projectId, byte[] privateKeyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
return createPassword(projectId, privateKeyBytes, "RSA", SignatureAlgorithm.RS256);
}
private static String createPassword(String projectId, byte[] privateKeyBytes, String algorithmName, SignatureAlgorithm signatureAlgorithm) throws NoSuchAlgorithmException, InvalidKeySpecException {
Instant now = Instant.now();
// Create a JWT to authenticate this device. The device will be disconnected after the token
// expires, and will have to reconnect with a new token. The audience field should always be set
// to the GCP project id.
JwtBuilder jwtBuilder =
Jwts.builder()
.setIssuedAt(Date.from(now))
.setExpiration(Date.from(now.plus(Duration.ofMinutes(20))))
.setAudience(projectId);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory kf = KeyFactory.getInstance(algorithmName);
return jwtBuilder.signWith(signatureAlgorithm, kf.generatePrivate(spec)).compact();
}
}
MainActivity class:
package com.cacaosd.sample1;
import android.app.Activity;
import android.hardware.SensorEvent;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Handler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import com.cacaosd.sample.AccelerometerActivity;
import com.cacaosd.sample.R;
import com.cacaosd.sample1.IotCoreCommunicator;
import com.google.android.things.pio.Gpio;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class MainActivity extends Activity {
AccelerometerActivity mAccelerometerActivity = new AccelerometerActivity();
private IotCoreCommunicator communicator;
private Handler handler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Setup the communication with your Google IoT Core details
communicator = new IotCoreCommunicator.Builder()
.withContext(this)
.withCloudRegion("us-central1") // ex: europe-west1
.withProjectId("my-first-project-198704") // ex: supercoolproject23236
.withRegistryId("vibration") // ex: my-devices
.withDeviceId("my-device") // ex: my-test-raspberry-pi
.withPrivateKeyRawFileId(R.raw.rsa_private)
.build();
HandlerThread thread = new HandlerThread("MyBackgroundThread");
thread.start();
handler = new Handler(thread.getLooper());
handler.post(connectOffTheMainThread); // Use whatever threading mechanism you want
}
private final Runnable connectOffTheMainThread = new Runnable() {
#Override
public void run() {
communicator.connect();
handler.post(sendMqttMessage);
}
};
private final Runnable sendMqttMessage = new Runnable() {
private int i;
/**
* We post 100 messages as an example, 1 a second
*/
#Override
public void run() {
if (i == 100) {
return;
}
SensorEvent event = null;
// events is the default topic for MQTT communication
String subtopic = "events";
// Your message you want to send
String message = "Hello World " + i++;
communicator.publishMessage(subtopic, message);
handler.postDelayed(this, TimeUnit.SECONDS.toMillis(1));
}
};
#Override
protected void onDestroy() {
communicator.disconnect();
super.onDestroy();
}
MainActivity class:
package com.cacaosd.sample1;
import android.app.Activity;
import android.hardware.SensorEvent;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Handler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import com.cacaosd.sample.AccelerometerActivity;
import com.cacaosd.sample.R;
import com.cacaosd.sample1.IotCoreCommunicator;
import com.google.android.things.pio.Gpio;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class MainActivity extends Activity {
AccelerometerActivity mAccelerometerActivity = new AccelerometerActivity();
private IotCoreCommunicator communicator;
private Handler handler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Setup the communication with your Google IoT Core details
communicator = new IotCoreCommunicator.Builder()
.withContext(this)
.withCloudRegion("us-central1") // ex: europe-west1
.withProjectId("my-first-project-198704") // ex: supercoolproject23236
.withRegistryId("vibration") // ex: my-devices
.withDeviceId("my-device") // ex: my-test-raspberry-pi
.withPrivateKeyRawFileId(R.raw.rsa_private)
.build();
HandlerThread thread = new HandlerThread("MyBackgroundThread");
thread.start();
handler = new Handler(thread.getLooper());
handler.post(connectOffTheMainThread); // Use whatever threading mechanism you want
}
private final Runnable connectOffTheMainThread = new Runnable() {
#Override
public void run() {
communicator.connect();
handler.post(sendMqttMessage);
}
};
private final Runnable sendMqttMessage = new Runnable() {
private int i;
/**
* We post 100 messages as an example, 1 a second
*/
#Override
public void run() {
if (i == 100) {
return;
}
SensorEvent event = null;
// events is the default topic for MQTT communication
String subtopic = "events";
// Your message you want to send
String message = "Hello World " + i++;
communicator.publishMessage(subtopic, message);
handler.postDelayed(this, TimeUnit.SECONDS.toMillis(1));
}
};
#Override
protected void onDestroy() {
communicator.disconnect();
super.onDestroy();
}
}
Update:
I added a third input (int acceleration) on publishMessage() method inside IotCoreCommunicator class like below:
public void publishMessage(String subtopic, String message, int acceleration) {
String topic = "/devices/" + deviceId + "/" + subtopic;
String payload = "{msg:\"" + message + "\"}";
MqttMessage mqttMessage = new MqttMessage(payload.getBytes());
mqttMessage.setQos(1);
try {
client.publish(topic, mqttMessage);
Log.d("TUT", "IoT Core message published. To topic: " + topic);
} catch (MqttException e) {
throw new IllegalStateException(e);
}
}
Then I call it on run() method inside the MainActivity class like below:
public void run() {
if (i == 100) {
return;
}
SensorEvent event = null;
// events is the default topic for MQTT communication
String subtopic = "events";
// Your message you want to send
String message = "Hello World " + i++;
int acceleration = mAccelerometerActivity.onSensorChanged(SensorEvent event.values);
communicator.publishMessage(subtopic, message, acceleration);
handler.postDelayed(this, TimeUnit.SECONDS.toMillis(1));
}
};
But I still have this error in the below screenshot:
(';' or ) expected)
Also the third input I added is shown like never been used as you see in the screenshot below, is this has any effect?
Thank you.
I am trying to add an extension function, but is failing with :
Caused by: net.sf.saxon.trans.XPathException: Unknown system function follow()
at net.sf.saxon.expr.parser.XPathParser.grumble(XPathParser.java:282)
I see (in debug) that function registered with the integrated library. I was expecting saxon to look for the function in the integrated library but it is searching in system functions and throwing error. What is causing this function to be represented as a system function.
I am using the following :
<dependency>
<groupId>net.sf.saxon</groupId>
<artifactId>Saxon-HE</artifactId>
<version>9.7.0-14</version>
</dependency>
Thank you
my code is
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.ExtensionFunctionCall;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.XPathCompiler;
import net.sf.saxon.s9api.XPathExecutable;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.SequenceType;
public class FollowTest {
public static void main(String[] args) throws Exception {
new FollowTest().test();
}
private void test () throws Exception {
Processor proc = new Processor(false);
proc.registerExtensionFunction(new Follow());
XPathCompiler xx = proc.newXPathCompiler();
XPathExecutable x = xx.compile("follow(/a/b/c)/type='xyz'");
}
public class Follow extends ExtensionFunctionDefinition {
#Override
public StructuredQName getFunctionQName() {
return new StructuredQName("", "http://example.com/saxon-extension", "follow");
}
#Override
public int getMinimumNumberOfArguments() {
return 1;
}
#Override
public int getMaximumNumberOfArguments() {
return 1;
}
#Override
public SequenceType[] getArgumentTypes() {
return new net.sf.saxon.value.SequenceType[] {SequenceType.SINGLE_STRING,};
}
#Override
public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
return SequenceType.NODE_SEQUENCE;
}
#Override
public boolean trustResultType() {
return true;
}
#Override
public boolean dependsOnFocus() {
return false;
}
#Override
public boolean hasSideEffects() {
return false;
}
#Override
public ExtensionFunctionCall makeCallExpression() {
return null;
}
private class followCall extends ExtensionFunctionCall {
#Override
public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
return null;
}
}
}
}
In the XPath expression you have written
follow(/a/b/c)
A function name with no namespace prefix is assumed to be in the default namespace for functions, which by default is the system function namespace http://www.w3.org/2005/xpath-functions. You need to use a prefix that's bound to the URI appearing in the extension function definition, namely http://example.com/saxon-extension
If I am using QOS type 1 means the broker will continue to send the message to the subscriber until it gets an acknowledgment. How can i set or return ack ? Please anyone shed some light on this.
This is my source code:
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;
import java.util.Vector;
import org.fusesource.hawtbuf.Buffer;
import org.fusesource.hawtbuf.UTF8Buffer;
import org.fusesource.mqtt.client.Callback;
import org.fusesource.mqtt.client.CallbackConnection;
import org.fusesource.mqtt.client.Listener;
import org.fusesource.mqtt.client.MQTT;
import org.fusesource.mqtt.client.QoS;
import org.fusesource.mqtt.client.Topic;
import com.adventnet.management.log.Log;
import com.adventnet.nms.util.NmsLogMgr;
public class DefaultMqttListener implements IMqttListener,Runnable{
long count = 0;
long start = System.currentTimeMillis();
private HashMap serverDetailsHash;
public DefaultMqttListener(HashMap serverProp)
{
this.serverDetailsHash = serverProp;
}
CallbackConnection myconnection;
#Override
public void init() {
MQTT mqtt = new MQTT();
String user = env("APOLLO_USER", (String)serverDetailsHash.get("userName")); //No I18N
String password = env("APOLLO_PASSWORD", (String)serverDetailsHash.get("password")); //No I18N
String host = env("APOLLO_HOST", (String)serverDetailsHash.get("hostName")); //No I18N
int port = Integer.parseInt(env("APOLLO_PORT", (String)serverDetailsHash.get("port")));
try {
mqtt.setHost(host, port);
mqtt.setUserName(user);
mqtt.setPassword(password);
final CallbackConnection connection = mqtt.callbackConnection();
myconnection = connection;
connection.listener(new org.fusesource.mqtt.client.Listener() {
public void onConnected() {
}
public void onDisconnected() {
}
public void onFailure(Throwable value) {
value.printStackTrace();
System.exit(-2);
}
public void onPublish(UTF8Buffer topic, Buffer msg, Runnable ack) {
long time = System.currentTimeMillis();
callback( topic, msg, ack,connection,time);
}
});
connection.connect(new Callback<Void>() {
#Override
public void onSuccess(Void value) {
NmsLogMgr.M2MERR.log("MQTT Listener connected in ::::", Log.SUMMARY);
ArrayList getTopics = (ArrayList)serverDetailsHash.get("Topics");
for(int i=0;i<getTopics.size();i++)
{
HashMap getTopic = (HashMap)getTopics.get(i);
String topicName = (String) getTopic.get("topicName");
String qosType = (String) getTopic.get("qosType");
Topic[] topic = {new Topic(topicName, getQosType(qosType))};
connection.subscribe(topic, new Callback<byte[]>() {
public void onSuccess(byte[] qoses) {
}
public void onFailure(Throwable value) {
value.printStackTrace();
System.exit(-2);
}
});
}
//Topic[] topics = {new Topic("adminTest", QoS.AT_LEAST_ONCE),new Topic("adminTest1", QoS.AT_LEAST_ONCE)};
}
#Override
public void onFailure(Throwable value) {
value.printStackTrace();
System.exit(-2);
}
});
// Wait forever..
synchronized (Listener.class) {
while(true){
Listener.class.wait();}
}
} catch (URISyntaxException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static String env(String key, String defaultValue) {
String rc = System.getenv(key);
if( rc== null ){
return defaultValue;}
return rc;
}
#Override
public void callback(UTF8Buffer topic, Buffer msg, Runnable ack, CallbackConnection connection, long time) {
// TODO Auto-generated method stub
try {
String Message = msg.utf8().toString();
MQTTMessage mqttMsg = new MQTTMessage();
mqttMsg.setMQTTMessage(Message);
mqttMsg.setTime(time);
mqttMsg.setTopic(topic);
mqttMsg.sethostName((String) serverDetailsHash.get("hostName"));
MQTTCacheManager.mgr.addToCache(mqttMsg);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void close() {
// TODO Auto-generated method stub
NmsLogMgr.M2MERR.log("myconnection closed", Log.SUMMARY);
myconnection.disconnect(new Callback<Void>() {
#Override
public void onSuccess(Void value) {
System.exit(0);
}
#Override
public void onFailure(Throwable value) {
value.printStackTrace();
System.exit(-2);
}
});
}
#Override
public void run() {
this.init();
// TODO Auto-generated method stub
}
public QoS getQosType(String name)
{
Properties qosContainer = new Properties();
qosContainer.put("0", QoS.AT_MOST_ONCE);
qosContainer.put("1", QoS.AT_LEAST_ONCE);
qosContainer.put("2", QoS.EXACTLY_ONCE);
QoS qosName = (QoS) qosContainer.get(name);
return qosName;
}
}
You don't send the acknowledgement in your code at all, it should all be handled by the MQTT library you are using.
The QOS ack's packets are between the publisher and the broker and then separately between the broker and any subscribers.
I didn't use the Java library but you need to subscribe to the topic specifying the QoS level 1 (to have at least one delivery) or QoS level 2 (to have exactly once delivery). In these cases, the underlying library sends the ACK packets to the broker.
Hi can you please tell me which is a better approach while getting data from server and displaying on a list?
package mypackage;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import net.rim.device.api.io.IOUtilities;
import net.rim.device.api.ui.component.Dialog;
public class ConnectJson extends Thread {
private String url;
public String response;
private String myinterface = ";deviceside=true";
private JsonObserver observer;
public void run() {
HttpConnection conn = null;
InputStream in = null;
int code;
try {
conn = (HttpConnection) Connector.open(this.url + this.myinterface, Connector.READ);
conn.setRequestMethod(HttpConnection.GET);
code = conn.getResponseCode();
System.out.println("naveen-------------------------");
if (code == HttpConnection.HTTP_OK) {
in = conn.openInputStream();
byte[] buffer = IOUtilities.streamToBytes(in);
this.response = new String(buffer,"UTF-8");
if (observer != null) {
observer.onResponseReceived(this.response);
}
if (in != null){
in.close();
}
if (conn != null){
conn.close();
}
}
} catch (Exception e) {
Dialog.inform(e.toString());
}
}
public String jsonResult(String url){
this.url = url;
this.start();
// this.run();
return response;
}
public void setObserver(JsonObserver o) {
this.observer = o;
}
}
package mypackage;
import java.util.Vector;
import xjson.me.JSONArray;
import xjson.me.JSONException;
import xjson.me.JSONObject;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.component.ObjectChoiceField;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;
/**
* A class extending the MainScreen class, which provides default standard
* behavior for BlackBerry GUI applications.
*/
public final class MyScreen extends MainScreen implements JsonObserver
{
/**
* Creates a new MyScreen object
*/
public MyScreen()
{
ConnectJson connectJson = new ConnectJson();
System.out.println("------------------");
connectJson.setObserver(this);
connectJson.jsonResult("http://musicbrainz.org/ws/2/release/59211ea4-ffd2-4ad9-9a4e-941d3148024a?inc=artist-credits+labels+discids+recordings&fmt=json");
System.out.println("---------bbbbb---------");
}
public void onResponseReceived(String response) {
System.out.println("000000000000000000000000000000000"+response);
// TODO: create or update your ListField here!!!
}
}
package mypackage;
public interface JsonObserver {
public void onResponseReceived(String response);
}
Well, first of all, this will not work:
public String jsonResult(String url){
this.url = url;
this.start();
return response;
}
Calling start() will cause run() to be called on a background thread (that's good). However, immediately after you start the background thread, you return the response member variable. That won't work, because the run() method hasn't had time to actually assign the response variable.
What you need to do is assign the response variable at the end of the run() method, and then notify the UI on the main/UI thread, to let it update the user interface. Something like this:
if (code == HttpConnection.HTTP_OK) {
in = conn.openInputStream();
byte[] buffer = IOUtilities.streamToBytes(in);
this.response = new String(buffer,"UTF-8");
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
// this code is run on the main/UI thread
if (observer != null) {
observer.onResponseReceived(this.response);
}
}
});
where you add a new observer interface
public interface JsonObserver {
public void onResponseReceived(String response);
}
and then you can let your MainScreen implement that interface:
public class MyScreen extends MainScreen implements JsonObserver {
public MyScreen()
{
connectJson.setObserver(this);
// start the json request
connectJson.jsonResult("http://musicbrainz.org/ws/2/release/59211ea4-ffd2-4ad9-9a4e-941d3148024a?inc=artist-credits+labels+d...");
}
public void onResponseReceived(String response) {
System.out.println("000000000000000000000000000000000"+response);
// TODO: create or update your ListField here!!!
}
Of course, you'll need a new member and method in the ConnectJson class, too:
private JsonObserver observer;
public void setObserver(JsonObserver o) {
observer = o;
}
Note: it also might be better, for thread safety, to change your response member variable to a simple local final variable. I can't know all the ways that you plan on using it, but if it's just a way to hold onto the JSON response, you probably don't need a member variable. Try this instead (inside of run()):
final String response = new String(buffer,"UTF-8");
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
observer.onResponseReceived(response);