i try to get my native android plugin on phonegap / cordova 3.0.0 running but i does not work,
the error from ripple:
Uncaught ReferenceError: torch is not defined
the call from the index.html
<button onclick="torch.shine(200);">dummy</button>
the plugin.xml
<!-- android -->
<platform name="android">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="Torch">
<param name="android-package" value="org.holzi.torch.Torch"/>
<param name="onload" value="true" />
</feature>
</config-file>
<js-module src="www/torch.js" name="Torch">
<clobbers target="torch" />
</js-module>
<source-file src="src/android/Torch.java" target-dir="src/org/holzi/torch" />
<config-file target="AndroidManifest.xml" parent="/manifest">
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.CAMERA"/>
</config-file>
</platform>
the torch.js in the www folder of the plugin
var exec = require('cordova/exec');
/* constructor */
function Torch() {}
Torch.shine = function() {
exec(
function(result){ alert('ok: '+reply); },
function(err){ alert('Error: '+err); }
, "Torch", "shine", ['200']);
}
var torch = new Torch();
module.exports = torch;
and the Torch.java
/*
*/
package org.holzi.torch;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
import android.content.Context;
import android.os.Vibrator;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
public class Torch extends CordovaPlugin {
Camera camera;
Camera.Parameters Parameters;
public Torch() {
}
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if (action.equals("shine")) {
this.shine(20);
}
else {
return false;
}
// Only alert and confirm are async.
callbackContext.success();
return true;
}
public void shine(int time) {
//Torch torch = (Torch) this.cordova.getActivity().getSystemService(Context.VIBRATOR_SERVICE);
//torch.shine(time);
camera = Camera.open();
Parameters p = camera.getParameters();
p.setFlashMode(Parameters.FLASH_MODE_TORCH);
camera.setParameters(p);
}
}
i solved it, here the code if anyone else have the same problem:
the index with the javascript
<!DOCTYPE html>
<html>
<head>
<title>Notification Example</title>
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8">
document.addEventListener("deviceready", onDeviceReady, false);
// Cordova is ready
function onDeviceReady() {
// Empty
}
function shine(torchOn) {
navigator.notification.shine(torchOn);
}
function alertTorchError(message) {
alert(message);
}
</script>
</head>
<body>
<p> </p>
<p> </p>
<p>AN</p>
<p> </p>
<p> </p>
<p>AUS</p>
</body>
</html>
the js file with the exec
var exec = require('cordova/exec');
module.exports = {
shine: function(turnOn) {
exec(null, function(error) { alertTorchError(error); }, "Torch", "shine", [turnOn]);
},
};
and the java file
package org.apache.holzi.torch;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.content.pm.PackageManager;
/**
* This class provides access to the Torch on the device.
*/
public class Torch extends CordovaPlugin {
Camera camera;
Camera.Parameters Parameters;
boolean hasFlash;
/* Constructor */
public Torch() { }
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if (action.equals("shine")) {
hasFlash = this.cordova.getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
if (!hasFlash) { callbackContext.error("no torch found"); }
else { this.shine(args.getBoolean(0));
}
}
else {
return false;
}
return true;
}
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
public void shine(boolean turnOn) {
if (camera == null) { camera = Camera.open(); }
if (turnOn) {
Parameters p = camera.getParameters();
p.setFlashMode(Parameters.FLASH_MODE_TORCH);
camera.setParameters(p);
}
else {
Parameters p = camera.getParameters();
p.setFlashMode(Parameters.FLASH_MODE_OFF);
camera.setParameters(p);
}
}
}
I don't know my answer could help you or not ,but I encounter this error many times.The following is my code use cordova1.7.0 one years ago, this error not in your java code.
var Music = function() {};
Music.prototype.exec = function(action, args, successCallback,errorCallback) {
if (typeof successCallback !== "function") {
console.log("GetResource: successCallback is not a function");
return;}
console.log("JS function execute --OK");
if (errorCallback && (typeof errorCallback !== "function")) {
console.log("GetResource: errorCallback is not a function");
return;}
PhoneGap.exec(successCallback, errorCallback, "MusicPlugin", action, args);
};
navigator.music=window.music = new Music();
then, register the plugin in the XML file like this:
--your plugin package--
<plugin name="MusicPlugin" value="***.***.***.MusicPlugin"></plugin>
finally,you should invoke this variable after the doucument's 'deviceready' event.
document.addEventListener('deviceready',function());
Related
i am trying to play a youtube video in android through NanoHTTPD android web server library using a html form by providing the youtube id of some video in the served html file into the html form. So far i got the youtube video player working. And i also get the form post parameter from the html. But i having trouble combining the two.
AndroidWebServer.class
package com.martin.androidwebplayer;
import android.app.Activity;
import android.content.Context;
import fi.iki.elonen.NanoHTTPD;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.youtube.player.YouTubeInitializationResult;
import com.google.android.youtube.player.YouTubePlayer;
import com.google.android.youtube.player.YouTubePlayerView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
public class AndroidWebServer extends NanoHTTPD {
private static final String TAG = "HTTPServer";
private Context ctx;
public AndroidWebServer(int port, Context ctx) {
super(port);
this.ctx = ctx;
try {
Log.d("TAG", "Starting web server..");
start();
}
catch(IOException ioe) {
Log.e(TAG, "Unable to start the server");
ioe.printStackTrace();
}
}
#Override
public Response serve(IHTTPSession session) {
Map<String, String> parms = session.getParms();
String content = null;
content = readFile().toString();
if (session.getMethod() == Method.POST) {
Map<String, String> files = new HashMap<String, String>();
try {
session.parseBody(files);
} catch (IOException e) {
e.printStackTrace();
} catch (ResponseException e) {
e.printStackTrace();
}
**youtube(String.valueOf(session.getParms().get("fname")));**
return newFixedLengthResponse(String.valueOf(session.getParms().get("fname")));
}
return newFixedLengthResponse(content );
}
private StringBuffer readFile() {
BufferedReader reader = null;
StringBuffer buffer = new StringBuffer();
try {
reader = new BufferedReader(
new InputStreamReader
(ctx.getAssets().open("index.html"), "UTF-8"));
String mLine;
while ((mLine = reader.readLine()) != null) {
buffer.append(mLine);
buffer.append("\n");
}
}
catch(IOException ioe) {
ioe.printStackTrace();
}
finally {
if (reader != null)
try {
reader.close();
}
catch (IOException ioe) {}
}
return buffer;
}
public void stopServer() {
this.stop();
}
public void youtube(String link) {
// Get reference to the view of Video player
YouTubePlayerView ytPlayer = (YouTubePlayerView) ((Activity)ctx).findViewById(R.id.ytPlayer);
String api_key = "apikey";
ytPlayer.initialize(
api_key,
new YouTubePlayer.OnInitializedListener() {
// Implement two methods by clicking on red
// error bulb inside onInitializationSuccess
// method add the video link or the playlist
// link that you want to play In here we
// also handle the play and pause
// functionality
#Override
public void onInitializationSuccess(
YouTubePlayer.Provider provider,
YouTubePlayer youTubePlayer, boolean b) {
youTubePlayer.loadVideo(link);
youTubePlayer.play();
}
// Inside onInitializationFailure
// implement the failure functionality
// Here we will show toast
#Override
public void onInitializationFailure(YouTubePlayer.Provider provider,
YouTubeInitializationResult
youTubeInitializationResult) {
}
});
}
}
MainActivity.class
package com.martin.androidwebplayer;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.youtube.player.YouTubeBaseActivity;
import com.google.android.youtube.player.YouTubePlayerView;
import java.io.IOException;
public class MainActivity extends YouTubeBaseActivity {
private AndroidWebServer aws;
private YouTubePlayerView ytPlayer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
YouTubePlayerView ytPlayer = (YouTubePlayerView) findViewById(R.id.ytPlayer);
aws = new AndroidWebServer(8180, MainActivity.this);
}
}
assets/index.html
<html>
<head>
<title>CSS Contact Form</title>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
function formSubmit(){
var fname = document.getElementById("fname").value;
var dataString = 'fname='+ fname;
jQuery.ajax({
url: "http://192.168.100.42:8180",
data: dataString,
type: "POST",
success:function(data){
$("#myForm").html(data);
},
error:function (){}
});
return true;
}
</script>
</head>
<body>
<h2>Contact Form</h2>
<form class="form" action="" method="post">
<p class="name">
<div id="myForm">
<input type="text" name="fname" id="fname"/>
<label for="fname">Name</label>
</p>
</div>
<p class="submit" >
<input type="button" onclick="formSubmit();" value="Send"/>
</p>
</form>
</body>
</html>
I do everything according to the documentation, but my application crashes. Function triggering when there is a phone call I want to make. I did catch the call with Broadreciver. I did show on the screen with a toast message. But I can not send it to the service I created for headless js and run it. I can't debug or know the method
index.js
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
const Fnc = async (taskData) => {
console.log('data',taskData);
}
AppRegistry.registerComponent(appName, () => App);
AppRegistry.registerHeadlessTask('CallListener', () => Fnc);
AndroidManifest.xml
<application>
...
<receiver android:name=".CallReceiver">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
<service android:name=".CallService" android:enabled="true" />
</application>
CallReceiver.java
public class CallReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
try {
if (!isAppOnForeground((context))) {
if(intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_RINGING)){
String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
Intent serviceIntent = new Intent(context, CallService.class);
//Bundle bundle = new Bundle();
//bundle.putString("even", "okkk");
showText(context, "Callsss Ring...No: " + number);
serviceIntent.putExtra("event", "Incoming");
// serviceIntent.putExtra("event", "Incoming");
// serviceIntent.putExtras(bundle);
context.startService(serviceIntent);
HeadlessJsTaskService.acquireWakeLockNow(context);
}
}
}catch (Exception ex){
Log.i("ex", ex.getMessage());
}
}
private boolean isAppOnForeground(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses =
activityManager.getRunningAppProcesses();
if (appProcesses == null) {
return false;
}
final String packageName = context.getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance ==
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
appProcess.processName.equals(packageName)) {
return true;
}
}
return false;
}
void showText(Context context, String message){
Toast toast = Toast.makeText(context, message, Toast.LENGTH_LONG);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
}
CallService.java
public class CallService extends HeadlessJsTaskService {
#Override
protected #Nullable HeadlessJsTaskConfig getTaskConfig(Intent intent) {
Bundle extras = intent.getExtras();
WritableMap data = extras != null ? Arguments.fromBundle(extras) : null;
return new HeadlessJsTaskConfig(
"CallListener",
data,
5000, // timeout for the task
false // optional: defines whether or not the task is allowed in foreground. Default is false,
);
}
}
I've been researching for days, but I can't find the problem
This is my very first post here, so please don't blame me if I'm not as complete and clear as I have to be.
The issue
I am new to React native and I recently began to develop a react native app which could read my incoming SMS's aloud. I already achieved to retrieve the incoming messages and to read them aloud... But only if the app is the foreground.
So, could you please advise me some libraries or tutorials on the subject ?
I'm working on a Nokia 5 with Android 9.
I currently use the following libraries :
React-native-android-sms-listener to retrieve the incoming messages.
React-native-tts to read the content aloud.
What I already tried
I'm searching the Internet for more than a week now (includig Stack Overflow and this example question) and I can't find what I'm looking for. I already tried React-native-background-timer and React-native-background-job. But I couldn't never get a background timer working and React-native-background-job allows tasks to be executed every 15 minutes only (due to the Android limitations).
So I read many articles like this one explaining how to use Headless JS and other libraries until I found this codeburst tutorial today, explaining how to develop a background service to record audio calls. I tried to adapt it, but the background service never starts.
My code
I must tell you that I don't have any knowledge in Java, so the native code below may contain mistakes, even if it is based on tutorials and the React native documentation.
Currently, when the app is launched, the service IncomingSMSService is called. This service, developed following the Codeburst tutorial referenced above, relies on Headless JS and a JS function that listen to the incoming messages and then read them aloud thanks to React-native-tts.
Here is these two files :
IncomingSMSService.java
package com.ava.service;
import android.content.Intent;
import android.os.Bundle;
import com.facebook.react.HeadlessJsTaskService;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.jstasks.HeadlessJsTaskConfig;
public class IncomingSMSService extends HeadlessJsTaskService {
#Override
protected HeadlessJsTaskConfig getTaskConfig(Intent intent) {
Bundle extras = intent.getExtras();
if (extras != null) {
return new HeadlessJsTaskConfig(
"HandleIncomingSMS",
Arguments.fromBundle(extras),
5000,
true
);
}
return null;
}
}
HandleIncomingSMS.js
import { AppRegistry } from 'react-native';
import SmsListener from 'react-native-android-sms-listener';
import Tts from 'react-native-tts';
const HandleIncomingSMS = async (taskData) => {
SmsListener.addListener(message => {
Tts.getInitStatus().then(() => {
Tts.speak(`New message from number ${message.originatingAddress} : ${message.body}`);
});
});
}
AppRegistry.registerHeadlessTask('HandleIncomingSMS', () => HandleIncomingSMS));
These pieces of code are called in a BroadcastReceiver here (IncomingSMSReceiver.java) :
package com.ava.receiver;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.ava.service.IncomingSMSService;
import com.facebook.react.HeadlessJsTaskService;
import java.util.List;
public final class IncomingSMSReceiver extends BroadcastReceiver {
#Override
public final void onReceive(Context context, Intent intent) {
if (!isAppOnForeground((context))) {
Intent service = new Intent(context, IncomingSMSService.class);
context.startService(service);
HeadlessJsTaskService.acquireWakeLockNow(context);
}
}
private boolean isAppOnForeground(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses =
activityManager.getRunningAppProcesses();
if (appProcesses == null) {
return false;
}
final String packageName = context.getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance ==
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND &&
appProcess.processName.equals(packageName)) {
return true;
}
}
return false;
}
}
I also requested the good permissions in my AndroidManifest file, and I registered the service like so :
<service
android:name="com.ava.service.IncomingSMSService"
android:enabled="true"
android:label="IncomingSMSService"
/>
<receiver android:name="com.ava.receiver.IncomingSMSReceiver">
<intent-filter android:priority="0">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
What am I doing wrong ? I don't even see service in the Running services tab of the Android Developer options... Any ideas ?
Thanks in advance for your help.
UPDATE (01/06/2019)
After reading or watching several tutorials like this one or this video, I managed to get my app working in the foreground. It now displays a persistent notification.
BUT, I don't know how I can "link" my service and my Broadcsat Receiver to this notification (for now, the service is called only if the app is in foreground).
Here is my updated code :
// IncomingSMSService
package com.ava.service;
import android.graphics.Color;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.ContextWrapper;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import com.facebook.react.HeadlessJsTaskService;
import com.ava.MainActivity;
import com.ava.R;
public class IncomingSMSService extends Service {
private NotificationManager notifManager;
private String CHANNEL_ID = "47";
private int SERVICE_NOTIFICATION_ID = 47;
private Handler handler = new Handler();
private Runnable runnableCode = new Runnable() {
#Override
public void run() {
Context context = getApplicationContext();
Intent myIntent = new Intent(context, IncomingSMSEventService.class);
context.startService(myIntent);
HeadlessJsTaskService.acquireWakeLockNow(context);
handler.postDelayed(this, 2000);
}
};
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
createNotificationChannel();
}
#Override
public void onDestroy() {
super.onDestroy();
}
public void createNotificationChannel() {
NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID, "General", notifManager.IMPORTANCE_HIGH);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setShowBadge(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
getManager().createNotificationChannel(notificationChannel);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.handler.post(this.runnableCode);
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Ava")
.setContentText("Listening for new messages...")
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentIntent(contentIntent)
.setOngoing(true)
.build();
startForeground(SERVICE_NOTIFICATION_ID, notification);
return START_NOT_STICKY;
}
private NotificationManager getManager() {
if (notifManager == null) {
notifManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
return notifManager;
}
}
My headlessJS task :
// HandleIncomingSMS.js
import SmsListener from 'react-native-android-sms-listener';
import Tts from 'react-native-tts';
import Contacts from 'react-native-contacts';
import { text } from 'react-native-communications';
module.exports = async () => {
// To lower other applications' sounds
Tts.setDucking(true);
// Prevent the TTS engine from repeating messages multiple times
Tts.addEventListener('tts-finish', (event) => Tts.stop());
SmsListener.addListener(message => {
Contacts.getAll((err, contacts) => {
if (err) throw err;
const contactsLoop = () => {
contacts.forEach((contact, index, contacts) => {
// Search only for mobile numbers
if (contact.phoneNumbers[0].label === 'mobile') {
// Format the contact number to be compared with the message.oritignatingAddress variable
let contactNumber = contact.phoneNumbers[0].number.replace(/^00/, '+');
contactNumber = contactNumber.replace(/[\s-]/g, '');
// Phone numbers comparison
if (contactNumber === message.originatingAddress) {
if (contact.familyName !== null) {
Tts.speak(`Nouveau message de ${contact.givenName} ${contact.familyName} : ${message.body}`);
} else {
// If the contact doesn't have a known family name, just say his first name
Tts.speak(`Nouveau message de ${contact.givenName} : ${message.body}`);
}
} else if (contactNumber !== message.originatingAddress && index === contacts.length) {
// If the number isn't recognized and if the contacts have been all checked, just say the phone number
Tts.speak(`Nouveau message du numéro ${message.originatingAddress} : ${message.body}`);
}
}
});
}
contactsLoop();
// Redirect to the SMS app
text(message.originatingAddress, message = false);
});
});
}
I also added the good permissions in my AndroidManifest.xml file like the following :
...
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
...
I made some progress but I am still stuck, so if you have any idea, please share them ! Thank you !
I wrote a simple cordova plugin which displays an alert.
JS file: alert.js
module.exports = {
alert: function(title, message, buttonLabel, successCallback) {
cordova.exec(successCallback,
null, // No failure callback
"Alert",
"alert",
[title, message, buttonLabel]);
}
};
Java File: Alert.java
package com.acme.plugin.alert;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class Alert extends CordovaPlugin {
protected void pluginInitialize() {
}
public boolean execute(String action, JSONArray args, CallbackContext callbackContext)
throws JSONException {
if (action.equals("alert")) {
alert(args.getString(0), args.getString(1), args.getString(2), callbackContext);
return true;
}
return false;
}
private synchronized void alert(final String title,
final String message,
final String buttonLabel,
final CallbackContext callbackContext) {
new AlertDialog.Builder(cordova.getActivity())
.setTitle(title)
.setMessage(message)
.setCancelable(false)
.setNeutralButton(buttonLabel, new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int which) {
dialogInterface.dismiss();
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
}
})
.create()
.show();
}
}
How do I call the alert function of alert.js from another js? And what parameter should i pass to map to successCallback??
according to cordova git for creating plugin see github page you can do it like this
Add the following code to wherever you need to call the plugin functionality:
cordova.plugins.<PluginName>.<method>();
where <PluginName> is your plugin name and <method> is your method.
I have implemented websocket in spring but the JavaScript client cannot connect to the websocket.
Here is the WebSocketConfig class:
package com.myapp.spring.security.config;
import com.myapp.spring.web.controller.MyWebSocketHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
//import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.handler.PerConnectionWebSocketHandler;
#Configuration
#EnableWebMvc
#EnableWebSocket
#ComponentScan(basePackages={"com.myapp.spring.*"})
public class WebSocketConfig implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry
.addHandler(myWebSocketHandler(), "/endpoint")
.setAllowedOrigins("*");
}
#Bean
public WebSocketHandler myWebSocketHandler() {
return new PerConnectionWebSocketHandler(MyWebSocketHandler.class);
}
}
Here is the test.html page that tries to connect to the websocket:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<body>
<p>Going to connect to the WebSocket at /endpoint. Check Console</p>
<button onclick="ss()">Send</button>
</body>
<script type="text/javascript">
var webSocket = new WebSocket("wss://"+document.location.hostname+":8443"+"/endpoint");
webSocket.onopen = function(message) {
processOpen(message);
};
webSocket.onmessage = function(message) {
processMessage(message);
};
webSocket.onclose = function(message) {
processClose(message);
};
webSocket.onerror = function(message) {
processError(message);
};
function processOpen(message) {
console.log("JS: Server Connected... "+message);
}
function processMessage(message) {
console.log("Getting a mess: "+message);
}
function processClose(message) {
console.log("JS: Client disconnected... "+message);
}
function processError(message) { //
console.log("Error occured: "+message);
}
function ss() {
webSocket.send("test");
}
</script>
</html>
I initialized the websocket path to be at /endpoint. This is evident by my server logs which say that this has occurred:
[org.springframework.web.socket.server.support.WebSocketHandlerMapping] (ServerService Thread Pool -- 75) Mapped URL path [/endpoint] onto handler of type [class org.springframework.web.socket.server.support.WebSocketHttpRequestHandler]
When I open up test.html, the connection opens and immediately disconnects. The processOpen(message) and processClose(message) function are immediately called, one after the other. So what am I doing wrong, and how can I fix this?
Your java-script code in test.html looks fine. There could be some issue with Spring Web socket configuration which is closing the connection. Following is the working code for web socket with spring boot. Please compare with your configuration.
Web socket dependency in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
WebSocketHandler class
#Component
public class EchoHandler extends TextWebSocketHandler {
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
TextMessage echoMessage = new TextMessage("Echo :" + message.getPayload());
System.out.println("Sending "+echoMessage.getPayload());
session.sendMessage(echoMessage);
}
}
WebSocket Controller class
#EnableWebSocket
#Controller
public class WSController implements WebSocketConfigurer {
#Autowired
private EchoHandler echoHandler;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(echoHandler, "/echo").setAllowedOrigins("*");
}
}
Spring Boot application class
#SpringBootApplication
public class WSApplication {
public static void main(String[] args) {
SpringApplication.run(WSApplication.class, args);
}
}