1 package org.simantics.scl.osgi.internal;
5 import java.io.IOException;
6 import java.io.InputStream;
8 import java.nio.charset.Charset;
9 import java.nio.file.Files;
10 import java.nio.file.Path;
11 import java.security.MessageDigest;
12 import java.security.NoSuchAlgorithmException;
13 import java.util.Arrays;
15 import org.eclipse.core.runtime.FileLocator;
16 import org.osgi.framework.Bundle;
17 import org.osgi.framework.BundleException;
18 import org.osgi.framework.wiring.BundleWiring;
19 import org.simantics.scl.compiler.internal.codegen.types.JavaReferenceValidatorFactory;
20 import org.simantics.scl.compiler.module.ImportDeclaration;
21 import org.simantics.scl.compiler.module.repository.UpdateListener;
22 import org.simantics.scl.compiler.source.EncodedTextualModuleSource;
23 import org.simantics.scl.compiler.types.Type;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
27 import gnu.trove.set.hash.THashSet;
29 public class BundleModuleSource extends EncodedTextualModuleSource implements UpdateListener.Observable {
31 private static final Logger LOGGER = LoggerFactory.getLogger(BundleModuleSource.class);
33 public static final ImportDeclaration[] DEFAULT_IMPORTS = new ImportDeclaration[] {
34 new ImportDeclaration("Builtin", ""),
35 new ImportDeclaration("StandardLibrary", "")
38 public static final ImportDeclaration[] DEFAULT_IMPORTS_FOR_STANDARD_LIBRARY = new ImportDeclaration[] {
39 new ImportDeclaration("Builtin", ""),
42 public final Bundle bundle;
45 private byte[] digest;
46 private THashSet<UpdateListener> listeners;
48 public BundleModuleSource(String moduleName, Bundle bundle, URL url) {
55 public void removeListener(UpdateListener listener) {
57 synchronized(listeners) {
58 listeners.remove(listener);
63 public ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
64 if(bundle.getSymbolicName().equals("org.simantics.scl.runtime"))
65 return DEFAULT_IMPORTS_FOR_STANDARD_LIBRARY;
67 return DEFAULT_IMPORTS;
70 private byte[] computeDigest() {
72 InputStream stream = url.openStream();
74 MessageDigest digest = MessageDigest.getInstance("SHA1");
75 byte[] buffer = new byte[1024];
77 int count = stream.read(buffer);
80 digest.update(buffer, 0, count);
82 return digest.digest();
83 } catch (NoSuchAlgorithmException e) {
84 LOGGER.error("No SHA1 algorithm found", e);
89 } catch(IOException e) {
90 LOGGER.error("Could not compute digest for {}", getModuleName(), e);
96 protected InputStream getSourceStream(UpdateListener listener)
99 digest = computeDigest();
100 if(listener != null) {
101 if(listeners == null)
102 listeners = new THashSet<UpdateListener>(4);
103 listeners.add(listener);
104 listener.addObservable(this);
106 return url.openStream();
110 public ClassLoader getClassLoader() {
111 if (bundle.getSymbolicName().equals("org.simantics.scl.runtime"))
112 return Type.class.getClassLoader();
114 BundleWiring wiring = bundle.adapt(BundleWiring.class);
115 if (wiring == null && bundle.getState() == Bundle.INSTALLED) {
118 } catch (BundleException e) {
119 LOGGER.error("Could not start bundle {}", bundle.getSymbolicName(), e);
121 wiring = bundle.adapt(BundleWiring.class);
123 if (wiring != null) {
124 return wiring.getClassLoader();
126 LOGGER.error("Couldn't find class loader for bundle {} with state {}", bundle.getSymbolicName(), BundleUtils.resolveBundleState(bundle));
127 return getClass().getClassLoader();
132 public void checkUpdates() {
133 if(digest != null && listeners != null) {
134 byte[] newDigest = computeDigest();
135 if(!Arrays.equals(digest, newDigest)) {
137 THashSet<UpdateListener> oldListeners = listeners;
139 for(UpdateListener listener : oldListeners)
140 listener.notifyAboutUpdate();
146 * This code is a copy from org.simantics.utils.ui.BundleUtils
148 public static File resolveWritableBundleFile(URL url) throws IOException {
149 // This returns file, jar, http etc. - essentially resolves the bundle protocol
150 URL resolved = FileLocator.resolve(url);
151 if (resolved.getProtocol().equals("file")) {
152 return new File(resolved.getPath());
157 private Path getPath() throws IOException {
158 File file = resolveWritableBundleFile(url);
159 return file != null ? file.toPath() : null;
163 public boolean isUpdateable() {
165 Path path = getPath();
168 return Files.exists(path);
169 } catch (IOException e) {
170 LOGGER.debug("Could not check if {} is updateable", this, e);
176 public void update(String newSourceText) {
178 Path path = getPath();
181 Files.write(path, newSourceText.getBytes(Charset.forName("UTF-8")));
182 } catch(IOException e) {
183 LOGGER.error("Could not update module {} in url {} with text {}", getModuleName(), url, newSourceText);
188 public void clear() {
189 if (listeners != null) {
195 public JavaReferenceValidatorFactory getJavaReferenceValidatorFactory() {
196 return new OsgiJavaReferenceValidatorFactory(bundle);