I want to send a message when the accelerometer tracks that someone has fallen with the phone. I have tried some stuff, but when I run it on my phone, it "Stops Unfortunately" as an app usually does when there is an error.
I got just one activity :
MainActivity.java:
package com.mycompany.accelerometer;
import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.MediaPlayer;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.telephony.gsm.SmsManager;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements SensorEventListener{
MediaPlayer dangerSound ;
private Sensor mySensor ;
private SensorManager SM ;
public TextView PhoneNumber ;
public String TextMessage ;
public String numText ;
public SmsManager manager ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
manager = SmsManager.getDefault();
SM = (SensorManager)getSystemService(SENSOR_SERVICE);
mySensor = SM.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
SM.registerListener(this , mySensor , SensorManager.SENSOR_DELAY_NORMAL);
dangerSound = MediaPlayer.create(this , R.raw.warning_sound);
PhoneNumber = (TextView) findViewById(R.id.PhoneNumber);
numText = PhoneNumber.getText().toString();
TextMessage = "Help me, I have fallen !!!";
}
#Override
public void onSensorChanged(SensorEvent event) {
double x = event.values[0];
double y = event.values[1];
double z = event.values[2];
double vector = Math.sqrt(x*x + y*y + z*z);
if(vector >= 20){
dangerSound.start();
manager.sendTextMessage(numText , null , TextMessage , null , null);
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Not using`enter code here`
}
}
my logcat
12-01 22:33:42.239 15762-15762/? E/Zygote: MountEmulatedStorage()
12-01 22:33:42.239 15762-15762/? I/libpersona: KNOX_SDCARD checking this for 10231
12-01 22:33:42.239 15762-15762/? E/Zygote: v2
12-01 22:33:42.239 15762-15762/? I/libpersona: KNOX_SDCARD not a persona
12-01 22:33:42.239 15762-15762/? I/SELinux: Function: selinux_compare_spd_ram, SPD-policy is existed. and_ver=SEPF_SM-E700H_4.4.4 ver=A046
12-01 22:33:42.239 15762-15762/? I/SELinux: Function: selinux_compare_spd_ram , priority [2] , priority version is VE=SEPF_SM-E700H_5.1.1_0039
12-01 22:33:42.239 15762-15762/? E/SELinux: [DEBUG] get_category: variable seinfo: default sensitivity: NULL, cateogry: NULL
12-01 22:33:42.249 15762-15762/? I/art: Late-enabling -Xcheck:jni
12-01 22:33:42.289 15762-15762/? D/TimaKeyStoreProvider: TimaSignature is unavailable
12-01 22:33:42.289 15762-15762/? D/ActivityThread: Added TimaKeyStore provider
12-01 22:33:42.359 15762-15762/com.mycompany.accelerometer D/SecWifiDisplayUtil: Metadata value : none
12-01 22:33:42.389 15762-15762/com.mycompany.accelerometer D/PhoneWindow: *FMB* installDecor mIsFloating : false
12-01 22:33:42.389 15762-15762/com.mycompany.accelerometer D/PhoneWindow: *FMB* installDecor flags : -2139029248
12-01 22:33:42.409 15762-15762/com.mycompany.accelerometer D/SensorManager: registerListener :: 0, K2HH Acceleration , 200000, 0,
12-01 22:33:42.419 15762-15762/com.mycompany.accelerometer V/MediaPlayer-JNI: native_setup
12-01 22:33:42.419 15762-15762/com.mycompany.accelerometer V/MediaPlayer: constructor
12-01 22:33:42.419 15762-15762/com.mycompany.accelerometer V/MediaPlayer: setListener
12-01 22:33:42.419 15762-15762/com.mycompany.accelerometer E/MediaPlayer-JNI: QCMediaPlayer mediaplayer NOT present
12-01 22:33:42.419 15762-15762/com.mycompany.accelerometer V/MediaPlayer- JNI: setParameter: key 1400
12-01 22:33:42.419 15762-15762/com.mycompany.accelerometer V/MediaPlayer: MediaPlayer::setParameter(1400)
12-01 22:33:42.419 15762-15762/com.mycompany.accelerometer V/MediaPlayer-JNI: setAudioStreamType: 3
12-01 22:33:42.419 15762-15762/com.mycompany.accelerometer V/MediaPlayer: MediaPlayer::setAudioStreamType
12-01 22:33:42.419 15762-15762/com.mycompany.accelerometer V/MediaPlayer-JNI: set_session_id(): 44
12-01 22:33:42.419 15762-15762/com.mycompany.accelerometer V/MediaPlayer: MediaPlayer::setAudioSessionId(44)
12-01 22:33:42.419 15762-15762/com.mycompany.accelerometer V/MediaPlayer-JNI: setDataSourceFD: fd 28
12-01 22:33:42.419 15762-15762/com.mycompany.accelerometer V/MediaPlayer: setDataSource(28, 213976, 6161372)
12-01 22:33:42.429 15762-15774/com.mycompany.accelerometer V/MediaPlayer: message received msg=8, ext1=0, ext2=0
12-01 22:33:42.429 15762-15774/com.mycompany.accelerometer V/MediaPlayer: notify(8, 0, 0) callback on disconnected mediaplayer
12-01 22:33:42.449 15762-15762/com.mycompany.accelerometer V/MediaPlayer: setVideoSurfaceTexture
12-01 22:33:42.449 15762-15762/com.mycompany.accelerometer V/MediaPlayer: prepare
12-01 22:33:42.449 15762-15771/com.mycompany.accelerometer V/MediaPlayer: message received msg=200, ext1=973, ext2=0
12-01 22:33:42.449 15762-15771/com.mycompany.accelerometer W/MediaPlayer: info/warning (973, 0)
12-01 22:33:42.459 15762-15771/com.mycompany.accelerometer V/MediaPlayer: callback application
12-01 22:33:42.459 15762-15771/com.mycompany.accelerometer V/MediaPlayer: back from callback
12-01 22:33:42.459 15762-15771/com.mycompany.accelerometer V/MediaPlayer: message received msg=5, ext1=0, ext2=0
12-01 22:33:42.459 15762-15771/com.mycompany.accelerometer V/MediaPlayer: New video size 0 x 0
12-01 22:33:42.459 15762-15771/com.mycompany.accelerometer V/MediaPlayer: callback application
12-01 22:33:42.459 15762-15771/com.mycompany.accelerometer V/MediaPlayer: back from callback
12-01 22:33:42.459 15762-15771/com.mycompany.accelerometer V/MediaPlayer: message received msg=1, ext1=0, ext2=0
12-01 22:33:42.459 15762-15771/com.mycompany.accelerometer V/MediaPlayer: prepared
12-01 22:33:42.459 15762-15771/com.mycompany.accelerometer V/MediaPlayer: signal application thread
12-01 22:33:42.459 15762-15771/com.mycompany.accelerometer V/MediaPlayer: callback application
12-01 22:33:42.459 15762-15771/com.mycompany.accelerometer V/MediaPlayer: back from callback
12-01 22:33:42.459 15762-15762/com.mycompany.accelerometer V/MediaPlayer: prepare complete - status=0
12-01 22:33:42.459 15762-15762/com.mycompany.accelerometer E/MediaPlayer: Should have subtitle controller already set
12-01 22:33:42.509 15762-15762/com.mycompany.accelerometer D/ViewRootImpl: Buffer Count from app info with ::-1 && -1 for :: com.mycompany.accelerometer from View :: -1 DBQ Enabled ::false false
12-01 22:33:42.509 15762-15793/com.mycompany.accelerometer D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
12-01 22:33:42.509 15762-15762/com.mycompany.accelerometer I/MediaPlayer: Don't send intent. msg.arg1 = 0, msg.arg2 = 0
12-01 22:33:42.509 15762-15762/com.mycompany.accelerometer E/MediaPlayer: Should have subtitle controller already set
12-01 22:33:42.519 15762-15762/com.mycompany.accelerometer D/PhoneWindow: *FMB* isFloatingMenuEnabled mFloatingMenuBtn : null
12-01 22:33:42.519 15762-15762/com.mycompany.accelerometer D/PhoneWindow: *FMB* isFloatingMenuEnabled return false
12-01 22:33:42.539 15762-15793/com.mycompany.accelerometer I/Adreno-EGL: <qeglDrvAPI_eglInitialize:379>: EGL 1.4 QUALCOMM build: (I716aebe4f9)
12-01 22:33:42.539 15762-15793/com.mycompany.accelerometer I/Adreno-EGL: OpenGL ES Shader Compiler Version: E031.25.03.04
12-01 22:33:42.539 15762-15793/com.mycompany.accelerometer I/Adreno-EGL: Build Date: 07/01/15 수
12-01 22:33:42.539 15762-15793/com.mycompany.accelerometer I/Adreno-EGL: Local Branch: AU_LINUX_ANDROID_LA.BR.1.1.3_RB1.05.01.00.032.031_02060873_02063264
12-01 22:33:42.539 15762-15793/com.mycompany.accelerometer I/Adreno-EGL: Remote Branch:
12-01 22:33:42.539 15762-15793/com.mycompany.accelerometer I/Adreno-EGL: Local Patches:
12-01 22:33:42.539 15762-15793/com.mycompany.accelerometer I/Adreno-EGL: Reconstruct Branch:
12-01 22:33:42.539 15762-15793/com.mycompany.accelerometer I/OpenGLRenderer: Initialized EGL, version 1.4
12-01 22:33:42.559 15762-15793/com.mycompany.accelerometer D/OpenGLRenderer: Get maximum texture size. GL_MAX_TEXTURE_SIZE is 4096
12-01 22:33:42.559 15762-15793/com.mycompany.accelerometer D/OpenGLRenderer: Enabling debug mode 0
12-01 22:33:42.629 15762-15762/com.mycompany.accelerometer I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy#ca442fd time:887051
Hi i am doing the same project ,may this code help you.
protected void sendSMS(Location location, boolean last) {
Geocoder geocoder = new Geocoder(mContext);
String addressText = "";
try {
List<Address> addresses = geocoder.getFromLocation(
location.getLatitude(), location.getLongitude(), 1);
if (addresses.size() > 0) {
Address a = addresses.get(0);
addressText = "Address: ";
for (int i = 0; i <= a.getMaxAddressLineIndex(); i++) {
addressText += a.getAddressLine(i) + " ";
}
}
} catch (Exception e) {
// unable to geocode
addressText = "";
}
String locationString;
if (location != null && !last) {
locationString = " New location: " + location.getLatitude()
+ ", " + location.getLongitude() + " ";
} else if (last) {
locationString = " Last known location: "
+ location.getLatitude() + ", "
+ location.getLongitude() + " ";
} else {
locationString = "Waiting for location";
}
SharedPreferences phoneNumbers = mContext.getSharedPreferences(
PREF_PHONE_NUMBERS, 0);
String currPhoneNumber;
for (int i = 0; !((currPhoneNumber = phoneNumbers.getString(
Integer.toString(i), "")).equals("")); i++) {
try {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendMultipartTextMessage(
currPhoneNumber,
null,
smsManager.divideMessage(message + locationString
+ addressText), null, null);
} catch (Exception e) {
e.printStackTrace();
}
}}
Thanks.
Related
I'm trying to write platform specific code in Flutter. First of all, created a method to obtain battery temperature inside MainActivity.kt
This is my full code in MainActivity.kt file:
package com.xyz.zyx
import android.os.BatteryManager;
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
class MainActivity: FlutterFragmentActivity() {
// ...
private var batteryManager: BatteryManager? = null
private var methodChannel: MethodChannel? = null
private var temperatureChannel: EventChannel? = null
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "battery").setMethodCallHandler {
call, result ->
if (call.method == "getBatteryTemperature") {
val batteryTemperature = getBatteryTemperature()
if (batteryTemperature != -1) {
result.success(batteryTemperature)
} else {
result.error("UNAVAILABLE", "Battery temperature not available.", null)
}
} else {
result.notImplemented()
}
}
}
private fun getBatteryTemperature(): Int {
val batteryStatus: Intent? =
IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { ifilter ->
this.registerReceiver(null, ifilter)
val batteryTemperature: Int
val batteryManager =
getSystemService(Context.BATTERY_SERVICE) as BatteryManager
batteryTemperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -99)
return batteryTemperature
}
}
}
This is how I call my platform specific code from Flutter side:
static const platform = MethodChannel('battery');
Future<void> _getBatteryTemp() async {
String batteryLevel;
try {
var result = await platform.invokeMethod('getBatteryTemperature');
batteryLevel = 'Battery temperature at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
print(batteryLevel);
}
Lastly, this is the error on Flutter app running on an Android device. It throws an MissingPluginException even though there is a method called getBatteryTemperature, it says there isn't.
E/flutter (25427): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: MissingPluginException(No implementation found for method getBatteryTemperature on channel battery)
E/flutter (25427): #0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:294:7)
Is there something did I miss to implement in my code? Why does it not work?
so I got a problem with the cooldown in my command. The command work perfectly but when I try to put a cooldown, it's not working. In the chat this error message is send: An internal error occurred while attempting to perform this command. In the console, it's say that:
> [14:50:33 INFO]: EvilestVirus7 issued server command: /rtpPlayer
> [14:50:33 ERROR]: null
org.bukkit.command.CommandException: Unhandled exception executing command 'rtpplayer' in plugin RandomTP v1.0
at org.bukkit.command.PluginCommand.execute(PluginCommand.java:46) ~[spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:141) ~[spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at org.bukkit.craftbukkit.v1_8_R3.CraftServer.dispatchCommand(CraftServer.java:641) ~[spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at net.minecraft.server.v1_8_R3.PlayerConnection.handleCommand(PlayerConnection.java:1162) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at net.minecraft.server.v1_8_R3.PlayerConnection.chat(PlayerConnection.java:1078) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer.chat(CraftPlayer.java:237) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at can.evilestvirus.randomtp.MenuHandler.onMenuClick(MenuHandler.java:20) [RandomTpPlugin.jar:git-Spigot-21fe707-e1ebe52]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_212]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_212]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_212]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_212]
at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:306) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:62) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:502) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:487) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at net.minecraft.server.v1_8_R3.PlayerConnection.a(PlayerConnection.java:1630) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at net.minecraft.server.v1_8_R3.PacketPlayInWindowClick.a(SourceFile:31) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at net.minecraft.server.v1_8_R3.PacketPlayInWindowClick.a(SourceFile:9) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at net.minecraft.server.v1_8_R3.PlayerConnectionUtils$1.run(SourceFile:13) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_212]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_212]
at net.minecraft.server.v1_8_R3.SystemUtils.a(SourceFile:44) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at net.minecraft.server.v1_8_R3.MinecraftServer.B(MinecraftServer.java:715) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at net.minecraft.server.v1_8_R3.DedicatedServer.B(DedicatedServer.java:374) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at net.minecraft.server.v1_8_R3.MinecraftServer.A(MinecraftServer.java:654) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at net.minecraft.server.v1_8_R3.MinecraftServer.run(MinecraftServer.java:557) [spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_212]
Caused by: java.lang.NullPointerException
at can.evilestvirus.randomtp.CooldownManager.checkCooldowns(CooldownManager.java:26) ~[?:?]
at can.evilestvirus.randomtp.CommandRtpPlayer.onCommand(CommandRtpPlayer.java:22) ~[?:?]
at org.bukkit.command.PluginCommand.execute(PluginCommand.java:44) ~[spigot1.8.8.jar:git-Spigot-21fe707-e1ebe52]
... 26 more
This is my main class:
package can.evilestvirus.randomtp;
import org.bukkit.Bukkit;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin{
#Override
public void onEnable() {
getCommand("rtpplayer").setExecutor(new CommandRtpPlayer());
getCommand("rtp").setExecutor(new CommandRtp());
Bukkit.getPluginManager().registerEvents(new MenuHandler(), this);
}
}
There the cooldownManager class:
package can.evilestvirus.randomtp;
import org.bukkit.entity.Player;
import java.util.HashMap;
import java.util.UUID;
public class CooldownManager {
public static HashMap<UUID, Double> cooldowns;
public static void setupCooldown(){
cooldowns = new HashMap<>();
}
public static void setCooldowns(Player player, int secondes){
double delay = System.currentTimeMillis() + (secondes * 1000);
cooldowns.put(player.getUniqueId(), delay);
}
public static int getCooldowns(Player player){
return Math.toIntExact(Math.round((cooldowns.get(player.getUniqueId()) - System.currentTimeMillis()/1000)));
}
public static boolean checkCooldowns(Player player){
if(!cooldowns.containsKey(player.getUniqueId()) || cooldowns.get(player.getUniqueId()) <= System.currentTimeMillis()){
return true;
}
return false;
}
}
And this is the command class:
package can.evilestvirus.randomtp;
import java.util.HashMap;
import java.util.Random;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
public class CommandRtpPlayer implements CommandExecutor {
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (sender instanceof Player) {
Player player = (Player) sender;
if(CooldownManager.checkCooldowns(player.getPlayer())){
CooldownManager.setCooldowns(player.getPlayer(), 10);
Location rtp, rtp2, rtp3, rtp4, rtp5;
Random random = new Random();
int nbrandom = (int) (Math.random() * 4.0D) + 1;
switch (nbrandom) {
case 1:
rtp = new Location(player.getWorld(), random.nextInt(2500), 90.0D,
random.nextInt(2500), 1.8F, 7.4F);
player.teleport(rtp);
player.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, 200, 99));
player.addPotionEffect(new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE, 200, 99));
break;
case 2:
rtp2 = new Location(player.getWorld(), -random.nextInt(2500), 90.0D,
-random.nextInt(2500), 1.8F, 7.4F);
player.teleport(rtp2);
player.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, 200, 99));
player.addPotionEffect(new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE, 200, 99));
break;
case 3:
rtp3 = new Location(player.getWorld(), -random.nextInt(2500), 90.0D,
random.nextInt(2500), 1.8F, 7.4F);
player.teleport(rtp3);
player.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, 200, 99));
player.addPotionEffect(new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE, 200, 99));
break;
case 4:
rtp4 = new Location(player.getWorld(), random.nextInt(2500), 90.0D,
-random.nextInt(2500), 1.8F, 7.4F);
player.teleport(rtp4);
player.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, 200, 99));
player.addPotionEffect(new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE, 200, 99));
break;
default:
rtp5 = new Location(player.getWorld(), random.nextInt(2500), 90.0D,
-random.nextInt(2500), 1.8F, 7.4F);
player.teleport(rtp5);
player.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, 200, 99));
player.addPotionEffect(new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE, 200, 99));
break;
}
player.sendMessage("§a§lVous venez d'être téléporté aléatoirement!");
}else{
player.getPlayer().sendMessage("Vous pourez utiliser la commandes dans " + CooldownManager.getCooldowns(player.getPlayer()) + " seconds");
}
}
return true;
}
}
You never run the setupCooldowns() method you have inside your CooldownManager class. I would recommend you remove this method anyways and simply instantiate the cooldown using Map<UUID, Double> cooldowns = new HashMap<>();
The reason you’re getting a NullPointerException is because your HashMap has not been instantiated (does not exist)
I have set up a Spring Cloud Environment, including an Eureka Server, a Config Server using git as data source, an Eureka client using the Config Server and polling config periodically. I have seen each time I change config through git, the client will first deregister itself from Eureka Server and then register again. How can I avoid the deregister-register process?
In the client, I used #EnableScheduling to enable Spring Scheduling, and created a class ConfigGitClientWatch to poll Config server, another class MyContextRefresher to apply polled config changes to Spring Environment.
ConfigGitClientWatch:
#Component
#Slf4j
public class ConfigGitClientWatch implements Closeable, EnvironmentAware {
private final AtomicBoolean running = new AtomicBoolean(false);
private final AtomicReference<String> version = new AtomicReference<>();
private final MyContextRefresher refresher;
private final ConfigServicePropertySourceLocator locator;
private Environment environment;
public ConfigGitClientWatch(
MyContextRefresher refresher, ConfigServicePropertySourceLocator locator) {
this.refresher = refresher;
this.locator = locator;
}
#Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
#PostConstruct
public void start() {
running.compareAndSet(false, true);
}
#Scheduled(
initialDelayString = "${spring.cloud.config.watch.git.initialDelay:180000}",
fixedDelayString = "${spring.cloud.config.watch.git.delay:500}"
)
public void watchConfigServer() {
if (running.get()) {
String newVersion = fetchNewVersion();
String oldVersion = version.get();
if (versionChanged(oldVersion, newVersion)) {
version.set(newVersion);
final Set<String> refreshedProperties = refresher.refresh();
if(!refreshedProperties.isEmpty()) {
log.info("Refreshed properties:{}", String.join(",", refreshedProperties));
}
}
}
}
private String fetchNewVersion() {
CompositePropertySource propertySource = (CompositePropertySource) locator.locate(environment);
return (String) propertySource.getProperty("config.client.version");
}
private static boolean versionChanged(String oldVersion, String newVersion) {
return !hasText(oldVersion) && hasText(newVersion)
|| hasText(oldVersion) && !oldVersion.equals(newVersion);
}
#Override
public void close() {
running.compareAndSet(true, false);
}
}
MyContextRefresher:
#Component
#Slf4j
public class MyContextRefresher {
private static final String REFRESH_ARGS_PROPERTY_SOURCE = "refreshArgs";
private static final String[] DEFAULT_PROPERTY_SOURCES = new String[] {
// order matters, if cli args aren't first, things get messy
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
"defaultProperties" };
private Set<String> standardSources = new HashSet<>(
Arrays.asList(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME,
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
StandardServletEnvironment.JNDI_PROPERTY_SOURCE_NAME,
StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME,
StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME,
"configurationProperties"));
private ConfigurableApplicationContext context;
private RefreshScope scope;
public MyContextRefresher(ConfigurableApplicationContext context, RefreshScope scope) {
this.context = context;
this.scope = scope;
}
protected ConfigurableApplicationContext getContext() {
return this.context;
}
protected RefreshScope getScope() {
return this.scope;
}
public synchronized Set<String> refresh() {
Set<String> keys = refreshEnvironment();
if(!keys.isEmpty()) {
this.scope.refreshAll();
}
return keys;
}
private final List<String> skippedKeys = Arrays.asList(
"config.client.version",
"spring.cloud.client.hostname",
"local.server.port"
);
public synchronized Set<String> refreshEnvironment() {
return addConfigFilesToEnvironment();
}
private Set<String> addConfigFilesToEnvironment() {
Map<String, Object> before = extract(
this.context.getEnvironment().getPropertySources());
ConfigurableApplicationContext capture = null;
Set<String> changedKeys = new HashSet<>();
try {
StandardEnvironment environment = copyEnvironment(
this.context.getEnvironment());
SpringApplicationBuilder builder = new SpringApplicationBuilder(Empty.class)
.bannerMode(Mode.OFF).web(WebApplicationType.NONE)
.environment(environment);
// Just the listeners that affect the environment (e.g. excluding logging
// listener because it has side effects)
builder.application()
.setListeners(Arrays.asList(new BootstrapApplicationListener(),
new ConfigFileApplicationListener()));
capture = builder.run();
if (environment.getPropertySources().contains(REFRESH_ARGS_PROPERTY_SOURCE)) {
environment.getPropertySources().remove(REFRESH_ARGS_PROPERTY_SOURCE);
}
MutablePropertySources target = this.context.getEnvironment()
.getPropertySources();
String targetName = null;
for (PropertySource<?> source : environment.getPropertySources()) {
String name = source.getName();
if (target.contains(name)) {
targetName = name;
}
if (!this.standardSources.contains(name)) {
if (target.contains(name)) {
target.replace(name, source);
}
else {
if (targetName != null) {
target.addAfter(targetName, source);
}
else {
// targetName was null so we are at the start of the list
target.addFirst(source);
targetName = name;
}
}
}
}
final Map<String, Object> after = extract(environment.getPropertySources());
changedKeys = changes(before, after).keySet();
changedKeys.removeAll(skippedKeys);
}
finally {
if(!changedKeys.isEmpty()) {
ConfigurableApplicationContext closeable = capture;
while (closeable != null) {
try {
closeable.close();
} catch (Exception e) {
// Ignore;
}
if (closeable.getParent() instanceof ConfigurableApplicationContext) {
closeable = (ConfigurableApplicationContext) closeable.getParent();
} else {
break;
}
}
this.context.publishEvent(new EnvironmentChangeEvent(this.context, changedKeys));
}
}
return changedKeys;
}
// Don't use ConfigurableEnvironment.merge() in case there are clashes with property
// source names
private StandardEnvironment copyEnvironment(ConfigurableEnvironment input) {
StandardEnvironment environment = new StandardEnvironment();
MutablePropertySources capturedPropertySources = environment.getPropertySources();
// Only copy the default property source(s) and the profiles over from the main
// environment (everything else should be pristine, just like it was on startup).
for (String name : DEFAULT_PROPERTY_SOURCES) {
if (input.getPropertySources().contains(name)) {
if (capturedPropertySources.contains(name)) {
capturedPropertySources.replace(name,
input.getPropertySources().get(name));
}
else {
capturedPropertySources.addLast(input.getPropertySources().get(name));
}
}
}
environment.setActiveProfiles(input.getActiveProfiles());
environment.setDefaultProfiles(input.getDefaultProfiles());
Map<String, Object> map = new HashMap<String, Object>();
map.put("spring.jmx.enabled", false);
map.put("spring.main.sources", "");
capturedPropertySources
.addFirst(new MapPropertySource(REFRESH_ARGS_PROPERTY_SOURCE, map));
return environment;
}
private Map<String, Object> changes(Map<String, Object> before,
Map<String, Object> after) {
Map<String, Object> result = new HashMap<String, Object>();
for (String key : before.keySet()) {
if (!after.containsKey(key)) {
result.put(key, null);
}
else if (!equal(before.get(key), after.get(key))) {
result.put(key, after.get(key));
}
}
for (String key : after.keySet()) {
if (!before.containsKey(key)) {
result.put(key, after.get(key));
}
}
return result;
}
private boolean equal(Object one, Object two) {
if (one == null && two == null) {
return true;
}
if (one == null || two == null) {
return false;
}
return one.equals(two);
}
private Map<String, Object> extract(MutablePropertySources propertySources) {
Map<String, Object> result = new HashMap<String, Object>();
List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
for (PropertySource<?> source : propertySources) {
sources.add(0, source);
}
for (PropertySource<?> source : sources) {
if (!this.standardSources.contains(source.getName())) {
extract(source, result);
}
}
return result;
}
private void extract(PropertySource<?> parent, Map<String, Object> result) {
if (parent instanceof CompositePropertySource) {
try {
List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
for (PropertySource<?> source : ((CompositePropertySource) parent)
.getPropertySources()) {
sources.add(0, source);
}
for (PropertySource<?> source : sources) {
extract(source, result);
}
}
catch (Exception e) {
return;
}
}
else if (parent instanceof EnumerablePropertySource) {
for (String key : ((EnumerablePropertySource<?>) parent).getPropertyNames()) {
result.put(key, parent.getProperty(key));
}
}
}
#Configuration
protected static class Empty {
}
}
The log of the client is as below:
c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://172.39.8.118:14102/
c.c.c.ConfigServicePropertySourceLocator : Located environment: name=user-service, profiles=[peer2], label=null, version=3961593acd49e60c194aebc224adc6a4dfa9f530, state=null
trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$39b14aa7] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
o.s.c.n.eureka.InstanceInfoFactory : Setting initial instance status as: STARTING
com.netflix.discovery.DiscoveryClient : Initializing Eureka in region us-east-1
c.n.d.provider.DiscoveryJerseyProvider : Using JSON encoding codec LegacyJacksonJson
c.n.d.provider.DiscoveryJerseyProvider : Using JSON decoding codec LegacyJacksonJson
c.n.d.provider.DiscoveryJerseyProvider : Using XML encoding codec XStreamXml
c.n.d.provider.DiscoveryJerseyProvider : Using XML decoding codec XStreamXml
c.n.d.s.r.aws.ConfigClusterResolver : Resolving eureka endpoints via configuration
com.netflix.discovery.DiscoveryClient : Disable delta property : false
com.netflix.discovery.DiscoveryClient : Single vip registry refresh property : null
com.netflix.discovery.DiscoveryClient : Force full registry fetch : false
com.netflix.discovery.DiscoveryClient : Application is null : false
com.netflix.discovery.DiscoveryClient : Registered Applications size is zero : true
com.netflix.discovery.DiscoveryClient : Application version is -1: true
com.netflix.discovery.DiscoveryClient : Getting all instance registry info from the eureka server
com.netflix.discovery.DiscoveryClient : The response status is 200
com.netflix.discovery.DiscoveryClient : Not registering with Eureka server per configuration
com.netflix.discovery.DiscoveryClient : Discovery Client initialized at timestamp 1570780512926 with initial instances count: 6
o.s.c.n.e.s.EurekaServiceRegistry : Registering application USER-SERVICE with eureka with status UP
c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://172.39.8.118:14102/
c.c.c.ConfigServicePropertySourceLocator : Located environment: name=user-service, profiles=[peer2], label=null, version=3961593acd49e60c194aebc224adc6a4dfa9f530, state=null
b.c.PropertySourceBootstrapConfiguration : Located property source: CompositePropertySource {name='configService', propertySources=[MapPropertySource {name='configClient'}, MapPropertySource {name='http://localhost:3000/longqinsi/demo-config.git/user-service.yml'}, MapPropertySource {name='http://localhost:3000/longqinsi/demo-config.git/application.yml'}]}
o.s.boot.SpringApplication : The following profiles are active: peer2
com.example.userservice.HelloController : Received heartbeat from user service at port 14105.
o.s.boot.SpringApplication : Started application in 0.299 seconds (JVM running for 10313.932)
o.s.c.n.e.s.EurekaServiceRegistry : Unregistering application USER-SERVICE with eureka with status DOWN
com.netflix.discovery.DiscoveryClient : Shutting down DiscoveryClient ...
com.netflix.discovery.DiscoveryClient : Completed shut down of DiscoveryClient
com.netflix.discovery.DiscoveryClient : Shutting down DiscoveryClient ...
com.example.userservice.HelloController : Received heartbeat from user service at port 14105.
com.netflix.discovery.DiscoveryClient : Unregistering ...
com.netflix.discovery.DiscoveryClient : DiscoveryClient_USER-SERVICE/eureka1:user-service:14104 - deregister status: 200
com.netflix.discovery.DiscoveryClient : Completed shut down of DiscoveryClient
o.s.c.n.eureka.InstanceInfoFactory : Setting initial instance status as: STARTING
com.netflix.discovery.DiscoveryClient : Initializing Eureka in region us-east-1
c.n.d.provider.DiscoveryJerseyProvider : Using JSON encoding codec LegacyJacksonJson
c.n.d.provider.DiscoveryJerseyProvider : Using JSON decoding codec LegacyJacksonJson
c.n.d.provider.DiscoveryJerseyProvider : Using XML encoding codec XStreamXml
c.n.d.provider.DiscoveryJerseyProvider : Using XML decoding codec XStreamXml
c.n.d.s.r.aws.ConfigClusterResolver : Resolving eureka endpoints via configuration
com.netflix.discovery.DiscoveryClient : Disable delta property : false
com.netflix.discovery.DiscoveryClient : Single vip registry refresh property : null
com.netflix.discovery.DiscoveryClient : Force full registry fetch : false
com.netflix.discovery.DiscoveryClient : Application is null : false
com.netflix.discovery.DiscoveryClient : Registered Applications size is zero : true
com.netflix.discovery.DiscoveryClient : Application version is -1: true
com.netflix.discovery.DiscoveryClient : Getting all instance registry info from the eureka server
com.netflix.discovery.DiscoveryClient : The response status is 200
com.netflix.discovery.DiscoveryClient : Starting heartbeat executor: renew interval is: 30
c.n.discovery.InstanceInfoReplicator : InstanceInfoReplicator onDemand update allowed rate per min is 4
com.netflix.discovery.DiscoveryClient : Discovery Client initialized at timestamp 1570780516317 with initial instances count: 6
o.s.c.n.e.s.EurekaServiceRegistry : Unregistering application USER-SERVICE with eureka with status DOWN
com.netflix.discovery.DiscoveryClient : Saw local status change event StatusChangeEvent [timestamp=1570780516320, current=DOWN, previous=STARTING]
com.netflix.discovery.DiscoveryClient : DiscoveryClient_USER-SERVICE/eureka1:user-service:14104: registering service...
o.s.c.n.e.s.EurekaServiceRegistry : Registering application USER-SERVICE with eureka with status UP
com.netflix.discovery.DiscoveryClient : Saw local status change event StatusChangeEvent [timestamp=1570780516320, current=UP, previous=DOWN]
o.s.c.n.e.s.EurekaServiceRegistry : Unregistering application USER-SERVICE with eureka with status DOWN
o.s.c.n.e.s.EurekaServiceRegistry : Registering application USER-SERVICE with eureka with status UP
com.netflix.discovery.DiscoveryClient : DiscoveryClient_USER-SERVICE/eureka1:user-service:14104 - registration status: 204
I am using Spring Kafka template for producing messages. And the rate at which it is producing the messages is too slow. Takes around 8 mins for producing 15000 messages.
Following is How I created the Kafka template:
#Bean
public ProducerFactory<String, GenericRecord> highSpeedAvroProducerFactory(
#Qualifier("highSpeedProducerProperties") KafkaProperties properties) {
final Map<String, Object> kafkaPropertiesMap = properties.getKafkaPropertiesMap();
System.out.println(kafkaPropertiesMap);
kafkaPropertiesMap.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
kafkaPropertiesMap.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, AvroGenericSerializer.class);
return new DefaultKafkaProducerFactory<>(kafkaPropertiesMap);
}
#Bean
public KafkaTemplate<String, GenericRecord> highSpeedAvroKafkaTemplate(
#Qualifier("highSpeedAvroProducerFactory") ProducerFactory<String, GenericRecord> highSpeedAvroProducerFactory) {
return new KafkaTemplate<>(highSpeedAvroProducerFactory);
}
Here is how I am using the template to send the messages:
#Async("servicingPlatformUpdateExecutor")
public void afterWrite(List<? extends Account> items) {
LOGGER.info("Batch start:{}",items.size());
for (Test test : items) {
if (test.isOmega()) {
ObjectKeyRecord objectKeyRecord = ObjectKeyRecord.newBuilder().setType("test").setId(test.getId()).build();
LOGGER.info("build start, {}",test.getId());
GenericRecord message = MessageUtils.buildEventRecord(
schemaService.findSchema(topicName)
.orElseThrow(() -> new OmegaException("SchemaNotFoundException", topicName)), objectKeyRecord, test);
LOGGER.info("build end, {}",account.getId());
LOGGER.info("send Started , {}",account.getId());
ListenableFuture<SendResult<String, GenericRecord>> future = highSpeedAvroKafkaTemplate.send(topicName, objectKeyRecord.toString(), message);
LOGGER.info("send Done , {}",test.getId());
future.addCallback(new KafkaProducerFutureCallback(kafkaSender, topicName, objectKeyRecord.toString(), message));
}
}
LOGGER.info("Batch end}");
}
Producer Properties:
metric.reporters = []
metadata.max.age.ms = 300000
reconnect.backoff.ms = 50
sasl.kerberos.ticket.renew.window.factor = 0.8
bootstrap.servers = [***VALID BROKERS****))]
ssl.keystore.type = JKS
sasl.mechanism = GSSAPI
max.block.ms = 9223372036854775807
interceptor.classes = null
ssl.truststore.password = null
client.id = producer-1
ssl.endpoint.identification.algorithm = null
request.timeout.ms = 30000
acks = all
receive.buffer.bytes = 32768
ssl.truststore.type = JKS
retries = 2147483647
ssl.truststore.location = null
ssl.keystore.password = null
send.buffer.bytes = 131072
compression.type = none
metadata.fetch.timeout.ms = 60000
retry.backoff.ms = 100
sasl.kerberos.kinit.cmd = /usr/bin/kinit
buffer.memory = 800000000
timeout.ms = 30000
key.serializer = class org.apache.kafka.common.serialization.StringSerializer
sasl.kerberos.service.name = kafka
sasl.kerberos.ticket.renew.jitter = 0.05
ssl.trustmanager.algorithm = PKIX
block.on.buffer.full = false
ssl.key.password = null
sasl.kerberos.min.time.before.relogin = 60000
connections.max.idle.ms = 540000
max.in.flight.requests.per.connection = 10
metrics.num.samples = 2
ssl.protocol = TLS
ssl.provider = null
ssl.enabled.protocols = [TLSv1.2]
batch.size = 40000000
ssl.keystore.location = null
ssl.cipher.suites = null
security.protocol = SASL_SSL
max.request.size = 1048576
value.serializer = class com.message.serialization.AvroGenericSerializer
ssl.keymanager.algorithm = SunX509
metrics.sample.window.ms = 30000
partitioner.class = class org.apache.kafka.clients.producer.internals.DefaultPartitioner
linger.ms = 2
Here is the log which shows call to the kakfatemplate send method takes few milliseconds:
2018-04-27 05:29:05.691 INFO - testservice - - UpdateExecutor-1 - com.test.testservice.adapter.batch.testsyncjob.UpdateWriteListener:70 - build start, 1
2018-04-27 05:29:05.691 INFO - testservice - - UpdateExecutor-1 - com.test.testservice.adapter.batch.testsyncjob.UpdateWriteListener:75 - build end, 1
2018-04-27 05:29:05.691 INFO - testservice - - UpdateExecutor-1 - com.test.testservice.adapter.batch.testsyncjob.UpdateWriteListener:76 - send Started , 1
2018-04-27 05:29:05.778 INFO - testservice - - UpdateExecutor-1 - com.test.testservice.adapter.batch.testsyncjob.UpdateWriteListener:79 - send Done , 1
2018-04-27 05:29:07.794 INFO - testservice - - kafka-producer-network-thread | producer-1 - com.test.testservice.adapter.batch.testsyncjob.KafkaProducerFutureCallback:38
Any suggestion on how can I improve the performance for the sender would be greatly appreciated
Spring Kakfa version: 1.2.3.RELEASE
Kafka client: 0.10.2.1
UPDATE 1:
Changed the Serializer to ByteArraySerializer and then produced the same.
I still see the each send method call on kafkatempate takes 100 to 200 milliseconds
ObjectKeyRecord objectKeyRecord = ObjectKeyRecord.newBuilder().setType("test").setId(test.getId()).build();
GenericRecord message = MessageUtils.buildEventRecord(
schemaService.findSchema(testConversionTopicName)
.orElseThrow(() -> new TestException("SchemaNotFoundException", testTopicName)), objectKeyRecord, test);
byte[] messageBytes = serializer.serialize(testConversionTopicName,message);
LOGGER.info("send Started , {}",test.getId());
ListenableFuture<SendResult<String, byte[]>> future = highSpeedAvroKafkaTemplate.send(testConversionTopicName, objectKeyRecord.toString(), messageBytes);
LOGGER.info("send Done , {}",test.getId());
future.addCallback(new KafkaProducerFutureCallback(kafkaSender, testConversionTopicName, objectKeyRecord.toString(), message));
Have you profiled your application? e.g. with YourKit.
I suspect it's the Avro serializer; I am able to send 15,000 1000 byte messages in 274ms.
#SpringBootApplication
public class So50060086Application {
public static void main(String[] args) {
SpringApplication.run(So50060086Application.class, args);
}
#Bean
public ApplicationRunner runner(KafkaTemplate<String, String> template) {
return args -> {
Thread.sleep(5_000);
String payload = new String(new byte[999]);
StopWatch watch = new StopWatch();
watch.start();
for (int i = 0; i < 15_000; i++) {
template.send("so50060086a", "" + i + payload);
}
watch.stop();
System.out.println(watch.prettyPrint());
};
}
#Bean
public NewTopic topic() {
return new NewTopic("so50060086a", 1, (short) 1);
}
}
and
StopWatch '': running time (millis) = 274
I'm trying some scenarios with JMS and JBoss 4.2.2 and I have few problems with it.
I have a Queue
<mbean code="org.jboss.mq.server.jmx.Queue" name="jboss.mq.destination:service=Queue,name=notificationQueue">
<attribute name="JNDIName">jms.queue.testQueue</attribute>
<depends optional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends>
<depends optional-attribute-name="SecurityManager">jboss.mq:service=SecurityManager</depends>
<attribute name="SecurityConf">
<security>
<role name="testUser" read="true" write="true" />
</security>
</attribute>
</mbean>
and
<invoker-proxy-binding>
<name>message-driven-bean</name>
<invoker-mbean>default</invoker-mbean>
<proxy-factory>org.jboss.ejb.plugins.jms.JMSContainerInvoker</proxy-factory>
<proxy-factory-config>
<JMSProviderAdapterJNDI>DefaultJMSProvider</JMSProviderAdapterJNDI>
<ServerSessionPoolFactoryJNDI>StdJMSPool</ServerSessionPoolFactoryJNDI>
<CreateJBossMQDestination>true</CreateJBossMQDestination>
<MinimumSize>1</MinimumSize>
<MaximumSize>15</MaximumSize>
<MaxMessages>16</MaxMessages>
<MDBConfig>
<ReconnectIntervalSec>10</ReconnectIntervalSec>
<DLQConfig>
<DestinationQueue>queue/DLQ</DestinationQueue>
<MaxTimesRedelivered>3</MaxTimesRedelivered>
<TimeToLive>0</TimeToLive>
<DLQUser>jbossmquser</DLQUser>
<DLQPassword>letmein</DLQPassword>
</DLQConfig>
</MDBConfig>
</proxy-factory-config>
</invoker-proxy-binding>
To test redelivery I wrote MessageListener
import java.util.*;
import javax.jms.*;
import javax.naming.*;
public class NotifyQueueMessageListener {
public static void main(String[] args) throws NamingException, JMSException {
Hashtable<String, String> contextProperties = new Hashtable<String, String>();
contextProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
contextProperties.put(Context.PROVIDER_URL, "jnp://localhost:7099");
InitialContext initContext = new InitialContext(contextProperties);
Queue queue = (Queue) initContext.lookup("jms.queue.testQueue");
QueueConnection queueConnection = null;
try {
QueueConnectionFactory connFactory = (QueueConnectionFactory) initContext.lookup("ConnectionFactory");
queueConnection = connFactory.createQueueConnection("jbossmquser", "letmein");
Session queueSession = queueConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
queueConnection.setExceptionListener(new MyExceptionListener());
MessageConsumer consumer = queueSession.createConsumer(queue);
MyMessageListener messageListener = new MyMessageListener();
consumer.setMessageListener(messageListener);
queueConnection.start();
Object o = new Object();
synchronized (o) {
o.wait();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
System.out.println("closing connection");
if (queueConnection != null) {
queueConnection.close();
}
}
}
static class MyMessageListener implements MessageListener {
#Override
public void onMessage(Message message) {
if (message instanceof ObjectMessage) {
ObjectMessage om = (ObjectMessage) message;
try {
System.out.printf("MyMessageListener.onMessage( %s ), %s\n\n", om, om.getObject());
boolean throwException = om.getBooleanProperty("throw");
if (throwException) {
System.out.println("throwing exception");
throw new NullPointerException("just for testing");
}
message.acknowledge();
} catch (JMSException jmse) {
jmse.printStackTrace();
}
}
}
}
static class MyExceptionListener implements ExceptionListener {
#Override
public void onException(JMSException jmse) {
jmse.printStackTrace();
}
}
}
and MessageSender
import java.text.*;
import java.util.*;
import javax.jms.*;
import javax.naming.*;
public class MessageSender {
public static void main(String[] args) throws NamingException, JMSException {
Hashtable<String, String> contextProperties = new Hashtable<String, String>();
contextProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
contextProperties.put(Context.PROVIDER_URL, "jnp://localhost:7099");
InitialContext initContext = new InitialContext(contextProperties);
Queue queue = (Queue) initContext.lookup("notificationQueue");
QueueConnection queueConnection = null;
try {
QueueConnectionFactory connFactory = (QueueConnectionFactory) initContext.lookup("ConnectionFactory");
queueConnection = connFactory.createQueueConnection("jbossmquser", "letmein");
// QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
// QueueSession queueSession = queueConnection.createQueueSession(true, Session.SESSION_TRANSACTED);
QueueSession queueSession = queueConnection.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE);
// QueueSession queueSession = queueConnection.createQueueSession(false, Session.DUPS_OK_ACKNOWLEDGE);
QueueSender sender = queueSession.createSender(queue);
ObjectMessage message = queueSession.createObjectMessage();
message.setBooleanProperty("throw", true); // to throw exception in listener
message.setJMSDeliveryMode(DeliveryMode.NON_PERSISTENT);
message.setIntProperty("JMS_JBOSS_REDELIVERY_LIMIT", 3);
sender.send(message);
} finally {
System.out.println("closing connection");
if (queueConnection != null) {
queueConnection.close();
}
}
}
}
Expected behavior
Because I'm throwing Exception in onMessage() I expect that message will tried again several times (<MaxTimesRedelivered>3</MaxTimesRedelivered>) and after that it will be moved to DLQ, but it's not.
What I tried
I tried all acknowledge modes (AUTO, CLIENT, DUPS_OK) together with commiting, acknowledging but nothing worked, even message wasn't sent again.
I have no idea what's wrong. There is nothing relevant in JBoss logs.
When I try to stop and run again MesageListener I'm getting:
MyMessageListener.onMessage( org.jboss.mq.SpyObjectMessage {
Header {
jmsDestination : QUEUE.notificationQueue
jmsDeliveryMode : 2
jmsExpiration : 0
jmsPriority : 4
jmsMessageID : ID:13-13577584629501
jmsTimeStamp : 1357758462950
jmsCorrelationID: 20130109200742
jmsReplyTo : null
jmsType : null
jmsRedelivered : true
jmsProperties : {JMSXDeliveryCount=7, throw=true, JMS_JBOSS_REDELIVERY_LIMIT=3, JMS_JBOSS_REDELIVERY_COUNT=6}
jmsPropReadWrite: false
msgReadOnly : true
producerClientId: ID:13
}
} ), my message (2013-01-09 20:07:42)
MyMessageListener.onMessage( org.jboss.mq.SpyObjectMessage {
Header {
jmsDestination : QUEUE.notificationQueue
jmsDeliveryMode : 2
jmsExpiration : 0
jmsPriority : 4
jmsMessageID : ID:15-13577584942741
jmsTimeStamp : 1357758494274
jmsCorrelationID: 20130109200814
jmsReplyTo : null
jmsType : null
jmsRedelivered : true
jmsProperties : {JMSXDeliveryCount=6, throw=true, JMS_JBOSS_REDELIVERY_LIMIT=3, JMS_JBOSS_REDELIVERY_COUNT=5}
jmsPropReadWrite: false
msgReadOnly : true
producerClientId: ID:15
}
} ), my message (2013-01-09 20:08:14)
MyMessageListener.onMessage( org.jboss.mq.SpyObjectMessage {
Header {
jmsDestination : QUEUE.notificationQueue
jmsDeliveryMode : 2
jmsExpiration : 0
jmsPriority : 4
jmsMessageID : ID:20-13577586971991
jmsTimeStamp : 1357758697199
jmsCorrelationID: 20130109201137
jmsReplyTo : null
jmsType : null
jmsRedelivered : true
jmsProperties : {JMSXDeliveryCount=2, throw=true, JMS_JBOSS_REDELIVERY_LIMIT=3, JMS_JBOSS_REDELIVERY_COUNT=1}
jmsPropReadWrite: false
msgReadOnly : true
producerClientId: ID:20
}
} ), my message (2013-01-09 20:11:37)
MyMessageListener.onMessage( org.jboss.mq.SpyObjectMessage {
Header {
jmsDestination : QUEUE.notificationQueue
jmsDeliveryMode : 2
jmsExpiration : 0
jmsPriority : 4
jmsMessageID : ID:21-13577587683201
jmsTimeStamp : 1357758768320
jmsCorrelationID: 20130109201248
jmsReplyTo : null
jmsType : null
jmsRedelivered : true
jmsProperties : {JMSXDeliveryCount=2, throw=true, JMS_JBOSS_REDELIVERY_LIMIT=3, JMS_JBOSS_REDELIVERY_COUNT=1}
jmsPropReadWrite: false
msgReadOnly : true
producerClientId: ID:21
}
} ), my message (2013-01-09 20:12:48)
as you can see I tried also JMS_JBOSS_REDELIVERY_LIMIT.
Any idea?
I found very helpful post
https://community.jboss.org/wiki/ThrowingExceptionsFromAnMDB
that reads:
What type of Exceptions should an MDB throw?
The quick answer is none.
When I used transaction and createQueueSession(true, Session.SESSION_TRANSACTED) it worked just fine (redelivery and also DLQ).