View Javadoc
1   package org.argeo.osgi.a2;
2   
3   import java.io.FileOutputStream;
4   import java.io.IOException;
5   import java.io.InputStream;
6   import java.net.URL;
7   import java.nio.file.FileVisitResult;
8   import java.nio.file.Files;
9   import java.nio.file.Path;
10  import java.nio.file.SimpleFileVisitor;
11  import java.nio.file.attribute.BasicFileAttributes;
12  import java.util.Collections;
13  import java.util.Map;
14  import java.util.SortedMap;
15  import java.util.TreeMap;
16  import java.util.jar.JarInputStream;
17  import java.util.jar.JarOutputStream;
18  import java.util.jar.Manifest;
19  import java.util.zip.ZipEntry;
20  
21  import org.osgi.framework.Bundle;
22  import org.osgi.framework.BundleContext;
23  import org.osgi.framework.BundleException;
24  import org.osgi.framework.Constants;
25  import org.osgi.framework.Version;
26  
27  /** Where components are retrieved from. */
28  public abstract class AbstractProvisioningSource implements ProvisioningSource {
29  	protected final Map<String, A2Contribution> contributions = Collections.synchronizedSortedMap(new TreeMap<>());
30  
31  	public Iterable<A2Contribution> listContributions(Object filter) {
32  		return contributions.values();
33  	}
34  
35  	@Override
36  	public Bundle install(BundleContext bc, A2Module module) {
37  		try {
38  			Path tempJar = null;
39  			if (module.getLocator() instanceof Path && Files.isDirectory((Path) module.getLocator()))
40  				tempJar = toTempJar((Path) module.getLocator());
41  			Bundle bundle;
42  			try (InputStream in = newInputStream(tempJar != null ? tempJar : module.getLocator())) {
43  				bundle = bc.installBundle(module.getBranch().getCoordinates(), in);
44  			}
45  			if (tempJar != null)
46  				Files.deleteIfExists(tempJar);
47  			return bundle;
48  		} catch (BundleException | IOException e) {
49  			throw new A2Exception("Cannot install module " + module, e);
50  		}
51  	}
52  
53  	@Override
54  	public void update(Bundle bundle, A2Module module) {
55  		try {
56  			Path tempJar = null;
57  			if (module.getLocator() instanceof Path && Files.isDirectory((Path) module.getLocator()))
58  				tempJar = toTempJar((Path) module.getLocator());
59  			try (InputStream in = newInputStream(tempJar != null ? tempJar : module.getLocator())) {
60  				bundle.update(in);
61  			}
62  			if (tempJar != null)
63  				Files.deleteIfExists(tempJar);
64  		} catch (BundleException | IOException e) {
65  			throw new A2Exception("Cannot update module " + module, e);
66  		}
67  	}
68  
69  	@Override
70  	public A2Branch findBranch(String componentId, Version version) {
71  		A2Component component = findComponent(componentId);
72  		if (component == null)
73  			return null;
74  		String branchId = version.getMajor() + "." + version.getMinor();
75  		if (!component.branches.containsKey(branchId))
76  			return null;
77  		return component.branches.get(branchId);
78  	}
79  
80  	protected A2Contribution getOrAddContribution(String contributionId) {
81  		if (contributions.containsKey(contributionId))
82  			return contributions.get(contributionId);
83  		else {
84  			A2Contribution contribution = new A2Contribution(this, contributionId);
85  			contributions.put(contributionId, contribution);
86  			return contribution;
87  		}
88  	}
89  
90  	protected void asTree(String prefix, StringBuffer buf) {
91  		if (prefix == null)
92  			prefix = "";
93  		for (String contributionId : contributions.keySet()) {
94  			buf.append(prefix);
95  			buf.append(contributionId);
96  			buf.append('\n');
97  			A2Contribution contribution = contributions.get(contributionId);
98  			contribution.asTree(prefix + " ", buf);
99  		}
100 	}
101 
102 	protected void asTree() {
103 		StringBuffer buf = new StringBuffer();
104 		asTree("", buf);
105 		System.out.println(buf);
106 	}
107 
108 	protected A2Component findComponent(String componentId) {
109 		SortedMap<A2Contribution, A2Component> res = new TreeMap<>();
110 		for (A2Contribution contribution : contributions.values()) {
111 			components: for (String componentIdKey : contribution.components.keySet()) {
112 				if (componentId.equals(componentIdKey)) {
113 					res.put(contribution, contribution.components.get(componentIdKey));
114 					break components;
115 				}
116 			}
117 		}
118 		if (res.size() == 0)
119 			return null;
120 		// TODO explicit contribution priorities
121 		return res.get(res.lastKey());
122 
123 	}
124 
125 	protected String readVersionFromModule(Path modulePath) {
126 		Manifest manifest;
127 		if (Files.isDirectory(modulePath)) {
128 			manifest = findManifest(modulePath);
129 		} else {
130 			try (JarInputStream in = new JarInputStream(newInputStream(modulePath))) {
131 				manifest = in.getManifest();
132 			} catch (IOException e) {
133 				throw new A2Exception("Cannot read manifest from " + modulePath, e);
134 			}
135 		}
136 		String versionStr = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
137 		return versionStr;
138 	}
139 
140 	protected String readSymbolicNameFromModule(Path modulePath) {
141 		Manifest manifest;
142 		if (Files.isDirectory(modulePath)) {
143 			manifest = findManifest(modulePath);
144 		} else {
145 			try (JarInputStream in = new JarInputStream(newInputStream(modulePath))) {
146 				manifest = in.getManifest();
147 			} catch (IOException e) {
148 				throw new A2Exception("Cannot read manifest from " + modulePath, e);
149 			}
150 		}
151 		String symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
152 		int semiColIndex = symbolicName.indexOf(';');
153 		if (semiColIndex >= 0)
154 			symbolicName = symbolicName.substring(0, semiColIndex);
155 		return symbolicName;
156 	}
157 
158 	private static Manifest findManifest(Path currentPath) {
159 		Path metaInfPath = currentPath.resolve("META-INF");
160 		if (Files.exists(metaInfPath) && Files.isDirectory(metaInfPath)) {
161 			Path manifestPath = metaInfPath.resolve("MANIFEST.MF");
162 			try {
163 				try (InputStream in = Files.newInputStream(manifestPath)) {
164 					Manifest manifest = new Manifest(in);
165 					return manifest;
166 				}
167 			} catch (IOException e) {
168 				throw new A2Exception("Cannot read manifest from " + manifestPath, e);
169 			}
170 		} else {
171 			Path parentPath = currentPath.getParent();
172 			if (parentPath == null)
173 				throw new A2Exception("MANIFEST.MF file not found.");
174 			return findManifest(currentPath.getParent());
175 		}
176 	}
177 
178 	private static Path toTempJar(Path dir) {
179 		try {
180 			Manifest manifest = findManifest(dir);
181 			Path jarPath = Files.createTempFile("a2Source", ".jar");
182 			try (JarOutputStream zos = new JarOutputStream(new FileOutputStream(jarPath.toFile()), manifest)) {
183 				Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
184 					public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
185 						Path relPath = dir.relativize(file);
186 						// skip MANIFEST from folder
187 						if (relPath.toString().contentEquals("META-INF/MANIFEST.MF"))
188 							return FileVisitResult.CONTINUE;
189 						zos.putNextEntry(new ZipEntry(relPath.toString()));
190 						Files.copy(file, zos);
191 						zos.closeEntry();
192 						return FileVisitResult.CONTINUE;
193 					}
194 				});
195 			}
196 			return jarPath;
197 		} catch (IOException e) {
198 			throw new A2Exception("Cannot install OSGi bundle from " + dir, e);
199 		}
200 
201 	}
202 
203 	private InputStream newInputStream(Object locator) throws IOException {
204 		if (locator instanceof Path) {
205 			return Files.newInputStream((Path) locator);
206 		} else if (locator instanceof URL) {
207 			return ((URL) locator).openStream();
208 		} else {
209 			throw new IllegalArgumentException("Unsupported module locator type " + locator.getClass());
210 		}
211 	}
212 }