/******************************************************************************
*
* Copyright 2011-2012 Tavendo GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.magnum.soda.transport.wamp;
import java.io.IOException;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ConcurrentHashMap;
import org.magnum.soda.transport.MsgContainer;
import org.magnum.soda.transport.wamp.WampConnection.CallMeta;
import org.magnum.soda.transport.wamp.WampConnection.SubMeta;
import android.os.Handler;
import android.util.Log;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Autobahn WAMP reader, the receiving leg of a WAMP connection.
*/
public class WampReader extends WebSocketReader {
private static final boolean DEBUG = true;
private static final String TAG = WampReader.class.getName();
/// Jackson JSON-to-object mapper.
private final ObjectMapper mJsonMapper;
/// Jackson JSON factory from which we create JSON parsers.
private final JsonFactory mJsonFactory;
/// Holds reference to call map created on master.
private final ConcurrentHashMap<String, CallMeta> mCalls;
/// Holds reference to event subscription map created on master.
private final ConcurrentHashMap<String, SubMeta> mSubs;
/**
* A reader object is created in AutobahnConnection.
*
* @param calls The call map created on master.
* @param subs The event subscription map created on master.
* @param master Message handler of master (used by us to notify the master).
* @param socket The TCP socket.
* @param options WebSockets connection options.
* @param threadName The thread name we announce.
*/
public WampReader(ConcurrentHashMap<String, CallMeta> calls,
ConcurrentHashMap<String, SubMeta> subs,
Handler master,
SocketChannel socket,
WebSocketOptions options,
String threadName) {
super(master, socket, options, threadName);
mCalls = calls;
mSubs = subs;
mJsonMapper = new ObjectMapper();
mJsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mJsonFactory = mJsonMapper.getJsonFactory();
if (DEBUG) Log.d(TAG, "created");
}
protected void onTextMessage(String payload) {
/// \todo make error propagation consistent
notify(new WebSocketMessage.Error(new WebSocketException("non-raw receive of text message")));
}
protected void onBinaryMessage(byte[] payload) {
/// \todo make error propagation consistent
notify(new WebSocketMessage.Error(new WebSocketException("received binary message")));
}
/**
* Unwraps a WAMP message which is a WebSockets text message with JSON
* payload conforming to WAMP.
*/
protected void onRawTextMessage(byte[] payload) {
try {
// create parser on top of raw UTF-8 payload
JsonParser parser = mJsonFactory.createJsonParser(payload);
// all Autobahn messages are JSON arrays
if (parser.nextToken() == JsonToken.START_ARRAY) {
// message type
if (parser.nextToken() == JsonToken.VALUE_NUMBER_INT) {
int msgType = parser.getIntValue();
if (msgType == WampMessage.MESSAGE_TYPE_CALL_RESULT) {
// call ID
parser.nextToken();
String callId = parser.getText();
// result
parser.nextToken();
Object result = null;
if (mCalls.containsKey(callId)) {
CallMeta meta = mCalls.get(callId);
if (meta.mResultClass != null) {
result = parser.readValueAs(meta.mResultClass);
} else if (meta.mResultTypeRef != null) {
result = parser.readValueAs(meta.mResultTypeRef);
} else {
}
notify(new WampMessage.CallResult(callId, result));
} else {
if (DEBUG) Log.d(TAG, "WAMP RPC success return for unknown call ID received");
}
} else if (msgType == WampMessage.MESSAGE_TYPE_CALL_ERROR) {
// call ID
parser.nextToken();
String callId = parser.getText();
// error URI
parser.nextToken();
String errorUri = parser.getText();
// error description
parser.nextToken();
String errorDesc = parser.getText();
if (mCalls.containsKey(callId)) {
notify(new WampMessage.CallError(callId, errorUri, errorDesc));
} else {
if (DEBUG) Log.d(TAG, "WAMP RPC error return for unknown call ID received");
}
} else if (msgType == WampMessage.MESSAGE_TYPE_EVENT) {
// topic URI
parser.nextToken();
String topicUri = parser.getText();
// event
parser.nextToken();
Object event = null;
if (mSubs.containsKey(topicUri)) {
SubMeta meta = mSubs.get(topicUri);
if (meta.mEventClass != null) {
event = parser.readValueAs(meta.mEventClass);
if(event instanceof MsgContainer && ((MsgContainer)event).getMsg() == null){
System.out.println("arg");
}
} else if (meta.mEventTypeRef != null) {
event = parser.readValueAs(meta.mEventTypeRef);
} else {
}
notify(new WampMessage.Event(topicUri, event));
} else {
if (DEBUG) Log.d(TAG, "WAMP event for not-subscribed topic received");
}
} else if (msgType == WampMessage.MESSAGE_TYPE_PREFIX) {
// prefix
parser.nextToken();
String prefix = parser.getText();
// URI
parser.nextToken();
String uri = parser.getText();
notify(new WampMessage.Prefix(prefix, uri));
} else if (msgType == WampMessage.MESSAGE_TYPE_WELCOME) {
// session ID
parser.nextToken();
String sessionId = parser.getText();
// protocol version
parser.nextToken();
int protocolVersion = parser.getIntValue();
// server ident
parser.nextToken();
String serverIdent = parser.getText();
notify(new WampMessage.Welcome(sessionId, protocolVersion, serverIdent));
} else {
// FIXME: invalid WAMP message
if (DEBUG) Log.d(TAG, "invalid WAMP message: unrecognized message type");
}
} else {
if (DEBUG) Log.d(TAG, "invalid WAMP message: missing message type or message type not an integer");
}
if (parser.nextToken() == JsonToken.END_ARRAY) {
// nothing to do here
} else {
if (DEBUG) Log.d(TAG, "invalid WAMP message: missing array close or invalid additional args");
}
} else {
if (DEBUG) Log.d(TAG, "invalid WAMP message: not an array");
}
parser.close();
} catch (JsonParseException e) {
if (DEBUG) e.printStackTrace();
} catch (IOException e) {
if (DEBUG) e.printStackTrace();
}
}
}