validate raw message against schema for method annotated with jmslistener - spring

I have a need to apply some pre-checks and common steps on the all the jms listeners like validating the raw message against a schema (JSON schema). Example -
#Component
public class MyService {
#JmsListener(destination = "myDestination")
public void processOrder(Order order) { ... }
}
Now, before the spring converts the Message from the queue to Order, I need to do the following -
log the original message with headers into a custom logger.
validate the json message (text message) against a json schema (lets assume I have only one schema here for the simplicity)
If schema validation fail, log the error and throw exception
If schema validation passes, continue to control to spring to do the conversion and continue with the process order method.
Does the spring JMS architecture provides any way to inject the above need?
I know AOP crosses the mind, but I am not sure will it work with #JmsListener.

A rather simple technique would be to set autoStartup to false on the listener container factory.
Then, use the JmsListenerEndpointRegistry bean to get the listener container.
Then getMessageListener(), wrap it in an AOP proxy and setMessageListener().
Then start the container.
There might be a more elegant way, but I think you'd have to get into the guts of the listener creation code, which is quite involved.
EDIT
Example with Spring Boot:
#SpringBootApplication
public class So49682934Application {
private final Logger logger = LoggerFactory.getLogger(getClass());
public static void main(String[] args) {
SpringApplication.run(So49682934Application.class, args);
}
#JmsListener(id = "listener1", destination = "so49682934")
public void listen(Foo foo) {
logger.info(foo.toString());
}
#Bean
public ApplicationRunner runner(JmsListenerEndpointRegistry registry, JmsTemplate template) {
return args -> {
DefaultMessageListenerContainer container =
(DefaultMessageListenerContainer) registry.getListenerContainer("listener1");
Object listener = container.getMessageListener();
ProxyFactory pf = new ProxyFactory(listener);
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor(new MyJmsInterceptor());
advisor.addMethodName("onMessage");
pf.addAdvisor(advisor);
container.setMessageListener(pf.getProxy());
registry.start();
Thread.sleep(5_000);
Foo foo = new Foo("baz");
template.convertAndSend("so49682934", foo);
};
}
#Bean
public MessageConverter converter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("typeId");
return converter;
}
public static class MyJmsInterceptor implements MethodInterceptor {
private final Logger logger = LoggerFactory.getLogger(getClass());
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Message message = (Message) invocation.getArguments()[0];
logger.info(message.toString());
// validate
return invocation.proceed();
}
}
public static class Foo {
private String bar;
public Foo() {
super();
}
public Foo(String bar) {
this.bar = bar;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
#Override
public String toString() {
return "Foo [bar=" + this.bar + "]";
}
}
}
and
spring.jms.listener.auto-startup=false
and
m2018-04-06 11:42:04.859 INFO 59745 --- [enerContainer-1] e.So49682934Application$MyJmsInterceptor : ActiveMQTextMessage {commandId = 5, responseRequired = true, messageId = ID:gollum.local-60138-1523029319662-4:2:1:1:1, originalDestination = null, originalTransactionId = null, producerId = ID:gollum.local-60138-1523029319662-4:2:1:1, destination = queue://so49682934, transactionId = null, expiration = 0, timestamp = 1523029324849, arrival = 0, brokerInTime = 1523029324849, brokerOutTime = 1523029324853, correlationId = null, replyTo = null, persistent = true, type = null, priority = 4, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = null, marshalledProperties = null, dataStructure = null, redeliveryCounter = 0, size = 1050, properties = {typeId=com.example.So49682934Application$Foo}, readOnlyProperties = true, readOnlyBody = true, droppable = false, jmsXGroupFirstForConsumer = false, text = {"bar":"baz"}}
2018-04-06 11:42:04.882 INFO 59745 --- [enerContainer-1] ication$$EnhancerBySpringCGLIB$$e29327b8 : Foo [bar=baz]
EDIT2
Here's how to do it via infrastructure...
#SpringBootApplication
#EnableJms
public class So496829341Application {
private final Logger logger = LoggerFactory.getLogger(getClass());
public static void main(String[] args) {
SpringApplication.run(So496829341Application.class, args);
}
#JmsListener(id = "listen1", destination="so496829341")
public void listen(Foo foo) {
logger.info(foo.toString());
}
#Bean
public ApplicationRunner runner(JmsTemplate template) {
return args -> {
Thread.sleep(5_000);
template.convertAndSend("so496829341", new Foo("baz"));
};
}
#Bean
public MessageConverter converter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("typeId");
return converter;
}
#Bean(JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
public static JmsListenerAnnotationBeanPostProcessor bpp() {
return new JmsListenerAnnotationBeanPostProcessor() {
#Override
protected MethodJmsListenerEndpoint createMethodJmsListenerEndpoint() {
return new MethodJmsListenerEndpoint() {
#Override
protected MessagingMessageListenerAdapter createMessageListener(
MessageListenerContainer container) {
MessagingMessageListenerAdapter listener = super.createMessageListener(container);
ProxyFactory pf = new ProxyFactory(listener);
pf.setProxyTargetClass(true);
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor(new MyJmsInterceptor());
advisor.addMethodName("onMessage");
pf.addAdvisor(advisor);
return (MessagingMessageListenerAdapter) pf.getProxy();
}
};
}
};
}
public static class MyJmsInterceptor implements MethodInterceptor {
private final Logger logger = LoggerFactory.getLogger(getClass());
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Message message = (Message) invocation.getArguments()[0];
logger.info(message.toString());
// validate
return invocation.proceed();
}
}
public static class Foo {
private String bar;
public Foo() {
super();
}
public Foo(String bar) {
this.bar = bar;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
#Override
public String toString() {
return "Foo [bar=" + this.bar + "]";
}
}
}
Note: the BPP must be static and #EnableJms is required since the presence of this BPP disables boot's.
2018-04-06 13:44:41.607 INFO 82669 --- [enerContainer-1] .So496829341Application$MyJmsInterceptor : ActiveMQTextMessage {commandId = 5, responseRequired = true, messageId = ID:gollum.local-63685-1523036676402-4:2:1:1:1, originalDestination = null, originalTransactionId = null, producerId = ID:gollum.local-63685-1523036676402-4:2:1:1, destination = queue://so496829341, transactionId = null, expiration = 0, timestamp = 1523036681598, arrival = 0, brokerInTime = 1523036681598, brokerOutTime = 1523036681602, correlationId = null, replyTo = null, persistent = true, type = null, priority = 4, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = null, marshalledProperties = null, dataStructure = null, redeliveryCounter = 0, size = 1050, properties = {typeId=com.example.So496829341Application$Foo}, readOnlyProperties = true, readOnlyBody = true, droppable = false, jmsXGroupFirstForConsumer = false, text = {"bar":"baz"}}
2018-04-06 13:44:41.634 INFO 82669 --- [enerContainer-1] ication$$EnhancerBySpringCGLIB$$9ff4b13f : Foo [bar=baz]
EDIT3
Avoiding AOP...
#SpringBootApplication
#EnableJms
public class So496829341Application {
private final Logger logger = LoggerFactory.getLogger(getClass());
public static void main(String[] args) {
SpringApplication.run(So496829341Application.class, args);
}
#JmsListener(id = "listen1", destination="so496829341")
public void listen(Foo foo) {
logger.info(foo.toString());
}
#Bean
public ApplicationRunner runner(JmsTemplate template) {
return args -> {
Thread.sleep(5_000);
template.convertAndSend("so496829341", new Foo("baz"));
};
}
#Bean
public MessageConverter converter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("typeId");
return converter;
}
#Bean(JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
public static JmsListenerAnnotationBeanPostProcessor bpp() {
return new JmsListenerAnnotationBeanPostProcessor() {
#Override
protected MethodJmsListenerEndpoint createMethodJmsListenerEndpoint() {
return new MethodJmsListenerEndpoint() {
#Override
protected MessagingMessageListenerAdapter createMessageListener(
MessageListenerContainer container) {
final MessagingMessageListenerAdapter listener = super.createMessageListener(container);
return new MessagingMessageListenerAdapter() {
#Override
public void onMessage(Message jmsMessage, Session session) throws JMSException {
logger.info(jmsMessage.toString());
// validate
listener.onMessage(jmsMessage, session);
}
};
}
};
}
};
}
public static class Foo {
private String bar;
public Foo() {
super();
}
public Foo(String bar) {
this.bar = bar;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
#Override
public String toString() {
return "Foo [bar=" + this.bar + "]";
}
}
}
EDIT4
To access other annotations on the listener method, it can be done, but reflection is needed to get a reference to the Method...
#JmsListener(id = "listen1", destination="so496829341")
#Schema("foo.bar")
public void listen(Foo foo) {
logger.info(foo.toString());
}
#Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface Schema {
String value();
}
#Bean(JmsListenerConfigUtils.JMS_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
public static JmsListenerAnnotationBeanPostProcessor bpp() {
return new JmsListenerAnnotationBeanPostProcessor() {
#Override
protected MethodJmsListenerEndpoint createMethodJmsListenerEndpoint() {
return new MethodJmsListenerEndpoint() {
#Override
protected MessagingMessageListenerAdapter createMessageListener(
MessageListenerContainer container) {
final MessagingMessageListenerAdapter listener = super.createMessageListener(container);
InvocableHandlerMethod handlerMethod =
(InvocableHandlerMethod) new DirectFieldAccessor(listener)
.getPropertyValue("handlerMethod");
final Schema schema = AnnotationUtils.getAnnotation(handlerMethod.getMethod(), Schema.class);
return new MessagingMessageListenerAdapter() {
#Override
public void onMessage(Message jmsMessage, Session session) throws JMSException {
logger.info(jmsMessage.toString());
logger.info(schema.value());
// validate
listener.onMessage(jmsMessage, session);
}
};
}
};
}
};
}

Related

Kafka Stream: can't get data from Kafka persistent keyValue state store

I am using Kafka streams and persistent KeyValue store in my application. There are two KeyValue stores I am using and two processors. I am facing issue with the stateStore which is shared between two processors. NameProcessor put data into nameStore and EventProcessor extracts data from nameStore. From Debugging it looks like, NameProcessor is able to put data successfully but when EventProcessor trying to get data from nameStore, it doesn't get any data. Below is the code snippet for Application class, Topology, NameProcessor and EventProcessor. Also, I am using Spring boot parent version 2.4.3, kafka-streams version 2.2.0 and kafka-clients version 2.2.0
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
Properties configs = getKafkaStreamProperties();
Topology builder = new Topology();
new ApplicationTopology(builder);
KafkaStreams stream = new KafkaStreams(builder, configs);
stream.setUncaughtExceptionHandler((Thread thread, Throwable throwable) -> {
// here you should examine the throwable/exception and perform an appropriate action!
logger.error("Uncaught exception in stream, MessageDetail: "+ ExceptionUtils.getRootCauseMessage(throwable) + ", Stack Trace: " + throwable.fillInStackTrace());
Runtime.getRuntime().halt(1);
});
Runtime.getRuntime().addShutdownHook(new Thread(stream::close));
stream.start();
}
private static Properties getKafkaStreamProperties() {
Properties configs = new Properties();
configs.setProperty(StreamsConfig.APPLICATION_ID_CONFIG, getApplicationId());
configs.setProperty(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, getBootstrapServers());
configs.setProperty(StreamsConfig.RETRIES_CONFIG, getRetries());
configs.setProperty(StreamsConfig.RETRY_BACKOFF_MS_CONFIG, getRetryBackOffMs());
configs.setProperty(StreamsConfig.REPLICATION_FACTOR_CONFIG, getReplicationFactor());
configs.setProperty(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, getMaxPollIntervalMs());
return configs;
}
public class ApplicationTopology {
public ApplicationTopology (Topology builder) {
StoreBuilder<KeyValueStore<String, Sensor>> nameStoreBuilder = Stores.
keyValueStoreBuilder(Stores.persistentKeyValueStore("nameStore"), Serdes.String(), CustomSerdes.getNameSerde()).withCachingEnabled().withLoggingEnabled(new HashMap<>());
StoreBuilder<KeyValueStore<String, String>> stateStoreBuilder = Stores.
keyValueStoreBuilder(Stores.persistentKeyValueStore("stateStore"), Serdes.String(), Serdes.String()).withCachingEnabled().withLoggingEnabled(new HashMap<>());
builder.addSource(AutoOffsetReset.LATEST, "source", Serdes.String().deserializer(), CustomSerdes.getIncomingEventSerde().deserializer(), getInboundTopic())
.addProcessor(TRANSFORMER, () -> new EventProcessor(), "source")
.addStateStore(nameStoreBuilder, TRANSFORMER)
.addSink("sink", getOutboundTopic(), Serdes.String().serializer(), CustomSerdes.getIncomingEventSerde().serializer(), TRANSFORMER);
//reset to earliest for model config topic as some models could be already on the topic
builder.addSource(AutoOffsetReset.EARLIEST, "nameStoreSource", Serdes.String().deserializer(), CustomSerdes.getSensorSerde().deserializer(), getInboundSensorUpdateTopic())
.addProcessor("process", () -> new NameProcessor(), "nameStoreSource")
.addStateStore(nameStoreBuilder, TRANSFORMER, "process");
}
public ApplicationTopology() {}
} }
public class NameProcessor extends AbstractProcessor<String, Sensor> {
private static final Logger LOGGER = LoggerFactory.getLogger(NameProcessor.class);
ProcessorContext context;
private KeyValueStore<String, Name> nameStore;
private static List<String> externalDeviceIdList = new ArrayList<>();
#Override
public void init(ProcessorContext processorContext) {
this.context = processorContext;
this.nameStore = (KeyValueStore<String, Name>) context.getStateStore("nameStore");
}
#Override
public void process(String externalDeviceId, Name name) {
if (StringUtils.isNotBlank(externalDeviceId)) {
String[] externalDeviceIds = SensorUtils.getExternalDeviceIdsWithoutSuffix(externalDeviceId);
if (Objects.isNull(name)) {
Arrays.stream(externalDeviceIds).forEach(id -> {
sensorStore.delete(id);
});
} else {
addOrUpdateNameInStore(sensor, externalDeviceIds);
}
}
}
private void addOrUpdateNameInStore(Sensor sensor, String[] externalDeviceIds) {
Arrays.stream(externalDeviceIds).forEach(id -> {
sensorStore.put(id, sensor);
});
// context.commit();
}
}
public class EventProcessor extends AbstractProcessor<String, IncomingEvent> {
private static final Logger LOGGER = LoggerFactory.getLogger(EventProcessor.class);
ProcessorContext context;
private KeyValueStore<String, Name> nameStore;
private KeyValueStore<String, String> stateStore;
#Override
public void init(ProcessorContext processorContext) {
this.context = processorContext;
this.nameStore = (KeyValueStore<String, Name>) context.getStateStore("nameStore");
this.stateStore = (KeyValueStore<String, String>) context.getStateStore("stateStore");
}
#Override
public void process(String key, IncomingEvent value) {
String correlationId = UUID.randomUUID().toString();
try {
String externalDeviceId = value.getExternalDeviceId();
Name nameFromStore = nameStore.get(externalDeviceId);
}
}
}
In nameFromStore variable, I don't get even value even after storing it in NameProcessor.

Receiving Sinch messages and call in background and show notifications for incoming message and show overlay for call

i am using Sinch in my app for video, audio and messaging purpose. calling and messaging works fine when the app is running in foreground. when the app is closed and removed from stack, incoming messages and calls still works as i implemented push notification to look for incoming calls and messages (i checked in debug mode). My problem is that
1) when i start incoming call activity from firebaseMessaginService it does not show the caller name and picture i am sending in headers as i start it through relayRemotePushNotificationPayload(HashMap);
2) i am unable to get the message content data from incoming message and show notification accordingly.
My Code.
public class MyFirebaseMessagingService extends FirebaseMessagingService implements ServiceConnection {
Context context;
private SinchService.SinchServiceInterface mSinchServiceInterface;
HashMap dataHashMap;
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
context = this;
if (SinchHelpers.isSinchPushPayload(remoteMessage.getData())) {
Map data = remoteMessage.getData();
dataHashMap = (data instanceof HashMap) ? (HashMap) data : new HashMap<>(data);
if (SinchHelpers.isSinchPushPayload(dataHashMap)) {
getApplicationContext().bindService(new Intent(getApplicationContext(), SinchService.class), this, Context.BIND_AUTO_CREATE);
}
} else {
Intent intent;
PendingIntent pendingIntent = null;
if (remoteMessage.getData().size() > 0) {
String identifier = remoteMessage.getData().get("identifier");
if (identifier.equals("0")) {
intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
} else if (identifier.equals("1")) {
intent = new Intent(this, Appointments.class);
pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
StaticInfo.saveData("HEALTH_TIP", "TIP", this);
}
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
notificationBuilder.setWhen(System.currentTimeMillis());
notificationBuilder.setContentTitle(remoteMessage.getNotification().getTitle());
notificationBuilder.setContentText(remoteMessage.getNotification().getBody());
notificationBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
notificationBuilder.setDefaults(Notification.DEFAULT_ALL | Notification.DEFAULT_LIGHTS | Notification.FLAG_SHOW_LIGHTS | Notification.DEFAULT_SOUND);
notificationBuilder.setAutoCancel(true);
notificationBuilder.setSmallIcon(R.mipmap.ic_launcher);
notificationBuilder.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
}
}
public static boolean foregrounded() {
ActivityManager.RunningAppProcessInfo appProcessInfo = new ActivityManager.RunningAppProcessInfo();
ActivityManager.getMyMemoryState(appProcessInfo);
return (appProcessInfo.importance == IMPORTANCE_FOREGROUND || appProcessInfo.importance == IMPORTANCE_VISIBLE);
}
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
if (SinchService.class.getName().equals(componentName.getClassName())) {
mSinchServiceInterface = (SinchService.SinchServiceInterface) iBinder;
}
// it starts incoming call activity which does not show incoming caller name and picture
NotificationResult result = mSinchServiceInterface.relayRemotePushNotificationPayload(dataHashMap);
if (result.isValid() && result.isCall()) {
CallNotificationResult callResult = result.getCallResult();
if (callResult.isCallCanceled() || callResult.isTimedOut()) {
createNotification("Missed Call from : ", callResult.getRemoteUserId());
return;
} else {
if (callResult.isVideoOffered()) {
Intent intent = new Intent(this, IncomingVideoCall.class);
intent.putExtra(SinchService.CALL_ID, callResult.getRemoteUserId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent);
} else {
Intent intent = new Intent(this, IncomingAudioCall.class);
intent.putExtra(SinchService.CALL_ID, callResult.getRemoteUserId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent);
}
}
} else if (result.isValid() && result.isMessage()) {
//i want to get message content here
MessageNotificationResult notificationResult = result.getMessageResult();
createNotification("Received Message from : ", notificationResult.getSenderId());
}
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
unbindService(this);
}
private void createNotification(String contentTitle, String userId) {
PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(getApplicationContext(), MainActivity.class), 0);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext()).setSmallIcon(R.mipmap.ic_launcher).setContentTitle(contentTitle).setContentText(userId);
mBuilder.setContentIntent(contentIntent);
mBuilder.setDefaults(Notification.DEFAULT_SOUND);
mBuilder.setAutoCancel(true);
NotificationManager mNotificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(1, mBuilder.build());
}
}
Sinch service is
public class SinchService extends Service {
private static final String APP_KEY = "123abc";
private static final String APP_SECRET = "123abc";
private static final String ENVIRONMENT = "sandbox.sinch.com";
public static final String CALL_ID = "CALL_ID";
static final String TAG = SinchService.class.getSimpleName();
private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient;
private String mUserId;
private StartFailedListener mListener;
private PersistedSettings mSettings;
#Override
public void onCreate() {
super.onCreate();
mSettings = new PersistedSettings(getApplicationContext());
String userName = mSettings.getUsername();
if (!userName.isEmpty()) {
start(userName);
}
}
#Override
public void onDestroy() {
if (mSinchClient != null && mSinchClient.isStarted()) {
mSinchClient.terminate();
}
super.onDestroy();
}
private void start(String userName) {
if (mSinchClient == null) {
mSettings.setUsername(userName);
mUserId = userName;
mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext()).userId(userName).applicationKey(APP_KEY).applicationSecret(APP_SECRET).environmentHost(ENVIRONMENT).build();
mSinchClient.setSupportCalling(true);
mSinchClient.setSupportMessaging(true);
mSinchClient.setSupportManagedPush(true);
mSinchClient.checkManifest();
mSinchClient.setSupportActiveConnectionInBackground(true);
mSinchClient.startListeningOnActiveConnection();
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().setRespectNativeCalls(false);
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.getVideoController().setResizeBehaviour(VideoScalingType.ASPECT_FILL);
mSinchClient.start();
}
}
private void stop() {
if (mSinchClient != null) {
mSinchClient.terminate();
mSinchClient = null;
}
mSettings.setUsername("");
}
private boolean isStarted() {
return (mSinchClient != null && mSinchClient.isStarted());
}
#Override
public IBinder onBind(Intent intent) {
return mSinchServiceInterface;
}
public class SinchServiceInterface extends Binder {
public NotificationResult relayRemotePushNotificationPayload(final Map payload) {
if (mSinchClient == null && !mSettings.getUsername().isEmpty()) {
start(mSettings.getUsername());
} else if (mSinchClient == null && mSettings.getUsername().isEmpty()) {
if (!StaticInfo.getSavedData("username", SinchService.this).equals("")) {
start(StaticInfo.getSavedData("username", SinchService.this));
}
return null;
}
return mSinchClient.relayRemotePushNotificationPayload(payload);
}
public void sendMessage(String recipientUserId, String Name, String textBody, String imageUrl) {
SinchService.this.sendMessage(recipientUserId, Name, textBody, imageUrl);
}
public void addMessageClientListener(MessageClientListener listener) {
SinchService.this.addMessageClientListener(listener);
}
public void removeMessageClientListener(MessageClientListener listener) {
SinchService.this.removeMessageClientListener(listener);
}
public Call callUser(String userId, HashMap<String, String> name) {
if (mSinchClient == null) {
return null;
}
return mSinchClient.getCallClient().callUser(userId, name);
}
public Call callUserVideo(String userId, HashMap<String, String> name) {
return mSinchClient.getCallClient().callUserVideo(userId, name);
}
public String getUserName() {
return mUserId;
}
public boolean isStarted() {
return SinchService.this.isStarted();
}
public void startClient(String userName) {
start(userName);
}
public void stopClient() {
stop();
}
public void setStartListener(StartFailedListener listener) {
mListener = listener;
}
public Call getCall(String callId) {
return mSinchClient.getCallClient().getCall(callId);
}
public VideoController getVideoController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getVideoController();
}
public AudioController getAudioController() {
if (!isStarted()) {
return null;
}
return mSinchClient.getAudioController();
}
public void LogOut() {
if (mSinchClient != null) {
mSinchClient.stopListeningOnActiveConnection();
mSinchClient.unregisterPushNotificationData();
mSinchClient.unregisterManagedPush();
//mSinchClient.terminate();
}
}
}
public interface StartFailedListener {
void onStartFailed(SinchError error);
void onStarted();
}
private class MySinchClientListener implements SinchClientListener {
#Override
public void onClientFailed(SinchClient client, SinchError error) {
if (mListener != null) {
mListener.onStartFailed(error);
}
mSinchClient.terminate();
mSinchClient = null;
}
#Override
public void onClientStarted(SinchClient client) {
Log.d(TAG, "SinchClient started");
if (mListener != null) {
mListener.onStarted();
}
}
#Override
public void onClientStopped(SinchClient client) {
Log.d(TAG, "SinchClient stopped");
}
#Override
public void onLogMessage(int level, String area, String message) {
switch (level) {
case Log.DEBUG:
Log.d(area, message);
break;
case Log.ERROR:
Log.e(area, message);
break;
case Log.INFO:
Log.i(area, message);
break;
case Log.VERBOSE:
Log.v(area, message);
break;
case Log.WARN:
Log.w(area, message);
break;
}
}
#Override
public void onRegistrationCredentialsRequired(SinchClient client, ClientRegistration clientRegistration) {
}
}
private class SinchCallClientListener implements CallClientListener {
#Override
public void onIncomingCall(CallClient callClient, Call call) {
if (call.getDetails().isVideoOffered()) {
Intent intent = new Intent(SinchService.this, IncomingVideoCall.class);
intent.putExtra(CALL_ID, call.getCallId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY);
SinchService.this.startActivity(intent);
} else {
Intent intent = new Intent(SinchService.this, IncomingAudioCall.class);
intent.putExtra(CALL_ID, call.getCallId());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY);
SinchService.this.startActivity(intent);
}
}
}
private class PersistedSettings {
private SharedPreferences mStore;
private static final String PREF_KEY = "Sinch";
public PersistedSettings(Context context) {
mStore = context.getSharedPreferences(PREF_KEY, MODE_PRIVATE);
}
public String getUsername() {
return mStore.getString("Username", "");
}
public void setUsername(String username) {
SharedPreferences.Editor editor = mStore.edit();
editor.putString("Username", username);
editor.commit();
}
}
public void sendMessage(String recipientUserId, String name, String textBody, String imageUrl) {
if (isStarted()) {
WritableMessage message = new WritableMessage();
message.addHeader("imageUrl", imageUrl);
message.addHeader("Name", name);
message.addRecipient(recipientUserId);
message.setTextBody(textBody);
mSinchClient.getMessageClient().send(message);
}
}
public void addMessageClientListener(MessageClientListener listener) {
if (mSinchClient != null) {
mSinchClient.getMessageClient().addMessageClientListener(listener);
}
}
public void removeMessageClientListener(MessageClientListener listener) {
if (mSinchClient != null) {
mSinchClient.getMessageClient().removeMessageClientListener(listener);
}
}
}
i am sending message like this.
private void sendMessage() {
String textBody = mTxtTextBody.getText().toString();
if (textBody.isEmpty()) {
Toast.makeText(this, "No text message", Toast.LENGTH_SHORT).show();
return;
}
getSinchServiceInterface().sendMessage(receiver_username, name, textBody, picture);
mTxtTextBody.setText("");
}
and the adapter where extracting message headers is
public class MessageAdapter extends BaseAdapter {
public static final int DIRECTION_INCOMING = 0;
public static final int DIRECTION_OUTGOING = 1;
private List<Pair<Message, Integer>> mMessages;
private SimpleDateFormat mFormatter;
private LayoutInflater mInflater;
Context context; // modification here
int res = 0;
String photo_url;
public MessageAdapter(Activity activity) {
mInflater = activity.getLayoutInflater();
context = activity;
mMessages = new ArrayList<>();
mFormatter = new SimpleDateFormat("hh:mm aa");
}
public void addMessage(Message message, int direction) {
mMessages.add(new Pair(message, direction));
notifyDataSetChanged();
}
#Override
public int getCount() {
return mMessages.size();
}
#Override
public Object getItem(int i) {
return mMessages.get(i);
}
#Override
public long getItemId(int i) {
return 0;
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public int getItemViewType(int i) {
return mMessages.get(i).second;
}
#Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
int direction = getItemViewType(i);
if (convertView == null) {
if (direction == DIRECTION_INCOMING) {
res = R.layout.message_right;
} else if (direction == DIRECTION_OUTGOING) {
res = R.layout.message_left;
}
convertView = mInflater.inflate(res, viewGroup, false);
}
Message message = mMessages.get(i).first;
String name = message.getHeaders().get("Name");
if (direction == DIRECTION_INCOMING) {
photo_url = message.getHeaders().get("imageUrl");
} else if (direction == DIRECTION_OUTGOING) {
photo_url = StaticInfo.getSavedData("photo", context);
}
CircleImageView photo = convertView.findViewById(R.id.pic);
Picasso.with(context).load(photo_url).into(photo);
TextView txtSender = convertView.findViewById(R.id.txtSender);
TextView txtMessage = convertView.findViewById(R.id.txtMessage);
TextView txtDate = convertView.findViewById(R.id.txtDate);
txtSender.setText(name);
txtMessage.setText(message.getTextBody());
txtDate.setText(mFormatter.format(message.getTimestamp()));
return convertView;
}
}

how to add entity listener programmable in Hibernate JPA

I use spring, hibernate, jpa2.1.
as follow:
#Entity
#EntityListeners(DemoListener.class)
public class Demo {
#Id
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
public class DemoListener {
#PersistenceContext
private EntityManager entityManager;
#PrePersist
public void prePersist(Demo demo){
}
}
the example works well, when I want to add more listener, I must modify the Demo entity, but the Demo is in other jar,I don't want to use the XML configuration, is there some way like this:
...addListener(Demo.class, new DemoListener());
...addListener(Demo.class, new OtherDemoListener());
base on hibernate-orm docs and hibernate-tutorials:
/**
* #param <T> one of {#link EventType#baseListenerInterface()}
* #see org.hibernate.event.service.spi.EventListenerRegistry
*/
public interface JpaEventListenerRegistry<T> {
/**
* add listener for entity class
*
* #param entityClass can't be null
* #param listener can't be null
*/
void addListener(Class<?> entityClass, T listener);
}
public class JpaEventListenerRegistryImpl implements JpaEventListenerRegistry<Object> {
private Logger logger = LoggerFactory.getLogger(getClass());
private EventListenerRegistry eventListenerRegistry;
private Map<EventType, JpaEventListenerRegistry> listeners = new HashMap<EventType, JpaEventListenerRegistry>();
public JpaEventListenerRegistryImpl(EventListenerRegistry eventListenerRegistry) {
this.eventListenerRegistry = eventListenerRegistry;
initDefault();
}
private void initDefault() {
listeners.put(EventType.PRE_INSERT, new DomainPreInsertEventListener());
listeners.put(EventType.POST_INSERT, new DomainPostInsertEventListener());
for (Map.Entry<EventType, JpaEventListenerRegistry> entry : listeners.entrySet()) {
eventListenerRegistry.appendListeners(entry.getKey(), entry.getValue());
}
}
#SuppressWarnings("unchecked")
public void addListener(Class<?> entityClass, Object listener) {
logger.info("add listener {} for entity {}", listener, entityClass.getName());
for (EventType eventType : EventType.values()) {
Class<?> listenerInterface = eventType.baseListenerInterface();
if (listenerInterface.isAssignableFrom(listener.getClass())) {
JpaEventListenerRegistry registry = listeners.get(eventType);
if (registry == null) {
logger.warn("the event type {} for listener {} is not supported", eventType, listener);
} else {
registry.addListener(entityClass, listener);
}
}
}
}
public static class Abstract<T> implements JpaEventListenerRegistry<T> {
private Logger logger = LoggerFactory.getLogger(getClass());
private Map<Class<?>, List<T>> listeners = new HashMap<Class<?>, List<T>>();
public void addListener(Class<?> entityClass, T listener) {
logger.info("add listener {} for entity {}", listener, entityClass.getName());
List<T> listeners = this.listeners.get(entityClass);
if (listeners == null) {
listeners = new ArrayList<T>();
this.listeners.put(entityClass, listeners);
}
listeners.add(listener);
}
List<T> findListener(Class<?> entityClass) {
for (Map.Entry<Class<?>, List<T>> entry : listeners.entrySet()) {
if (entry.getKey().isAssignableFrom(entityClass)) {
return entry.getValue();
}
}
return null;
}
}
public static class DomainPreInsertEventListener extends Abstract<PreInsertEventListener> implements PreInsertEventListener {
public boolean onPreInsert(PreInsertEvent event) {
return onPreInsert(event, findListener(event.getEntity().getClass()));
}
private boolean onPreInsert(PreInsertEvent event, List<PreInsertEventListener> listeners) {
if (listeners == null) return false;
for (PreInsertEventListener listener : listeners) {
if (listener.onPreInsert(event)) return true;
}
return false;
}
}
public static class DomainPostInsertEventListener extends Abstract<PostInsertEventListener> implements PostInsertEventListener {
public void onPostInsert(PostInsertEvent event) {
onPostInsert(event, findListener(event.getEntity().getClass()));
}
private void onPostInsert(PostInsertEvent event, List<PostInsertEventListener> listeners) {
if (listeners == null) return;
for (PostInsertEventListener listener : listeners) {
listener.onPostInsert(event);
}
}
public boolean requiresPostCommitHanding(EntityPersister persister) {
return false;
}
}
}
public class EntityManagerIllustrationTest extends TestCase {
private EntityManagerFactory entityManagerFactory;
#Override
protected void setUp() throws Exception {
// like discussed with regards to SessionFactory, an EntityManagerFactory is set up once for an application
// IMPORTANT: notice how the name here matches the name we gave the persistence-unit in persistence.xml!
entityManagerFactory = Persistence.createEntityManagerFactory("org.hibernate.tutorial.jpa");
SessionFactoryImplementor sessionFactory = entityManagerFactory.unwrap(SessionFactoryImplementor.class);
EventListenerRegistry eventListenerRegistry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
JpaEventListenerRegistryImpl jpaEventListenerRegistry = new JpaEventListenerRegistryImpl(eventListenerRegistry);
jpaEventListenerRegistry.addListener(EventListener.class, new JpaEventListener());
}
private static class JpaEventListener implements PreInsertEventListener, PostInsertEventListener {
public boolean onPreInsert(PreInsertEvent event) {
Event entity = (Event) event.getEntity();
System.out.println("onPreInsert:" + entity);
return false;
}
public void onPostInsert(PostInsertEvent event) {
Event entity = (Event) event.getEntity();
System.out.println("onPostInsert:" + entity);
}
public boolean requiresPostCommitHanding(EntityPersister persister) {
return false;
}
}
#Override
protected void tearDown() throws Exception {
entityManagerFactory.close();
}
public void testBasicUsage() {
// create a couple of events...
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
entityManager.persist(new Event("Our very first event!", new Date()));
// entityManager.persist(new Event("A follow up event", new Date()));
entityManager.getTransaction().commit();
entityManager.close();
// now lets pull events from the database and list them
entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
List<Event> result = entityManager.createQuery("from Event", Event.class).getResultList();
for (Event event : result) {
System.out.println("Event (" + event.getDate() + ") : " + event.getTitle());
}
entityManager.getTransaction().commit();
entityManager.close();
}
}

Dynamic Routing key on RabbitListener Annotation

I need to create a queue linked to Direct Exchange for every user who has logged in to the application. The basement routing will be 'user_' + userId.
That is, every time I receive a message through the user management queue that a user is logged on. Instantiate a bean with scope 'prototype' that contains a method annotated with RabbitListener to declare its queue. To this bean, I passed the userId to be able to configure the name of the queue and routingKey. But I can not access this instance variable in the Spel expression due to a circular reference error.
Here I put the bean with which declares the queue:
#Component("usersHandler")
#Scope(value = "prototype")
public class UsersHandler {
private static Logger logger = LoggerFactory.getLogger(UsersHandler.class);
private Long userId;
public UsersHandler(Long userId) {
this.userId = userId;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
#RabbitListener(bindings
= #QueueBinding(
value = #Queue(
value = "#{'queue_'.concat(usersHandler.userId)}",
durable = "false",
autoDelete = "true",
arguments = {
#Argument(
name = "x-message-ttl",
value = "#{rabbitCustomProperties.directExchange.queueArguments['x-message-ttl']}",
type = "java.lang.Integer"
)
,
#Argument(
name = "x-expires",
value = "#{rabbitCustomProperties.directExchange.queueArguments['x-expires']}",
type = "java.lang.Integer"
)
,
#Argument(
name = "x-dead-letter-exchange",
value = "#{rabbitCustomProperties.directExchange.queueArguments['x-dead-letter-exchange']}",
type = "java.lang.String"
)
}
),
exchange = #Exchange(
value = "#{rabbitCustomProperties.directExchange.name}",
type = ExchangeTypes.DIRECT,
durable = "#{rabbitCustomProperties.directExchange.durable}",
autoDelete = "#{rabbitCustomProperties.directExchange.autoDelete}",
arguments = {
#Argument(
name = "alternate-exchange",
value = "#{rabbitCustomProperties.directExchange.arguments['alternate-exchange']}",
type = "java.lang.String"
)
}
),
key = "#{'user_'.concat(usersHandler.userId)}")
)
public void handleMessage(#Payload Notification notification) {
logger.info("Notification Received : " + notification);
}
}
This is the other bean in charge of creating as many UserHandler as users have logged in:
#Component("adminHandler")
public class AdminHandler implements UsersManadgementVisitor {
#Autowired
private ApplicationContext appCtx;
private Map<Long, UsersHandler> handlers = new HashMap<Long, UsersHandler>();
private static Logger logger = LoggerFactory.getLogger(AdminHandler.class);
#RabbitListener(queues="#{rabbitCustomProperties.adminExchange.queues['users'].name}")
public void handleMessage(#Payload UsersManadgementMessage message) {
logger.info("Message -> " + message);
message.getType().accept(this, message.getId());
}
#Override
public void visitUserConnected(Long idUser) {
logger.info("Declare new queue for user: " + idUser );
UsersHandler userHandler = appCtx.getBean(UsersHandler.class, idUser);
handlers.put(idUser, userHandler);
}
#Override
public void visitUserDisconnected(Long idUser) {
logger.info("Remove queue for user: " + idUser );
handlers.remove(idUser);
}
}
My question is this:
How can I make the variable userId available in the evaluation context of the SpEL expressions?
You could use a ThreadLocal and the T operator...
#SpringBootApplication
public class So43717710Application {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(So43717710Application.class, args);
UserHolder.setUser("someUser");
context.getBean(Listener.class);
UserHolder.clearUser();
context.getBean(RabbitTemplate.class).convertAndSend("foo", "user_someUser", "bar");
Thread.sleep(5000);
context.close();
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Listener listener() {
return new Listener();
}
public static class Listener {
#RabbitListener(bindings = #QueueBinding(value = #Queue("#{'queue_' + T(com.example.UserHolder).getUser()}"),
exchange = #Exchange(value = "foo"),
key = "#{'user_' + T(com.example.UserHolder).getUser()}"))
public void listen(String in) {
System.out.println(in);
}
}
}
public class UserHolder {
private static final ThreadLocal<String> user = new ThreadLocal<String>();
public static void setUser(String userId) {
user.set(userId);
}
public static String getUser() {
return user.get();
}
public static void clearUser() {
user.remove();
}
}
If the ThreadLocal is in a #Bean you can use a bean reference...
#SpringBootApplication
public class So43717710Application {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(So43717710Application.class, args);
UserHolder.setUser("someUser");
context.getBean(Listener.class);
UserHolder.clearUser();
context.getBean(RabbitTemplate.class).convertAndSend("foo", "user_someUser", "bar");
Thread.sleep(5000);
context.close();
}
#Bean
public UserHolder holder() {
return new UserHolder();
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Listener listener() {
return new Listener();
}
public static class Listener {
#RabbitListener(bindings = #QueueBinding(value = #Queue("#{'queue_' + #holder.user}"),
exchange = #Exchange(value = "foo"),
key = "#{'user_' + #holder.user}"))
public void listen(String in) {
System.out.println(in);
}
}
}

Spring 4.0.5 websockt integration apollo with the exception "Message broker is not active"

For a project,i want to switch the project from simple broker to full-feature broker(like rabiitmq, apollo).
the exception stack:
Caused by: org.springframework.messaging.MessageDeliveryException: Message broker is not active.
at org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler.handleMessageInternal(StompBrokerRelayMessageHandler.java:392)
at org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler.handleMessage(AbstractBrokerMessageHandler.java:177)
at org.springframework.messaging.support.ExecutorSubscribableChannel.sendInternal(ExecutorSubscribableChannel.java:64)
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:116)
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:98)
at org.springframework.messaging.simp.SimpMessagingTemplate.doSend(SimpMessagingTemplate.java:125)
at org.springframework.messaging.simp.SimpMessagingTemplate.doSend(SimpMessagingTemplate.java:48)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:94)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.convertAndSend(AbstractMessageSendingTemplate.java:144)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.convertAndSend(AbstractMessageSendingTemplate.java:112)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.convertAndSend(AbstractMessageSendingTemplate.java:107)
at cn.clickmed.cmcp.websocket.plain.util.WebSocketUtil.sendMessage(WebSocketUtil.java:61)
at cn.clickmed.cmcp.websocket.plain.util.WebSocketUtil.sendMessage(WebSocketUtil.java:65)
at cn.clickmed.cmcp.websocket.plain.entity.WebSocketEntity.postPersist(WebSocketEntity.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
here is the config code:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
for(String mapping : WebSocketConstant.WEBSOCKETTYPE.keySet()) {
registry.addEndpoint(mapping).withSockJS();
}
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/websocket");
// registry.enableSimpleBroker("/topic");
registry.enableStompBrokerRelay("/topic","/queue").setRelayHost("localhost").setRelayPort(61613).setSystemHeartbeatReceiveInterval(2000).setSystemHeartbeatSendInterval(2000);
// registry.setUserDestinationPrefix("/topic/");
}
#Bean
public Broker broker() throws Exception {
final Broker broker = new Broker();
// Configure STOMP over WebSockects connector
final AcceptingConnectorDTO ws = new AcceptingConnectorDTO();
ws.id = "ws";
ws.bind = "ws://localhost:61613";
ws.protocols.add( new StompDTO() );
// Create a topic with name 'test'
final TopicDTO topic = new TopicDTO();
topic.id = "todoListener";
// Create virtual host (based on localhost)
final VirtualHostDTO host = new VirtualHostDTO();
host.id = "localhost";
host.topics.add( topic );
host.host_names.add( "localhost" );
host.host_names.add( "127.0.0.1" );
host.auto_create_destinations = false;
// Create a web admin UI (REST) accessible at: http://localhost:61680/api/index.html#!/
final WebAdminDTO webadmin = new WebAdminDTO();
webadmin.bind = "http://localhost:61680";
// Create JMX instrumentation
final JmxDTO jmxService = new JmxDTO();
jmxService.enabled = true;
// Finally, glue all together inside broker configuration
final BrokerDTO config = new BrokerDTO();
config.connectors.add( ws );
config.virtual_hosts.add( host );
config.web_admins.add( webadmin );
config.services.add( jmxService );
broker.setConfig( config );
broker.setTmp( new File( System.getProperty( "java.io.tmpdir" ) ) );
broker.start( new Runnable() {
#Override
public void run() {
System.out.println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:启动了");
}
} );
return broker;
}
}
send message code:
public class WebSocketUtil implements ILog{
public static Map<String, List<WebSocketSession>> webSocketSessionMap = new HashMap<String, List<WebSocketSession>>();
public static SimpMessagingTemplate simpMessagingTemplate = null;
public static void doSendMessage(String webSocketType) throws IOException {
if(WebSocketUtil.webSocketSessionMap.get(webSocketType) != null) {
String msg = "";
if(WebSocketConstant.WEBSOCKETTYPE_TODOLIST.equals(webSocketType)) {
msg = "change";
}
for(WebSocketSession webSocketSession :WebSocketUtil.webSocketSessionMap.get(webSocketType)) {
webSocketSession.sendMessage(new TextMessage(msg));
}
}
}
public static String getWebSocketType(String url) {
int length = url.indexOf("/cmcp/");
String theUrl = url.substring(length+5);
if(theUrl.startsWith(WebSocketConstant.COMPATIBLEMAPPING)) {
String [] arr = theUrl.split("/");
theUrl = "/"+arr[2];
}
if(!WebSocketConstant.WEBSOCKETTYPE.containsKey(theUrl)) {
logger.error("please config the websocketConstant !!!!");
throw new RuntimeException();
}
String theType = WebSocketConstant.WEBSOCKETTYPE.get(theUrl);
return theType;
}
public synchronized static void initSimpMessagingTemplate() {
if(simpMessagingTemplate == null) {
simpMessagingTemplate = SpringUtil.getBeanByName("brokerMessagingTemplate");
}
}
public static void sendMessage(String topic, String message) {
if(simpMessagingTemplate == null) {
initSimpMessagingTemplate();
}
simpMessagingTemplate.convertAndSend("/topic"+topic, message);
}
public static void sendMessage(String topic) {
sendMessage(topic,"change");
}
}
this is the trigger entity:
#MappedSuperclass
public class WebSocketEntity extends CommonEntity {
/**
* serialVersionUID
*/
private static final long serialVersionUID = 1L;
#PostPersist
public void postPersist() throws IOException{
if(this instanceof TodoEntity) {
WebSocketUtil.sendMessage(WebSocketConstant.WEBSOCKETTYPE_TODOLIST_MAPPING);
}
}
#PostRemove
public void postRemove() throws IOException{
if(this instanceof TodoEntity) {
WebSocketUtil.sendMessage(WebSocketConstant.WEBSOCKETTYPE_TODOLIST_MAPPING);
}
}
}
please give me help! thanks!

Resources