| /* |
| * 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.subsystem.internal; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.commons.fileupload.FileItem; |
| import org.apache.felix.utils.json.JSONWriter; |
| import org.apache.felix.webconsole.AbstractWebConsolePlugin; |
| import org.apache.felix.webconsole.DefaultVariableResolver; |
| import org.apache.felix.webconsole.SimpleWebConsolePlugin; |
| import org.apache.felix.webconsole.WebConsoleUtil; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.InvalidSyntaxException; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.service.log.LogService; |
| import org.osgi.service.subsystem.Subsystem; |
| import org.osgi.service.subsystem.SubsystemConstants; |
| |
| public class WebConsolePlugin extends SimpleWebConsolePlugin |
| { |
| private static final long serialVersionUID = 4329827842860201817L; |
| private static final String UNABLE_TO_FIND_TARGET_SUBSYSTEM = "Unable to find target subsystem"; |
| |
| private static final String LABEL = "subsystems"; |
| private static final String TITLE = "Subsystems"; |
| private static final String CATEGORY = "OSGi"; |
| private static final String CSS[] = { "/res/ui/bundles.css" }; // yes, it's correct! |
| private static final String RES = "/" + LABEL + "/res/"; |
| |
| private final BundleContext bundleContext; |
| private final String template; |
| |
| public WebConsolePlugin(BundleContext bc) |
| { |
| super(LABEL, TITLE, CSS); |
| bundleContext = bc; |
| |
| template = readTemplateFile("/res/plugin.html"); |
| } |
| |
| @Override |
| public String getCategory() |
| { |
| return CATEGORY; |
| } |
| |
| @Override |
| protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException |
| { |
| String path = request.getPathInfo(); |
| // don't process if this is request to load a resource |
| if (!path.startsWith(RES)) |
| { |
| RequestInfo reqInfo = new RequestInfo(request); |
| if (reqInfo.extension.equals("json")) |
| { |
| renderResult(response, reqInfo); |
| |
| // nothing more to do |
| return; |
| } |
| } |
| super.doGet(request, response); |
| } |
| |
| @Override |
| protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException |
| { |
| String action = WebConsoleUtil.getParameter(req, "action"); |
| if ("install".equals(action)) |
| { |
| installSubsystem(req); |
| |
| if (req.getRequestURI().endsWith("/install")) { |
| // just send 200/OK, no content |
| resp.setContentLength( 0 ); |
| } else { |
| // redirect to URL |
| resp.sendRedirect(req.getRequestURI()); |
| } |
| |
| return; |
| } |
| else |
| { |
| boolean success = false; |
| RequestInfo reqInfo = new RequestInfo(req); |
| if ("start".equals(action)) |
| { |
| startSubsystem(reqInfo.id); |
| success = true; |
| } |
| else if ("stop".equals(action)) |
| { |
| stopSubsystem(reqInfo.id); |
| success = true; |
| } |
| else if ("uninstall".equals(action)) |
| { |
| uninstallSubsystem(reqInfo.id); |
| success = true; |
| } |
| else |
| { |
| super.doPost(req, resp); |
| } |
| |
| if (success) |
| { |
| renderResult(resp, reqInfo); |
| } |
| } |
| } |
| |
| private void installSubsystem(HttpServletRequest req) throws IOException |
| { |
| @SuppressWarnings("rawtypes") |
| Map params = (Map) req.getAttribute( AbstractWebConsolePlugin.ATTR_FILEUPLOAD ); |
| |
| final boolean start = getParameter(params, "subsystemstart") != null; |
| FileItem[] subsystemItems = getFileItems(params, "subsystemfile"); |
| |
| for (final FileItem subsystemItem : subsystemItems) |
| { |
| File tmpFile = null; |
| try |
| { |
| // copy the data to a file for better processing |
| tmpFile = File.createTempFile("installSubsystem", ".tmp"); |
| subsystemItem.write(tmpFile); |
| } |
| catch (Exception e) |
| { |
| log(LogService.LOG_ERROR, "Problem accessing uploaded subsystem file: " + subsystemItem.getName(), e); |
| |
| // remove the temporary file |
| if (tmpFile != null) |
| { |
| tmpFile.delete(); |
| tmpFile = null; |
| } |
| } |
| |
| if (tmpFile != null) |
| { |
| final File file = tmpFile; |
| // TODO support install in other subsystems than the root one |
| // TODO currently this means that when installing more than one subsystem they |
| // will be installed concurrently. Not sure if this is the best idea. |
| // However the client UI does not support selecting more than one file, so |
| // from a practical point of view this is currently not an issue. |
| asyncSubsystemOperation(0, new SubsystemOperation() |
| { |
| @Override |
| public void exec(Subsystem ss) |
| { |
| try |
| { |
| InputStream is = new FileInputStream(file); |
| try |
| { |
| Subsystem nss = ss.install("inputstream:" + subsystemItem.getName(), is); |
| if (start) |
| nss.start(); |
| } |
| finally |
| { |
| is.close(); |
| file.delete(); |
| } |
| } |
| catch (IOException e) |
| { |
| log(LogService.LOG_ERROR, "Problem installing subsystem", e); |
| } |
| } |
| }); |
| } |
| } |
| } |
| |
| private void startSubsystem(long id) throws IOException |
| { |
| asyncSubsystemOperation(id, new SubsystemOperation() |
| { |
| // Lamba, where art thou. So close yet so far away... |
| @Override |
| public void exec(Subsystem ss) |
| { |
| ss.start(); |
| } |
| }); |
| } |
| |
| private void stopSubsystem(long id) throws IOException |
| { |
| asyncSubsystemOperation(id, new SubsystemOperation() |
| { |
| @Override |
| public void exec(Subsystem ss) |
| { |
| ss.stop(); |
| } |
| }); |
| } |
| |
| private void uninstallSubsystem(long id) throws IOException |
| { |
| asyncSubsystemOperation(id, new SubsystemOperation() |
| { |
| @Override |
| public void exec(Subsystem ss) |
| { |
| ss.uninstall(); |
| } |
| }); |
| } |
| |
| private void asyncSubsystemOperation(long id, final SubsystemOperation op) throws IOException |
| { |
| try |
| { |
| Collection<ServiceReference<Subsystem>> refs = |
| bundleContext.getServiceReferences(Subsystem.class, |
| "(" + SubsystemConstants.SUBSYSTEM_ID_PROPERTY + "=" + id + ")"); |
| |
| if (refs.size() < 1) |
| throw new IOException(UNABLE_TO_FIND_TARGET_SUBSYSTEM); |
| |
| final ServiceReference<Subsystem> ref = refs.iterator().next(); |
| new Thread(new Runnable() { |
| @Override |
| public void run() |
| { |
| Subsystem ss = bundleContext.getService(ref); |
| try |
| { |
| op.exec(ss); |
| } |
| finally |
| { |
| bundleContext.ungetService(ref); |
| } |
| } |
| }).start(); |
| } |
| catch (InvalidSyntaxException e) |
| { |
| throw new IOException(e); |
| } |
| } |
| |
| @SuppressWarnings("rawtypes") |
| private FileItem getParameter(Map params, String name) |
| { |
| FileItem[] items = (FileItem[]) params.get(name); |
| if (items != null) |
| { |
| for (int i = 0; i < items.length; i++) |
| { |
| if (items[i].isFormField()) |
| { |
| return items[i]; |
| } |
| } |
| } |
| |
| // nothing found, fail |
| return null; |
| } |
| |
| @SuppressWarnings("rawtypes") |
| private FileItem[] getFileItems(Map params, String name) |
| { |
| final List<FileItem> files = new ArrayList<FileItem>(); |
| FileItem[] items = (FileItem[]) params.get(name); |
| if (items != null) |
| { |
| for (int i = 0; i < items.length; i++) |
| { |
| if (!items[i].isFormField() && items[i].getSize() > 0) |
| { |
| files.add(items[i]); |
| } |
| } |
| } |
| |
| return files.toArray(new FileItem[files.size()]); |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| protected void renderContent(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException |
| { |
| RequestInfo reqInfo = getRequestInfo(req); |
| |
| StringWriter w = new StringWriter(); |
| PrintWriter w2 = new PrintWriter(w); |
| renderResult(w2, reqInfo); |
| |
| // prepare variables |
| DefaultVariableResolver vars = ((DefaultVariableResolver) WebConsoleUtil.getVariableResolver(req)); |
| vars.put("__data__", w.toString()); |
| |
| res.getWriter().print(template); |
| } |
| |
| private void renderResult(HttpServletResponse response, RequestInfo reqInfo) throws IOException |
| { |
| response.setContentType("application/json"); |
| response.setCharacterEncoding("UTF-8"); |
| renderResult(response.getWriter(), reqInfo); |
| } |
| |
| private void renderResult(PrintWriter pw, RequestInfo reqInfo) throws IOException |
| { |
| JSONWriter jw = new JSONWriter(pw); |
| jw.object(); |
| |
| List<Subsystem> subsystems = getSubsystems(); |
| |
| jw.key("status"); |
| jw.value(subsystems.size()); |
| |
| jw.key("data"); |
| jw.array(); |
| for (Subsystem ss : subsystems) |
| { |
| subsystem(jw, ss); |
| } |
| jw.endArray(); |
| |
| jw.endObject(); |
| } |
| |
| private void subsystem(JSONWriter jw, Subsystem ss) throws IOException |
| { |
| jw.object(); |
| |
| jw.key("id"); |
| jw.value(ss.getSubsystemId()); |
| jw.key("name"); |
| jw.value(ss.getSymbolicName()); |
| jw.key("version"); |
| jw.value(ss.getVersion()); |
| jw.key("state"); |
| jw.value(ss.getState()); |
| |
| jw.endObject(); |
| } |
| |
| private List<Subsystem> getSubsystems() throws IOException |
| { |
| try |
| { |
| List<Subsystem> l = new ArrayList<Subsystem>(); |
| for (ServiceReference<Subsystem> ref : bundleContext.getServiceReferences(Subsystem.class, null)) |
| { |
| l.add(bundleContext.getService(ref)); |
| } |
| return l; |
| } |
| catch (InvalidSyntaxException e) |
| { |
| throw new IOException(e); |
| } |
| } |
| |
| public SimpleWebConsolePlugin register() |
| { |
| return register(bundleContext); |
| } |
| |
| static RequestInfo getRequestInfo(HttpServletRequest request) |
| { |
| return (RequestInfo) request.getAttribute(WebConsolePlugin.class.getName()); |
| } |
| |
| class RequestInfo |
| { |
| public final long id; |
| public final Object extension; |
| |
| protected RequestInfo(HttpServletRequest req) |
| { |
| String info = req.getPathInfo(); |
| // remove label and starting slash |
| info = info.substring(getLabel().length() + 1); |
| |
| // get extension |
| if (info.endsWith(".json")) |
| { |
| extension = "json"; |
| info = info.substring(0, info.length() - 5); |
| } |
| else |
| { |
| extension = "html"; |
| } |
| |
| if (info.startsWith("/")) |
| info = info.substring(1); |
| |
| if ("".equals(info)) |
| id = -1; |
| else |
| id = Long.parseLong(info); |
| |
| req.setAttribute(WebConsolePlugin.this.getClass().getName(), this); |
| } |
| } |
| |
| interface SubsystemOperation |
| { |
| void exec(Subsystem ss); |
| } |
| } |