17c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet/*
27c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * Copyright (C) 2009 Google Inc.  All rights reserved.
37c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet *
47c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * Licensed under the Apache License, Version 2.0 (the "License");
57c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * you may not use this file except in compliance with the License.
67c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * You may obtain a copy of the License at
77c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet *
87c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet *      http://www.apache.org/licenses/LICENSE-2.0
97c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet *
107c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * Unless required by applicable law or agreed to in writing, software
117c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * distributed under the License is distributed on an "AS IS" BASIS,
127c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * See the License for the specific language governing permissions and
147c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * limitations under the License.
157c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet */
167c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
177c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetpackage com.google.polo.wire.json;
187c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
197c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetimport com.google.polo.exception.PoloException;
207c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetimport com.google.polo.json.JSONException;
217c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetimport com.google.polo.json.JSONObject;
227c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetimport com.google.polo.pairing.PairingContext;
237c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetimport com.google.polo.pairing.PoloUtil;
247c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetimport com.google.polo.pairing.message.PoloMessage;
257c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetimport com.google.polo.pairing.message.PoloMessage.PoloMessageType;
267c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetimport com.google.polo.wire.PoloWireInterface;
277c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
287c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetimport java.io.DataInputStream;
297c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetimport java.io.DataOutputStream;
307c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetimport java.io.IOException;
317c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetimport java.io.InputStream;
327c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetimport java.io.OutputStream;
337c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
347c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet/**
357c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * A {@link PoloWireInterface} which uses JavaScript Object Notation (JSON) for
367c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * the message representation.
377c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * <p>
387c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * Messages are streamed over the wire prepended with an integer which indicates
397c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * the total length, in bytes, of the message which follows. The format of the
407c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * message is JSON.
417c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * <p>
427c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * See {@link JsonMessageBuilder} for the underlying message translation
437c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet * implementation.
447c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet */
457c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichetpublic class JsonWireAdapter implements PoloWireInterface {
467c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
477c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  /**
487c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   * The output coming from the peer.
497c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   */
507c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  private final DataInputStream mInputStream;
517c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
527c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  /**
537c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   * The input going to the peer.
547c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   */
557c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  private final DataOutputStream mOutputStream;
567c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
577c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  /**
587c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   * Constructor.
5922c862eaf3f88bb389c3b53b1f7a22415da7177eJerome Poichet   *
607c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   * @param input the {@link InputStream} from the peer
617c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   * @param output the {@link OutputStream} to the peer
627c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   */
637c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  public JsonWireAdapter(InputStream input, OutputStream output) {
647c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    mInputStream = new DataInputStream(input);
657c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    mOutputStream = new DataOutputStream(output);
667c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  }
677c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
687c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  /**
697c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   * Generates a new instance from a {@link PairingContext}.
7022c862eaf3f88bb389c3b53b1f7a22415da7177eJerome Poichet   *
717c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   * @param context the {@link PairingContext}
727c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   * @return the new instance
737c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   */
747c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  public static JsonWireAdapter fromContext(PairingContext context) {
757c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    return new JsonWireAdapter(context.getPeerInputStream(), context
767c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet        .getPeerOutputStream());
777c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  }
787c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
797c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  public PoloMessage getNextMessage() throws IOException, PoloException {
807c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    byte[] payloadLenBytes = new byte[4];
817c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    mInputStream.readFully(payloadLenBytes);
827c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    long payloadLen = PoloUtil.intBigEndianBytesToLong(payloadLenBytes);
837c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    byte[] outerJsonBytes = new byte[(int) payloadLen];
847c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    mInputStream.readFully(outerJsonBytes);
857c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    return parseOuterMessageString(new String(outerJsonBytes));
867c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  }
877c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
887c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  public PoloMessage parseOuterMessageString(String outerString)
897c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet      throws PoloException {
907c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    JSONObject outerMessage;
917c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    try {
927c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet      outerMessage = new JSONObject(outerString);
937c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    } catch (JSONException e) {
947c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet      throw new PoloException("Error parsing incoming message", e);
957c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    }
967c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    return JsonMessageBuilder.outerJsonToPoloMessage(outerMessage);
977c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  }
987c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
997c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  public PoloMessage getNextMessage(PoloMessageType type) throws IOException,
1007c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet      PoloException {
1017c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    PoloMessage message = getNextMessage();
1027c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    if (message.getType() != type) {
1037c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet      throw new PoloException("Wrong message type (wanted " + type + ", got "
1047c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet          + message.getType() + ")");
1057c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    }
1067c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    return message;
1077c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  }
1087c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
1097c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  public void sendErrorMessage(Exception exception) throws IOException {
1107c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    try {
1117c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet      writeJson(JsonMessageBuilder.getErrorJson(exception));
1127c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    } catch (PoloException e) {
1137c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet      throw new IOException("Error sending error message");
1147c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    }
1157c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  }
1167c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
1177c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  public void sendMessage(PoloMessage message) throws IOException {
1187c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    String outString;
1197c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    JSONObject outerJson;
1207c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
1217c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    try {
1227c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet      outerJson = JsonMessageBuilder.getOuterJson(message);
1237c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    } catch (PoloException e) {
1247c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet      throw new IOException("Error generating message");
1257c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    }
1267c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
1277c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    writeJson(outerJson);
1287c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  }
1297c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
1307c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  /**
1317c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   * Writes a {@link JSONObject} to the output stream as a {@link String}.
13222c862eaf3f88bb389c3b53b1f7a22415da7177eJerome Poichet   *
1337c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   * @param  message      the message to write
1347c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   * @throws IOException  on error generating the serialized message
1357c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet   */
1367c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  private void writeJson(JSONObject message) throws IOException {
1377c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    byte[] outBytes = message.toString().getBytes();
1387c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    mOutputStream.write(PoloUtil.intToBigEndianIntBytes(outBytes.length));
1397c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet    mOutputStream.write(outBytes);
1407c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet  }
1417c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet
1427c9978567a202d6aa98beac5da5e1b3b34792862Jerome Poichet}
143