/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* 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.
*/
package org.apache.cocoon.profiling.statistics;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.cocoon.auth.ApplicationUtil;
import org.apache.cocoon.auth.User;
import org.apache.cocoon.components.ContextHelper;
import org.apache.cocoon.environment.ObjectModelHelper;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.environment.Session;
import org.apache.excalibur.store.Store;
import org.apache.excalibur.store.StoreJanitor;
/**
* Implementation for the statistics component.
* In order to prevent an out of memory exception we implement the Store
* interface (dummy implementation) so we can register this component with
* the StoreJanitor component which will invoke this component to free
* memory in case of low memory.
*
* @version $Id$
* @since 2.1.10
*/
public class CollectorImpl
extends AbstractLogEnabled
implements Collector, Store, ThreadSafe, Serviceable, Disposable, Contextualizable {
private static final String COUNT_ATTRIBUTE = CollectorImpl.class.getName();
/** Are we currently collecting? */
protected boolean isCollecting = false;
/** All reports. */
protected Map reports = new HashMap();
/** All page reports. */
protected Map pageReports = new HashMap();
/** The store janitor for registering ourself. */
protected StoreJanitor janitor;
/** The service manager. */
protected ServiceManager manager;
/** The component context. */
protected Context context;
/**
* @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
*/
public void contextualize(Context c) throws ContextException {
this.context = c;
}
/**
* @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
*/
public void service(ServiceManager aManager) throws ServiceException {
this.manager = aManager;
this.janitor = (StoreJanitor)this.manager.lookup(StoreJanitor.ROLE);
this.janitor.register(this);
}
/**
* @see org.apache.avalon.framework.activity.Disposable#dispose()
*/
public void dispose() {
if ( this.manager != null ) {
if ( this.janitor != null ) {
this.janitor.unregister(this);
}
this.manager.release(this.janitor);
this.janitor = null;
this.manager = null;
}
}
/**
* @see org.apache.cocoon.profiling.statistics.Collector#addStatistics(org.apache.cocoon.profiling.statistics.Statistics)
*/
public void addStatistics(final Statistics stats) {
if ( this.isCollecting && stats != null ) {
// get report for this category
ReportImpl report = (ReportImpl)this.reports.get(stats.getCategory());
if ( report == null ) {
synchronized (this) {
report = (ReportImpl)this.reports.get(stats.getCategory());
if ( report == null ) {
// create new report
report = new ReportImpl(stats.getCategory());
this.reports.put(stats.getCategory(), report);
}
}
}
synchronized (report) {
report.add(stats);
}
final String pageKey = this.getRequestKey();
PageReportImpl pageReport = (PageReportImpl)this.pageReports.get(pageKey);
if ( pageReport == null ) {
pageReport = new PageReportImpl(pageKey);
this.pageReports.put(pageKey, pageReport);
}
pageReport.addStatistics(stats);
}
}
/**
* @see org.apache.cocoon.profiling.statistics.Collector#getStatistics()
*/
public Collection getStatistics() {
return this.reports.values();
}
/**
* @see org.apache.cocoon.profiling.statistics.Collector#getPageReports()
*/
public Collection getPageReports() {
return this.pageReports.values();
}
/**
* @see org.apache.cocoon.profiling.statistics.Collector#isCollectingStatistics()
*/
public boolean isCollectingStatistics() {
return this.isCollecting;
}
/**
* @see org.apache.cocoon.profiling.statistics.Collector#setCollectingStatistics(boolean)
*/
public void setCollectingStatistics(boolean value) {
this.isCollecting = value;
if ( !this.isCollecting ) {
this.reports.clear();
this.pageReports.clear();
}
}
/**
* @see org.apache.cocoon.profiling.statistics.Collector#clear()
*/
public void clear() {
this.reports.clear();
this.pageReports.clear();
}
protected String getRequestKey() {
final Map objectModel = ContextHelper.getObjectModel(this.context);
final User user = ApplicationUtil.getUser(objectModel);
final Request request = ObjectModelHelper.getRequest(objectModel);
final Session session = request.getSession();
Integer counter = (Integer) request.getAttribute(CollectorImpl.COUNT_ATTRIBUTE);
if ( counter == null) {
counter = (Integer) session.getAttribute(CollectorImpl.COUNT_ATTRIBUTE);
if ( counter == null ) {
counter = new Integer(0);
} else {
counter = new Integer(counter.intValue() + 1);
}
session.setAttribute(CollectorImpl.COUNT_ATTRIBUTE, counter);
request.setAttribute(CollectorImpl.COUNT_ATTRIBUTE, counter);
}
return (user == null ? "anon" : user.getId()) + ':' + session.getId() + '/' + counter;
}
/**
* @see org.apache.excalibur.store.Store#containsKey(java.lang.Object)
*/
public boolean containsKey(Object arg0) {
return false;
}
/**
* @see org.apache.excalibur.store.Store#free()
*/
public void free() {
// we simply free everything
synchronized ( this ) {
this.reports.clear();
this.pageReports.clear();
}
}
/**
* @see org.apache.excalibur.store.Store#get(java.lang.Object)
*/
public Object get(Object arg0) {
return null;
}
/**
* @see org.apache.excalibur.store.Store#keys()
*/
public Enumeration keys() {
return Collections.enumeration(Collections.EMPTY_LIST);
}
/**
* @see org.apache.excalibur.store.Store#remove(java.lang.Object)
*/
public void remove(Object arg0) {
// nothing to do here
}
/**
* @see org.apache.excalibur.store.Store#size()
*/
public int size() {
return this.reports.size();
}
/**
* @see org.apache.excalibur.store.Store#store(java.lang.Object, java.lang.Object)
*/
public void store(Object arg0, Object arg1) throws IOException {
// nothing to do here
}
}