/*******************************************************************************
* Copyright 2013 Open mHealth
*
* 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.openmhealth.reference.request;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.openmhealth.reference.data.DataSet;
import org.openmhealth.reference.data.Registry;
import org.openmhealth.reference.domain.AuthenticationToken;
import org.openmhealth.reference.domain.Data;
import org.openmhealth.reference.domain.MetaData;
import org.openmhealth.reference.domain.MultiValueResult;
import org.openmhealth.reference.domain.Schema;
import org.openmhealth.reference.domain.User;
import org.openmhealth.reference.exception.InvalidAuthenticationException;
import org.openmhealth.reference.exception.OmhException;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MappingJsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* <p>
* Stores the given data.
* </p>
*
* @author John Jenkins
*/
public class DataWriteRequest extends Request<Object> {
/**
* The JSON factory that is used to create the parser that will be used to
* parse the data.
*/
private static final JsonFactory JSON_FACTORY = new MappingJsonFactory();
/**
* The authentication token for the requesting user.
*/
private final AuthenticationToken authToken;
/**
* The ID of the schema from which the data was generated.
*/
private final String schemaId;
/**
* The version of the schema from which the data was generated.
*/
private final long version;
/**
* The data to validate and store.
*/
private final String data;
/**
* Creates a request to store some data.
*
* @param authToken
* The requesting user's authentication token.
*
* @param schemaId
* The ID of the schema which should be used to validate the data.
*
* @param version
* The version of the schema which should be used to validate the
* data.
*
* @param data
* The data to validate and store.
*
* @throws OmhException
* A parameter was invalid.
*/
public DataWriteRequest(
final AuthenticationToken authToken,
final String schemaId,
final long version,
final String data)
throws OmhException {
if(authToken == null) {
throw
new InvalidAuthenticationException(
"The authentication token is missing.");
}
if(schemaId == null) {
throw new OmhException("The schema ID is missing.");
}
if(data == null) {
throw new OmhException("The data is missing.");
}
this.authToken = authToken;
this.schemaId = schemaId;
this.version = version;
this.data = data;
}
/**
* Validates the data and, if valid, stores it.
*/
@Override
public void service() throws OmhException {
// First, short-circuit if this request has already been serviced.
if(isServiced()) {
return;
}
else {
setServiced();
}
// Check to be sure the schema is known.
MultiValueResult<? extends Schema> schemas =
Registry.getInstance().getSchemas(schemaId, version, 0, 1);
if(schemas.count() == 0) {
throw
new OmhException(
"The schema ID, '" +
schemaId +
"', and version, '" +
version +
"', pair is unknown.");
}
Schema schema = schemas.iterator().next();
// Get the user that owns this token.
User requestingUser = authToken.getUser();
// Parse the data.
JsonNode dataNode;
try {
dataNode =
JSON_FACTORY
.createJsonParser(data).readValueAs(JsonNode.class);
}
catch(JsonParseException e) {
throw new OmhException("The data was not well-formed JSON.", e);
}
catch(JsonProcessingException e) {
throw new OmhException("The data was not well-formed JSON.", e);
}
catch(IOException e) {
throw new OmhException("The data could not be read.", e);
}
// Make sure it is a JSON array.
if(! (dataNode instanceof ArrayNode)) {
throw new OmhException("The data was not a JSON array.");
}
ArrayNode dataArray = (ArrayNode) dataNode;
// Get the number of data points.
int numDataPoints = dataArray.size();
// Create the result list of data points.
List<Data> dataPoints = new ArrayList<Data>(numDataPoints);
// Create a new ObjectMapper that will be used to convert the meta-data
// node into a MetaData object.
ObjectMapper mapper = new ObjectMapper();
// For each element in the array, be sure it is a JSON object that
// represents a valid data point for this schema.
for(int i = 0; i < numDataPoints; i++) {
// Get the current data point.
JsonNode dataPoint = dataArray.get(i);
// Validate that it is a JSON object.
if(! (dataPoint instanceof ObjectNode)) {
throw
new OmhException(
"A data point was not a JSON object: " + i);
}
ObjectNode dataObject = (ObjectNode) dataPoint;
// Attempt to get the meta-data;
MetaData metaData = null;
JsonNode metaDataNode = dataObject.get(Data.JSON_KEY_METADATA);
if(metaDataNode != null) {
metaData = mapper.convertValue(metaDataNode, MetaData.class);
}
// Attempt to get the schema data.
JsonNode schemaData = dataObject.get(Data.JSON_KEY_DATA);
// If the data is missing, fail the request.
if(schemaData == null) {
throw
new OmhException(
"A data point's '" +
Data.JSON_KEY_DATA +
"' field is missing.");
}
// Create and add the point to the set of data.
dataPoints
.add(
schema
.validateData(
requestingUser.getUsername(),
metaData,
schemaData));
}
// Store the data.
DataSet.getInstance().storeData(dataPoints);
}
}