/* * * * Licensed to the Apache Software Foundation (ASF) under one or more * * contributor license agreements. The ASF licenses this file to You * * 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. For additional information regarding * * copyright in this work, please see the NOTICE file in the top level * * directory of this distribution. * */ package org.apache.usergrid.rest.system; import com.codahale.metrics.Timer; import com.fasterxml.jackson.databind.node.BooleanNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.usergrid.corepersistence.asyncevents.AsyncEventService; import org.apache.usergrid.persistence.qakka.MetricsService; import org.apache.usergrid.persistence.qakka.QakkaFig; import org.apache.usergrid.persistence.qakka.core.Queue; import org.apache.usergrid.persistence.qakka.core.QueueManager; import org.apache.usergrid.persistence.qakka.core.QueueMessageManager; import org.apache.usergrid.persistence.qakka.core.impl.InMemoryQueue; import org.apache.usergrid.persistence.qakka.core.impl.QueueManagerImpl; import org.apache.usergrid.persistence.qakka.core.impl.QueueMessageManagerImpl; import org.apache.usergrid.persistence.qakka.serialization.queuemessages.DatabaseQueueMessage; import org.apache.usergrid.rest.AbstractContextResource; import org.apache.usergrid.rest.ApiResponse; import org.apache.usergrid.rest.security.annotations.RequireSystemAccess; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import java.net.InetAddress; import java.net.UnknownHostException; import java.text.DecimalFormat; import java.util.*; /** * retrieves queue stats */ @Component @Scope( "singleton" ) @Produces( { MediaType.APPLICATION_JSON, "application/javascript", "application/x-javascript", "text/ecmascript", "application/ecmascript", "text/jscript" } ) public class QueueSystemResource extends AbstractContextResource { private static final Logger logger = LoggerFactory.getLogger(QueueSystemResource.class); public QueueSystemResource(){logger.info("queue resource initialized");} /** * Return queue depth of this Usergrid instance in JSON format. * * By Default this end-point will ignore errors but if you call it with ignore_status=false * then it will return HTTP 500 if either the Entity store or the Index for the management * application are in a bad state. * */ @GET @RequireSystemAccess @Path("size") public ApiResponse getQueueDepth( @QueryParam("callback") @DefaultValue("callback") String callback ) { ApiResponse response = createApiResponse(); response.setAction( "get queue depth" ); AsyncEventService eventService = injector.getInstance(AsyncEventService.class); ObjectNode node = JsonNodeFactory.instance.objectNode(); node.put("queueDepth", eventService.getQueueDepth()); response.setProperty( "data", node ); return response; } @GET @RequireSystemAccess @Path("metrics") public ApiResponse getQueueMetrics() { ApiResponse response = createApiResponse(); response.setAction( "get queue metrics" ); MetricsService metricsService = injector.getInstance( MetricsService.class ); final DecimalFormat format = new DecimalFormat("##.###"); final long nano = 1000000000; Map<String, Object> info = new HashMap<String, Object>() {{ put( "name", "Queue Metrics" ); try { put( "host", InetAddress.getLocalHost().getHostName() ); } catch (UnknownHostException e) { put( "host", "unknown" ); } SortedSet<String> names = metricsService.getMetricRegistry().getNames(); for (String name : names) { Timer timer = null; try { timer = metricsService.getMetricRegistry().timer( name ); } catch ( Exception e ) {} if ( timer == null ) { put( name, new HashMap<String, Object>() {{ put( name, "missing" ); }} ); } else { final Timer t = timer; put( name, new HashMap<String, Object>() {{ put( "count", "" + t.getCount() ); put( "mean_rate", "" + format.format( t.getMeanRate() ) ); put( "one_minute_rate", "" + format.format( t.getOneMinuteRate() ) ); put( "five_minute_rate", "" + format.format( t.getFiveMinuteRate() ) ); put( "mean (s)", "" + format.format( t.getSnapshot().getMean() / nano ) ); put( "min (s)", "" + format.format( (double) t.getSnapshot().getMin() / nano ) ); put( "max (s)", "" + format.format( (double) t.getSnapshot().getMax() / nano ) ); }} ); } } }}; response.setProperty( "data", info ); return response; } @GET @RequireSystemAccess @Path("info") public ApiResponse getQueueInfo( @QueryParam("callback") @DefaultValue("callback") String callback ) { ApiResponse response = createApiResponse(); response.setAction( "get queue info" ); Map<String, Object> info = new HashMap<String, Object>() {{ put( "name", "Queue Info" ); }}; QueueManager queueManager = injector.getInstance( QueueManagerImpl.class ); QueueMessageManager queueMessageManager = injector.getInstance( QueueMessageManagerImpl.class ); InMemoryQueue inMemoryQueue = injector.getInstance( InMemoryQueue.class ); List queues = new ArrayList(); final List<String> listOfQueues = queueManager.getListOfQueues(); for ( String queueName : listOfQueues ) { Map<String, Object> queueInfo = new HashMap<>(); try { queueInfo.put( "name", queueName ); queueInfo.put( "inmemory", inMemoryQueue.size( queueName ) ); UUID newest = inMemoryQueue.getNewest( queueName ); queueInfo.put( "since", newest == null ? "null" : newest.timestamp() ); try { queueInfo.put( "depth", queueMessageManager.getQueueDepth( queueName, DatabaseQueueMessage.Type.DEFAULT ) ); } catch ( Exception intentionallyIgnored ) {} try { queueInfo.put( "inflight", queueMessageManager.getQueueDepth( queueName, DatabaseQueueMessage.Type.INFLIGHT ) ); } catch ( Exception intentionallyIgnored ) {} } catch ( Exception e ) { logger.error("Error getting queue info for queue: " + queueName, e); } queues.add( queueInfo ); } info.put("queues", queues); response.setProperty( "data", info ); return response; } @POST @RequireSystemAccess @Path("clear/{queueName}") public ApiResponse clearQueue( @PathParam("queueName") String queueName, @QueryParam("callback") @DefaultValue("callback") String callback ) { logger.debug("clearQueue"); QueueManager queueManager = injector.getInstance( QueueManager.class ); QueueMessageManager queueMessageManager = injector.getInstance( QueueMessageManagerImpl.class ); if ( queueName == null ) { throw new IllegalArgumentException( "queueName net specified in path" ); } if ( queueManager.getQueueConfig( queueName ) == null ) { throw new NotFoundException( "Queue not found: " + queueName ); } ApiResponse response = createApiResponse(); response.setAction( "clear queue: " + queueName ); queueMessageManager.clearMessages( queueName ); response.setProperty( "cleared", Boolean.TRUE ); return response; } }