/**
   * Reserved for internal use. Parses the operation response as an entity. Reads entity data from
   * the specified <code>JsonParser</code> using the specified class type and optionally projects
   * the entity result with the specified resolver into a {@link TableResult} object.
   *
   * @param parser The <code>JsonParser</code> to read the data to parse from.
   * @param httpStatusCode The HTTP status code returned with the operation response.
   * @param clazzType The class type <code>T</code> implementing {@link TableEntity} for the entity
   *     returned. Set to <code>null</code> to ignore the returned entity and copy only response
   *     properties into the {@link TableResult} object.
   * @param resolver An {@link EntityResolver} instance to project the entity into an instance of
   *     type <code>R</code>. Set to <code>null</code> to return the entitys as instance of the
   *     class type <code>T</code>.
   * @param options A {@link TableRequestOptions} object that specifies execution options such as
   *     retry policy and timeout settings for the operation.
   * @param opContext An {@link OperationContext} object used to track the execution of the
   *     operation.
   * @return A {@link TableResult} object with the parsed operation response.
   * @throws InstantiationException if an error occurs while constructing the result.
   * @throws IllegalAccessException if an error occurs in reflection while parsing the result.
   * @throws StorageException if a storage service error occurs.
   * @throws IOException if an error occurs while accessing the stream.
   * @throws JsonParseException if an error occurs while parsing the stream.
   */
  static <T extends TableEntity, R> TableResult parseSingleOpResponse(
      final InputStream inStream,
      final TableRequestOptions options,
      final int httpStatusCode,
      final Class<T> clazzType,
      final EntityResolver<R> resolver,
      final OperationContext opContext)
      throws JsonParseException, IOException, InstantiationException, IllegalAccessException,
          StorageException {
    JsonParser parser = createJsonParserFromStream(inStream);

    try {
      final TableResult res =
          parseJsonEntity(
              parser,
              clazzType,
              null /*HashMap<String, PropertyPair> classProperties*/,
              resolver,
              options,
              opContext);
      res.setHttpStatusCode(httpStatusCode);
      return res;
    } finally {
      parser.close();
    }
  }
  /**
   * Reserved for internal use. Parses the table operation response into a {@link TableResult} to
   * return.
   *
   * @param xmlr An <code>XMLStreamReader</code> containing the response to an insert operation.
   * @param httpStatusCode The HTTP status code returned from the operation request.
   * @param etagFromHeader The <code>String</code> containing the Etag returned with the operation
   *     response.
   * @param opContext An {@link OperationContext} object that represents the context for the current
   *     operation.
   * @return The {@link TableResult} representing the result of the operation.
   * @throws XMLStreamException if an error occurs accessing the <code>XMLStreamReader</code>.
   * @throws ParseException if an error occurs in parsing the response.
   * @throws InstantiationException if an error occurs in object construction.
   * @throws IllegalAccessException if an error occurs in reflection on an object type.
   * @throws StorageException if an error occurs in the storage operation.
   */
  protected TableResult parseResponse(
      final XMLStreamReader xmlr,
      final int httpStatusCode,
      final String etagFromHeader,
      final OperationContext opContext)
      throws XMLStreamException, ParseException, InstantiationException, IllegalAccessException,
          StorageException {
    TableResult resObj = null;
    if (this.opType == TableOperationType.INSERT) {
      // Sending null for class type and resolver will ignore parsing the return payload.
      resObj = AtomPubParser.parseSingleOpResponse(xmlr, httpStatusCode, null, null, opContext);
      resObj.updateResultObject(this.getEntity());
    } else {
      resObj = new TableResult(httpStatusCode);
      resObj.setResult(this.getEntity());

      if (this.opType != TableOperationType.DELETE) {
        this.getEntity().setEtag(etagFromHeader);
      }
    }

    return resObj;
  }
Example #3
0
 public static JSONRPCResult fromJSON(JSONObject result) throws RPCException {
   ResultType type = ResultType.fromID(result.getString("type").charAt(0));
   switch (type) { // TODO
     case ACK:
       return new AckResponse();
     case AT:
       return TableResult.fromJSON(result);
     case INDEX:
       return IndexResult.fromJSON(result);
     case NAK:
       return new NakResult();
     case PONG:
       return new PongResult();
     case VALUE:
       return ValueResult.fromJSON(result);
     default:
       throw new RPCException(JSONRPCError.MISSING_RESULT_TYPE);
   }
 }
  private void doEscapeTestHelper(
      String data, boolean useBatch, boolean includeInKey, TableRequestOptions options)
      throws StorageException {
    Class1 ref = new Class1();
    ref.setA(data);
    ref.setPartitionKey(includeInKey ? "temp" + data : "temp");
    ref.setRowKey(UUID.randomUUID().toString());

    if (useBatch) {
      TableBatchOperation batch = new TableBatchOperation();
      batch.insert(ref);
      this.table.execute(batch, options, null);
    } else {
      this.table.execute(TableOperation.insert(ref), options, null);
    }

    TableResult res = null;

    if (useBatch) {
      TableBatchOperation batch = new TableBatchOperation();
      batch.retrieve(ref.getPartitionKey(), ref.getRowKey(), Class1.class);
      res = this.table.execute(batch, options, null).get(0);
    } else {
      res =
          this.table.execute(
              TableOperation.retrieve(ref.getPartitionKey(), ref.getRowKey(), Class1.class),
              options,
              null);
    }

    Class1 retObj = res.getResultAsType();
    assertEquals(ref.getA(), retObj.getA());
    assertEquals(ref.getPartitionKey(), retObj.getPartitionKey());

    ref.setEtag(retObj.getEtag());
    ref.setB(data);

    // Merge
    if (useBatch) {
      TableBatchOperation batch = new TableBatchOperation();
      batch.merge(ref);
      this.table.execute(batch, options, null);
    } else {
      this.table.execute(TableOperation.merge(ref), options, null);
    }

    if (useBatch) {
      TableBatchOperation batch = new TableBatchOperation();
      batch.retrieve(ref.getPartitionKey(), ref.getRowKey(), Class1.class);
      res = this.table.execute(batch, options, null).get(0);
    } else {
      res =
          this.table.execute(
              TableOperation.retrieve(ref.getPartitionKey(), ref.getRowKey(), Class1.class),
              options,
              null);
    }

    retObj = res.getResultAsType();
    assertEquals(ref.getA(), retObj.getA());
    assertEquals(ref.getB(), retObj.getB());

    // Replace
    ref.setEtag(retObj.getEtag());
    ref.setC(data);

    if (useBatch) {
      TableBatchOperation batch = new TableBatchOperation();
      batch.replace(ref);
      this.table.execute(batch, options, null);
    } else {
      this.table.execute(TableOperation.replace(ref), options, null);
    }

    if (useBatch) {
      TableBatchOperation batch = new TableBatchOperation();
      batch.retrieve(ref.getPartitionKey(), ref.getRowKey(), Class1.class);
      res = this.table.execute(batch, options, null).get(0);
    } else {
      res =
          this.table.execute(
              TableOperation.retrieve(ref.getPartitionKey(), ref.getRowKey(), Class1.class),
              options,
              null);
    }

    retObj = res.getResultAsType();
    assertEquals(ref.getA(), retObj.getA());
    assertEquals(ref.getB(), retObj.getB());
    assertEquals(ref.getC(), retObj.getC());

    if (useBatch) {
      TableBatchOperation batch = new TableBatchOperation();
      batch.delete(retObj);
      res = this.table.execute(batch, options, null).get(0);
    } else {
      res = this.table.execute(TableOperation.delete(retObj), options, null);
    }
  }
  /**
   * Reserved for internal use. Parses the operation response as a collection of entities. Reads
   * entity data from the specified input stream using the specified class type and optionally
   * projects each entity result with the specified resolver into an {@link ODataPayload} containing
   * a collection of {@link TableResult} objects.
   *
   * @param inStream The <code>InputStream</code> to read the data to parse from.
   * @param clazzType The class type <code>T</code> implementing {@link TableEntity} for the
   *     entities returned. Set to <code>null</code> to ignore the returned entities and copy only
   *     response properties into the {@link TableResult} objects.
   * @param resolver An {@link EntityResolver} instance to project the entities into instances of
   *     type <code>R</code>. Set to <code>null</code> to return the entities as instances of the
   *     class type <code>T</code>.
   * @param options A {@link TableRequestOptions} object that specifies execution options such as
   *     retry policy and timeout settings for the operation.
   * @param opContext An {@link OperationContext} object used to track the execution of the
   *     operation.
   * @return An {@link ODataPayload} containing a collection of {@link TableResult} objects with the
   *     parsed operation response.
   * @throws InstantiationException if an error occurs while constructing the result.
   * @throws IllegalAccessException if an error occurs in reflection while parsing the result.
   * @throws StorageException if a storage service error occurs.
   * @throws IOException if an error occurs while accessing the stream.
   * @throws JsonParseException if an error occurs while parsing the stream.
   */
  @SuppressWarnings("unchecked")
  static <T extends TableEntity, R> ODataPayload<?> parseQueryResponse(
      final InputStream inStream,
      final TableRequestOptions options,
      final Class<T> clazzType,
      final EntityResolver<R> resolver,
      final OperationContext opContext)
      throws JsonParseException, IOException, InstantiationException, IllegalAccessException,
          StorageException {
    ODataPayload<T> corePayload = null;
    ODataPayload<R> resolvedPayload = null;
    ODataPayload<?> commonPayload = null;

    JsonParser parser = createJsonParserFromStream(inStream);

    try {

      if (resolver != null) {
        resolvedPayload = new ODataPayload<R>();
        commonPayload = resolvedPayload;
      } else {
        corePayload = new ODataPayload<T>();
        commonPayload = corePayload;
      }

      if (!parser.hasCurrentToken()) {
        parser.nextToken();
      }

      JsonUtilities.assertIsStartObjectJsonToken(parser);

      // move into data
      parser.nextToken();

      // if there is a clazz type and if JsonNoMetadata, create a classProperties dictionary to use
      // for type inference once
      // instead of querying the cache many times
      HashMap<String, PropertyPair> classProperties = null;
      if (options.getTablePayloadFormat() == TablePayloadFormat.JsonNoMetadata
          && clazzType != null) {
        classProperties = PropertyPair.generatePropertyPairs(clazzType);
      }

      while (parser.getCurrentToken() != null) {
        if (parser.getCurrentToken() == JsonToken.FIELD_NAME
            && parser.getCurrentName().equals(ODataConstants.VALUE)) {
          // move to start of array
          parser.nextToken();

          JsonUtilities.assertIsStartArrayJsonToken(parser);

          // go to properties
          parser.nextToken();

          while (parser.getCurrentToken() == JsonToken.START_OBJECT) {
            final TableResult res =
                parseJsonEntity(parser, clazzType, classProperties, resolver, options, opContext);
            if (corePayload != null) {
              corePayload.tableResults.add(res);
            }

            if (resolver != null) {
              resolvedPayload.results.add((R) res.getResult());
            } else {
              corePayload.results.add((T) res.getResult());
            }

            parser.nextToken();
          }

          JsonUtilities.assertIsEndArrayJsonToken(parser);
        }

        parser.nextToken();
      }
    } finally {
      parser.close();
    }

    return commonPayload;
  }
  /**
   * Reserved for internal use. Parses the operation response as an entity. Parses the result
   * returned in the specified stream in JSON format into a {@link TableResult} containing an entity
   * of the specified class type projected using the specified resolver.
   *
   * @param parser The <code>JsonParser</code> to read the data to parse from.
   * @param clazzType The class type <code>T</code> implementing {@link TableEntity} for the entity
   *     returned. Set to <code>null</code> to ignore the returned entity and copy only response
   *     properties into the {@link TableResult} object.
   * @param resolver An {@link EntityResolver} instance to project the entity into an instance of
   *     type <code>R</code>. Set to <code>null</code> to return the entity as an instance of the
   *     class type <code>T</code>.
   * @param options A {@link TableRequestOptions} object that specifies execution options such as
   *     retry policy and timeout settings for the operation.
   * @param opContext An {@link OperationContext} object used to track the execution of the
   *     operation.
   * @return A {@link TableResult} containing the parsed entity result of the operation.
   * @throws IOException if an error occurs while accessing the stream.
   * @throws InstantiationException if an error occurs while constructing the result.
   * @throws IllegalAccessException if an error occurs in reflection while parsing the result.
   * @throws StorageException if a storage service error occurs.
   * @throws IOException if an error occurs while accessing the stream.
   * @throws JsonParseException if an error occurs while parsing the stream.
   */
  private static <T extends TableEntity, R> TableResult parseJsonEntity(
      final JsonParser parser,
      final Class<T> clazzType,
      HashMap<String, PropertyPair> classProperties,
      final EntityResolver<R> resolver,
      final TableRequestOptions options,
      final OperationContext opContext)
      throws JsonParseException, IOException, StorageException, InstantiationException,
          IllegalAccessException {
    final TableResult res = new TableResult();

    final HashMap<String, EntityProperty> properties = new HashMap<String, EntityProperty>();

    if (!parser.hasCurrentToken()) {
      parser.nextToken();
    }

    JsonUtilities.assertIsStartObjectJsonToken(parser);

    parser.nextToken();

    // get all metadata, if present
    while (parser.getCurrentName().startsWith(ODataConstants.ODATA_PREFIX)) {
      final String name = parser.getCurrentName().substring(ODataConstants.ODATA_PREFIX.length());

      // get the value token
      parser.nextToken();

      if (name.equals(ODataConstants.ETAG)) {
        String etag = parser.getValueAsString();
        res.setEtag(etag);
      }

      // get the key token
      parser.nextToken();
    }

    if (resolver == null && clazzType == null) {
      return res;
    }

    // get object properties
    while (parser.getCurrentToken() != JsonToken.END_OBJECT) {
      String key = Constants.EMPTY_STRING;
      String val = Constants.EMPTY_STRING;
      EdmType edmType = null;

      // checks if this property is preceded by an OData property type annotation
      if (options.getTablePayloadFormat() != TablePayloadFormat.JsonNoMetadata
          && parser.getCurrentName().endsWith(ODataConstants.ODATA_TYPE_SUFFIX)) {
        parser.nextToken();
        edmType = EdmType.parse(parser.getValueAsString());

        parser.nextValue();
        key = parser.getCurrentName();
        val = parser.getValueAsString();
      } else {
        key = parser.getCurrentName();

        parser.nextToken();
        val = parser.getValueAsString();
        edmType = evaluateEdmType(parser.getCurrentToken(), parser.getValueAsString());
      }

      final EntityProperty newProp = new EntityProperty(val, edmType);
      newProp.setDateBackwardCompatibility(options.getDateBackwardCompatibility());
      properties.put(key, newProp);

      parser.nextToken();
    }

    String partitionKey = null;
    String rowKey = null;
    Date timestamp = null;
    String etag = null;

    // Remove core properties from map and set individually
    EntityProperty tempProp = properties.remove(TableConstants.PARTITION_KEY);
    if (tempProp != null) {
      partitionKey = tempProp.getValueAsString();
    }

    tempProp = properties.remove(TableConstants.ROW_KEY);
    if (tempProp != null) {
      rowKey = tempProp.getValueAsString();
    }

    tempProp = properties.remove(TableConstants.TIMESTAMP);
    if (tempProp != null) {
      tempProp.setDateBackwardCompatibility(false);
      timestamp = tempProp.getValueAsDate();

      if (res.getEtag() == null) {
        etag = getETagFromTimestamp(tempProp.getValueAsString());
        res.setEtag(etag);
      }
    }

    // do further processing for type if JsonNoMetdata by inferring type information via resolver or
    // clazzType
    if (options.getTablePayloadFormat() == TablePayloadFormat.JsonNoMetadata
        && (options.getPropertyResolver() != null || clazzType != null)) {
      if (options.getPropertyResolver() != null) {
        for (final Entry<String, EntityProperty> p : properties.entrySet()) {
          final String key = p.getKey();
          final String value = p.getValue().getValueAsString();
          EdmType edmType;

          // try to use the property resolver to get the type
          try {
            edmType =
                options.getPropertyResolver().propertyResolver(partitionKey, rowKey, key, value);
          } catch (Exception e) {
            throw new StorageException(
                StorageErrorCodeStrings.INTERNAL_ERROR,
                SR.CUSTOM_RESOLVER_THREW,
                Constants.HeaderConstants.HTTP_UNUSED_306,
                null,
                e);
          }

          // try to create a new entity property using the returned type
          try {
            final EntityProperty newProp = new EntityProperty(value, edmType);
            newProp.setDateBackwardCompatibility(options.getDateBackwardCompatibility());
            properties.put(p.getKey(), newProp);
          } catch (IllegalArgumentException e) {
            throw new StorageException(
                StorageErrorCodeStrings.INVALID_TYPE,
                String.format(SR.FAILED_TO_PARSE_PROPERTY, key, value, edmType),
                Constants.HeaderConstants.HTTP_UNUSED_306,
                null,
                e);
          }
        }
      } else if (clazzType != null) {
        if (classProperties == null) {
          classProperties = PropertyPair.generatePropertyPairs(clazzType);
        }
        for (final Entry<String, EntityProperty> p : properties.entrySet()) {
          PropertyPair propPair = classProperties.get(p.getKey());
          if (propPair != null) {
            final EntityProperty newProp =
                new EntityProperty(p.getValue().getValueAsString(), propPair.type);
            newProp.setDateBackwardCompatibility(options.getDateBackwardCompatibility());
            properties.put(p.getKey(), newProp);
          }
        }
      }
    }

    // set the result properties, now that they are appropriately parsed
    res.setProperties(properties);

    // use resolver if provided, else create entity based on clazz type
    if (resolver != null) {
      res.setResult(
          resolver.resolve(partitionKey, rowKey, timestamp, res.getProperties(), res.getEtag()));
    } else if (clazzType != null) {
      // Generate new entity and return
      final T entity = clazzType.newInstance();
      entity.setEtag(res.getEtag());

      entity.setPartitionKey(partitionKey);
      entity.setRowKey(rowKey);
      entity.setTimestamp(timestamp);

      entity.readEntity(res.getProperties(), opContext);

      res.setResult(entity);
    }

    return res;
  }