예제 #1
0
    /**
     * Builds a new Keen client using the interfaces which have been specified explicitly on this
     * builder instance via the set* or with* methods, or the default interfaces if none have been
     * specified.
     *
     * @return A newly constructed Keen client.
     */
    public KeenClient build() {
      try {
        if (httpHandler == null) {
          httpHandler = getDefaultHttpHandler();
        }
      } catch (Exception e) {
        KeenLogging.log("Exception building HTTP handler: " + e.getMessage());
      }

      try {
        if (jsonHandler == null) {
          jsonHandler = getDefaultJsonHandler();
        }
      } catch (Exception e) {
        KeenLogging.log("Exception building JSON handler: " + e.getMessage());
      }

      try {
        if (eventStore == null) {
          eventStore = getDefaultEventStore();
        }
      } catch (Exception e) {
        KeenLogging.log("Exception building event store: " + e.getMessage());
      }

      try {
        if (publishExecutor == null) {
          publishExecutor = getDefaultPublishExecutor();
        }
      } catch (Exception e) {
        KeenLogging.log("Exception building publish executor: " + e.getMessage());
      }

      return buildInstance();
    }
예제 #2
0
  /**
   * Synchronously sends all queued events for the given project. This method will immediately
   * publish the events to the Keen server in the current thread.
   *
   * @param project The project for which to send queued events. If a default project has been set
   *     on the client this parameter may be null, in which case the default project will be used.
   * @param callback An optional callback to receive notification of success or failure.
   */
  public synchronized void sendQueuedEvents(KeenProject project, KeenCallback callback) {

    if (!isActive) {
      handleLibraryInactive(callback);
      return;
    }

    if (project == null && defaultProject == null) {
      handleFailure(
          null, new IllegalStateException("No project specified, but no default project found"));
      return;
    }
    KeenProject useProject = (project == null ? defaultProject : project);

    try {
      String projectId = useProject.getProjectId();
      Map<String, List<Object>> eventHandles = eventStore.getHandles(projectId);
      Map<String, List<Map<String, Object>>> events = buildEventMap(eventHandles);
      String response = publishAll(useProject, events);
      if (response != null) {
        try {
          handleAddEventsResponse(eventHandles, response);
        } catch (Exception e) {
          // Errors handling the response are non-fatal; just log them.
          KeenLogging.log("Error handling response to batch publish: " + e.getMessage());
        }
      }
      handleSuccess(callback);
    } catch (Exception e) {
      handleFailure(callback, e);
    }
  }
예제 #3
0
  /**
   * Validates an event and inserts global properties, producing a new event object which is ready
   * to be published to the Keen service.
   *
   * @param project The project in which the event will be published.
   * @param eventCollection The name of the collection in which the event will be published.
   * @param event A Map that consists of key/value pairs.
   * @param keenProperties A Map that consists of key/value pairs to override default properties.
   * @return A new event Map containing Keen properties and global properties.
   */
  protected Map<String, Object> validateAndBuildEvent(
      KeenProject project,
      String eventCollection,
      Map<String, Object> event,
      Map<String, Object> keenProperties) {

    if (project.getWriteKey() == null) {
      throw new NoWriteKeyException(
          "You can't send events to Keen IO if you haven't set a write key.");
    }

    validateEventCollection(eventCollection);
    validateEvent(event);

    KeenLogging.log(String.format(Locale.US, "Adding event to collection: %s", eventCollection));

    // build the event
    Map<String, Object> newEvent = new HashMap<String, Object>();
    // handle keen properties
    Calendar currentTime = Calendar.getInstance();
    String timestamp = ISO_8601_FORMAT.format(currentTime.getTime());
    if (keenProperties == null) {
      keenProperties = new HashMap<String, Object>();
      keenProperties.put("timestamp", timestamp);
    } else {
      if (!keenProperties.containsKey("timestamp")) {
        keenProperties.put("timestamp", timestamp);
      }
    }
    newEvent.put("keen", keenProperties);

    // handle global properties
    Map<String, Object> globalProperties = getGlobalProperties();
    if (globalProperties != null) {
      newEvent.putAll(globalProperties);
    }
    GlobalPropertiesEvaluator globalPropertiesEvaluator = getGlobalPropertiesEvaluator();
    if (globalPropertiesEvaluator != null) {
      Map<String, Object> props = globalPropertiesEvaluator.getGlobalProperties(eventCollection);
      if (props != null) {
        newEvent.putAll(props);
      }
    }

    // now handle user-defined properties
    newEvent.putAll(event);
    return newEvent;
  }
예제 #4
0
 /**
  * Handles a failure in the Keen library. If the client is running in debug mode, this will
  * immediately throw a runtime exception. Otherwise, this will log an error message and, if the
  * callback is non-null, call the {@link KeenCallback#onFailure(Exception)} method. Any exceptions
  * thrown by the callback are silently ignored.
  *
  * @param callback A callback; may be null.
  * @param e The exception which caused the failure.
  */
 private void handleFailure(KeenCallback callback, Exception e) {
   if (isDebugMode) {
     if (e instanceof RuntimeException) {
       throw (RuntimeException) e;
     } else {
       throw new RuntimeException(e);
     }
   } else {
     KeenLogging.log("Encountered error: " + e.getMessage());
     if (callback != null) {
       try {
         callback.onFailure(e);
       } catch (Exception userException) {
         // Do nothing.
       }
     }
   }
 }
예제 #5
0
  /**
   * 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);
    }
  }
예제 #6
0
 /**
  * Sets whether or not the client is in active mode. When the client is inactive, all requests
  * will be ignored.
  *
  * @param isActive {@code true} to make the client active, or {@code false} to make it inactive.
  */
 protected void setActive(boolean isActive) {
   this.isActive = isActive;
   KeenLogging.log("Keen Client set to " + (isActive ? "active" : "inactive"));
 }
예제 #7
0
  /**
   * 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++;
      }
    }
  }