Java tutorial
/** * 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.jive.myco.seyren.mongo; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; import javax.inject.Inject; import javax.inject.Named; import org.bson.types.ObjectId; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.mongodb.BasicDBObject; import com.mongodb.Bytes; import com.mongodb.CommandFailureException; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; import com.mongodb.MongoClient; import com.mongodb.MongoClientURI; import com.mongodb.MongoException; import com.mongodb.WriteConcern; import com.jive.myco.seyren.core.domain.Alert; import com.jive.myco.seyren.core.domain.AlertType; import com.jive.myco.seyren.core.domain.Check; import com.jive.myco.seyren.core.domain.SeyrenResponse; import com.jive.myco.seyren.core.domain.Subscription; import com.jive.myco.seyren.core.store.AlertsStore; import com.jive.myco.seyren.core.store.ChecksStore; import com.jive.myco.seyren.core.store.SubscriptionsStore; import com.jive.myco.seyren.core.util.config.SeyrenConfig; import com.jive.myco.seyren.core.util.hashing.TargetHash; @Named public class MongoStore implements ChecksStore, AlertsStore, SubscriptionsStore { private static final Logger LOGGER = LoggerFactory.getLogger(MongoStore.class); private MongoMapper mapper = new MongoMapper(); private DB mongo; @Inject public MongoStore(SeyrenConfig seyrenConfig) { try { String uri = seyrenConfig.getMongoUrl(); MongoClientURI mongoClientUri = new MongoClientURI(uri); MongoClient mongoClient = new MongoClient(mongoClientUri); DB mongo = mongoClient.getDB(mongoClientUri.getDatabase()); mongo.setWriteConcern(WriteConcern.ACKNOWLEDGED); this.mongo = mongo; bootstrapMongo(); } catch (Exception e) { throw new RuntimeException(e); } } private void bootstrapMongo() { LOGGER.info( "Bootstrapping Mongo indexes. Depending on the number of checks and alerts you've got it may take a little while."); try { createIndices(); removeOldIndices(); addTargetHashToAlerts(); } catch (MongoException e) { LOGGER.error("Failure while bootstrapping Mongo indexes.\n" + "If you've hit this problem it's possible that you have two checks which are named the same and violate an index which we've tried to add.\n" + "Please correct the problem by removing the clash. If it's something else, please let us know on Github!", e); throw new RuntimeException( "Failed to bootstrap Mongo indexes. Please refer to the logs for more information.", e); } LOGGER.info("Done bootstrapping Mongo indexes."); } private void createIndices() { LOGGER.info("Ensuring that we have all the indices we need"); getChecksCollection().createIndex(new BasicDBObject("name", 1), new BasicDBObject("unique", true)); getChecksCollection().createIndex(new BasicDBObject("enabled", 1).append("live", 1)); getAlertsCollection().createIndex(new BasicDBObject("timestamp", -1)); getAlertsCollection().createIndex(new BasicDBObject("checkId", 1).append("targetHash", 1)); } private void removeOldIndices() { LOGGER.info("Dropping old indices"); try { getAlertsCollection().dropIndex(new BasicDBObject("checkId", 1).append("target", 1)); } catch (CommandFailureException e) { if (e.getCode() != -5) { // -5 is the code which appears when the index doesn't exist (which we're happy with, anything else is bad news) throw e; } } } private void addTargetHashToAlerts() { LOGGER.info("Adding targetHash field to any alerts which don't have it"); DBCursor alerts = getAlertsCollection() .find(new BasicDBObject("targetHash", new BasicDBObject("$exists", false))); alerts.addOption(Bytes.QUERYOPTION_NOTIMEOUT); int alertCount = alerts.count(); if (alertCount > 0) { LOGGER.info("Found {} alert(s) which need updating", alertCount); } while (alerts.hasNext()) { DBObject alertObject = alerts.next(); Alert alert = mapper.alertFrom(alertObject); getAlertsCollection().save(mapper.alertToDBObject(alert)); } } private DBCollection getChecksCollection() { return mongo.getCollection("checks"); } private DBCollection getAlertsCollection() { return mongo.getCollection("alerts"); } @Override public SeyrenResponse<Check> getChecks(Boolean enabled, Boolean live) { List<Check> checks = new ArrayList<Check>(); DBObject query = new BasicDBObject(); if (enabled != null) { query.put("enabled", enabled); } if (live != null) { query.put("live", live); } DBCursor dbc = getChecksCollection().find(query); while (dbc.hasNext()) { checks.add(mapper.checkFrom(dbc.next())); } return new SeyrenResponse<Check>().withValues(checks).withTotal(dbc.count()); } @Override public SeyrenResponse<Check> getChecksByState(Set<String> states, Boolean enabled) { List<Check> checks = new ArrayList<Check>(); DBObject query = new BasicDBObject(); query.put("state", NiceDBObject.object("$in", states.toArray())); if (enabled != null) { query.put("enabled", enabled); } DBCursor dbc = getChecksCollection().find(query); while (dbc.hasNext()) { checks.add(mapper.checkFrom(dbc.next())); } dbc.close(); return new SeyrenResponse<Check>().withValues(checks).withTotal(dbc.count()); } @Override public Check getCheck(String checkId) { DBObject dbo = getChecksCollection().findOne(NiceDBObject.object("_id", checkId)); if (dbo == null) { return null; } return mapper.checkFrom(dbo); } @Override public void deleteCheck(String checkId) { getChecksCollection().remove(NiceDBObject.forId(checkId)); deleteAlerts(checkId, null); } @Override public Check createCheck(Check check) { check.setId(ObjectId.get().toString()); getChecksCollection().insert(mapper.checkToDBObject(check)); return check; } @Override public Check saveCheck(Check check) { DBObject findObject = NiceDBObject.forId(check.getId()); DateTime lastCheck = check.getLastCheck(); DBObject partialObject = NiceDBObject.object("name", check.getName()) .with("description", check.getDescription()).with("target", check.getTarget()) .with("warn", check.getWarn().toPlainString()).with("error", check.getError().toPlainString()) .with("enabled", check.isEnabled()).with("live", check.isLive()) .with("lastCheck", lastCheck == null ? null : new Date(lastCheck.getMillis())) .with("state", check.getState().toString()); DBObject setObject = NiceDBObject.object("$set", partialObject); getChecksCollection().update(findObject, setObject); return check; } @Override public void updateStateAndLastCheck(String checkId, AlertType state, DateTime lastCheck) { DBObject findObject = NiceDBObject.forId(checkId); DBObject partialObject = NiceDBObject.object("lastCheck", new Date(lastCheck.getMillis())).with("state", state.toString()); DBObject setObject = NiceDBObject.object("$set", partialObject); getChecksCollection().update(findObject, setObject); } @Override public Alert createAlert(String checkId, Alert alert) { alert.setId(ObjectId.get().toString()); alert.setCheckId(checkId); getAlertsCollection().insert(mapper.alertToDBObject(alert)); return alert; } @Override public SeyrenResponse<Alert> getAlerts(String checkId, int start, int items) { DBCursor dbc = getAlertsCollection().find(NiceDBObject.object("checkId", checkId)).sort(NiceDBObject .object("timestamp", -1)).skip(start).limit(items); List<Alert> alerts = new ArrayList<Alert>(); while (dbc.hasNext()) { alerts.add(mapper.alertFrom(dbc.next())); } dbc.close(); return new SeyrenResponse<Alert>().withValues(alerts).withItems(items).withStart(start) .withTotal(dbc.count()); } @Override public SeyrenResponse<Alert> getAlerts(int start, int items) { DBCursor dbc = getAlertsCollection().find().sort(NiceDBObject.object("timestamp", -1)).skip(start) .limit(items); List<Alert> alerts = new ArrayList<Alert>(); while (dbc.hasNext()) { alerts.add(mapper.alertFrom(dbc.next())); } dbc.close(); return new SeyrenResponse<Alert>().withValues(alerts).withItems(items).withStart(start) .withTotal(dbc.count()); } @Override public void deleteAlerts(String checkId, DateTime before) { DBObject query = NiceDBObject.object("checkId", checkId); if (before != null) { query.put("timestamp", NiceDBObject.object("$lt", new Date(before.getMillis()))); } getAlertsCollection().remove(query); } @Override public Alert getLastAlertForTargetOfCheck(String target, String checkId) { DBObject query = NiceDBObject.object("checkId", checkId).with("targetHash", TargetHash.create(target)); DBCursor cursor = getAlertsCollection().find(query).sort(NiceDBObject.object("timestamp", -1)).limit(1); try { while (cursor.hasNext()) { return mapper.alertFrom(cursor.next()); } } finally { cursor.close(); } return null; } @Override public Subscription createSubscription(String checkId, Subscription subscription) { subscription.setId(ObjectId.get().toString()); DBObject check = NiceDBObject.forId(checkId); DBObject query = NiceDBObject.object("$push", NiceDBObject.object("subscriptions", mapper.subscriptionToDBObject(subscription))); getChecksCollection().update(check, query); return subscription; } @Override public void deleteSubscription(String checkId, String subscriptionId) { DBObject check = NiceDBObject.forId(checkId); BasicDBObject subscription = NiceDBObject.object("$pull", NiceDBObject.object("subscriptions", NiceDBObject.forId(subscriptionId))); getChecksCollection().update(check, subscription); } @Override public void updateSubscription(String checkId, Subscription subscription) { DBObject subscriptionObject = mapper.subscriptionToDBObject(subscription); DBObject subscriptionFindObject = NiceDBObject.forId(subscription.getId()); DBObject checkFindObject = NiceDBObject.forId(checkId).with("subscriptions", NiceDBObject.object("$elemMatch", subscriptionFindObject)); DBObject updateObject = NiceDBObject.object("$set", NiceDBObject.object("subscriptions.$", subscriptionObject)); getChecksCollection().update(checkFindObject, updateObject); } }