/*******************************************************************************
* Copyright (c) 2008, 2009 Bug Labs, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - Neither the name of Bug Labs, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
package com.buglabs.bug.dragonfly;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpService;
import org.osgi.service.log.LogService;
import com.buglabs.bug.dragonfly.module.IModuleControl;
import com.buglabs.util.osgi.FilterUtil;
import com.buglabs.util.osgi.LogServiceUtil;
import com.buglabs.util.osgi.ServiceTrackerUtil.ManagedInlineRunnable;
import com.buglabs.util.xml.XmlNode;
/**
* This bundle is responsible for accepting clients for callback notifications
* based on model change events.
*
* @author ken
*
*/
public class Activator implements BundleActivator, ServiceListener, ManagedInlineRunnable {
public static final int MODEL_CHANGE_EVENT_LISTEN_PORT = 8990;
private static final String DRAGONFLY_WS_PATH = "/event";
private Map<String, List<DragonflyEventSubscriber>> eventMap;
private BundleContext context;
private LogService logService;
private HttpService httpService;
/* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
public void start(BundleContext context) throws Exception {
this.context = context;
logService = LogServiceUtil.getLogService(context);
eventMap = new Hashtable<String, List<DragonflyEventSubscriber>>();
context.addServiceListener(this, FilterUtil.generateServiceFilter(IModuleControl.class.getName()));
}
/* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
public void stop(BundleContext context) throws Exception {
context.removeServiceListener(this);
}
/**
* Generate XML message based on event.
* @param topic
* @param type
* @param module
* @return
*/
private String createMessage(String topic, String type, String module) {
XmlNode root = new XmlNode("event");
new XmlNode(root, "topic", topic);
new XmlNode(root, "source", module);
new XmlNode(root, "type", type);
return root.toString();
}
/**
* Send message to subscriber.
*
* @param s subscriber
* @param message message to send
* @throws IOException on IO failure
*/
private void notifySubscriber(DragonflyEventSubscriber s, String message) throws IOException {
logService.log(LogService.LOG_DEBUG, "Notifying " + s.getUrl() + " of model update.");
URL url = new URL(s.getUrl());
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
OutputStreamWriter osr = new OutputStreamWriter(conn.getOutputStream());
osr.write(message);
osr.flush();
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
//Read but discard response from subscriber.
while ((rd.readLine() != null))
osr.close();
rd.close();
}
/* (non-Javadoc)
* @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
*/
public void serviceChanged(ServiceEvent event) {
ServiceReference sr = event.getServiceReference();
IModuleControl control = (IModuleControl) context.getService(sr);
String type;
// TODO fix this so the event queue name is no longer needed. this is
// artifact of using EventAdmin originally.
if (event.getType() == ServiceEvent.REGISTERED) {
type = "INSERT";
} else if (event.getType() == ServiceEvent.UNREGISTERING) {
type = "REMOVE";
} else {
// For now we ignore service modification events.
return;
}
String module = control.getModuleName();
String topic = "com/buglabs/event/module";
logService.log(LogService.LOG_INFO, "Received new " + type + " model change event for module " + module);
List<DragonflyEventSubscriber> subList = eventMap.get(topic);
List<DragonflyEventSubscriber> removeList = null;
if (subList != null) {
for (DragonflyEventSubscriber s : subList) {
try {
notifySubscriber(s, createMessage(topic, type, module));
} catch (IOException e) {
if (!e.getMessage().equals("Unexpected end of file from server")
&& !e.getMessage().equals("Connection reset")) {
if (removeList == null) {
removeList = new ArrayList<DragonflyEventSubscriber>();
}
logService.log(LogService.LOG_WARNING, "Removing subscriber " + s.getUrl()
+ " due to I/O Exception: " + e.getMessage());
removeList.add(s);
}
}
}
if (removeList != null) {
for (Object o : removeList) {
subList.remove(o);
}
}
}
}
/* (non-Javadoc)
* @see com.buglabs.util.osgi.ServiceTrackerUtil.ManagedRunnable#run(java.util.Map)
*/
public void run(Map<String, Object> services) {
httpService = (HttpService) services.get(HttpService.class.getName());
try {
httpService.registerServlet(DRAGONFLY_WS_PATH, new DragonflyEventServlet(eventMap), null , null);
logService.log(LogService.LOG_INFO, "Registered servlet at: " + DRAGONFLY_WS_PATH);
} catch (Exception e) {
logService.log(LogService.LOG_ERROR, "Failed to register servlet.", e);
}
}
/* (non-Javadoc)
* @see com.buglabs.util.osgi.ServiceTrackerUtil.ManagedRunnable#shutdown()
*/
public void shutdown() {
if (httpService != null)
httpService.unregister(DRAGONFLY_WS_PATH);
}
}