this is my very first thread on StackOverflow so please be patient!
Starting from a simple thrift service, I want to create a java client/server application, where each node offers one or more services to other nodes in a distributed computing system. I want to start with a simple system where each node is characterized by two strings, "hostname" and "servicename", that identify each from others. I have created this class:
class NodePort <Node, integer> {
private Node node;
private int port;
NodePort(Node node, int port) {
this.node = node;
this.port = port;
}
Node getNode() {
return node;
}
int getPort() {
return port;
}
}
Then, I've written a management thrift service that does two things:
1) registers a node to a server, returning the port used
2) lists all the services available at the moment
typedef i32 int
struct Node {
1: string hostName,
2: string serviceName,
}
service ManagementService {
int NodeRegister(i32 port)
Node getNodeForService(Node node)
}
When NodeRegister is used, the server creates a Map where keys are given by serviceNames and values are NodePorts, and returns the port used by the client.
When getNodeForService is used, it should return a list of services present in the Map registered by a client.
I'm not sure about how to write the implementation of the two methods. I've written this but when I try a test it doesn't work:
import org.apache.thrift.TException;
import it.uniud.atta.profiling.thrift.ManagementService;
public class ManagementServiceImpl implements ManagementService.Iface {
public int NodeRegister(int port) throws TException {
return port;
}
public Node getNodeForService(Node node) throws TException {
return node;
}
}
I'm pretty sure that this doesn't work the way I want, and also I don't really know where to put the Map code. How could I proceed?
Thanks in advance for any help!
edit: I'm adding the content of my "answer to the question with a question" post. Sorry about it, next time i'll read the rules better!
i actually need help writing the test file. So far i've created a standard thrift server listening on a port, and a thrift client. The code is the following:
public class ManagementServiceTest {
public static class Server implements Runnable {
private TServer tserver;
public void run() {
try {
TServerSocket serverTransport = new TServerSocket(7911);
ManagementService.Processor<ManagementServiceHandler> processor = new ManagementService.Processor<ManagementServiceHandler>(new ManagementServiceHandler());
tserver = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).
processor(processor));
System.out.println("Starting server on port 7911 ...");
tserver.serve();
} catch (TTransportException e) {
e.printStackTrace();
}
}
public void start() {
new Thread(this).start();
}
public void unserve() {
tserver.stop();
}
}
#Test
public void testServerCompletion() {
Server server = new Server();
server.start();
TTransport transport;
try {
Thread.sleep(200);
transport = new TSocket("localhost", 7911);
TProtocol protocol = new TBinaryProtocol(transport);
ManagementService.Client client = new ManagementService.Client(protocol);
transport.open();
// client.getNodes(testNode.hostName, testNode.serviceName);
// System.out.println("Servizi disponibili: " + testNode.serviceName);
//
// for (String key: nodes.keySet()) {
//
// System.out.println("key : " + key);
// System.out.println("value : " + nodes.get(key));
transport.close();
server.unserve();
} catch (Exception e) {
e.printStackTrace();
}
}
}
I'm not sure about what do I have to write after the transport.open() line. Right now, I should have a server open on port 7911 and listening, a transport open and an unused client variable that I need to initialize. I want to use the RegisterNode() method and the getNodes() method, but so far all my attempts have been unsuccessfull. i've tried with:
Node node1;
node1 = client.RegisterNode(1025);
but it returns "The method RegisterNode(Node, int) in the type ManagementService.Client is not applicable for the arguments (String, int)"
So i tried with
Node node1;
node1 = client.RegisterNode(node1, 1025);
but it returns "Type mismatch: cannot convert from int to Node".
I haven't tried with the other method yet, but I guess i'll receive a similar kind of errors.
I'm not sure I've understood how to write a test file... Any hint?
edit: ok, I've managed to solve the problem with this:
int port = 0;
Node node1 = new Node();
node1.hostname = "Node 1";
node1.serviceName = "Hello World Service";
port = client.RegisterNode(node1, 1025);
System.out.println("Node hostname: " + node1.hostName + "Node Port Used: " + port);
it indeed returns the name of the node and the port used.
Now the second method, getNodes(), presents issues as well. I'm using this:
Nodes nodes = new Nodes();
client.getNodes(node1.hostName, node1.serviceName);
System.out.println("Servizi disponibili: " + node1.serviceName);
for (String key: nodes.keySet()) {
System.out.println("key : " + key);
System.out.println("value : " + nodes.get(key));
}
but it returns two errors about keySet() and get() methods not being defined for the tyoe Nodes. I'm not sure on how should I proceed...
I would structure the service differently, like so:
struct Node {
1: string hostName,
2: string serviceName,
}
struct Nodes {
1: map<Node, i32> allNodes
}
service ManagementService {
/** registers a node to a server, returning the port used */
i32 RegisterNode(1 : Node hostAndService, 2 : i32 preferredPort)
/** lists all the services available at the moment.
Optionally specify host and/or service name to narrow the search */
Nodes GetNodes(1: string host, 2: string service)
}
The map<> code would be in both service routines. In RegisterNode() a node is added to the map, in GetNodes() the map would be scanned for the values needed.
Sample code, not tested (not even compiled, to be honest):
public static class ManagementServiceHandler implements ManagementService.Iface {
private Map<Node,int> nodes;
public ManagementServiceHandler()
{
nodes = new Map<Node,int>()
}
/**
* registers a node to a server, returning the port used
*/
public int RegisterNode(Node hostAndService, int preferredPort) {
int port = preferredPort;
// TODO: check whether port can be used
nodes.Add( hostAndService, port);
return port;
}
/**
* lists all the services available at the moment.
* Optionally specify host and/or service name to narrow the search
*/
public Nodes GetNodes(String host, String svcname) {
var Nodes retval = new Nodes();
retval.allNodes = new Map<Node,int>();
for( var node : nodes) {
if( (host.isEmpty() || (host == node.key.host)) {
if( (svcname.isEmpty() || (svcname == node.key.serviceName)) {
retval.allNodes.add(node)
}
}
}
return retval;
}
}
Related
I have classes Source, Intermediate and Destination. Source class has method which with some probability receives new packets to send. Whenever packet is received it should be broadcasted (simulated by some setPacket method) to all neighbor intermediate nodes. Intermediate nodes should broadcast those packets to their neighbors until packets reach destination. The problem is whenever the chain of packet transmission is started source node stops running its method for getting new packets. So, basically the source class is frozen while chain of functions are working. Is there any way to run these two processes simultaneously? (It's like Source node will send request to Intermediate class and that one will do it's work independently) I tried for each of three classes to extend Thread class but still transmission chain is freezing Source class. The language used is Java, but if there any other language that could do the job, I can switch to them.
public class Main {
public static void main(String[] args) {
// init nodes
SourceNode sourceNode = new SourceNode();
IntermediateNode intermediateNode1 = new IntermediateNode();
IntermediateNode intermediateNode2 = new IntermediateNode();
IntermediateNode intermediateNode3 = new IntermediateNode();
DestinationNode destinationNode = new DestinationNode();
// create network topology, S - I - I - I - D
sourceNode.setNextNode(intermediateNode1);
intermediateNode1.setNextNode(intermediateNode2);
intermediateNode2.setNextNode(intermediateNode3);
intermediateNode3.setNextNode(destinationNode);
// setup listeners
sourceNode.setSetupMessageListener(intermediateNode1);
intermediateNode1.setSetupMessageListener(intermediateNode2);
intermediateNode2.setSetupMessageListener(intermediateNode3);
intermediateNode3.setSetupMessageListener(destinationNode);
sourceNode.run();
}
}
public interface SetupMessageListener {
void onNewSetupMessage();
}
public class Node {
protected SetupMessageListener setupMessageListener;
protected Node nextNode;
public void setNextNode(Node nextNode) {
this.nextNode = nextNode;
}
public void setSetupMessageListener(SetupMessageListener setupMessageListener) {
this.setupMessageListener = setupMessageListener;
}
}
import java.util.Random;
public class SourceNode extends Node implements Runnable {
#Override
public void run() {
while(true) {
// simulate generating new setup message with probability 1/10
Random random = new Random();
int rv = random.nextInt(10);
if (rv == 0) {
createNewSetupMessage();
}
}
}
public void createNewSetupMessage() {
System.out.println("New setup message was created in Source Node");
if (setupMessageListener != null) {
setupMessageListener.onNewSetupMessage();
}
}
}
public class IntermediateNode extends Node implements SetupMessageListener {
public static int count = 0;
private int id;
public IntermediateNode() {
id = count++;
}
#Override
public void onNewSetupMessage() {
System.out.println("Intermediate Node " + id + " got notified about setup message");
// pass setup message to next neighbor
setupMessageListener.onNewSetupMessage();
}
}
public class DestinationNode extends Node implements SetupMessageListener {
#Override
public void onNewSetupMessage() {
System.out.println("Destination Node got notified about new setup message");
}
}
And the example output is
New setup message was created in Source Node
Intermediate Node 0 got notified about setup message
Intermediate Node 1 got notified about setup message
Intermediate Node 2 got notified about setup message
Destination Node got notified about new setup message
New setup message was created in Source Node
Intermediate Node 0 got notified about setup message
However, I want it to be smth like
New setup message was created in Source Node
Intermediate Node 0 got notified about setup message
New setup message was created in Source Node
Intermediate Node 1 got notified about setup message
Intermediate Node 0 got notified about setup message
Intermediate Node 2 got notified about setup message
Destination Node got notified about new setup message
The thing is that the whole message passing is done in the main thread....you need to implement the functionality of createNewSetupMessage and onNewMessageSetup as instances of Runnable and initiate new threads to run them.
public class SetupMessageSender implements Runnable{
private SetupMessageListener setupMessageListener;
public SetupMessageSender(SetupMessageListener setupMessageListener){
this.setupMessageListener = setupMessageListener;
}
#Override
public void run() {
if (setupMessageListener != null) {
setupMessageListener.onNewSetupMessage();
}
}
public SetupMessageListener getSetupMessageListener() {
return this.setupMessageListener;
}
public void setSetupMessageListener(SetupMessageListener setupMessageListener) {
this.setupMessageListener = setupMessageListener;
}
}
then
public void createNewSetupMessage() {
System.out.println("New setup message was created in Source Node");
Thread smService = new Thread(new SetupMessageSender(this.setupMessageListener));
smService.start();
}
Be careful though at node listeners onNewMessageSetup, because it will need a bit of syncronization depending on the topology(e.g if an intermediate node is listener for more than one different nodes), so make it
#Override
public synchronized void onNewSetupMessage() {
System.out.println("Intermediate Node " + id + " got notified about setup message");
// pass setup message to next neighbor
Thread smService = new Thread(new SetupMessageSender(this.setupMessageListener));
smService.start();
}
Although the 2 methods provide the same functionality and you could implement them at your base class Node as a single method.
In Apache Flink, I am not able to see the output in std out, but my job is running successfully and data is coming
As you are running your job on a cluster, DataStreams are printed to the stdout of the TaskManager process. This TaskManager stdout is directed to an .out file in the ./log/ directory of the Flink root directory. I believe this is here you have seen your output.
I don't know if it is possible to change the stdout of TaskManagers, however, a quick and dirty solution could be to write the output to a socket :
output.writeToSocket(outputHost, outputPort, new SimpleStringSchema())
public static void main(String[] args) throws Exception {
// the host and the port to connect to
final String hostname = "192.168.1.73";
final int port = 9000;
final StreamExecutionEnvironment env = StreamExecutionEnvironment.createRemoteEnvironment("192.168.1.68", 6123);
// get input data by connecting to the socket
DataStream<String> text = env.socketTextStream(hostname, port, "\n");
// parse the data, group it, window it, and aggregate the counts
DataStream<WordWithCount> windowCounts = text
.flatMap(new FlatMapFunction<String, WordWithCount>() {
public void flatMap(String value, Collector<WordWithCount> out) {
for (String word : value.split("\\s")) {
out.collect(new WordWithCount(word, 1L));
}
}
})
.keyBy("word").timeWindow(Time.seconds(5))
.reduce(new ReduceFunction<WordWithCount>() {
public WordWithCount reduce(WordWithCount a, WordWithCount b) {
return new WordWithCount(a.word, a.count + b.count);
}
});
// print the results with a single thread, rather than in parallel
windowCounts.print().setParallelism(1);
env.execute("Socket Window WordCount");
}
public static class WordWithCount {
public String word;
public long count;
public WordWithCount() {
}
public WordWithCount(String word, long count) {
this.word = word;
this.count = count;
}
#Override
public String toString() {
return word + " : " + count;
}
}
We use hazelcast in client-server mode. The hazelcast cluster contains 2 hazelcast nodes and we have about 25 clients connected to the cluster.
What I am lookin for now is a simple check that tries to figure out if the cluster is still alive. It should be a rather cheap operation because this check will occure on every client quite frequently (once every second I could imagine).
What is the best way to do so?
The simplest way would be the register a LifecycleListener to the client HazelcastInstance:
HazelcastInstance client = HazelcastClient.newHazelcastClient();
client.getLifecycleService().addLifecycleListener(new LifecycleListener() {
#Override
public void stateChanged(LifecycleEvent event) {
}
})
The client uses a periodic heartbeat to detect if the cluster is still running.
You can use the LifecycleService.isRunning() method as well:
HazelcastInstance hzInstance = HazelcastClient.newHazelcastClient();
hzInstance.getLifecycleService().isRunning()
As isRunning() may be true even if cluster is down, I'd go for the following approach (a mixture of #konstantin-zyubin's answer and this). This doesn't need an event-listener, which is an advantage in my setup:
if (!hazelcastInstance.getLifecycleService().isRunning()) {
return Health.down().build();
}
int parameterCount;
LocalTopicStats topicStats;
try {
parameterCount = hazelcastInstance.getMap("parameters").size();
topicStats = hazelcastInstance.getTopic("myTopic").getLocalTopicStats();
} catch (Exception e) {
// instance may run but cluster is down:
Health.Builder builder = Health.down();
builder.withDetail("Error", e.getMessage());
return builder.build();
}
Health.Builder builder = Health.up();
builder.withDetail("parameterCount", parameterCount);
builder.withDetail("receivedMsgs", topicStats.getReceiveOperationCount());
builder.withDetail("publishedMsgs", topicStats.getPublishOperationCount());
return builder.build();
I have found a more reliable way to check hazelcast availability, because
client.getLifecycleService().isRunning()
when you use async reconnection mode is always return true, as was mentioned.
#Slf4j
public class DistributedCacheServiceImpl implements DistributedCacheService {
private HazelcastInstance client;
#Autowired
protected ConfigLoader<ServersConfig> serversConfigLoader;
#PostConstruct
private void initHazelcastClient() {
ClientConfig config = new ClientConfig();
if (isCacheEnabled()) {
ServersConfig.Hazelсast hazelcastConfig = getWidgetCacheSettings().getHazelcast();
config.getGroupConfig().setName(hazelcastConfig.getName());
config.getGroupConfig().setPassword(hazelcastConfig.getPassword());
for (String address : hazelcastConfig.getAddresses()) {
config.getNetworkConfig().addAddress(address);
}
config.getConnectionStrategyConfig()
.setAsyncStart(true)
.setReconnectMode(ClientConnectionStrategyConfig.ReconnectMode.ASYNC);
config.getNetworkConfig()
.setConnectionAttemptLimit(0) // infinite (Integer.MAX_VALUE) attempts to reconnect
.setConnectionTimeout(5000);
client = HazelcastClient.newHazelcastClient(config);
}
}
#Override
public boolean isCacheEnabled() {
ServersConfig.WidgetCache widgetCache = getWidgetCacheSettings();
return widgetCache != null && widgetCache.getEnabled();
}
#Override
public boolean isCacheAlive() {
boolean aliveResult = false;
if (isCacheEnabled() && client != null) {
try {
IMap<Object, Object> defaultMap = client.getMap("default");
if (defaultMap != null) {
defaultMap.size(); // will throw Hazelcast exception if cluster is down
aliveResult = true;
}
} catch (Exception e) {
log.error("Connection to hazelcast cluster is lost. Reason : {}", e.getMessage());
}
}
return aliveResult;
}
}
I am currently developing a small Java Application which needs to read some data from SAP.
Nearly everything is working fine.
I can connect to SAP, I can call a BAPI and get a result and I also can handle the given result. But....
I have two different SAP Systems (System A and System B).
If I start my application and make a connection to System A, everything is fine. But after all data from System A are handled I want to call System B (without stopping / restarting my application). In this situation I can't connect to System B.
I think there must be something wrong with the part where I establish the connection to my SAP System.
Can anybody tell my how to do this right?
This is my Code:
This is how I make a connection (SapLogOn and SapSystem are only wrapper classes for the needed parameters)
private void connectToSap(ISapLogOn logOn, ISapSystem system)
throws JCoException {
connectProperties = new Properties();
connectProperties.setProperty("ACTION", "CREATE");
connectProperties.setProperty(DestinationDataProvider.JCO_DEST, "POOL_DE");
connectProperties.setProperty(DestinationDataProvider.JCO_ASHOST, system.getAsHost());
connectProperties.setProperty(DestinationDataProvider.JCO_SYSNR, system.getSysNr());
connectProperties.setProperty(DestinationDataProvider.JCO_CLIENT, system.getClient());
connectProperties.setProperty(DestinationDataProvider.JCO_USER, logOn.getUserName());
connectProperties.setProperty(DestinationDataProvider.JCO_PASSWD, logOn.getPassword());
connectProperties.setProperty(DestinationDataProvider.JCO_LANG, system.getLanguage());
connectProperties.setProperty(DestinationDataProvider.JCO_SAPROUTER, system.getSapRouterString());
connectProperties.setProperty(DestinationDataProvider.JCO_POOL_CAPACITY, system.getPoolCapacity());
connectProperties.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT, system.getPeakLimit());
MyDestinationDataProvider myProvider = new MyDestinationDataProvider();
if (!com.sap.conn.jco.ext.Environment
.isDestinationDataProviderRegistered()) {
com.sap.conn.jco.ext.Environment
.registerDestinationDataProvider(myProvider);
}
myProvider.changePropertiesForABAP_AS(connectProperties);
}
And here comes part two:
public class MyDestinationDataProvider implements DestinationDataProvider {
public static Logger LOGGER = Logger.getLogger(MyDestinationDataProvider.class.getName());
#SuppressWarnings("unused")
private DestinationDataEventListener eL;
private Hashtable<String, Properties> propertiesTab;
public MyDestinationDataProvider() {
this.propertiesTab = new Hashtable<String, Properties>();
this.eL = new DestinationDataEventListener() {
#Override
public void updated(String arg0) {}
#Override
public void deleted(String arg0) {}
};
}
public Properties getDestinationProperties(String destinationName)
{
if(propertiesTab.containsKey(destinationName)){
return propertiesTab.get(destinationName);
}
LOGGER.error("Destination " + destinationName + " is not available");
throw new RuntimeException("Destination " + destinationName + " is not available");
}
public void setDestinationDataEventListener(DestinationDataEventListener eventListener)
{
this.eL = eventListener;
}
public boolean supportsEvents()
{
return true;
}
void changePropertiesForABAP_AS(Properties pConProps)
{
if(pConProps.getProperty("ACTION").equalsIgnoreCase("CREATE")){
propertiesTab.put(pConProps.getProperty("jco.client.dest"), pConProps);
}
else if(pConProps.getProperty("ACTION").equalsIgnoreCase("DELETE")){
propertiesTab.remove(pConProps.getProperty("jco.client.dest"));
}
}
}
I use Java 6 and JCo3.
Regards LStrike
I'm currently trying to communicate between java and flex by using sockets and AMF serialized objects.
On the java side I use Amf3Input and Amf3Output from BlazeDS (flex-messaging-common.jar and flex-messaging-core.jar).
The connection is correctly established, and if i try to send object from flex to java, i can easily read objects :
FLEX side :
protected function button2_clickHandler(event:MouseEvent):void
{
var tmp:FlexAck = new FlexAck;
tmp.id="123456789123456789123456789";
tmp.name="A";
tmp.source="Aaaaaa";
tmp.ackGroup=false;
s.writeObject(tmp);
s.flush();
}
JAVA side :
ServerSocket servSoc = new ServerSocket(8888);
Socket s = servSoc.accept();
Amf3Output amf3Output = new Amf3Output(SerializationContext.getSerializationContext());
amf3Output.setOutputStream(s.getOutputStream());
Amf3Input amf3Input = new Amf3Input(SerializationContext.getSerializationContext());
amf3Input.setInputStream(s.getInputStream());
while(true)
{
try
{
Object obj = amf3Input.readObject();
if(obj!=null){
if (obj instanceof AckOrder){
System.out.println(((AckOrder)obj).getId());
}
}
}
catch (Exception e)
{
e.printStackTrace();
break;
}
}
amf3Output.close();
amf3Input.close();
servSoc.close();
In this way it works perfectly, but the problem is to read objects sent from the java side.
The code I use in java is :
for(int i=0;i<10;i++){
ack = new AckOrder(i,"A","B", true);
amf3Output.writeObject(ack);
amf3Output.writeObjectEnd();
amf3Output.flush();
}
I have an handler on ProgressEvent.SOCKET_DATA :
trace((s.readObject() as FlexAck).id);
But I have errors such as :
Error #2030: End of File detected
Error #2006: Index Out of bound
If i add manipulations on ByteArrays, i manage to read the first object, but not the following.
s.readBytes(tmp,tmp.length);
content = clone(tmp);
(content.readObject());
trace("########################## OK OBJECT RECEIVED");
var ack:FlexAck = (tmp.readObject() as FlexAck);
trace("**********************> id = "+ack.id);
I've spent many our trying to find something in several forums etc, but nothing helped.
So if someone could help me it would be great.
Thanks
Sylvain
EDIT :
Here is an example that I thought should work, but doesn't I hope that it's better illustrate what I aim to do (permanent connection with socket and an exchange of messages).
Java class :
import java.net.ServerSocket;
import java.net.Socket;
import awl.oscare.protocol.AckOrder;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.Amf3Input;
import flex.messaging.io.amf.Amf3Output;
public class Main {
public static void main(String[] args) {
while(true)
{
try {
ServerSocket servSoc = new ServerSocket(8888);
Socket s = servSoc.accept();
System.out.println("connection accepted");
Amf3Output amf3Output = new Amf3Output(SerializationContext.getSerializationContext());
amf3Output.setOutputStream(s.getOutputStream());
Amf3Input amf3Input = new Amf3Input(SerializationContext.getSerializationContext());
amf3Input.setInputStream(s.getInputStream());
while(true)
{
try
{
System.out.println("Reading object");
Object obj = amf3Input.readObject();
if(obj!=null)
{
System.out.println(obj.getClass());
if (obj instanceof AckOrder)
{
AckOrder order = new AckOrder();
order.setId(((AckOrder)obj).getId());
order.setName(((AckOrder)obj).getName());
order.setSource(((AckOrder)obj).getSource());
order.setAckGroup(((AckOrder)obj).isAckGroup());
System.out.println(((AckOrder)obj).getId());
amf3Output.writeObject(order);
amf3Output.writeObjectEnd();
amf3Output.flush();
}
}
}
catch (Exception e)
{
e.printStackTrace();
break;
}
}
amf3Output.close();
amf3Input.close();
servSoc.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Java Serializable object :
package protocol;
import java.io.Serializable;
public class AckOrder implements Serializable {
private static final long serialVersionUID = 5106528318894546695L;
private String id;
private String name;
private String source;
private boolean ackGroup = false;
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public void setSource(String source) {
this.source = source;
}
public String getSource() {
return this.source;
}
public void setAckGroup(boolean ackGroup) {
this.ackGroup = ackGroup;
}
public boolean isAckGroup() {
return this.ackGroup;
}
public AckOrder()
{
super();
}
}
Flex Side :
Main flex code :
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.utils.object_proxy;
private var _socket:Socket = new Socket();;
private function onCreationComplete():void
{
this._socket.connect("localhost",8888);
this._socket.addEventListener(ProgressEvent.SOCKET_DATA, onData);
}
private function onData(e:ProgressEvent):void
{
if(this._socket.bytesAvailable)
{
this._socket.endian = Endian.LITTLE_ENDIAN;
var objects:Array = [];
try{
while(this._socket.bytesAvailable > 0)
{
objects.push(this._socket.readObject());
}
}catch(e:Error){trace(e.message);}
trace("|"+(objects)+"|");
}
}
protected function sendButton_clickHandler(event:MouseEvent):void
{
var tmp:FlexAck = new FlexAck;
tmp.id="1";
tmp.name="A";
tmp.source="B";
tmp.ackGroup=false;
this._socket.writeObject(tmp);
this._socket.flush();
}
]]>
</fx:Script>
<s:Button x="0" y="0" name="send" label="Send" click="sendButton_clickHandler(event)"/>
Flex serializable object :
package
{
[Bindable]
[RemoteClass(alias="protocol.AckOrder")]
public class FlexAck
{
public function FlexAck()
{
}
public var id:String;
public var name:String;
public var source:String;
public var ackGroup:Boolean;
}
}
Edit 25/05/2011 :
I've added those listeners in my flex code :
this._socket.addEventListener(Event.ACTIVATE,onActivate);
this._socket.addEventListener(Event.CLOSE,onClose);
this._socket.addEventListener(Event.CONNECT,onConnect);
this._socket.addEventListener(Event.DEACTIVATE,onDeactivate);
this._socket.addEventListener(IOErrorEvent.IO_ERROR,onIOerror);
this._socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR,onSecurityError);
But There's no errors and I still don't manage to receive objects correctly.
You have to send the AMF data as ByteArray on the server:
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
amf3Output.setOutputStream(baos);
amf3Output.writeObject(order);
amf3Output.flush();
amf3Output.close();
s.getOutputStream().write(baos.toByteArray());
Then
this._socket.readObject()
works as expected !
Hi the problem is caused by the following:
An AMF stream is stateful. When it serializes objects, it compresses them relative to objects that it have already been written.
Compression is achieved by referencing previously sent class descriptions, string values and objects using indexes (so for example, if the first string you sent was "heloWorld", when you later send that string, the AMF stream will sent string index 0).
Unfortunately, ByteArray and Socket do not maintain reference tables between readObject calls. Thus, even if you keep appending your newly read objects to the end of the same ByteArray object, each call to readObject instantiates new reference tables, discarding previously created ones (this means it should work for repeated references to the same string within an object tree)
In your example, you are always writing the same string values to properties. Thus when you send the second object, its string properties are not serialized as strings, but as references to the strings in the previously written object.
The solution, is to create a new AMF stream for each object you send.
This is complete rubbish of course(!) It means we can't really utilize the compression in custom protocols. It would be much better if our protocols could decide when to reset the these reference tables, perhaps when they got too big.
For example, if you have an RPC protocol, it would be nice to have an AMF stream pass the remote method names as references rather than strings for speed...
I haven't checked but I think this sort of thing is done by RTMP. The reason it probably wouldn't have been made available in developer objects like ByteArray and Socket (sigh, I hope this isn't true) is because Adobe wants to push us towards LCDS...
Addendum/edit: just found this, which provides a solution http://code.google.com/p/cvlib/
After looking at the code, I think what you want to do on the Java end is this:
for(int i=0;i<10;i++){
ack = new AckOrder(i,"A","B", true);
amf3Output.writeObject(ack);
}
amf3Output.flush();
When you do 'flush', you're sending information over the socket so you only had one object being sent at a time. On the Flex end, you should always try to see what's the length of the object and make sure you're not going over it which would cause this error.
EDIT:
private var _socket:Socket = new Socket();
private function onCreationComplete():void
{
// Add connection socket info here
this._socket.addEventListener(ProgressEvent.SOCKET_DATA, onData);
}
// This gets called every time we get new info, as in after the server flushes
private function onData(e:ProgressEvent):void
{
if(this._socket.bytesAvailable)
{
this._socket.endian = Endian.LITTLE_ENDIAN; // Might not be needed, but often is
// Try to get objects
var objects:Array = [];
try{
while(this._socket.bytesAvailable > 0)
{
objects.push(this._socket.readObject());
}
}catch(e:Error){}
// Do something with objects array
}
}
The onData function is called continually (every time the server sends info) since everything is asynchronous.