| /* |
| * 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.karaf.webconsole.features; |
| |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.PrintWriter; |
| import java.net.URI; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.karaf.features.Feature; |
| import org.apache.karaf.features.FeaturesService; |
| import org.apache.karaf.features.Repository; |
| import org.apache.felix.webconsole.AbstractWebConsolePlugin; |
| import org.apache.felix.webconsole.WebConsoleConstants; |
| import org.json.JSONException; |
| import org.json.JSONWriter; |
| import org.osgi.framework.BundleContext; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * WebConsole plugin to use {@link FeaturesService}. |
| */ |
| public class FeaturesPlugin extends AbstractWebConsolePlugin { |
| |
| private final Logger log = LoggerFactory.getLogger(FeaturesPlugin.class); |
| |
| public static final String NAME = "features"; |
| public static final String LABEL = "Features"; |
| private ClassLoader classLoader; |
| private String featuresJs = "/features/res/ui/features.js"; |
| private FeaturesService featuresService; |
| private BundleContext bundleContext; |
| |
| @Override |
| protected boolean isHtmlRequest(HttpServletRequest request) { |
| return true; |
| } |
| |
| public void start() { |
| super.activate(bundleContext); |
| this.classLoader = this.getClass().getClassLoader(); |
| this.log.info(LABEL + " plugin activated"); |
| } |
| |
| public void stop() { |
| this.log.info(LABEL + " plugin deactivated"); |
| super.deactivate(); |
| } |
| |
| @Override |
| public String getLabel() { |
| return NAME; |
| } |
| |
| @Override |
| public String getTitle() { |
| return LABEL; |
| } |
| |
| @Override |
| protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { |
| boolean success = false; |
| |
| final String action = req.getParameter("action"); |
| final String feature = req.getParameter("feature"); |
| final String version = req.getParameter("version"); |
| final String url = req.getParameter("url"); |
| |
| if (action == null) { |
| success = true; |
| } else if ("installFeature".equals(action)) { |
| success = this.installFeature(feature, version); |
| } else if ("uninstallFeature".equals(action)) { |
| success = this.uninstallFeature(feature, version); |
| } else if ("refreshRepository".equals(action)) { |
| success = this.refreshRepository(url); |
| } else if ("removeRepository".equals(action)) { |
| success = this.removeRepository(url); |
| } else if ("addRepository".equals(action)) { |
| success = this.addRepository(url); |
| } |
| |
| if (success) { |
| // let's wait a little bit to give the framework time |
| // to process our request |
| try { |
| Thread.sleep(800); |
| } catch (InterruptedException e) { |
| // ignore |
| } |
| this.renderJSON(resp); |
| } else { |
| super.doPost(req, resp); |
| } |
| } |
| |
| @Override |
| protected void renderContent(HttpServletRequest request, HttpServletResponse response) throws IOException { |
| |
| final PrintWriter pw = response.getWriter(); |
| |
| String appRoot = (String) request.getAttribute(WebConsoleConstants.ATTR_APP_ROOT); |
| |
| final String featuresScriptTag = "<script src='" + appRoot + this.featuresJs + "' language='JavaScript'></script>"; |
| pw.println(featuresScriptTag); |
| |
| pw.println("<script type='text/javascript'>"); |
| pw.println("// <![CDATA["); |
| pw.println("var imgRoot = '" + appRoot + "/res/imgs';"); |
| pw.println("// ]]>"); |
| pw.println("</script>"); |
| |
| pw.println("<div id='plugin_content'>"); |
| |
| pw.println("<script type='text/javascript'>"); |
| pw.println("// <![CDATA["); |
| pw.print("renderFeatures( "); |
| writeJSON(pw); |
| pw.println(" )"); |
| pw.println("// ]]>"); |
| pw.println("</script>"); |
| } |
| |
| protected URL getResource(String path) { |
| path = path.substring(NAME.length() + 1); |
| if (path == null || path.isEmpty()) { |
| return null; |
| } |
| URL url = this.classLoader.getResource(path); |
| if (url != null) { |
| InputStream ins = null; |
| try { |
| ins = url.openStream(); |
| if (ins == null) { |
| this.log.error("failed to open " + url); |
| url = null; |
| } |
| } catch (IOException e) { |
| this.log.error(e.getMessage(), e); |
| url = null; |
| } finally { |
| if (ins != null) { |
| try { |
| ins.close(); |
| } catch (IOException e) { |
| this.log.error(e.getMessage(), e); |
| } |
| } |
| } |
| } |
| return url; |
| } |
| |
| private boolean installFeature(String feature, String version) { |
| boolean success = false; |
| if (featuresService == null) { |
| this.log.error("Features service is not available"); |
| } |
| try { |
| featuresService.installFeature(feature, version); |
| success = true; |
| } catch (Exception e) { |
| this.log.error("Can't install feature {}/{}", feature, version, e); |
| } |
| return success; |
| } |
| |
| private boolean uninstallFeature(String feature, String version) { |
| boolean success = false; |
| if (featuresService == null) { |
| this.log.error("Features service is not available"); |
| } |
| try { |
| featuresService.uninstallFeature(feature, version); |
| success = true; |
| } catch (Exception e) { |
| this.log.error("Can't uninstall feature {}/{}", feature, version, e); |
| } |
| return success; |
| } |
| |
| private boolean removeRepository(String url) { |
| boolean success = false; |
| if (featuresService == null) { |
| this.log.error("Features service is not available"); |
| } |
| try { |
| featuresService.removeRepository(new URI(url)); |
| success = true; |
| } catch (Exception e) { |
| this.log.error("Can't remove features repository {}", url, e); |
| } |
| return success; |
| } |
| |
| private boolean refreshRepository(String url) { |
| boolean success = false; |
| if (featuresService == null) { |
| this.log.error("Features service is not available"); |
| } |
| try { |
| featuresService.removeRepository(new URI(url)); |
| featuresService.addRepository(new URI(url)); |
| success = true; |
| } catch (Exception e) { |
| this.log.error("Can't refresh features repository {}", url, e); |
| } |
| return success; |
| } |
| |
| private boolean addRepository(String url) { |
| boolean success = false; |
| if (featuresService == null) { |
| this.log.error("Features service is not available"); |
| } |
| try { |
| featuresService.addRepository(new URI(url)); |
| success = true; |
| } catch (Exception e) { |
| this.log.error("Can't add features repository {}", url, e); |
| } |
| return success; |
| } |
| |
| private void renderJSON(final HttpServletResponse response) throws IOException { |
| response.setContentType("application/json"); |
| response.setCharacterEncoding("UTF-8"); |
| |
| final PrintWriter pw = response.getWriter(); |
| writeJSON(pw); |
| } |
| |
| private void writeJSON(final PrintWriter pw) throws IOException { |
| final List<Repository> repositories = this.getRepositories(); |
| final List<ExtendedFeature> features = this.getFeatures(repositories); |
| final String statusLine = this.getStatusLine(features); |
| |
| final JSONWriter jw = new JSONWriter(pw); |
| |
| try { |
| jw.object(); |
| |
| jw.key("status"); |
| jw.value(statusLine); |
| |
| jw.key("repositories"); |
| jw.array(); |
| for (Repository r : repositories) { |
| jw.object(); |
| jw.key("name"); |
| String name = r.getName(); |
| jw.value(name); |
| jw.key("url"); |
| String uri = r.getURI().toString(); |
| jw.value(uri); |
| jw.key("actions"); |
| jw.array(); |
| boolean enable = true; |
| if (uri.startsWith("bundle")) { |
| enable = false; |
| } |
| action(jw, enable, "refreshRepository", "Refresh", "refresh"); |
| action(jw, enable, "removeRepository", "Remove", "delete"); |
| jw.endArray(); |
| jw.endObject(); |
| } |
| jw.endArray(); |
| |
| jw.key("features"); |
| jw.array(); |
| for (ExtendedFeature f : features) { |
| featureInfo(jw, f); |
| } |
| jw.endArray(); |
| |
| jw.endObject(); |
| |
| } catch (JSONException je) { |
| throw new IOException(je.toString()); |
| } |
| |
| } |
| |
| private List<Repository> getRepositories() { |
| List<Repository> repositories = new ArrayList<Repository>(); |
| |
| if (featuresService == null) { |
| this.log.error("Features service is not available"); |
| return repositories; |
| } |
| |
| try { |
| for (Repository r : featuresService.listRepositories()) { |
| repositories.add(r); |
| } |
| } catch (Exception e) { |
| this.log.error(e.getMessage()); |
| } |
| |
| return repositories; |
| } |
| |
| private List<ExtendedFeature> getFeatures(List<Repository> repositories) { |
| List<ExtendedFeature> features = new ArrayList<ExtendedFeature>(); |
| |
| if (featuresService == null) { |
| this.log.error("Features service is not available"); |
| return features; |
| } |
| |
| try { |
| for (Repository r : repositories) { |
| for (Feature f : r.getFeatures()) { |
| ExtendedFeature.State state = |
| featuresService.isInstalled(f) ? ExtendedFeature.State.INSTALLED : ExtendedFeature.State.UNINSTALLED; |
| features.add(new ExtendedFeature(state, r.getName(), f)); |
| } |
| } |
| } catch (Exception e) { |
| this.log.error(e.getMessage()); |
| } |
| |
| Collections.sort(features, new ExtendedFeatureComparator()); |
| return features; |
| } |
| |
| class ExtendedFeatureComparator implements Comparator<ExtendedFeature> { |
| @Override |
| public int compare(ExtendedFeature o1, ExtendedFeature o2) { |
| return o1.getName().toLowerCase().compareTo(o2.getName().toLowerCase()); |
| } |
| } |
| |
| private String getStatusLine(final List<ExtendedFeature> features) { |
| int installed = 0; |
| for (ExtendedFeature f : features) { |
| if (f.getState() == ExtendedFeature.State.INSTALLED) { |
| installed++; |
| } |
| } |
| final StringBuffer buffer = new StringBuffer(); |
| buffer.append("Feature information: "); |
| appendFeatureInfoCount(buffer, "in total", features.size()); |
| if (installed == features.size()) { |
| buffer.append(" - all "); |
| appendFeatureInfoCount(buffer, "active.", features.size()); |
| } else { |
| if (installed != 0) { |
| buffer.append(", "); |
| appendFeatureInfoCount(buffer, "installed", installed); |
| } |
| buffer.append('.'); |
| } |
| return buffer.toString(); |
| } |
| |
| private void appendFeatureInfoCount(final StringBuffer buf, String msg, int count) { |
| buf.append(count); |
| buf.append(" feature"); |
| if (count != 1) |
| buf.append('s'); |
| buf.append(' '); |
| buf.append(msg); |
| } |
| |
| private void featureInfo(JSONWriter jw, ExtendedFeature feature) throws JSONException { |
| jw.object(); |
| jw.key("id"); |
| jw.value(feature.getId()); |
| jw.key("name"); |
| jw.value(feature.getName()); |
| jw.key("version"); |
| jw.value(feature.getVersion()); |
| jw.key("repository"); |
| jw.value(feature.getRepository()); |
| jw.key("state"); |
| ExtendedFeature.State state = feature.getState(); |
| jw.value(state.toString()); |
| |
| jw.key("actions"); |
| jw.array(); |
| |
| if (state == ExtendedFeature.State.INSTALLED) { |
| action(jw, true, "uninstallFeature", "Uninstall", "delete"); |
| } else if (state == ExtendedFeature.State.UNINSTALLED) { |
| action(jw, true, "installFeature", "Install", "start"); |
| } |
| jw.endArray(); |
| |
| jw.endObject(); |
| } |
| |
| private void action(JSONWriter jw, boolean enabled, String op, String title, String image) throws JSONException { |
| jw.object(); |
| jw.key("enabled").value(enabled); |
| jw.key("op").value(op); |
| jw.key("title").value(title); |
| jw.key("image").value(image); |
| jw.endObject(); |
| } |
| |
| public void setFeaturesService(FeaturesService featuresService) { |
| this.featuresService = featuresService; |
| } |
| |
| public void setBundleContext(BundleContext bundleContext) { |
| this.bundleContext = bundleContext; |
| } |
| |
| } |