Java tutorial
package ch.icclab.cyclops.consume.data; /* * Copyright (c) 2017. Zuercher Hochschule fuer Angewandte Wissenschaften * All Rights Reserved. * * 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. */ import ch.icclab.cyclops.consume.ConsumerEntry; import ch.icclab.cyclops.dao.Usage; import ch.icclab.cyclops.executor.TaskExecutor; import ch.icclab.cyclops.health.HealthCheck; import ch.icclab.cyclops.publish.Messenger; import ch.icclab.cyclops.timeseries.DbAccess; import ch.icclab.cyclops.util.loggers.DataLogger; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import io.gsonfire.GsonFireBuilder; import java.lang.reflect.Type; import java.util.Collections; import java.util.List; /** * Author: Martin Skoviera (linkedin.com/in/skoviera) * Created: 09/08/16 * Description: Process data frame in runnable manner */ public class UsageProcess implements Runnable { private static String INVALID_RECORDS_ROUTING_KEY = "invalid_record"; private String content; private Long deliveryTag = null; private ConsumerEntry consumer = null; private boolean healthCheck = false; private Status status = new Status(); public class Status { private boolean parsed = false; private DbAccess.PersistenceStatus persisted; private int numberOfRecords = 0; public boolean isParsed() { return parsed; } public void setParsed(int records) { numberOfRecords = records; parsed = true; } public void setPersisted(DbAccess.PersistenceStatus persisted) { this.persisted = persisted; } public boolean isPersisted() { return persisted == DbAccess.PersistenceStatus.OK; } public boolean isInvalid() { return persisted == DbAccess.PersistenceStatus.INVALID_RECORDS; } public boolean isDbDown() { return persisted == DbAccess.PersistenceStatus.DB_DOWN; } public int getNumberOfRecords() { return numberOfRecords; } } public UsageProcess(String content) { this.content = content; } public UsageProcess(String content, ConsumerEntry consumer, Long deliveryTag, boolean healthCheck) { this.content = content; this.consumer = consumer; this.deliveryTag = deliveryTag; this.healthCheck = healthCheck; } @Override public void run() { // parse JSON into list of Usage objects List<Usage> parsed = parseWhatCouldBeJSONObject(content); // in case we received just one entry and not an array if (parsed == null) parsed = parseWhatCouldBeJSONList(content); // we got list of Usage objects if (parsed != null && !parsed.isEmpty()) { // parsing was successful status.setParsed(parsed.size()); // persist the usage data DbAccess.PersistenceStatus persisted = persistList(parsed); status.setPersisted(persisted); switch (persisted) { case DB_DOWN: DataLogger.log( String.format("Received %d metrics, but unable to persist (db is down)", parsed.size())); nackAndScheduleHealthCheck(); break; case INVALID_RECORDS: DataLogger.log(String.format("Received %d metrics, but some of them are invalid", parsed.size())); publishAndIfSuccessfulAck(); break; case OK: DataLogger.log(String.format("Received and persisted %d metrics", parsed.size())); ackIfFromRabbitMQ(); break; } } else { DataLogger.log("Received a message that is either invalid or empty"); publishAndIfSuccessfulAck(); } } /** * Nack and optionally schedule a health check */ private void nackAndScheduleHealthCheck() { if (deliveryTag != null) consumer.nackMessage(deliveryTag); if (healthCheck) TaskExecutor.getInstance().executeNow(new HealthCheck()); } /** * ACK the message if it is coming from RabbitMQ, but publish it via an exchange * In case that publishing fails, NACK it and optionally schedule a health check */ private void publishAndIfSuccessfulAck() { if (deliveryTag != null) { boolean published = Messenger.publish(content, INVALID_RECORDS_ROUTING_KEY); if (published) consumer.ackMessage(deliveryTag); else { consumer.nackMessage(deliveryTag); if (healthCheck) TaskExecutor.getInstance().executeNow(new HealthCheck()); } } } /** * Ack a message if it is coming from RabbitMQ */ private void ackIfFromRabbitMQ() { if (deliveryTag != null) consumer.ackMessage(deliveryTag); } /** * Persist list to database * @param list of usage data * @return status */ private DbAccess.PersistenceStatus persistList(List<Usage> list) { DbAccess db = new DbAccess(); // persist the list transactionally return db.storePersistentObjects(list); } /** * Parse JSON into list of Usage objects * @param json text as array * @return list or null */ private List<Usage> parseWhatCouldBeJSONList(String json) { try { Type usageType = new TypeToken<List<Usage>>() { }.getType(); return getNewGson().fromJson(json, usageType); } catch (Exception e) { return null; } } /** * Parse JSON into list of Usage objects * @param json text as object * @return list or null */ private List<Usage> parseWhatCouldBeJSONObject(String json) { try { Usage parsed = getNewGson().fromJson(json, Usage.class); return Collections.singletonList(parsed); } catch (Exception e) { return null; } } /** * Was the processing and persisting successful * @return status */ public Status getStatus() { return status; } /** * Get new Gson instance with registered processor that handles Usage object * @return Gson */ private Gson getNewGson() { return new GsonFireBuilder().registerPreProcessor(Usage.class, new UsageDeserializer<>()).createGson(); } }