package de.joerghoh.cq5.jmx.requests;

import java.io.IOException;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.felix.scr.annotations.Component;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.day.cq.wcm.api.Page;

/**
 * RequestFilter -- provide information about requests
 * 
 * @author joerg
 * 
 *         This service maintains a set of services to deliver information about
 *         incoming requests. These services are then exported as MBeans via JMX
 *         Whiteboard.
 */
@Component(immediate = true)
@Service
@Properties({ @Property(name = "sling.filter.scope", value = "request") })
public class RequestFilter implements Filter {

	private static String MBEAN_PREFIX = "de.joerghoh.cq5.jmx.requests";

	private Logger log = LoggerFactory.getLogger(RequestFilter.class);

	private Map<String, ServiceRegistration> services = new HashMap<String, ServiceRegistration>();

	private BundleContext bundleContext;

	private boolean shutdownInProgress = false;

	// livecycle stuff

	@Activate
	protected void activate(ComponentContext ctx) {
		bundleContext = ctx.getBundleContext();
		shutdownInProgress = false;
	}

	@Deactivate
	protected void deactivate() {
		shutdownInProgress = true;
		for (ServiceRegistration sr : services.values()) {
			sr.unregister();
		}
	}

	public void init(FilterConfig config) throws ServletException {

	}

	public void destroy() {

	}

	// implementation

	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {

		RequestInformationImpl rii = null;

		if (!shutdownInProgress) {
			SlingHttpServletRequest req = (SlingHttpServletRequest) request;
			Resource r = req.getResource();
			String contentType = req.getResponseContentType();
			Page p = r.adaptTo(Page.class);
			String templatePath = "";
			if (p != null) {
				templatePath = p.getTemplate().getPath();
			} else {
				// not a page
			}

			// If there is no service defined for this mime type yet, we
			// register it
			String designator = buildMBeanName(contentType, templatePath);
			if (!services.containsKey(designator)) {
				registerReportingService(designator, contentType);
			}

			// TODO cache service objects for performance???

			ServiceRegistration reg = services.get(designator);
			Object o = bundleContext.getService(reg.getReference());
			if (o instanceof RequestInformationImpl) {
				rii = (RequestInformationImpl) o;
			} else {
				log.error("Something went wrong with the retrieval of the service object for meban "
						+ designator);
				rii = null;
			}
		}

		// forward the request down the chain and collect timing information
		long t1 = System.currentTimeMillis();
		chain.doFilter(request, response);
		long t2 = System.currentTimeMillis();

		if (!shutdownInProgress && rii != null) {
			rii.update(t2 - t1);
		}

	}

	// helper

	/**
	 * Create a valid ObjectName for this specific combination
	 * 
	 * @param mimetype
	 *            -- the mimetype for the request
	 * @param templatePath
	 *            -- the path to the template (if provided)
	 * @return a valid name which can be converted to an ObjectName
	 */
	private String buildMBeanName(String mimetype, String templatePath) {
		if (templatePath.equals("")) {
			return MBEAN_PREFIX + ".mime:type=" + mimetype;
		} else {
			return MBEAN_PREFIX + ".template:template=" + templatePath;
		}

	}

	private ServiceRegistration registerReportingService(String mbeanName,
			String mimeType) {

		RequestInformationImpl rii = new RequestInformationImpl(mimeType);
		Dictionary<String, String> props = new Hashtable<String, String>();
		props.put("jmx.objectname", mbeanName);

		log.debug("Registering mbean for " + mbeanName);

		ServiceRegistration reg = bundleContext.registerService(
				RequestInformationMBean.class.getName(), rii, props);
		services.put(mbeanName, reg);

		return reg;

	}
}

@@ -2635,16 +2635,43 @@
 if (p !=
+ null && p.getTemplate() !=
  null) %7B
@@ -2746,16 +2746,41 @@
 t a page
+ or no template specified
 %0A%09%09%09%7D%0A%0A%09

fix NPE when for a CQ page no template is specified