/** * Builds a map from collection name to a list of event maps, given a map from collection name to * a list of event handles. This method just uses the event store to retrieve each event by its * handle. * * @param eventHandles A map from collection name to a list of event handles in the event store. * @return A map from collection name to a list of event maps. * @throws IOException If there is an error retrieving events from the store. */ private Map<String, List<Map<String, Object>>> buildEventMap( Map<String, List<Object>> eventHandles) throws IOException { Map<String, List<Map<String, Object>>> result = new HashMap<String, List<Map<String, Object>>>(); for (Map.Entry<String, List<Object>> entry : eventHandles.entrySet()) { String eventCollection = entry.getKey(); List<Object> handles = entry.getValue(); // Skip event collections that don't contain any events. if (handles == null || handles.size() == 0) { continue; } // Build the event list by retrieving events from the store. List<Map<String, Object>> events = new ArrayList<Map<String, Object>>(handles.size()); for (Object handle : handles) { // Get the event from the store. String jsonEvent = eventStore.get(handle); // De-serialize the event from its JSON. StringReader reader = new StringReader(jsonEvent); Map<String, Object> event = jsonHandler.readJson(reader); KeenUtils.closeQuietly(reader); events.add(event); } result.put(eventCollection, events); } return result; }
/** * Posts a request to the server in the specified project, using the given URL and request data. * The request data will be serialized into JSON using the client's {@link * io.keen.client.java.KeenJsonHandler}. * * @param project The project in which the event(s) will be published; this is used to determine * the write key to use for authentication. * @param url The URL to which the POST should be sent. * @param requestData The request data, which will be serialized into JSON and sent in the request * body. * @return The response from the server. * @throws IOException If there was an error communicating with the server. */ private synchronized String publishObject( KeenProject project, URL url, final Map<String, ?> requestData) throws IOException { if (requestData == null || requestData.size() == 0) { KeenLogging.log("No API calls were made because there were no events to upload"); return null; } // Build an output source which simply writes the serialized JSON to the output. OutputSource source = new OutputSource() { @Override public void writeTo(OutputStream out) throws IOException { OutputStreamWriter writer = new OutputStreamWriter(out, ENCODING); jsonHandler.writeJson(writer, requestData); } }; // If logging is enabled, log the request being sent. if (KeenLogging.isLoggingEnabled()) { try { StringWriter writer = new StringWriter(); jsonHandler.writeJson(writer, requestData); String request = writer.toString(); KeenLogging.log( String.format(Locale.US, "Sent request '%s' to URL '%s'", request, url.toString())); } catch (IOException e) { KeenLogging.log("Couldn't log event written to file: "); e.printStackTrace(); } } // Send the request. String writeKey = project.getWriteKey(); Request request = new Request(url, "POST", writeKey, source); Response response = httpHandler.execute(request); // If logging is enabled, log the response. if (KeenLogging.isLoggingEnabled()) { KeenLogging.log( String.format( Locale.US, "Received response: '%s' (%d)", response.body, response.statusCode)); } // If the request succeeded, return the response body. Otherwise throw an exception. if (response.isSuccess()) { return response.body; } else { throw new ServerException(response.body); } }
/** * Handles a response from the Keen service to a batch post events operation. In particular, this * method will iterate through the responses and remove any successfully processed events (or * events which failed for known fatal reasons) from the event store so they won't be sent in * subsequent posts. * * @param handles A map from collection names to lists of handles in the event store. This is * referenced against the response from the server to determine which events to remove from * the store. * @param response The response from the server. * @throws IOException If there is an error removing events from the store. */ @SuppressWarnings("unchecked") private void handleAddEventsResponse(Map<String, List<Object>> handles, String response) throws IOException { // Parse the response into a map. StringReader reader = new StringReader(response); Map<String, Object> responseMap; responseMap = jsonHandler.readJson(reader); // It's not obvious what the best way is to try and recover from them, but just hoping it // doesn't happen is probably the wrong answer. // Loop through all the event collections. for (Map.Entry<String, Object> entry : responseMap.entrySet()) { String collectionName = entry.getKey(); // Get the list of handles in this collection. List<Object> collectionHandles = handles.get(collectionName); // Iterate through the elements in the collection List<Map<String, Object>> eventResults = (List<Map<String, Object>>) entry.getValue(); int index = 0; for (Map<String, Object> eventResult : eventResults) { // now loop through each event collection's individual results boolean removeCacheEntry = true; boolean success = (Boolean) eventResult.get(KeenConstants.SUCCESS_PARAM); if (!success) { // grab error code and description Map errorDict = (Map) eventResult.get(KeenConstants.ERROR_PARAM); String errorCode = (String) errorDict.get(KeenConstants.NAME_PARAM); if (errorCode.equals(KeenConstants.INVALID_COLLECTION_NAME_ERROR) || errorCode.equals(KeenConstants.INVALID_PROPERTY_NAME_ERROR) || errorCode.equals(KeenConstants.INVALID_PROPERTY_VALUE_ERROR)) { removeCacheEntry = true; KeenLogging.log( "An invalid event was found. Deleting it. Error: " + errorDict.get(KeenConstants.DESCRIPTION_PARAM)); } else { String description = (String) errorDict.get(KeenConstants.DESCRIPTION_PARAM); removeCacheEntry = false; KeenLogging.log( String.format( Locale.US, "The event could not be inserted for some reason. " + "Error name and description: %s %s", errorCode, description)); } } // If the cache entry should be removed, get the handle at the appropriate index // and ask the event store to remove it. if (removeCacheEntry) { Object handle = collectionHandles.get(index); // Try to remove the object from the cache. Catch and log exceptions to prevent // a single failure from derailing the rest of the cleanup. try { eventStore.remove(handle); } catch (IOException e) { KeenLogging.log("Failed to remove object '" + handle + "' from cache"); } } index++; } } }