# String[] wird zu null bei Serialisierung



## lumo (11. Okt 2011)

Hallo,

ich schreibe gerade eine client/server anwendung und muss zugeben dass ich etwas dumm in die röhre kucke.

ich schicke meine objekte hin und her, ohne probleme.
wenn ich aber ein String[] als parameter eines objektes mitsende, wird daraus *null*
muss ich das array wirklich in einen string mit trennzeichen umwandeln? - oder in eine liste...

hoffe dass ich sonst irgendwas falsch mache... denn arrays wären mir am liebsten...

hier mal kurz der CLIENT log


> (20:33:43.174) Client: sending Command. enum Chat = {name=Chat, cmd='/chat', args.length='1', args='[test]'}
> (20:33:43.174) Client.onData(): DataPacket [type=10, data='ChatMessage [timestamp=20:33:43.174, from=BulletCollector, to=All, msg=null]', msg='Sending ChatMessage']


also der client sendet den enum chat raus, der client returniert ein datenpaket, wo die args von enum als msg drin sein sollte. tun sie aber nicht. nun denkt ihr sicher: der macht was beim umwandeln falsch... dachte ich auch!
doch dann hab ich den SERVER log gesehen


> (20:33:43.174) Server.onData(): DataPacket [type=99, data='enum Chat = {name=Chat, cmd='/chat', args.length='1', args='[null]'}', msg='Sending Command enum Chat = {name=Chat, cmd='/chat', args.length='1', args='[test]'}']
> (20:33:43.174) Server Broadcast: DataPacket [type=10, data='ChatMessage [timestamp=20:33:43.174, from=BulletCollector, to=All, msg=null]', msg='Sending ChatMessage']


und da sagt er mir, dass er bereits im enum chat als args ein [null] stehen hat (und dann steht dort, nach sending command, dass er aber einen enum MIT args =[test] weggeschickt hat!)

ergo, da ging was schief...

kann mir jemand erklären was da falsch läuft - SOLL das so sein?


----------



## SlaterB (11. Okt 2011)

zur Implementierung der Versendung schreibst du wenig, ObjectStreams?
was genau sind die Logs, Beschreibungen der Objekte jeweils VOR dem Versenden, NACH dem Entpacken?

falls du normale Objekte mehrfach über denselben ObjectStreams versendest, werden sie standardmäßig gecacht, da hilft reset()

bei richtigen Java-Enum-Klassen kannst man durchaus damit rechnen, das sparsamerweise nur die Nummer versendet und auf anderer Seite einfach der x-te Enum-Wert mit dem dortigen Zustand genommen wird,
Enums eignen sich nun wahrlich nicht zum dynamischen Informationstransport,
alles intensiv auszuprobieren


----------



## lumo (11. Okt 2011)

hier mal die funktionen zum senden  und empfangen:

```
public static boolean send(INonBlockingConnection nbc, DataPacket packet) {
		try {
			if (packet == null) {
				return false;
			}
			ByteArrayOutputStream bout = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(bout);
			oos.writeObject(packet);
			nbc.write(bout.toByteArray());
			nbc.write(DELIMITER);
			oos.close();
			bout.close();
		} catch (BufferOverflowException e) {
			return false;
		} catch (IOException e) {
			System.err
					.println("you try to send an Object (packet.getData()), which is not Serializable: "
							+ packet);
		}
		return true;
	}

	public static DataPacket receive(INonBlockingConnection nbc)
			throws IOException {
		if (!nbc.isOpen()) {
			System.out.println("cannot receive, nbc is closed.");
		}
		if (nbc.isReceivingSuspended()) {
			System.out.println("cannot receive, receiving is suspended!");
		}
		if (nbc.available() <= 0) {
			System.out.println("cannot receive, nothing available to read");
		}
		byte[] bytes = null;
		DataPacket packet = null;
		do {
			try {
				bytes = nbc.readBytesByDelimiter(DELIMITER);
				ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
				ObjectInputStream in = new ObjectInputStream(bin);
				packet = (DataPacket) in.readObject();
				in.close();
				bin.close();
			} catch (BufferUnderflowException bue) {
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		} while (bytes == null);
		return packet;
	}
```

ich schicke z.Z. enums, die werden dann mit integer werten ersetzt um daten zu sparen, jetzt will ich erstmal dass es übersichtlich ist und alles funktioniert


----------



## SlaterB (11. Okt 2011)

dieser Code schließt Cache-Probleme aus, 
dass du beim Senden das byte[] vor dem close() holst könnte ein Problem sein, aber wird deswegen einfach ein Wert null?
eher sollte ja alles kaputt gehen..

ansonsten sehe ich nichts, mit INonBlockingConnection und deiner Schleife beim Lesen sind aber noch manch mir etwas unbekannte Bestandteile dabei,
vorallem wäre jetzt aber spannend wie DataPacket aufgebaut ist, was genau zum Senden übergeben wird usw., der Komplettablauf

anbei ein vollständiges Testprogramm von mir, welches selbstverständlich funktioniert, arbeitet nur mit bis zum byte[], 
auf dieser Ebene kannst du ja testen ob das zumindest bei dir auch geht:

```
public class Test {
	public static void main(String[] args) throws Exception {
		DataPacket d = new DataPacket();
		d.data = new String[] { "test" };
		byte[] b = send(d);
		d.data = new String[] { "test2" };

		DataPacket d2 = receive(b);
		System.out.println(Arrays.toString((String[]) d2.data));
		System.out.println(Arrays.toString((String[]) d.data));
	}

	public static byte[] send(DataPacket packet) throws Exception {
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(bout);
		oos.writeObject(packet);
		oos.close();
		bout.close();
		return bout.toByteArray();
	}

	public static DataPacket receive(byte[] bytes) throws Exception {

		ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
		ObjectInputStream in = new ObjectInputStream(bin);
		DataPacket packet = (DataPacket) in.readObject();
		in.close();
		bin.close();
		return packet;
	}
}

class DataPacket implements Serializable {
	Object data;
}
```
Ausgabe:

```
[test]
[test2]
```


----------



## lumo (12. Okt 2011)

hier noch die daten-klassen Command & DataPacket

ich habe allerdings schon eine klasse zum testen dazu geschrieben und...
es kommt das richtige raus.

achja, der INonBlockingConnection über den ich sende ist der von xSocket!


```
package tests;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import lumo.net.clientserver.protocol.Command;
import lumo.net.clientserver.ssl.DataPacket;

public class Test {
    public static void main(String[] args) throws Exception {
        DataPacket d = new DataPacket();
        d.setData(Command.fromString("test"));
        byte[] b = send(d);
        d.setData(Command.fromString("/pm slater test"));
 
        DataPacket d2 = receive(b);
        System.out.println(d);
        System.out.println(d2);
    }
 
    public static byte[] send(DataPacket packet) throws Exception {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bout);
        oos.writeObject(packet);
        oos.close();
        bout.close();
        return bout.toByteArray();
    }
 
    public static DataPacket receive(byte[] bytes) throws Exception {
 
        ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
        ObjectInputStream in = new ObjectInputStream(bin);
        DataPacket packet = (DataPacket) in.readObject();
        in.close();
        bin.close();
        return packet;
    }
}
```



```
package lumo.net.clientserver.ssl;

import java.io.Serializable;

public class DataPacket implements Serializable {
	private static final long serialVersionUID = -5899002285730447746L;

	private int header;
	private Object data = null;
	private String msg = "";

	public DataPacket setHeader(int header) {
		this.header = header;
		return this;
	}

	public int getHeader() {
		return header;
	}

	public void setData(Object data) {
		this.data = data;
	}

	public Object getData() {
		return data;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	@Override
	public String toString() {
		return String.format("DataPacket [type=%s, data='%s', msg='%s']",
				header, data, msg);
	}
}
```


```
package lumo.net.clientserver.protocol;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public enum Command implements Serializable, Comparable<Command> {
	ChangeNick("/nick", 1), ChangeOtherNick("/nick", 2), ChangeRights(
			"/rights", 2), PrivateMessage("/pm", 2), Chat("/chat", 1), Kick(
			"/kick", 1), KickBecause("/kick", 2);

	private transient String cmdString;
	private String[] args;

	Command(String cmd, int argCount) {
		this.cmdString = cmd;
		args = new String[argCount];
	}

	public String getCmd() {
		return cmdString;
	}

	public int getArgCount() {
		return args.length;
	}

	public String[] getArgs() {
		return args;
	}

	public String getArg(int i) {
		try {
			return args[i];
		} catch (Exception e) {
			return "Index Out of Range!";
		}
	}

	public static Command getStartCommand(String str) {
		str = str.trim();
		Command[] cmds = Command.values();
		int argCount = str.split(" ").length;
		List<Command> possibleHits = new ArrayList<Command>();
		for (int i = 0; i < cmds.length; i++) {
			if (str.toLowerCase().startsWith(cmds[i].getCmd())) {
				possibleHits.add(cmds[i]);
			}
		}

		// only one possible hit!
		if (possibleHits.isEmpty()) {
			return Command.Chat;
		}
		if (possibleHits.size() == 1) {
			return possibleHits.get(0);
		}
		// we got more choices!
		Collections.sort(possibleHits, new CommandSorter());
		for (int i = 0; i < possibleHits.size(); i++) {
			Command c = possibleHits.get(i);
			if ((argCount - 1) == c.getArgCount()) {
				return c;
			}
		}
		return possibleHits.get(possibleHits.size() - 1);
	}

	public static Command fromString(String input) {
		Command cmd = Command.getStartCommand(input);
		if (cmd.equals(Command.Chat)) {
			cmd.args[0] = input;
		} else {
			// its a command
			input = input.substring(cmd.getCmd().length() + 1);
			String[] args = input.split(" ");
			if (args.length == cmd.getArgCount()) {
				// arg count does match
				cmd.args = args;
			} else if (args.length > cmd.getArgCount()) {
				int beginIndex = 0;
				for (int i = 0; i < cmd.getArgCount() - 1; i++) {
					cmd.args[i] = args[i];
					beginIndex += args[i].length() + 1; // +1 due space
				}
				cmd.args[cmd.getArgCount() - 1] = input.substring(beginIndex);
			}
		}
		return cmd;
	}

	public String toString() {
		return String.format(
				"enum Chat = {name=%s, cmd='%s', args.length='%d', args='%s'}",
				name(), getCmd(), args.length, Arrays.toString(args));
	}

	public static void check(List<String> strings) {
		for (int i = 0; i < strings.size(); i++) {
			String cmd = strings.get(i);
			System.out.println("\n" + Command.fromString(cmd));
		}
	}

	public static void main(String[] args) {
		List<String> strings = new ArrayList<String>();
		strings.add("/nick bulletcollector");
		strings.add("/nick lumo bulletcollector");
		strings.add("/rights lumo EDIT");
		strings.add("/pm lumo hallo alter sack");
		strings.add("/kick bulletcollector");
		strings.add("/kick bulletcollector lamer! laber mich nicht voll!");
		strings.add("Hello World!");
		check(strings);
	}
}
```


----------



## lumo (12. Okt 2011)

so hab jetzt noch was versucht...
und zwar hab ich deine funktion zum senden aus der test app in meine app geschrieben

und zwar bekommt der client immer seine msg zurück.
wenn ich zwei clients habe und je eine msg schicke
bekommt A die msg die A geschickt hat.
und B die msg die B geschickt hat.

egal ob a oder b senden...

also A sendet TEST
A empfängt angeblich TEST
B empfängt NULL

B sendet HALLO
A empfängt TEST
B empfängt HALLO

das kann doch eigentlich gar nicht sein...

EDIT: wenn du dich bereiterklärst den code anzusehen würde ich dir das ganze paketchen mal schicken... als zip (per email)


----------



## lumo (12. Okt 2011)

achja, viel mehr code ists nicht mehr


----------



## SlaterB (12. Okt 2011)

wie schon in erster Antwort recht deutlich geschrieben bietet sich Enum absolut nicht zur Übertragung an,

ändere das temporär in eine normale Klasse, lasse Command.getStartCommand(input) neue Objekte erzeugen, 
passe weiterhin an was anzupassen ist (equals), teste dann ob die Übertragung klappt

Enums sind feste vorgegebene Werte, dass aus der Deserialisierung neue zusätzliche Enum-Werte herauskommen wirst du sicher auch nicht annehmen, widerspräche ja komplett dem Konzept, 
allerhöchstens könnte nach Deserialisierung im vorhandenen Enum-Wert die Daten geändert werden,
einfach so während andere Klassen vielleicht parallel damit arbeiten? reichlich merkwürdig, selbst wenn es funktioniert,
ich weiß es nicht, will ich auch gar nicht testen, das ist kein sinnvolles Konzept,

was ist wenn ein Server relativ gleichzeitig von zwei Clients dieselben Commands erhält, wird dann zweimal schnell überschrieben?,
sieht es dann für den Server in der weiteren Verarbeitung aus als hätten beide dieselbe Nachricht geschickt, einem Client wird die Nachricht des anderen untergemogelt?
abenteuerlich

nein, Enum bleibt unveränderlich, du kannst ja gerne die Menge der Commands begrenzen und unterscheiden,
aber args sind dann zusätzliche Nutzdaten im DataPacket


-----

übrigens sind alle Enums vom Haus aus Serializable, vielleicht weil eben nur die Nummer/ der Name übertragen


----------



## lumo (12. Okt 2011)

also es lag wirklich am *enum*
danke dass du mir das erklärt hast, ich wusste nicht, dass der enum was statisches sein muss/ist.

für mich waren nur die funktionen wie verwendung im switch von interesse... das geht jetzt mit der id = Header int...

DANKE!


----------

