/*******************************************************************************
* 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.ws.module;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.service.log.LogService;
import com.buglabs.bug.dragonfly.module.IModuleControl;
import com.buglabs.bug.dragonfly.module.IModuleProperty;
import com.buglabs.bug.dragonfly.module.MutableModuleProperty;
import com.buglabs.bug.ws.Activator;
import com.buglabs.util.osgi.BUGBundleConstants;
import com.buglabs.util.xml.XmlNode;
/**
* A servlet to expose BUG module data to web clients.
*
*/
public class ModuleServlet extends HttpServlet implements ServiceListener {
private static final long serialVersionUID = -6977609397049223637L;
private IModuleControl[] modules;
private BundleContext context;
/**
* Construct a ModuleServlet with no existing modules.
*/
public ModuleServlet() {
context = Activator.getContext();
//Initialize the array that tracks modules.
this.modules = new IModuleControl[BUGBundleConstants.BUG_TOTAL_BMI_SLOTS];
for (int i = 0; i < modules.length; ++i)
modules[i] = null;
}
/**
* Construct a ModuleServlet with pre-existing modules.
*
* @param mcontrols IModuleControl[]
*/
public ModuleServlet(IModuleControl[] mcontrols) {
context = Activator.getContext();
this.modules = mcontrols;
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String path = req.getPathInfo();
// Must have a module in the path.
if (path == null) {
resp.sendError(BUGBundleConstants.WS_HTTP_ERROR_INVALID_MODULE, "Error: invalid module.");
return;
}
int index = Integer.parseInt(path.substring(1).trim().toUpperCase());
IModuleControl mc = modules[index];
// Must be a valid module
if (mc == null) {
resp.sendError(BUGBundleConstants.WS_HTTP_ERROR_UNKNOWN_MODULE, "Error: unknown module " + path);
return;
}
// Iterate thru post variables and set them on
for (Enumeration names = req.getParameterNames(); names.hasMoreElements();) {
String paramName = (String) names.nextElement();
String paramValue = req.getParameter(paramName);
// TODO: Make sure all properties are valid before setting them.
if (!containsProperty(mc.getModuleProperties(), paramName)) {
resp.sendError(BUGBundleConstants.WS_HTTP_ERROR_INVALID_PROPERTY, "Error: invalid property " + paramName);
return;
}
if (!mc.setModuleProperty(new MutableModuleProperty(paramName, paramValue))) {
resp.sendError(BUGBundleConstants.WS_HTTP_ERROR_CANT_SET_PROPERTY, "Error: unable to set property " + paramName);
}
}
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String path = req.getPathInfo();
String response = null;
resp.setContentType("text/xml");
if (path == null) {
response = getModules();
} else {
try {
int index = Integer.parseInt(path.substring(1).trim());
if (index < 0 || index > 3) {
resp.setContentType("text/plain");
response = "Error: expected BMI slot index (0 - 3)";
}
IModuleControl mc = modules[index];
if (mc != null) {
response = getModuleXML(mc);
} else {
resp.setContentType("text/plain");
response = "Error: no module.";
}
} catch (NumberFormatException e) {
resp.setContentType("text/plain");
response = "Error: expected BMI slot index (0 - 3)";
}
}
resp.getWriter().print(response);
}
/**
* @param mc IModuleControl
* @return module XML
*/
private String getModuleXML(IModuleControl mc) {
XmlNode root = new XmlNode("module");
root.setAttribute("name", mc.getModuleName());
for (Iterator i = mc.getModuleProperties().iterator(); i.hasNext();) {
IModuleProperty prop = (IModuleProperty) i.next();
XmlNode propNode = new XmlNode("property");
propNode.setAttribute("name", prop.getName());
if (prop.getValue() != null) {
propNode.setAttribute("value", prop.getValue().toString());
} else {
propNode.setAttribute("value", "null");
}
propNode.setAttribute("type", prop.getType());
propNode.setAttribute("mutable", Boolean.toString(prop.isMutable()));
root.addChild(propNode);
}
return root.toString();
}
/**
* @return XML as string of modules
*/
private String getModules() {
XmlNode root = new XmlNode("modules");
for (int i = 0; i < modules.length; ++i) {
if (modules[i] != null) {
String moduleName = modules[i].getModuleName();
XmlNode mod = new XmlNode("module");
mod.setAttribute("name", moduleName);
mod.setAttribute("index", "" + modules[i].getSlotId());
root.addChild(mod);
}
}
return root.toString();
}
// TODO rewrite property storage from List to map to reduce tc of search.
private boolean containsProperty(List properties, String name) {
for (Iterator i = properties.iterator(); i.hasNext();) {
IModuleProperty p = (IModuleProperty) i.next();
if (p.getName().toUpperCase().equals(name.toUpperCase())) {
return true;
}
}
return false;
}
@Override
public void serviceChanged(ServiceEvent event) {
//Here we get notified when IModuleControls are added and removed from OSGi service registry.
//We want to keep our state up-to-date with the modules array.
IModuleControl imc = (IModuleControl) context.getService(event.getServiceReference());
if (event.getType() == ServiceEvent.REGISTERED) {
if (modules[imc.getSlotId()] != null)
Activator.getLog().log(LogService.LOG_WARNING
, "A new IModuleControl registration is overwriting a pre-existing module.");
modules[imc.getSlotId()] = imc;
} else if (event.getType() == ServiceEvent.UNREGISTERING) {
modules[imc.getSlotId()] = null;
}
}
}