/* * 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.internal.core; import java.io.File; import java.io.InputStream; import org.apache.felix.bundlerepository.Reason; import org.apache.felix.webconsole.SimpleWebConsolePlugin; import org.osgi.framework.Bundle; import org.osgi.framework.BundleException; import org.osgi.framework.Constants; import org.osgi.service.log.LogService; import org.osgi.service.obr.RepositoryAdmin; import org.osgi.service.obr.Requirement; import org.osgi.service.obr.Resolver; import org.osgi.service.obr.Resource; class UpdateHelper extends BaseUpdateInstallHelper { private final Bundle bundle; UpdateHelper( final SimpleWebConsolePlugin plugin, final Bundle bundle, boolean refreshPackages ) { this( plugin, bundle, null, refreshPackages ); } UpdateHelper( final SimpleWebConsolePlugin plugin, final Bundle bundle, final File bundleFile, boolean refreshPackages ) { super( plugin, "Background Update " + bundle.getSymbolicName() + " (" + bundle.getBundleId() + ")", bundleFile, refreshPackages ); this.bundle = bundle; } protected Bundle doRun( final InputStream bundleStream ) throws BundleException { bundle.update( bundleStream ); return bundle; } protected Bundle getTargetBundle() { return bundle; } protected Bundle doRun() throws Exception { // update the bundle from the file if defined if ( getBundleFile() != null ) { return super.doRun(); } // try updating from the bundle location if ( updateFromBundleLocation() ) { return bundle; } // ensure we have a symbolic name for the OBR update to follow if ( bundle.getSymbolicName() == null ) { throw new BundleException( "Cannot update bundle: Symbolic Name is required for OBR update" ); } // try updating from Apache Felix OBR if ( updateFromFelixOBR() ) { return bundle; } // try updating from OSGi OBR if ( updateFromOsgiOBR() ) { return bundle; } // bundle was not updated, return nothing return null; } private boolean updateFromBundleLocation() { getLog().log( LogService.LOG_DEBUG, "Trying to update with Bundle.update()" ); try { bundle.update(); getLog().log( LogService.LOG_INFO, "Bundle updated from bundle provided (update) location" ); return true; } catch ( Throwable ioe ) { // BundleException, IllegalStateException or SecurityException? lets use OBR then getLog().log( LogService.LOG_DEBUG, "Update failure using Bundle.update()", ioe ); } // not installed from the bundle location return false; } private boolean updateFromFelixOBR() { org.apache.felix.bundlerepository.RepositoryAdmin ra = ( org.apache.felix.bundlerepository.RepositoryAdmin ) getService( "org.apache.felix.bundlerepository.RepositoryAdmin" ); if ( ra != null ) { getLog().log( LogService.LOG_DEBUG, "Trying to update from OSGi Bundle Repository (Apache Felix API)" ); final org.apache.felix.bundlerepository.Resolver resolver = ra.resolver(); String version = ( String ) bundle.getHeaders().get( Constants.BUNDLE_VERSION ); if ( version == null ) { version = "0.0.0"; } final String filter = "(&(symbolicname=" + bundle.getSymbolicName() + ")(!(version=" + version + "))(version>=" + version + "))"; final org.apache.felix.bundlerepository.Requirement req = ra.getHelper().requirement( bundle.getSymbolicName(), filter ); final org.apache.felix.bundlerepository.Resource[] resources = ra .discoverResources( new org.apache.felix.bundlerepository.Requirement[] { req } ); final org.apache.felix.bundlerepository.Resource resource = selectHighestVersion( resources ); if ( resource != null ) { resolver.add( resource ); if ( !resolver.resolve() ) { logRequirements( "Cannot updated bundle from OBR due to unsatisfied requirements", resolver .getUnsatisfiedRequirements() ); } else { logResource( "Installing Requested Resources", resolver.getAddedResources() ); logResource( "Installing Required Resources", resolver.getRequiredResources() ); logResource( "Installing Optional Resources", resolver.getOptionalResources() ); // deploy the resolved bundles and ensure they are started resolver.deploy( org.apache.felix.bundlerepository.Resolver.START ); getLog().log( LogService.LOG_INFO, "Bundle updated from OSGi Bundle Repository" ); return true; } } else { getLog().log( LogService.LOG_INFO, "Nothing to update, OSGi Bundle Repository does not provide more recent version" ); } } else { getLog().log( LogService.LOG_DEBUG, "Cannot updated from OSGi Bundle Repository: Service not available" ); } // fallback to false, nothing done return false; } private boolean updateFromOsgiOBR() { RepositoryAdmin ra = ( RepositoryAdmin ) getService( "org.osgi.service.obr.RepositoryAdmin" ); if ( ra != null ) { getLog().log( LogService.LOG_DEBUG, "Trying to update from OSGi Bundle Repository (OSGi API)" ); final Resolver resolver = ra.resolver(); String version = ( String ) bundle.getHeaders().get( Constants.BUNDLE_VERSION ); if ( version == null ) { version = "0.0.0"; } final String filter = "(&(symbolicname=" + bundle.getSymbolicName() + ")(!(version=" + version + "))(version>=" + version + "))"; final Resource[] resources = ra.discoverResources( filter ); final Resource resource = selectHighestVersion( resources ); if ( resource != null ) { resolver.add( resource ); if ( !resolver.resolve() ) { logRequirements( "Cannot updated bundle from OBR due to unsatisfied requirements", resolver .getUnsatisfiedRequirements() ); } else { logResource( "Installing Requested Resources", resolver.getAddedResources() ); logResource( "Installing Required Resources", resolver.getRequiredResources() ); logResource( "Installing Optional Resources", resolver.getOptionalResources() ); // deploy the resolved bundles and ensure they are started resolver.deploy( true ); getLog().log( LogService.LOG_INFO, "Bundle updated from OSGi Bundle Repository" ); return true; } } else { getLog().log( LogService.LOG_INFO, "Nothing to update, OSGi Bundle Repository does not provide more recent version" ); } } else { getLog().log( LogService.LOG_DEBUG, "Cannot updated from OSGi Bundle Repository: Service not available" ); } // fallback to false, nothing done return false; } //---------- Apache Felix OBR API helper private org.apache.felix.bundlerepository.Resource selectHighestVersion( final org.apache.felix.bundlerepository.Resource[] candidates ) { if ( candidates == null || candidates.length == 0 ) { // nothing to do if there are none return null; } else if ( candidates.length == 1 ) { // simple choice if there is a single one return candidates[0]; } // now go on looking for the highest version org.apache.felix.bundlerepository.Resource best = candidates[0]; for ( int i = 1; i < candidates.length; i++ ) { if ( best.getVersion().compareTo( candidates[i].getVersion() ) < 0 ) { best = candidates[i]; } } return best; } private void logResource( String message, org.apache.felix.bundlerepository.Resource[] res ) { if ( res != null && res.length > 0 ) { getLog().log( LogService.LOG_INFO, message ); for ( int i = 0; i < res.length; i++ ) { getLog().log( LogService.LOG_INFO, " " + i + ": " + res[i].getSymbolicName() + ", " + res[i].getVersion() ); } } } private void logRequirements( String message, Reason[] reasons ) { getLog().log( LogService.LOG_ERROR, message ); for ( int i = 0; reasons != null && i < reasons.length; i++ ) { String moreInfo = reasons[i].getRequirement().getComment(); if ( moreInfo == null ) { moreInfo = reasons[i].getRequirement().getFilter().toString(); } getLog().log( LogService.LOG_ERROR, " " + i + ": " + reasons[i].getRequirement().getName() + " (" + moreInfo + ")" ); } } //---------- OSGi OBR API helper private Resource selectHighestVersion( final Resource[] candidates ) { if ( candidates == null || candidates.length == 0 ) { // nothing to do if there are none return null; } else if ( candidates.length == 1 ) { // simple choice if there is a single one return candidates[0]; } // now go on looking for the highest version Resource best = candidates[0]; for ( int i = 1; i < candidates.length; i++ ) { if ( best.getVersion().compareTo( candidates[i].getVersion() ) < 0) { best = candidates[i]; } } return best; } private void logResource( String message, Resource[] res ) { if ( res != null && res.length > 0 ) { getLog().log( LogService.LOG_INFO, message ); for ( int i = 0; i < res.length; i++ ) { getLog().log( LogService.LOG_INFO, " " + i + ": " + res[i].getSymbolicName() + ", " + res[i].getVersion() ); } } } private void logRequirements( String message, Requirement[] reasons ) { getLog().log( LogService.LOG_ERROR, message ); for ( int i = 0; reasons != null && i < reasons.length; i++ ) { String moreInfo = reasons[i].getComment(); if ( moreInfo == null ) { moreInfo = reasons[i].getFilter().toString(); } getLog().log( LogService.LOG_ERROR, " " + i + ": " + reasons[i].getName() + " (" + moreInfo + ")" ); } } }