/* * Copyright 2009 NCHOVY * * Licensed 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.krakenapps.bundle; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import org.krakenapps.api.BundleManager; import org.krakenapps.api.BundleRepository; import org.krakenapps.api.BundleStatus; import org.krakenapps.api.KeyStoreManager; import org.krakenapps.api.MavenArtifact; import org.krakenapps.api.MavenResolveException; import org.krakenapps.api.ProgressMonitor; import org.krakenapps.api.Version; import org.krakenapps.confdb.ConfigService; import org.krakenapps.pkg.MavenResolver; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.BundleException; import org.osgi.framework.ServiceReference; import org.osgi.framework.SynchronousBundleListener; import org.osgi.service.packageadmin.PackageAdmin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Provides all OSGi bundle related functions. * * @author xeraph * */ public class BundleManagerService implements SynchronousBundleListener, BundleManager { private Logger logger = LoggerFactory.getLogger(BundleManagerService.class); private BundleContext context; private BundleConfig config; public BundleManagerService(BundleContext bc) { this.context = bc; bc.addBundleListener(this); config = new BundleConfig(getConfigService()); } private ConfigService getConfigService() { ServiceReference ref = context.getServiceReference(ConfigService.class.getName()); return (ConfigService) context.getService(ref); } /* * (non-Javadoc) * * @see org.krakenapps.bundle.BundleManager#refresh() */ @Override public void refresh() { ServiceReference ref = context.getServiceReference(PackageAdmin.class.getName()); PackageAdmin packageAdmin = (PackageAdmin) context.getService(ref); packageAdmin.refreshPackages(null); } /* * (non-Javadoc) * * @see org.krakenapps.bundle.BundleManager#getRemoteRepositories() */ @Override public List<BundleRepository> getRemoteRepositories() { return config.getRepositories(); } @Override public List<BundleRepository> getRepositories() { return config.getRepositories(); } @Override public BundleRepository getRepository(String alias) { return config.getRepository(alias); } @Override public void addRepository(BundleRepository repo) { config.addRepository(repo); } @Override public void updateRepository(BundleRepository repo) { config.updateRepository(repo); } @Override public void removeRepository(String alias) { config.removeRepository(alias); } /* * (non-Javadoc) * * @see * org.krakenapps.bundle.BundleManager#addRemoteRepository(java.lang.String, * java.net.URL) */ @Override public void addRemoteRepository(String alias, URL url) { config.addRepository(new BundleRepository(alias, url, 0)); } /* * (non-Javadoc) * * @see * org.krakenapps.bundle.BundleManager#addSecureRemoteRepository(java.lang * .String, java.net.URL, java.lang.String, java.lang.String) */ @Override public void addSecureRemoteRepository(String alias, URL url, String trustStoreAlias, String keyStoreAlias) { BundleRepository repo = new BundleRepository(alias, url); repo.setTrustStoreAlias(trustStoreAlias); repo.setKeyStoreAlias(keyStoreAlias); config.addRepository(repo); } /* * (non-Javadoc) * * @see * org.krakenapps.bundle.BundleManager#removeRemoteRepository(java.lang. * String) */ @Override public void removeRemoteRepository(String alias) { config.removeRepository(alias); } /* * (non-Javadoc) * * @see org.krakenapps.bundle.BundleManager#installBundle(java.lang.String) */ @Override public long installBundle(String filePath) { try { Bundle bundle = context.installBundle(filePath); return bundle.getBundleId(); } catch (BundleException e) { throw new IllegalStateException(e); } } /* * (non-Javadoc) * * @see * org.krakenapps.bundle.BundleManager#installBundle(org.krakenapps.pkg. * ProgressMonitor, java.lang.String, java.lang.String, java.lang.String) */ @Override public long installBundle(ProgressMonitor monitor, String groupId, String artifactId, String version) throws MavenResolveException { MavenResolver resolver = new MavenResolver(getLocalRepository(), config.getRepositories(), monitor, getKeyStoreManager()); Version v = (version != null) ? new Version(version) : null; MavenArtifact artifact = new MavenArtifact(groupId, artifactId, v); if (isBuiltinArtifact(artifact.getGroupId(), artifact.getArtifactId())) throw new IllegalStateException("provided in system bundle"); if (monitor != null) monitor.writeln(String.format("Resolving %s/%s", artifact.getGroupId(), artifact.getArtifactId()) + (artifact.getVersion() != null ? (" (" + artifact.getVersion() + ")") : "")); File file = resolver.resolve(artifact); String filePath = file.getAbsolutePath(); try { Bundle newBundle = context.installBundle(getPrefix() + filePath.replace('\\', '/')); if (newBundle.getSymbolicName() == null) newBundle.uninstall(); return newBundle.getBundleId(); } catch (BundleException e) { throw new IllegalStateException(e); } } private String getPrefix() { String prefix = "file://"; if (File.separatorChar != '/') prefix += "/"; return prefix; } private File getLocalRepository() { return new File(getDownloadRoot()); } private String getDownloadRoot() { return new File(System.getProperty("kraken.download.dir")).getAbsolutePath().replaceAll("\\\\", "/"); } /* * (non-Javadoc) * * @see org.krakenapps.bundle.BundleManager#uninstallBundle(long) */ @Override public boolean uninstallBundle(long bundleId) { Bundle bundle = context.getBundle(bundleId); if (bundle == null) { logger.warn(String.format("bundle %d not found.", bundleId)); return false; } try { String downloadRoot = getDownloadRoot(); String prefix = getPrefix(); File bundleLocation = new File(bundle.getLocation().replace(prefix, "")); // prevents destruction out of the download directory. // delete cached bundle in download directory for redownloading if (bundle.getLocation().startsWith(prefix + downloadRoot)) { File bundleDirectory = new File(bundleLocation.getParent()); bundleLocation.delete(); deleteDirectory(bundleDirectory); } bundle.uninstall(); return true; } catch (BundleException e) { logger.error(e.getMessage()); return false; } } /** * Delete the directory recursively. * * @param path * the directory will be removed * @return true if and only if the directory is successfully deleted */ private boolean deleteDirectory(File path) { if (path.exists()) { for (File file : path.listFiles()) { if (file.isDirectory()) { deleteDirectory(file); } else { file.delete(); } } } logger.info("deleting " + path.getAbsolutePath()); return path.delete(); } /* * (non-Javadoc) * * @see org.krakenapps.bundle.BundleManager#startBundle(long) */ @Override public void startBundle(long bundleId) { Bundle bundle = context.getBundle(bundleId); if (bundle == null) { logger.warn(String.format("bundle %d not found", bundleId)); throw new IllegalStateException("bundle " + bundleId + " not found"); } try { bundle.start(); } catch (BundleException e) { throw new IllegalStateException(e); } } /* * (non-Javadoc) * * @see org.krakenapps.bundle.BundleManager#stopBundle(long) */ @Override public void stopBundle(long bundleId) { Bundle bundle = context.getBundle(bundleId); if (bundle == null) { logger.warn(String.format("bundle %d not found", bundleId)); throw new IllegalStateException("bundle " + bundleId + " not found"); } logger.info("Stopping Bundle " + bundle.getSymbolicName()); try { bundle.stop(); } catch (BundleException e) { logger.error("kraken core: stopping bundle failed.", e); } } /* * (non-Javadoc) * * @see org.krakenapps.bundle.BundleManager#updateBundle(long) */ @Override public void updateBundle(long bundleId) { Bundle bundle = context.getBundle(bundleId); if (bundle == null) { logger.warn(String.format("bundle %d not found", bundleId)); throw new IllegalStateException("bundle " + bundleId + " not found"); } try { if (!isLocalJar(bundle)) { try { File before = new File(bundle.getLocation().replace("file://", "")); if (before.exists()) { File temp = File.createTempFile(before.getName(), "", before.getParentFile()); temp.delete(); if (before.renameTo(temp)) { MavenResolver resolver = new MavenResolver(getLocalRepository(), config.getRepositories(), null, getKeyStoreManager()); MavenArtifact artifact = getArtifact(bundle); File after = resolver.resolve(artifact); if (after.exists()) temp.delete(); else temp.renameTo(before); } } else { before.getParentFile().mkdirs(); MavenResolver resolver = new MavenResolver(getLocalRepository(), config.getRepositories(), null, getKeyStoreManager()); MavenArtifact artifact = getArtifact(bundle); resolver.resolve(artifact); } } catch (MavenResolveException e) { logger.error("kraken core: maven resolve failed.", e); throw new RuntimeException("maven resolve failed. (" + e.getMessage() + ")"); } catch (IOException e) { logger.error("kraken core: create temp file failed.", e); throw new RuntimeException("create temp file failed."); } } bundle.update(); } catch (BundleException e) { logger.error("kraken core: updating bundle failed.", e); } } private boolean isLocalJar(Bundle bundle) { File location = new File(bundle.getLocation().replace("file://", "")); File krakenDownload = new File(System.getProperty("kraken.download.dir")); while (location.getParentFile() != null) { if (location.equals(krakenDownload)) return false; location = location.getParentFile(); } return true; } private MavenArtifact getArtifact(Bundle bundle) { File location = new File(bundle.getLocation().replace("file://", "")); File krakenDownload = new File(System.getProperty("kraken.download.dir")); String groupId = null; String artifactId = null; Version version = null; while (location.getParentFile() != null) { location = location.getParentFile(); if (location.equals(krakenDownload)) break; String name = location.getName(); if (version == null) version = new Version(name); else if (artifactId == null) artifactId = name; else if (groupId == null) groupId = name; else groupId = name + "." + groupId; } return new MavenArtifact(groupId, artifactId, version); } /* * (non-Javadoc) * * @see org.krakenapps.bundle.BundleManager#getBundles() */ @Override public Map<Long, BundleStatus> getBundles() { Map<Long, BundleStatus> bundles = new HashMap<Long, BundleStatus>(); for (Bundle bundle : context.getBundles()) { String version = getBundleVersion(bundle); Date packagedDate = null; Object object = bundle.getHeaders().get("Bnd-LastModified"); if (object != null) packagedDate = new Date(Long.parseLong((String) object)); BundleStatus bundleStatus = new BundleStatus(bundle.getSymbolicName(), version, bundle.getState(), packagedDate); bundles.put(bundle.getBundleId(), bundleStatus); } return bundles; } private String getBundleVersion(Bundle bundle) { return (String) bundle.getHeaders().get("Bundle-Version"); } @Override public void bundleChanged(BundleEvent event) { Bundle bundle = event.getBundle(); if (event.getType() == BundleEvent.STARTED) { logger.info(String.format("Starting %s [%d] bundle.", bundle.getSymbolicName(), bundle.getBundleId())); } else if (event.getType() == BundleEvent.STOPPED) { logger.info(String.format("Stopping %s [%d] bundle.", bundle.getSymbolicName(), bundle.getBundleId())); } } public boolean isBuiltinArtifact(String groupId, String artifactId) { if (groupId.equals("org.apache.felix") && artifactId.startsWith("org.osgi.")) return true; if (groupId.equals("org.apache.felix") && artifactId.equals("org.apache.felix.main")) return true; if (groupId.equals("org.krakenapps") && artifactId.equals("kraken-api")) return true; if (groupId.equals("org.slf4j") && artifactId.equals("slf4j-api")) return true; return false; } /* * (non-Javadoc) * * @see org.krakenapps.bundle.BundleManager#getBundleLocation(long) */ @Override public String getBundleLocation(long bundleId) { Bundle bundle = context.getBundle(bundleId); if (bundle == null) throw new IllegalStateException("bundle not found: " + bundleId); return bundle.getLocation(); } @SuppressWarnings("unchecked") @Override public List<String> getEntryPaths(long bundleId, String directory) { Bundle bundle = context.getBundle(bundleId); if (bundle == null) throw new IllegalStateException("bundle not found: " + bundleId); List<String> paths = new ArrayList<String>(); Enumeration<String> e = bundle.getEntryPaths(directory); if (e == null) { return paths; } while (e.hasMoreElements()) { paths.add(e.nextElement()); } return paths; } @Override public String getEntry(long bundleId, String path) throws IOException { Bundle bundle = context.getBundle(bundleId); if (bundle == null) throw new IllegalStateException("bundle not found: " + bundleId); URL url = bundle.getEntry(path); if (url == null) throw new FileNotFoundException(path); InputStream is = url.openStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is, Charset.forName("utf-8"))); StringBuilder sb = new StringBuilder(4096); while (true) { String line = br.readLine(); if (line == null) break; sb.append(line); sb.append("\n"); } return sb.toString(); } private KeyStoreManager getKeyStoreManager() { ServiceReference ref = context.getServiceReference(KeyStoreManager.class.getName()); KeyStoreManager keyman = (KeyStoreManager) context.getService(ref); return keyman; } @Override public boolean isLocallyInstalledBundle(long bundleId) { String bundleLocation = getBundleLocation(bundleId); if (bundleLocation.equals("System Bundle")) return false; File bundleFile = null; try { bundleFile = new File(new URI(bundleLocation)); return !bundleFile.getAbsolutePath().startsWith(getLocalRepository().getAbsolutePath()); } catch (URISyntaxException e) { return false; } } }