001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2014 ForgeRock AS. 015 */ 016 017package org.forgerock.openig.audit.monitor; 018 019import static org.forgerock.openig.audit.Tag.*; 020 021import java.io.IOException; 022import java.util.LinkedHashMap; 023import java.util.Set; 024import java.util.concurrent.ConcurrentHashMap; 025import java.util.concurrent.atomic.AtomicLong; 026 027import org.forgerock.openig.audit.AuditEvent; 028import org.forgerock.openig.audit.AuditEventListener; 029import org.forgerock.openig.audit.ConditionalAuditEventListener; 030import org.forgerock.openig.audit.Tag; 031import org.forgerock.openig.handler.GenericHandler; 032import org.forgerock.openig.handler.HandlerException; 033import org.forgerock.openig.http.Exchange; 034import org.forgerock.openig.http.Response; 035import org.forgerock.openig.util.EnumUtil; 036 037 038/** 039 * Sample statistic endpoint provider that returns JSON-formatted collected statistic values. 040 */ 041public class MonitorEndpointHandler extends GenericHandler implements AuditEventListener { 042 043 private static final Set<String> STANDARD_TAG_NAMES = EnumUtil.names(Tag.class); 044 045 private ConcurrentHashMap<String, TagMetric> metrics = new ConcurrentHashMap<String, TagMetric>(); 046 047 @Override 048 public void handle(final Exchange exchange) throws HandlerException, IOException { 049 Response response = new Response(); 050 response.getEntity().setJson(metrics); 051 response.setStatus(200); 052 exchange.response = response; 053 } 054 055 @Override 056 public void onAuditEvent(final AuditEvent event) { 057 // Extract the set of additional tags 058 Set<String> tags = event.getTags(); 059 060 // Manage counter for each of the additional tags, effectively performing correlations 061 for (String tag : tags) { 062 // Ignore tag if it is a standard one 063 if (STANDARD_TAG_NAMES.contains(tag)) { 064 continue; 065 } 066 TagMetric metric = getMetric(tag); 067 if (tags.contains(request.name())) { 068 metric.active.incrementAndGet(); 069 } 070 if (tags.contains(response.name())) { 071 metric.active.decrementAndGet(); 072 if (tags.contains(completed.name())) { 073 metric.completed.incrementAndGet(); 074 } 075 if (tags.contains(exception.name())) { 076 metric.errors.incrementAndGet(); 077 } 078 } 079 } 080 } 081 082 private TagMetric getMetric(final String name) { 083 TagMetric counter = metrics.get(name); 084 if (counter != null) { 085 return counter; 086 } 087 TagMetric newCounter = new TagMetric(); 088 TagMetric oldCounter = metrics.putIfAbsent(name, newCounter); 089 return oldCounter != null ? oldCounter : newCounter; 090 } 091 092 /** 093 * TagMetric extends a Map implementation to benefit of the natural mapping into a JSON object. 094 * Still have 'in progress', 'completed' and 'internal errors' fields for easy programmatic access. 095 * TagMetric is thread-safe because no additional fields are put in the structure after creation. 096 */ 097 private static class TagMetric extends LinkedHashMap<String, AtomicLong> { 098 099 public static final long serialVersionUID = 1L; 100 101 final AtomicLong active = new AtomicLong(); 102 final AtomicLong completed = new AtomicLong(); 103 final AtomicLong errors = new AtomicLong(); 104 105 public TagMetric() { 106 put("in progress", active); 107 put("completed", completed); 108 put("internal errors", errors); 109 } 110 } 111 112 /** 113 * Creates and initializes a MonitorEndpointHandler in a heap environment. 114 */ 115 public static class Heaplet extends ConditionalAuditEventListener.ConditionalListenerHeaplet { 116 @Override 117 protected AuditEventListener createListener() { 118 return new MonitorEndpointHandler(); 119 } 120 } 121 122}