001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.xbean.osgi; 018 019 import org.osgi.framework.Bundle; 020 import org.osgi.framework.BundleContext; 021 import org.osgi.framework.Constants; 022 023 import java.io.ByteArrayInputStream; 024 import java.io.ByteArrayOutputStream; 025 import java.io.File; 026 import java.io.FileInputStream; 027 import java.io.IOException; 028 import java.util.Collections; 029 import java.util.HashSet; 030 import java.util.Iterator; 031 import java.util.Set; 032 import java.util.StringTokenizer; 033 import java.util.jar.Attributes; 034 import java.util.jar.JarEntry; 035 import java.util.jar.JarInputStream; 036 import java.util.jar.JarOutputStream; 037 import java.util.jar.Manifest; 038 039 /** 040 * @author Dain Sundstrom 041 * @version $Id$ 042 * @since 2.0 043 */ 044 public class MavenBundleManager { 045 private final BundleContext bundleContext; 046 private final File localRepository; 047 048 public MavenBundleManager(BundleContext bundleContext, File localRepository) { 049 this.bundleContext = bundleContext; 050 this.localRepository = localRepository; 051 } 052 053 public Project loadProject(Artifact artifact) { 054 if (artifact instanceof Project) { 055 return (Project) artifact; 056 } else { 057 return new Project(artifact.getGroupId(), 058 artifact.getArtifactId(), 059 artifact.getVersion(), 060 artifact.getType(), 061 Collections.EMPTY_SET); 062 } 063 } 064 065 public Project loadProject(String groupId, String artifactId, String version) { 066 return new Project(groupId, artifactId, version, "jar", Collections.EMPTY_SET); 067 } 068 069 public Bundle installBundle(String groupId, String artifactId, String version) throws Exception { 070 return installBundle(loadProject(groupId, artifactId, version)); 071 } 072 073 public Bundle installBundle(Artifact artifact) throws Exception { 074 String symbolicName = artifact.getGroupId() + "." + artifact.getArtifactId(); 075 String bundleVersion = coerceToOsgiVersion(artifact.getVersion()); 076 077 // check if we already loaded this bundle 078 Bundle[] bundles = bundleContext.getBundles(); 079 for (int i = 0; i < bundles.length; i++) { 080 Bundle bundle = bundles[i]; 081 if (symbolicName.equals(bundle.getSymbolicName()) && 082 bundleVersion.equals(bundle.getHeaders().get(Constants.BUNDLE_VERSION))) { 083 return bundle; 084 } 085 } 086 087 // load the project object model for this artiface 088 Project project = loadProject(artifact); 089 090 // build an OSGi manifest for the project 091 Manifest manifest = createOsgiManifest(project); 092 093 // create a jar in memory for the manifest 094 ByteArrayOutputStream out = new ByteArrayOutputStream(); 095 JarOutputStream jarOut = new JarOutputStream(out, manifest); 096 jarOut.close(); 097 out.close(); 098 ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); 099 100 // install the in memory jar 101 Bundle bundle = bundleContext.installBundle(symbolicName, in); 102 103 // install bundles for all of the dependencies 104 for (Iterator iterator = project.getDependencies().iterator(); iterator.hasNext();) { 105 Artifact dependency = (Artifact) iterator.next(); 106 installBundle(dependency); 107 } 108 109 return bundle; 110 } 111 112 public Manifest createOsgiManifest(Project project) throws IOException { 113 String groupId = project.getGroupId(); 114 String artifactId = project.getArtifactId(); 115 String version = project.getVersion(); 116 String jar = groupId.replace('.', '/') + "/" + artifactId + "/" + version + "/" + project.getJar(); 117 118 StringBuffer requireBundle = new StringBuffer(); 119 for (Iterator iterator = project.getDependencies().iterator(); iterator.hasNext();) { 120 Artifact dependency = (Artifact) iterator.next(); 121 if (requireBundle.length() > 0) requireBundle.append(','); 122 123 requireBundle.append(dependency.getGroupId()).append('.').append(dependency.getArtifactId()); 124 requireBundle.append(";visibility:=reexport;bundle-version:=").append(coerceToOsgiVersion(dependency.getVersion())); 125 126 } 127 128 String jarPath = new File(localRepository, jar).getAbsolutePath(); 129 StringBuffer exports = createExportList(jarPath); 130 131 Manifest manifest = new Manifest(); 132 Attributes attributes = manifest.getMainAttributes(); 133 attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); 134 attributes.putValue(Constants.BUNDLE_MANIFESTVERSION, "2"); 135 attributes.putValue(Constants.BUNDLE_VENDOR, groupId); 136 attributes.putValue(Constants.BUNDLE_NAME, artifactId); 137 attributes.putValue(Constants.BUNDLE_VERSION, coerceToOsgiVersion(version)); 138 attributes.putValue(Constants.BUNDLE_SYMBOLICNAME, groupId + "." + artifactId); 139 attributes.putValue("Eclipse-AutoStart", "true"); 140 141 142 attributes.putValue(Constants.BUNDLE_CLASSPATH, ".,external:" + jarPath); 143 144 attributes.putValue(Constants.EXPORT_PACKAGE, exports.toString()); 145 if (requireBundle != null && requireBundle.length() > 0) { 146 attributes.putValue(Constants.REQUIRE_BUNDLE, requireBundle.toString()); 147 } 148 149 return manifest; 150 } 151 152 private static String coerceToOsgiVersion(String version) { 153 int partsFound = 0; 154 String[] versionParts = new String[] { "0", "0", "0"}; 155 StringBuffer qualifier = new StringBuffer(); 156 for (StringTokenizer stringTokenizer = new StringTokenizer(version, ".-"); stringTokenizer.hasMoreTokens();) { 157 String part = stringTokenizer.nextToken(); 158 if (partsFound < 4) { 159 try { 160 Integer.parseInt(part); 161 versionParts[partsFound++] = part; 162 } catch (NumberFormatException e) { 163 partsFound = 4; 164 qualifier.append(coerceToOsgiQualifier(part)); 165 } 166 } else { 167 if (qualifier.length() > 0) qualifier.append("_"); 168 qualifier.append(coerceToOsgiQualifier(part)); 169 } 170 } 171 172 StringBuffer osgiVersion = new StringBuffer(); 173 osgiVersion.append(versionParts[0]).append(".").append(versionParts[1]).append(".").append(versionParts[2]); 174 if (qualifier.length() > 0) { 175 osgiVersion.append(".").append(qualifier); 176 } 177 return osgiVersion.toString(); 178 } 179 180 private static String coerceToOsgiQualifier(String qualifier) { 181 char[] chars = qualifier.toCharArray(); 182 for (int i = 0; i < chars.length; i++) { 183 char c = chars[i]; 184 if (!Character.isLetterOrDigit(c) && c != '_' && c != '-') { 185 chars[i] = '_'; 186 } 187 } 188 return new String(chars); 189 } 190 191 192 private static StringBuffer createExportList(String jarPath) throws IOException { 193 Set packages = new HashSet(); 194 FileInputStream in = null; 195 try { 196 in = new FileInputStream(jarPath); 197 JarInputStream jarIn = new JarInputStream(in); 198 for (JarEntry jarEntry = jarIn.getNextJarEntry(); jarEntry != null; jarEntry = jarIn.getNextJarEntry()) { 199 String packageName = jarEntry.getName(); 200 if (!jarEntry.isDirectory()) { 201 int index = packageName.lastIndexOf("/"); 202 // we can't export the default package 203 if (index > 0) { 204 packageName = packageName.substring(0, index); 205 if (!packageName.equals("META-INF")) { 206 packageName = packageName.replace('/', '.'); 207 packages.add(packageName); 208 } 209 } 210 } 211 } 212 } finally { 213 if (in != null) { 214 try { 215 in.close(); 216 } catch (IOException e) { 217 } 218 } 219 } 220 221 StringBuffer exports = new StringBuffer(); 222 for (Iterator iterator = packages.iterator(); iterator.hasNext();) { 223 String packageName = (String) iterator.next(); 224 if (exports.length() > 0) exports.append(";"); 225 exports.append(packageName); 226 } 227 return exports; 228 } 229 }