blob: 15d7e612285368cb02e25b790ec6b97af062362c [file] [log] [blame]
/*
* 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.felix.webconsole.plugins.event.internal;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.util.Date;
import java.util.Dictionary;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.felix.utils.json.JSONWriter;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
/**
* The Event Plugin
*/
public class PluginServlet extends HttpServlet
{
private static final String ACTION_POST = "post"; //$NON-NLS-1$
private static final String ACTION_SEND = "send"; //$NON-NLS-1$
private static final String ACTION_CLEAR = "clear"; //$NON-NLS-1$
private static final String PARAMETER_ACTION = "action"; //$NON-NLS-1$
/** The event collector. */
private final EventCollector collector;
/** Is the config admin available? */
private volatile boolean configAdminAvailable = false;
private EventAdmin eventAdmin;
private final String TEMPLATE;
public PluginServlet()
{
this.collector = new EventCollector(null);
TEMPLATE = readTemplateFile(getClass(), "/res/events.html"); //$NON-NLS-1$
}
private final String readTemplateFile(final Class clazz, final String templateFile)
{
InputStream templateStream = getClass().getResourceAsStream(templateFile);
if (templateStream != null)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] data = new byte[1024];
try
{
int len = 0;
while ((len = templateStream.read(data)) > 0)
{
baos.write(data, 0, len);
}
return baos.toString("UTF-8"); //$NON-NLS-1$
}
catch (IOException e)
{
// don't use new Exception(message, cause) because cause is 1.4+
throw new RuntimeException("readTemplateFile: Error loading "
+ templateFile + ": " + e);
}
finally
{
try
{
templateStream.close();
}
catch (IOException e)
{
/* ignore */
}
}
}
// template file does not exist, return an empty string
log("readTemplateFile: File '" + templateFile + "' not found through class "
+ clazz);
return ""; //$NON-NLS-1$
}
private static final Event newEvent(HttpServletRequest request)
{
String topic = request.getParameter("topic"); //$NON-NLS-1$
return new Event(topic, PropertiesEditorSupport.convertProperties(request));
}
/**
* @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doPost( HttpServletRequest req, HttpServletResponse resp )
throws ServletException, IOException
{
final String action = req.getParameter( PARAMETER_ACTION );
if ( ACTION_POST.equals(action) ) {
final Event event = newEvent(req);
eventAdmin.postEvent(event);
} else if (ACTION_SEND.equals(action)) {
final Event event = newEvent(req);
eventAdmin.sendEvent(event);
} else if ( ACTION_CLEAR.equals( action ) ) {
this.collector.clear();
}
// we always send back the json data
resp.setContentType( "application/json" ); //$NON-NLS-1$
resp.setCharacterEncoding( "utf-8" ); //$NON-NLS-1$
renderJSON( resp.getWriter() );
}
private void renderJSON( final PrintWriter pw ) throws IOException
{
List events = this.collector.getEvents();
StringBuffer statusLine = new StringBuffer();
statusLine.append( events.size() );
statusLine.append( " Event");
if ( events.size() != 1 )
{
statusLine.append('s');
}
statusLine.append( " received" );
if ( !events.isEmpty() )
{
statusLine.append( " since " );
Date d = new Date();
d.setTime( ( ( EventInfo ) events.get( 0 ) ).received );
statusLine.append( d );
}
statusLine.append( ". (Event admin: " );
if ( this.eventAdmin == null )
{
statusLine.append("un");
}
statusLine.append("available; Config admin: ");
if ( !this.configAdminAvailable )
{
statusLine.append("un");
}
statusLine.append("available)");
// Compute scale: startTime is 0, lastTimestamp is 100%
final long startTime = this.collector.getStartTime();
final long endTime = (events.size() == 0 ? startTime : ((EventInfo)events.get(events.size() - 1)).received);
final float scale = (endTime == startTime ? 100.0f : 100.0f / (endTime - startTime));
final JSONWriter writer = new JSONWriter(pw);
writer.object();
writer.key( "status" );
writer.value( statusLine.toString() );
writer.key( "data" );
writer.array();
// display list in reverse order
for ( int index = events.size() - 1; index >= 0; index-- )
{
eventJson( writer, ( EventInfo ) events.get( index ), index, startTime, scale );
}
writer.endArray();
writer.endObject();
}
protected void doGet( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException
{
final String info = request.getPathInfo();
if ( info.endsWith( ".json" ) ) //$NON-NLS-1$
{
response.setContentType( "application/json" ); //$NON-NLS-1$
response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
PrintWriter pw = response.getWriter();
this.renderJSON( pw );
// nothing more to do
return;
}
this.renderContent( request, response );
}
protected void renderContent( HttpServletRequest request, HttpServletResponse response )
throws ServletException, IOException
{
final PrintWriter pw = response.getWriter();
//final String appRoot = ( String ) request.getAttribute( "felix.webconsole.appRoot" );
//pw.println( "<script src='" + appRoot + "/events/res/ui/" + "events.js" + "' type='text/javascript'></script>" );
pw.print(TEMPLATE);
}
public URL getResource(String path)
{
if ( path.startsWith("/events/res/ui/") ) //$NON-NLS-1$
{
return this.getClass().getResource(path.substring(7));
}
return null;
}
private void eventJson( JSONWriter jw, EventInfo info, int index, final long start, final float scale )
throws IOException
{
final long msec = info.received - start;
// Compute color bar size and make sure the bar is visible
final int percent = Math.max((int)(msec * scale), 2);
jw.object();
jw.key( "id" );
jw.value( String.valueOf( index ) );
jw.key( "offset" );
jw.value( msec );
jw.key( "width" );
jw.value( percent );
jw.key( "category" );
jw.value( info.category );
jw.key( "received" );
jw.value( info.received );
jw.key( "topic" );
jw.value( info.topic );
if ( info.info != null )
{
jw.key( "info" );
jw.value( info.info );
}
jw.key( "properties" );
jw.object();
if ( info.properties != null && info.properties.size() > 0 )
{
final Iterator i = info.properties.entrySet().iterator();
while ( i.hasNext() )
{
final Map.Entry current = (Entry) i.next();
jw.key( current.getKey().toString() );
jw.value(current.getValue());
}
}
jw.endObject();
jw.endObject();
}
public void updateConfiguration( Dictionary dict)
{
this.collector.updateConfiguration(dict);
}
public EventCollector getCollector()
{
return this.collector;
}
public void setEventAdmin(final EventAdmin eventAdmin)
{
this.eventAdmin = eventAdmin;
}
public void setConfigAdminAvailable(final boolean flag)
{
this.configAdminAvailable = flag;
}
}