Fixing a deadlock. Some improvements to ModuleSourceRepository API
[simantics/platform.git] / bundles / org.simantics.scl.osgi / src / org / simantics / scl / osgi / internal / BundleModuleSourceRepository.java
1 package org.simantics.scl.osgi.internal;
2
3 import java.net.URL;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.Enumeration;
7
8 import org.osgi.framework.Bundle;
9 import org.osgi.framework.BundleContext;
10 import org.osgi.framework.BundleEvent;
11 import org.osgi.service.component.ComponentContext;
12 import org.osgi.service.component.annotations.Activate;
13 import org.osgi.service.component.annotations.Component;
14 import org.osgi.service.component.annotations.Deactivate;
15 import org.osgi.util.tracker.BundleTracker;
16 import org.simantics.scl.compiler.module.repository.UpdateListener;
17 import org.simantics.scl.compiler.source.ModuleSource;
18 import org.simantics.scl.compiler.source.repository.ModuleSourceRepository;
19
20 import gnu.trove.map.hash.THashMap;
21 import gnu.trove.procedure.TObjectObjectProcedure;
22 import gnu.trove.set.hash.THashSet;
23
24 @Component
25 public class BundleModuleSourceRepository implements ModuleSourceRepository {
26     
27     Tracker tracker;
28     THashMap<String, BundleModuleSource> modules = new THashMap<String, BundleModuleSource>();
29     THashMap<Bundle, ArrayList<String>> modulesPerBundle = new THashMap<Bundle, ArrayList<String>>();
30     THashMap<String, BundleDocumentationSource> documentations = new THashMap<String, BundleDocumentationSource>();
31     THashMap<Bundle, ArrayList<String>> documentationsPerBundle = new THashMap<Bundle, ArrayList<String>>();
32             
33     class Tracker extends BundleTracker<Bundle> {
34         public Tracker(BundleContext context) {
35             super(context, 0xffffffff, null);
36         }
37         
38         @Override
39         public Bundle addingBundle(Bundle bundle, BundleEvent event) {
40             synchronized(BundleModuleSourceRepository.this) {
41                 Enumeration<URL> moduleEntries = bundle.findEntries("scl", "*.scl", true);
42                 if(moduleEntries != null) {
43                     ArrayList<String> modulesInThisBundle = new ArrayList<String>();
44                     while(moduleEntries.hasMoreElements()) {
45                         URL url = moduleEntries.nextElement();
46                         String path = url.getPath();
47                         String moduleName = path.substring(5, path.length()-4);
48                         modules.put(moduleName, new BundleModuleSource(moduleName, bundle, url));
49                         modulesInThisBundle.add(moduleName);
50                     }
51                     modulesPerBundle.put(bundle, modulesInThisBundle);
52                 }
53                 else
54                     modulesPerBundle.put(bundle, new ArrayList<String>(0));
55                 
56                 Enumeration<URL> documentationEntries = bundle.findEntries("scl", "*.md", true);
57                 if(documentationEntries != null) {
58                     ArrayList<String> documentationsInThisBundle = new ArrayList<String>();
59                     while(documentationEntries.hasMoreElements()) {
60                         URL url = documentationEntries.nextElement();
61                         String path = url.getPath();
62                         String documentationName = path.substring(5, path.length()-3);
63                         documentations.put(documentationName, new BundleDocumentationSource(documentationName, bundle, url));
64                         documentationsInThisBundle.add(documentationName);
65                     }
66                     documentationsPerBundle.put(bundle, documentationsInThisBundle);
67                 }
68                 else
69                     documentationsPerBundle.put(bundle, new ArrayList<String>(0));
70                 
71                 return bundle;
72             }
73         }
74         
75         @Override
76         public void removedBundle(Bundle bundle, BundleEvent event,
77                 Bundle object) {
78             synchronized(BundleModuleSourceRepository.this) {
79                 ArrayList<String> moduleList = modulesPerBundle.get(bundle);
80                 if(moduleList != null)
81                     for(String moduleName : moduleList)
82                         modules.remove(moduleName);
83                 ArrayList<String> documentationsList = documentationsPerBundle.get(bundle);
84                 if(documentationsList != null)
85                     for(String documentation : documentationsList)
86                         documentations.remove(documentation);
87             }
88         }
89     };
90     
91     @Activate
92     public void activate(ComponentContext context) {
93         tracker = new Tracker(context.getBundleContext());
94         tracker.open();
95     }
96     
97     @Deactivate
98     public void deactivate(ComponentContext context) {
99         tracker.close();
100     }
101
102     @Override
103     synchronized public ModuleSource getModuleSource(String moduleName,
104             UpdateListener listener) {
105         return modules.get(moduleName);
106     }
107
108     @Override
109     public synchronized Collection<String> getModuleNames() {
110         return new ArrayList<String>(modules.keySet());
111     }
112     
113     @Override
114     public synchronized Collection<String> getDocumentationNames() {
115         return new ArrayList<String>(documentations.keySet());
116     }
117     
118     @Override
119     synchronized public String getDocumentation(String documentationName) {
120         BundleDocumentationSource source = documentations.get(documentationName);
121         if(source == null)
122             return null;
123         else
124             return source.getText();
125     }
126     
127     @Override
128     public void checkUpdates() {
129         synchronized(this) {
130             modulesPerBundle.forEachEntry(new TObjectObjectProcedure<Bundle, ArrayList<String>>() {
131                 @Override
132                 public boolean execute(Bundle bundle, ArrayList<String> oldModules) {
133                     Enumeration<URL> moduleEntries = bundle.findEntries("scl", "*.scl", true);
134                     if(moduleEntries != null) {
135                         THashSet<String> oldModuleSet = new THashSet<String>(oldModules);
136                         ArrayList<String> modulesInThisBundle = new ArrayList<String>();
137                         while(moduleEntries.hasMoreElements()) {
138                             URL url = moduleEntries.nextElement();
139                             String path = url.getPath();
140                             String moduleName = path.substring(5, path.length()-4);
141                             if(!oldModuleSet.remove(moduleName))
142                                 modules.put(moduleName, new BundleModuleSource(moduleName, bundle, url));
143                             modulesInThisBundle.add(moduleName);
144                         }
145                         for(String oldModule : oldModuleSet)
146                             modules.remove(oldModule);
147                         modulesPerBundle.put(bundle, modulesInThisBundle);
148                     }
149                     else {
150                         for(String oldModule : oldModules)
151                             modules.remove(oldModule);
152                         modulesPerBundle.put(bundle, new ArrayList<String>(0));
153                     }
154                     return true;
155                 }
156             });
157             documentationsPerBundle.forEachEntry(new TObjectObjectProcedure<Bundle, ArrayList<String>>() {
158                 @Override
159                 public boolean execute(Bundle bundle, ArrayList<String> oldDocumentations) {
160                     Enumeration<URL> documentationEntries = bundle.findEntries("scl", "*.md", true);
161                     if(documentationEntries != null) {
162                         THashSet<String> oldDocSet = new THashSet<String>(oldDocumentations);
163                         ArrayList<String> documentationsInThisBundle = new ArrayList<String>();
164                         while(documentationEntries.hasMoreElements()) {
165                             URL url = documentationEntries.nextElement();
166                             String path = url.getPath();
167                             String documentationName = path.substring(5, path.length()-3);
168                             if(!oldDocSet.remove(documentationName))
169                                 documentations.put(documentationName, new BundleDocumentationSource(documentationName, bundle, url));
170                             documentationsInThisBundle.add(documentationName);
171                         }
172                         for(String oldDocumentation : oldDocSet)
173                             documentations.remove(oldDocumentation);
174                         documentationsPerBundle.put(bundle, documentationsInThisBundle);
175                     }
176                     else {
177                         for(String oldDocumentation : oldDocumentations)
178                             documentations.remove(oldDocumentation);
179                         documentationsPerBundle.put(bundle, new ArrayList<String>(0));
180                     }
181                     return true;
182                 }
183             });
184         }
185         for(BundleModuleSource source : modules.values())
186             source.checkUpdates();
187     }
188
189     @Override
190     public void clear() {
191         for (BundleModuleSource source : modules.values()) {
192             source.clear();
193         }
194     }
195
196 }