blob: 640d0fad84a3ccd817ae1430425ad3ad83d05cf5 [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.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.json.JSONException;
import org.json.JSONWriter;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The <code>FeaturesPlugin</code>
*/
public class FeaturesPlugin extends AbstractWebConsolePlugin
{
/** Pseudo class version ID to keep the IDE quite. */
private static final long serialVersionUID = 1L;
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;
//
// Blueprint lifecycle callback methods
//
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();
}
//
// AbstractWebConsolePlugin interface
//
public String getLabel()
{
return NAME;
}
public String getTitle()
{
return LABEL;
}
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 )
{
// we ignore this
}
this.renderJSON( resp, null );
}
else
{
super.doPost( req, resp );
}
}
protected void renderContent( HttpServletRequest request, HttpServletResponse response ) throws IOException
{
// get request info from request attribute
final PrintWriter pw = response.getWriter();
String appRoot = ( String ) request
.getAttribute( "org.apache.felix.webconsole.internal.servlet.OsgiManager.appRoot" );
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>" );
}
//
// Additional methods
//
protected URL getResource( String path )
{
path = path.substring( NAME.length() + 1 );
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( "Shell Features service is unavailable." );
}
try
{
featuresService.installFeature( feature, version );
success = true;
}
catch ( Exception e )
{
this.log.error( "failed to install feature: ", e );
}
return success;
}
private boolean uninstallFeature(String feature, String version) {
boolean success = false;
if ( featuresService == null )
{
this.log.error( "Shell Features service is unavailable." );
}
try
{
featuresService.uninstallFeature( feature, version );
success = true;
}
catch ( Exception e )
{
this.log.error( "failed to install feature: ", e );
}
return success;
}
private boolean removeRepository(String url) {
boolean success = false;
if ( featuresService == null )
{
this.log.error( "Shell Features service is unavailable." );
}
try
{
featuresService.removeRepository( new URI( url ) );
success = true;
}
catch ( Exception e )
{
this.log.error( "failed to install feature: ", e );
}
return success;
}
private boolean refreshRepository(String url) {
boolean success = false;
if ( featuresService == null )
{
this.log.error( "Shell Features service is unavailable." );
}
try
{
featuresService.removeRepository( new URI( url ) );
featuresService.addRepository( new URI( url ) );
success = true;
}
catch ( Exception e )
{
this.log.error( "failed to install feature: ", e );
}
return success;
}
private boolean addRepository(String url) {
boolean success = false;
if ( featuresService == null )
{
this.log.error( "Shell Features service is unavailable." );
}
try
{
featuresService.addRepository( new URI( url ) );
success = true;
}
catch ( Exception e )
{
this.log.error( "failed to install feature: ", e );
}
return success;
}
private void renderJSON( final HttpServletResponse response, final String feature ) 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" );
jw.value( r.getName() );
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( "Shell Features service is unavailable." );
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( "Shell Features service is unavailable." );
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>
{
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();
}
//
// Dependency Injection setters
//
public void setFeaturesService(FeaturesService featuresService)
{
this.featuresService = featuresService;
}
public void setBundleContext(BundleContext bundleContext)
{
this.bundleContext = bundleContext;
}
}