Android/Java - ClassNotFoundException for ServerSocket and Socket communication - java

I'm developing an app where the phone (Android program) is the client trying to send data through a socket to a Java receiver program on my computer.
So far I've been able to send simple strings or whatever, but now I'm trying to send custom objects that I create. I should note that both programs are separate Eclipse projects, and I seem to be having trouble including the same custom class "Order" on the server side (even though I have Order.java in the src folder of the server project).
Some code, for reference:
Server:
private void run() throws Exception {
ServerSocket mySS = new ServerSocket(4443);
while(true) {
Socket SS_accept = mySS.accept();
InputStream is = SS_accept.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
Order order = (Order) ois.readObject();
if (order!=null){
java.util.List<String> items = order.getOrders();
int chair = order.getChair();
int table = order.getTable();
double price = order.getPrice();
System.out.println("Table: "+ table + " || Chair: " +chair);
for(String food: items) {
System.out.println(food);
}
System.out.println("Price: $"+price);
}
is.close();
SS_accept.close();
mySS.close();
}
And the relevant part of the client:
try {
mySocket = new Socket(serverService.ipAddress, serverService.port);
os = mySocket.getOutputStream();
oos = new ObjectOutputStream(os);
} catch(Exception e) {
Toast.makeText(PlaceOrder.this, "Error - not connected to server.", Toast.LENGTH_SHORT).show();
}
try {
oos.writeObject(order);
Toast.makeText(PlaceOrder.this, "Order Submitted!", Toast.LENGTH_SHORT).show();
refreshOrderPage(); //refresh page, allow waiter to make more orders
oos.close();
os.close();
mySocket.close();
} catch (Exception e) {
Toast.makeText(PlaceOrder.this, "Error - not connected to server.", Toast.LENGTH_SHORT).show();
}
Any ideas why I'm getting this error when trying to send objects through sockets?
Thanks.

You should probably set the serialVersionUID in the class, then build a jar with the shared classes and include that jar in both projects.
However, given you are using different JVMs (Oracle and Dalvik) there's no guarantee that the byte-level encoding is the same. You should either manually override the serialization using readObject/writeObject or use a different object encoding system that is guaranteed to be identical independent of the environment.

A stack trace would help, but almost certainly you're serializing an object on one side, sending it to the other, and the other side doesn't have a class definition with which it can reconstruct the object. In this case, it sounds like maybe your Server doesn't know about the com.foo.Order class.

You can also serialize object to some string format (json, yaml, xml) and pass it. It would much easier to maintain, I suppose.

Related

How do I deserialize the Minecraft player.dat?

I'm trying to make a plugin for Velocity to sync player data among different sub servers.
I want to deserialize the player.dat which is saved in the /world/playerdata/ directory, then upload ut to MySQL. When a player connect a different sub server, I'll read the data in MySQL and send the data to the targeted sub server to make the player data synchronous.
It's solved by myself.
We can use NBTCompressedStreamTools, which you need to know about NMS, like this
File playerDataFolder = new File(getDataFolder().getParentFile().getParentFile(), "world\\playerdata\\");
File playerDat = new File(playerDataFolder, player.getUniqueId().toString() + ".dat");
try {
FileInputStream inputStream = new FileInputStream(playerDat);
NBTTagCompound nbt = NBTCompressedStreamTools.a(inputStream);
}catch (Exception e) {
e.printStackTrace();
}

Save a variable when the server is off

In fact I am making a Minecraft plugin and I was wondering how some plugins (without using DB) manage to keep information even when the server is off.
For example if we make a grade plugin and we create a different list or we stack the players who constitute each. When the server will shut down and restart afterwards, the lists will become empty again (as I initialized them).
So I wanted to know if anyone had any idea how to keep this information.
If a plugin want to save informations only for itself, and it don't need to make it accessible from another way (a PHP website for example), you can use YAML format.
Create the config file :
File usersFile = new File(plugin.getDataFolder(), "user-data.yml");
if(!usersFile.exists()) { // don't exist
usersFile.createNewFile();
// OR you can copy file, but the plugin should contains a default file
/*try (InputStream in = plugin.getResource("user-data.yml");
OutputStream out = new FileOutputStream(usersFile)) {
ByteStreams.copy(in, out);
} catch (Exception e) {
e.printStackTrace();
}*/
}
Load the file as Yaml content :
YamlConfiguration config = YamlConfiguration.loadConfiguration(usersFile);
Edit content :
config.set(playerUUID, myVar);
Save content :
config.save(usersFile);
Also, I suggest you to make I/O async (read & write) with scheduler.
Bonus:
If you want to make ONE config file per user, and with default config, do like that :
File oneUsersFile = new File(plugin.getDataFolder(), playerUUID + ".yml");
if(!oneUsersFile.exists()) { // don't exist
try (InputStream in = plugin.getResource("my-def-file.yml");
OutputStream out = new FileOutputStream(oneUsersFile)) {
ByteStreams.copy(in, out); // copy default to current
} catch (Exception e) {
e.printStackTrace();
}
}
YamlConfiguration userConfig = YamlConfiguration.loadConfiguration(oneUsersFile);
PS: the variable plugin is the instance of your plugin, i.e. the class which extends "JavaPlugin".
You can use PersistentDataContainers:
To read data from a player, use
PersistentDataContainer p = player.getPersistentDataContainer();
int blocksBroken = p.get(new NamespacedKey(plugin, "blocks_broken"), PersistentDataType.INTEGER); // You can also use DOUBLE, STRING, etc.
The Namespaced key refers to the name or pointer to the data being stored. The PersistentDataType refers to the type of data that is being stored, which can be any Java primitive type or String. To write data to a player, use
p.set(new NamespacedKey(plugin, "blocks_broken"), PersistentDataType.INTEGER, blocksBroken + 1);

Chat client-server with file transfer and hybrid cryptography

I'm trying to realize a chat client-server (in local) that permits to exchange text and files. I utilize java.security and java.crypto to implement hybrid cryptography (and the tool Swing).
I exchange text in a serialized way (using ObjectInputStream and ObjectOutputStream), inserting it (after I crypted it with an apposite function) in byte[] format in Message (that is an object that i created and that is effectively exchanged between sockets):
import java.io.Serializable;
public class Message implements Serializable{
private static final long serialVersionUID = 1L;
byte[] data;
Message(byte[] data){
this.data = data;
}
byte[] getData(){
return data;
}
}
Chat works fine until I exchange only Message. Now I'm trying to implement file transfer (first I tried to implement file transfer from server to client, without cryptography), therefor I took a file "selectedFile" with FileChoser and I sent it to the client thanks ObjectOutputStream's method writeObject(selectedFile). On the client side I recognized if the object that is arrived is File or Message with:
class ListenFromServer extends Thread{
public void run(){
while(true){
try{
if((Message.class.isInstance(sInput.readObject()))==true){ //I verify if the recived object belongs to Message class
m=(Message) sInput.readObject();//sInput is an instance of ObjectInputStream class, connected to client socket's InputeStream
decryptMessage(m.getData()); //function that decrypts the content of m and inserts the result in a String that after I append in a text area
}
else
{
File recivedFile= (File) sInput.readObject();
File saveFile=new File(path+"/"+ recivedFile.getName());
save(recivedFile,saveFile);//function that insert the recived file in a specific folder
System.out.println("file ricevuto");
}
} catch (ClassNotFoundException ex) {
System.out.println("INFO: Classe non trovata durante la lettura dell'oggetto Messaggio: " + ex.getMessage() + "\n");
} catch(IOException e){
System.out.println("Connection closed");
break;
}
catch(NullPointerException ne){
ne.printStackTrace();
ne.getCause();
System.out.println("Errore"+ ne.getMessage());
}
The problem is that file is recived by the client only clicking twice server's "sendFile" button , moreover now this problem regards also the action of sending text to client, because client receives Message object only when I send it twice (I use two different methods to send a Message object and a File object).
This problem doesn't occur when I eliminate instruction:
if((Message.class.isInstance(sInput.readObject()))==true){
...
}
I ask you how to overcome this problem, or if there is a better way to distinguish File and Message objects in reception.
You're actually reading two objects in sequence, not one.
sInput.readObject()
This is an order to read an object. You give this twice in sequence, so that's two requests to reads different objects.
To fix this, just read the object once, test the type of the object, and cast it when appropriate:
Object inputObject = sInput.readObject(); // Read the object once
if (inputObject instanceof Message) { // If the object is a message
Message m = (Message) inputObject; // cast as a Message
... // use the Message m
} else if (inputObject instanceof File) { // else if it's a file
File f = (File) inputObject; // cast as a File
... // use the File f
}

pass object to another JVM using serialization - same Java version and jars (both running our app)

Updates:
For now using a Map. Class that wants to send something to other instance sends the object, the routing string.
Use an object stream, use Java serializable to write the object to servlet.
Write String first and then the object.
Receiving servlet wraps input stream around a ObjectInputStream. Reads string first and then the Object. Routing string decides were it goes.
A more generic way might have been to send a class name and its declared method or a Spring bean name, but this was enough for us.
Original question
Know the basic way but want details of steps. Also know I can use Jaxb or RMI or EJB ... but would like to do this using pure serialization to a bytearray and then encode that send it from servlet 1 in jvm 1 to servlet 2 in jvm 2 (two app server instances in same LAN, same java versions and jars set up in both J2EE apps)
Basic steps are (Approcah 1) :-
serialize any Serializable object to a byte array and make a string. Exact code see below
Base64 output of 1. Is it required to base 64 or can skip step 2?
use java.util.URLEncode.encode to encode the string
use apache http components or URL class to send from servlet 1 to 2 after naming params
on Servlet 2 J2EE framework would have already URLDecoced it, now just do reverse steps and cast to object according to param name.
Since both are our apps we would know the param name to type / class mapping. Basically looking for the fastest & most convenient way of sending objects between JVMs.
Example :
POJO class to send
package tst.ser;
import java.io.Serializable;
public class Bean1 implements Serializable {
/**
* make it 2 if add something without default handling
*/
private static final long serialVersionUID = 1L;
private String s;
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
}
* Utility *
package tst.ser;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URLEncoder;
public class SerUtl {
public static String serialize(Object o) {
String s = null;
ObjectOutputStream os = null;
try {
os = new ObjectOutputStream(new ByteArrayOutputStream());
os.writeObject(o);
s = BAse64.encode(os.toByeArray());
//s = URLEncoder.encode(s, "UTF-8");//keep this for sending part
} catch (Exception e) {
// TODO: logger
e.printStackTrace();
return null;
} finally {
// close OS but is in RAM
try {
os.close();// not required in RAM
} catch (Exception e2) {// TODO: handle exception logger
}
os = null;
}
return s;
}
public static Object deserialize(String s) {
Object o = null;
ObjectInputStream is = null;
try {
// do base 64 decode if done in serialize
is = new ObjectInputStream(new ByteArrayInputStream(
Base64.decode(s)));
o = is.readObject();
} catch (Exception e) {
// TODO: logger
e.printStackTrace();
return null;
} finally {
// close OS but is in RAM
try {
is.close();// not required in RAM
} catch (Exception e2) {// TODO: handle exception logger
}
is = null;
}
return o;
}
}
**** sample sending servlet ***
Bean1 b = new Bean1(); b.setS("asdd");
String s = SerUtl.serialize(b);
//do UrlEncode.encode here if sending lib does not.
HttpParam p = new HttpParam ("bean1", s);
//http components send obj
**** sample receiving servlet ***
String s = request.getParameter("bean1");
Bean1 b1 = (Beean1)SerUtl.deserialize(s);
Serialize any Serializable object with to a byte array
Yes.
and make a string.
No.
Exact statements see below
os = new ObjectOutputStream(new ByteArrayOutputStream());
os.writeObject(o);
s = os.toString();
// s = Base64.encode(s);//Need this some base 64 impl like Apache ?
s = URLEncoder.encode(s, "UTF-8");
These statements don't even do what you have described, which is in any case incorrect. OutputStream.toString() doesn't turn any bytes into Strings, it just returns a unique object identifier.
Base64 output of 1.
The base64 output should use the byte array as the input, not a String. String is not a container for binary data. See below for corrected code.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
os = new ObjectOutputStream(baos);
os.writeObject(o);
os.close();
s = Base64.encode(baos.toByeArray()); // adjust to suit your API
s = URLEncoder.encode(s, "UTF-8");
This at least accomplishes your objective.
Is it required to base 64 or can skip step 2?
If you want a String you must encode it somehow.
Use java.util.URLEncode.encode to encode the string
This is only necessary if you're sending it as a GET or POST parameter.
Use apache http components or URL class to send from servlet 1 to 2 after naming params
Yes.
On Servlet 2 J2EE framework would have already URLDecoded it, now just do reverse steps and cast to object according to param name.
Yes, but remember to go directly from the base64-encoded string to the byte array, no intermediate String.
Basically looking for the fastest & most convenient way of sending objects between JVMs.
These objectives aren't necessarily reconcilable. The most convenient these days is probably XML or JSON but I doubt that these are faster than Serialization.
os = null;
Setting references that are about to fall out of scope to null is pointless.
HttpParam p = new HttpParam ("bean1", s);
It's possible that HttpParam does the URLEncoding for you. Check this.
You need not convert to string. You can post the binary data straight to the servlet, for example by creating an ObjectOutputStream on top of a HttpUrlConnection's outputstream. Set the request method to POST.
The servlet handling the post can deserialize from an ObjectStream created from the HttpServletRequest's ServletInputStream.
I'd recommend JAXB any time over binary serialization, though. The frameworks are not only great for interoperability, they also speed up development and create more robust solutions.
The advantages I see are way better tooling, type safety, and code generation, keeping your options open so you can call your code from another version or another language, and easier debugging. Don't underestimate the cost of hard to solve bugs caused by accidentally sending the wrong type or doubly escaped data to the servlet. I'd expect the performance benefits to be too small to compensate for this.
Found this Base64 impl that does a lot of the heavy lifting for me : http://iharder.net/base64
Has utility methods :
String encodeObject(java.io.Serializable serializableObject, int options )
Object decodeToObject(String encodedObject, int options, final ClassLoader loader )
Using :
try {
String dat = Base64.encodeObject(srlzblObj, options);
StringBuilder data = new StringBuilder().append("type=");
data.append(appObjTyp).append("&obj=").append(java.net.URLEncoder.encode(dat, "UTF-8"));
Use the type param to tell the receiving JVM what type of object I'm sending. Each servlet/ jsps at most receives 4 types, usually 1. Again since its our own app and classes that we are sending this is quick (as in time to send over the network) and simple.
On the other end unpack it by :
String objData = request.getParameter("obj");
Object obj = Base64.decodeToObject(objData, options, null);
Process it, encode the result, send result back:
reply = Base64.encodeObject(result, options);
out.print("rsp=" + reply);
Calling servlet / jsp gets the result:
if (reply != null && reply.length() > 4) {
String objDataFromServletParam = reply.substring(4);
Object obj = Base64.decodeToObject(objDataFromServletParam, options, null);
options can be 0 or Base64.GZIP
You can use JMS as well.
Apache Active-MQ is one good solution. You will not have to bother with all this conversion.
/**
* #param objectToQueue
* #throws JMSException
*/
public void sendMessage(Serializable objectToQueue) throws JMSException
{
ObjectMessage message = session.createObjectMessage();
message.setObject(objectToQueue);
producerForQueue.send(message);
}
/**
* #param objectToQueue
* #throws JMSException
*/
public Serializable receiveMessage() throws JMSException
{
Message message = consumerForQueue.receive(timeout);
if (message instanceof ObjectMessage)
{
ObjectMessage objMsg = (ObjectMessage) message;
Serializable sobject = objMsg.getObject();
return sobject;
}
return null;
}
My point is do not write custom code for Serialization, iff it can be avoided.
When you use AMQ, all you need to do is make your POJO serializable.
Active-MQ functions take care of serialization.
If you want fast response from AMQ, use vm-transport. It will minimize n/w overhead.
You will automatically get benefits of AMQ features.
I am suggesting this because
You have your own Applications running on network.
You need a mechanism to transfer objects.
You will need a way to monitor it as well.
If you go for custom solution, you might have to solve above things yourselves.

NFC with NFC-Tools, Creating NDEF Application

I am attempting to do what I would have guessed would be pretty simple, but as it turns out is not. I have an ACR122 NFC reader and a bunch of Mifare Classic and Mifare Ultralight tags, and all I want to do is read and write a mime-type and a short text string to each card from a Java application. Here's what I've got working so far:
I can connect to my reader and listen for tags
I can detect which type of tag is on the reader
On the Mifare Classic tags I can loop through all of the data on the tag (after programming the tag from my phone) and build an ascii string, but most of the data is "junk" data
I can determine whether or not there is an Application directory on the tag.
Here's my code for doing that:
Main:
public static void main(String[] args){
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals;
try{
TerminalHandler handler = new TerminalHandler();
terminals = factory.terminals().list();
CardTerminal cardTerminal = terminals.get(0);
AcsTerminal terminal = new AcsTerminal();
terminal.setCardTerminal(cardTerminal);
handler.addTerminal(terminal);
NfcAdapter adapter = new NfcAdapter(handler.getAvailableTerminal(), TerminalMode.INITIATOR);
adapter.registerTagListener(new CustomNDEFListener());
adapter.startListening();
System.in.read();
adapter.stopListening();
}
catch(IOException e){
}
catch(CardException e){
System.out.println("CardException: " + e.getMessage());
}
}
CustomNDEFListener:
public class CustomNDEFListener extends AbstractCardTool
{
#Override
public void doWithReaderWriter(MfClassicReaderWriter readerWriter)
throws IOException{
NdefMessageDecoder decoder = NdefContext.getNdefMessageDecoder();
MadKeyConfig config = MfConstants.NDEF_KEY_CONFIG;
if(readerWriter.hasApplicationDirectory()){
System.out.println("Application Directory Found!");
ApplicationDirectory directory = readerWriter.getApplicationDirectory();
}
else{
System.out.println("No Application Directory Found, creating one.");
readerWriter.createApplicationDirectory(config);
}
}
}
From here, I seem to be at a loss as for how to actually create and interact with an application. Once I can create the application and write Record objects to it, I should be able to write the data I need using the TextMimeRecord type, I just don't know how to get there. Any thoughts?
::Addendum::
Apparently there is no nfc-tools tag, and there probably should be. Would someone with enough rep be kind enough to create one and retag my question to include it?
::Second Addendum::
Also, I am willing to ditch NFC-Tools if someone can point me in the direction of a library that works for what I need, is well documented, and will run in a Windows environment.
Did you checked this library ? It is well written, how ever has poor documentation. Actually no more than JavaDoc.

Categories

Resources