/*
* Copyright 2012 Nodeable Inc
*
* 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 com.streamreduce.rest.resource.agent;
import com.streamreduce.core.event.EventId;
import com.streamreduce.core.model.Event;
import com.streamreduce.core.model.InventoryItem;
import com.streamreduce.core.model.messages.MessageType;
import com.streamreduce.rest.resource.AbstractResource;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import net.sf.json.JSONArray;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Component;
@Component
@Path("agent/metrics")
public class AgentResource extends AbstractResource {
/**
* Takes a JSON payload representing an agent metrics payload, validates it and
* persists validated payloads. This will also result in a stream message
* with the agent metric information in the message stream. (Note: The JSON
* payload can be either a singular agent metrics payload or an array of agent
* metrics payload objects.)
* <br /><br />
* The request object should be structured as described below:
*
* <ul class="indented">
* <li><b>node_id*:</b> The instance id as known by the cloud provider (Must correspond to a cloud inventory item's node id)</li>
* <li><b>data*:</b> The JSON object describing the agent metric payload
* <ul>
* <li><b>generated*:</b> The timestamp of when the metric was gathered/created (Format: yyyy-MM-dd'T'HH:mm:ss.SSSSSS)</li>
* <li>... (This data can be whatever you want really and since we do not validate this, no need in documenting it at this time.)</li>
* </ul>
* </li>
* </ul>
*
* <b>* denotes a required field.</b>
*
* @param jsonString the raw JSON payload representing either a singular agent
* metrics payload or an array of singular agent metrics payloads
*
* @return the result of the request
*
* @response.representation.405.doc Returned if the request was invalid in any way:
* Invalid agent metrics payload structure, invalid cloud inventory item id in the
* payload or whenever any deeper level exception occurs
*
* @response.representation.201.doc Returned if the request was successful
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createAgentMetricsEntry(String jsonString) {
if (isEmpty(jsonString)) {
return error("Missing payload", Response.status(Response.Status.BAD_REQUEST));
}
try {
JSONObject json = JSONObject.fromObject(jsonString);
if (!json.has("node_id")) {
return error("Request is missing the node id to associate the metrics with.",
Response.status(Response.Status.BAD_REQUEST));
}
if (!json.has("data")) {
return error("Request is missing the 'data'.", Response.status(Response.Status.BAD_REQUEST));
}
try {
json.getJSONArray("data");
} catch (JSONException je) {
return error("'data' should be an array.", Response.status(Response.Status.BAD_REQUEST));
}
String nodeId = json.getString("node_id");
List<InventoryItem> inventoryItems =
applicationManager.getInventoryService().getInventoryItemsForExternalId(nodeId);
if (inventoryItems == null || inventoryItems.isEmpty()) {
return error("'node_id' does not correspond with an inventory item, no metrics stored.",
Response.status(Response.Status.BAD_REQUEST));
}
// we might have multiple inventoryItems with this nodeId
// if keys are shared across the account boundary (you never know!)
for (InventoryItem inventoryItem : inventoryItems) {
JSONArray metrics = json.getJSONArray("data");
for (Object rawMetric : metrics) {
JSONObject metric = (JSONObject)rawMetric;
String generated = metric.getString("generated");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS");
Date dateGenerated;
try {
dateGenerated = df.parse(generated);
} catch (ParseException pe) {
logger.error("Unable to parse the generated date of the metric.", pe);
dateGenerated = new Date();
}
Map<String, Object> eventContext = new HashMap<>();
eventContext.put("activityGenerated", dateGenerated);
eventContext.put("activityPayload", metric);
inventoryItem.addHashtag("agent");
// Create the event stream entry
Event event = applicationManager.getEventService().createEvent(EventId.ACTIVITY,
inventoryItem,
eventContext);
// create the message
// note: all messages will have the #agent tag now.
applicationManager.getMessageService().sendAccountMessage(event,
inventoryItem,
inventoryItem.getConnection(),
dateGenerated.getTime(),
MessageType.AGENT,
inventoryItem.getHashtags(),
null);
}
}
return Response
.status(Response.Status.CREATED)
.build();
} catch (Exception e) {
logger.error("Agent Exception", e);
return error(e.getMessage(), Response.status(Response.Status.BAD_REQUEST));
}
}
}