I'm trying to convert the following reflection into Kotlin. The following uses reflection to call an RFCOMMs function so it can take a port/channel as an input instead of UUID. I have all my program in Kotlin. Anyone know how to write this in Kotlin?
int bt_port_to_connect = 5;
BluetoothDevice device = mDevice;
BluetoothSocket deviceSocket = null;
...
// IMPORTANT: we create a reference to the 'createInsecureRfcommSocket' method
// and not(!) to the 'createInsecureRfcommSocketToServiceRecord' (which is what the
// android SDK documentation publishes
Method m = device.getClass().getMethod("createInsecureRfcommSocket", new Class[] {int.class});
deviceSocket = (BluetoothSocket) m.invoke(device,bt_port_to_connect);
Updating with recommendation:
class BluetoothClient(device: BluetoothDevice): Thread() {
// https://stackoverflow.com/questions/9703779/connecting-to-a-specific-bluetooth-port-on-a-bluetooth-device-using-android
// Need to reflection - create RFCOMM socket to a port number instead of UUID
// Invoke btdevice as 1st parameter and then the port number
var bt_port_to_connect = 5
var deviceSocket: BluetoothSocket? = null
private val socket = device.createInsecureRfcommSocketToServiceRecord(uuid)
val m = device::class.declaredFunctions.single { it.name == "createInsecureRfcommSocket" }
m.call(device, bt_port_to_connect)
override fun run() {
try {
Log.i("client", "Connecting")
this.socket.connect()
Log.i("client", "Sending")
val outputStream = this.socket.outputStream
val inputStream = this.socket.inputStream
try {
outputStream.write(message.toByteArray())
outputStream.flush()
Log.i("client", "Sent")
} catch(e: Exception) {
Log.e("client", "Cannot send", e)
} finally {
outputStream.close()
inputStream.close()
this.socket.close()
}
}
catch (e: IOException) {
println("Socket Failed")
}
}
}
You can really use exactly the same code, just convert it to Kotlin:
val m = device::class.java.getMethod("createInsecureRfcommSocket", Int::class.java)
m.invoke(device, bt_port_to_connect)
Or you can use Kotlin reflection:
val m = device::class.declaredFunctions.single { it.name == "createInsecureRfcommSocket" }
m.call(device, bt_port_to_connect)
I don't know if there is any better way to find a function with provided name. You can create an extension function to make it cleaner. You may also need to check parameters if this function has overrides.
Related
Currently, we are trying to write a Flutter-Dart client (ported from a Java Client) using sockets for transferring plaintext-data and files.
The Server is written in Kotlin and works well with Kotlin clients sending a UTF Package (information about the binary package sent after), followed by a long package defining the package size and finally the binary data.
A small truncated sample code for the server is:
class DataProcessRunnable internal constructor(private val socket: Socket, private val onFileReceivedListener: OnFileReceivedListener) : Runnable {
private val dateFormat = SimpleDateFormat("HH-mm-ss")
private val dateDayFormat = SimpleDateFormat("yyyy-MM-dd")
override fun run() {
try {
DataInputStream(socket.getInputStream()).use { stream ->
print ("connected ${stream.toString()}")
val rawData = stream.readUTF();
print("Rawdata: $rawData")
val fileData = gson.fromJson(rawData, FileData::class.java)
fileData.ipAddress = socket.inetAddress.hostAddress
val fileSize = stream.readLong()
println("sender: " + socket.inetAddress)
println("Filedata:$fileData")
println("filesize:$fileSize")
var fileName = "noname"
val fileTransfer = FileTransfer()
fileTransfer.fileSize = fileSize
fileTransfer.filename = fileName
println("filename:$fileName")
try {
try {
try {
GZIPInputStream(stream).use { input ->
FileOutputStream(fileName).use { out ->
input.copyTo(out)
}
}
} catch (ex: Exception) {
ex.printStackTrace()
} finally {
println("Wrote File in GZIP")
}
fileTransfer.transferedSize = fileTransfer.fileSize
val fData = FileData()
fData.file = File(fileName)
fData.extras = fileData.extras
fData.ipAddress = socket.inetAddress.toString()
fData.type = fileData.type
onFileReceivedListener.fileReceived(fData)
} catch (ex: Exception) {
ex.printStackTrace()
}
} catch (ex: Exception) {
ex.printStackTrace()
}
}
} catch (ex: Exception) {
ex.printStackTrace()
}
}
}
When we use Flutter-Dart to establish a socket, we are trying to send the first UTF8-Package using that codesample. The connection is established but readUTF can't read any data.
await Socket.connect('dev1.local-cloud-server.local', 2555).then((socket) {
socket.writeln(gzip.encode(utf8.encode(fData.toString())));
socket.flush();
});
});
After trying around several methods to send a utf8 encoded string from flutter dart to java, the only way I was able to read the first package was using (on the serverside, code truncated):
private fun validateSocketConnection2(socket: Socket) {
Thread {
DataInputStream(socket.getInputStream()).use { stream ->
val rawData = stream.bufferedReader().forEachLine { println(it) }
print("Rawdata: $rawData")
}.start()
}
Using a bufferedreader i was able to read the utf8 encoded string properly, but that would break the whole kotlin server code for other clients. In Flutter-Dart we tried to use sockets.add(), sockets.write("...\n"), sockets.writeln(), await socket.flush() after every package sent and also socket.close() to ensure that all data were transmitted.
But we were not able to read data using readUTF as we do for any other Java/Kotlin/C# client.
What are we missing to get readUTF working?
I have developed a compose app which retrieves crypto data (a list of 1063 objects) from Socket each 3 seconds. While using app I recognized that after some time working with app it crashes with Out of memory. I profiled my app (release version) and recognized that while using app java code increase until it crashes with OOM error.
Let me show you some code for better understanding.
For socket, I created a SocketManager class which instantiates on app startup via Dagger Hilt in singleton component.
class SocketManager #Inject constructor(
socket: Socket,
private val json: Json
) {
var cryptoFlow = MutableStateFlow<List<CryptoDataSetItemDto>>(emptyList())
init {
socket.connect()
socket.on("crypto_data") {
it.forEach { data ->
try {
val coinList =
json.decodeFromString<List<CryptoDataSetItemDto>>(data.toString())
cryptoFlow.tryEmit(coinList)
} catch (e: Exception) {
Sentry.captureException(e)
}
}
}
}
}
#Module
#InstallIn(SingletonComponent::class)
object SocketModule {
#Provides
#Singleton
fun provideJson(): Json {
return Json {
encodeDefaults = true
ignoreUnknownKeys = true
}
}
#Provides
#Singleton
fun provideSocket(client: OkHttpClient): Socket {
val socket: Socket
val opts = IO.Options()
opts.path = Constants.SOCKET_PATH
opts.secure = true
socket = IO.socket(Constants.SOCKET_BASE_URL, opts)
return socket
}
#Provides
#Singleton
fun provideSocketManager(socket: Socket, json: Json): SocketManager {
return SocketManager(socket, json)
}
}
Then I inject SocketManager to viewModel to get data on the screens I need those data. I create a function which gets a coroutine context from composable and collects data in that scope.
#HiltViewModel
class WithdrawDepositViewModel #Inject constructor(
private val useCase: WalletUseCases,
private val socketManager: SocketManager,
savedStateHandle: SavedStateHandle
) : ViewModel() {
fun getCoinData(context: CoroutineContext) {
CoroutineScope(context).launch(Dispatchers.Default) {
socketManager.cryptoFlow.asStateFlow()
.shareIn(CoroutineScope(context), SharingStarted.WhileSubscribed())
.collectLatest { cryptoListDto ->
val cryptoList = cryptoListDto.map { it.toCryptoList() }.toMutableList()
val irt = CryptoDataItem(
enName = "Toman",
faName = "تومان",
symbol = "IRT"
)
cryptoList.add(0, irt)
_state.value = state.value.copy(
cryptoList = cryptoList,
isLoading = false
)
}
}
}
and in screen I call the function like this:
#Composable
fun HomeScreen(
onNavigate: (String) -> Unit,
scaffoldState: ScaffoldState,
viewModel: HomeScreenViewModel = hiltViewModel()
) {
val scope = rememberCoroutineScope()
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(key1 = lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_CREATE) {
viewModel.getCryptoData(scope.coroutineContext)
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
}
This is my architecture to get data. when looked at heap dump result, I recognized that CryptoDataSetItemDto object which I get it on socket, has allocated lots of memory. I can not find the problem also I know that there is better architecture which I hope to learn from you.
I'm looking for using MC|Brand channel on a sponge minecraft server.
When i'm trying to use :
Sponge.getChannelRegistrar().getOrCreateRaw(plugin, channel).addListener((data, connection, side) -> {
if(side == Type.CLIENT) {
// do something
}
});
I'm getting this issue:
org.spongepowered.api.network.ChannelRegistrationException: Reserved channels cannot be registered by plugins
at org.spongepowered.server.network.VanillaChannelRegistrar.validateChannel(VanillaChannelRegistrar.java:71) ~[VanillaChannelRegistrar.class:1.12.2-7.3.0]
at org.spongepowered.server.network.VanillaChannelRegistrar.createRawChannel(VanillaChannelRegistrar.java:104) ~[VanillaChannelRegistrar.class:1.12.2-7.3.0]
at org.spongepowered.api.network.ChannelRegistrar.getOrCreateRaw(ChannelRegistrar.java:122) ~[ChannelRegistrar.class:1.12.2-7.3.0]
How can I fix it, just by using channel ? Is there an event for reserved MC channel message ?
I tried to register the channel exactly as Sponge does, but without the check that create the issue.
To do it, I use Java Reflection like that :
RawDataChannel spongeChannel = null; // declare channel
try {
// firstly, try default channel registration to go faster
spongeChannel = Sponge.getChannelRegistrar().getOrCreateRaw(plugin, channel);
} catch (ChannelRegistrationException e) { // error -> can't register
try {
// load class
Class<?> vanillaRawChannelClass = Class.forName("org.spongepowered.server.network.VanillaRawDataChannel");
Class<?> vanillaChannelRegistrarClass = Class.forName("org.spongepowered.server.network.VanillaChannelRegistrar");
Class<?> vanillaBindingClass = Class.forName("org.spongepowered.server.network.VanillaChannelBinding");
// get constructor of raw channel
Constructor<?> rawChannelConstructor = vanillaRawChannelClass.getConstructor(ChannelRegistrar.class, String.class, PluginContainer.class);
spongeChannel = (RawDataChannel) rawChannelConstructor.newInstance(Sponge.getChannelRegistrar(), channel, plugin.getContainer()); // new channel instance
// now register channel
Method registerChannel = vanillaChannelRegistrarClass.getDeclaredMethod("registerChannel", vanillaBindingClass); // get the method to register
registerChannel.setAccessible(true); // it's a private method, so set as accessible
registerChannel.invoke(Sponge.getChannelRegistrar(), spongeChannel); // run channel registration
} catch (Exception exc) {
exc.printStackTrace(); // reflection failed
}
}
if(spongeChannel == null) // channel not registered
return;
// my channel is now registered, by one of both available method. That's perfect
spongeChannel.addListener((data, connection, side) -> { // my listener
if(side == Type.CLIENT) {
// do something
}
});;
If there already have an error, specially when the reflection failed, I suggest you to check for new version, maybe method have change her parameter or class have been moved.
You can find Sponge code on their github.
I am trying to override some class of vertx web project, since I have to change some of the features. So the tricky part comes here.
#Override
public void reroute(HttpMethod method, String path) {
int split = path.indexOf('?');
if (split == -1) {
split = path.indexOf('#');
}
if (split != -1) {
log.warn("Non path segment is not considered: " + path.substring(split));
// reroute is path based so we trim out the non url path parts
path = path.substring(0, split);
}
/*((HttpServerRequestWrapper) request).setMethod(method);
((HttpServerRequestWrapper) request).setPath(path);*/
((HttpServerRequestWrapper) request).setMethod(method);
((HttpServerRequestWrapper) request).setPath(path);
request.params().clear();
// we need to reset the normalized path
normalisedPath = null;
// we also need to reset any previous status
statusCode = -1;
// we need to reset any response headers
response().headers().clear();
// special header case cookies are parsed and cached
if (cookies != null) {
cookies.clear();
}
// reset the end handlers
if (headersEndHandlers != null) {
headersEndHandlers.clear();
}
if (bodyEndHandlers != null) {
bodyEndHandlers.clear();
}
failure = null;
restart();
}
This code throws me a compilation error saying:
'HttpServerRequestWrapper cannot be accessed from outside package'
I know for a fact that we can use reflection to create objects of a class that cannot be accessed. Can reflection be used in this case? How can I fix such an issue.
Any help will be much appreciated.
In java 8 and/or without modules it is possible to just place class like that in same package as original one to get access to all package-default classes.
Otherwise you need to use reflections like in other response, but I would add that it is good idea to cache that Class and Method instance, as using Class.forName and clazz.getDeclaredMethod each time will slowdown code.
What about getting the Class object and then calling the methods on your specific (uncasted) object?
I assume request is a class attribute of type HttpServerRequestWrapper. Then, this is what I suggest:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
...
private final Method setMethod;
private final Method setPath;
public MyConstructor() {
Method tmp1 = null, tmp2 = null;
try {
final Class<?> clazz = Class.forName("io.vertx.ext.web.impl.HttpServerRequestWrapper");
tmp1 = clazz.getMethod("setMethod", HttpMethod.class);
tmp1.setAccessible(true);
tmp2 = clazz.getMethod("setPath", String.class);
tmp2.setAccessible(true);
} catch (ClassNotFoundException e) {
// do something
} catch (NoSuchMethodException e) {
// do something
} catch (SecurityException e) {
// do something
}
this.setMethod = tmp1;
this.setPath = tmp2;
}
...
#Override
public void reroute(HttpMethod method, String path) {
...
try {
this.setMethod.invoke(request, method);
this.setPath.invoke(request, path);
} catch (IllegalAccessException e) {
// do something
} catch (IllegalArgumentException e) {
// do something
} catch (InvocationTargetException e) {
// do something
}
...
}
EDIT: I updated this answer based on #GotoFinal's suggestion.
It looks like HttpServerRequestWrapper implements HttpServerRequest. So, you can change "HttpServerRequestWrapper" to "HttpServerRequest" in your code. But remember that by doing so, you'll only be able to call methods specified in the interface.
You can see those methods in https://vertx.io/docs/apidocs/io/vertx/rxjava/core/http/HttpServerRequest.html.
I am trying to write a test case for checkRegistry method, which is a private method, I am using PowerMock-EasyMock-Juint to realize this.
Now to test this methods I want to suppress the calls to super calls methods
eg:
intigration = super.getParam("integritycheck");
I dont want the call to go to superClass method, but at the same time I want the variable integration to be set. How do I realize this?
The difficulty is
super.getParam("integritycheck"); and
sTmpOverride = super.getParam("RESPONSE_OVERRIDE"); will return different results.
Method that I am trying to write unit test.
private String checkRegistry()
{
String intigration = "";
String sresponse = "";
try
{
try
{
intigration = super.getParam("integritycheck");
sresponse = CustomImpl.getParam("responseWrite");
**Some Business Logic**
sTmpOverride = super.getParam("RESPONSE_OVERRIDE");
if (sTmpOverride == null) {
this._bRespOverride = true;
} else {
this._bRespOverride = sTmpOverride.trim().equalsIgnoreCase(
"true");
}
sTmpOverride = super.getParam("ERROR_OVERRIDE");
if (sTmpOverride == null) {
this._bErrOverride = true;
} else {
this._bErrOverride = sTmpOverride.trim().equalsIgnoreCase(
"true");
}
**Some Business Logic**
Logging.info("integritycheck : " + intigration );
Logging.info("responseWrite : " + sresponse );
super.track("Error Directory : " + sErrorPath);
}
}
catch (Exception ex)
{
_result= false;
}
return _result;
}
I am struck on using the below method
suppress(method(CustomRegisterChecker.class, "getParam"));
where CustomRegisterChecker.class is the super class.
UPDATE1:
//Here I am creating a mock for the super class
CustomRegisterChecker customRegisterMock=createMock(AbstractListener.class);
//I am supresssing the calls made to the super class and giving my own response
expect(abstractListenerMock.getParam("integritycheck ")).andReturn(null);
expect(abstractListenerMock.getParam("responseWrite")).andReturn(null);
But How do I invoke and test the method. I tried using Reflection API. BUT that does not work. it just simply run the program, supressing the super class methods does not happen here.