blob: e59266a9c52a713bd495076aa527d5c91b8eaabc [file] [log] [blame]
Stuart McCulloch26e7a5a2011-10-17 10:31:43 +00001package aQute.lib.deployer.obr;
2
3import java.io.ByteArrayInputStream;
4import java.io.ByteArrayOutputStream;
5import java.io.File;
6import java.io.FileInputStream;
7import java.io.FileOutputStream;
8import java.io.IOException;
9import java.net.URL;
10import java.util.ArrayList;
11import java.util.Collection;
12import java.util.Collections;
13import java.util.HashMap;
14import java.util.HashSet;
15import java.util.List;
16import java.util.Map;
17import java.util.Set;
18
19import javax.xml.transform.stream.StreamResult;
20
21import org.osgi.service.bindex.BundleIndexer;
22import org.xml.sax.InputSource;
23import org.xml.sax.XMLReader;
24
25import aQute.bnd.service.Refreshable;
26import aQute.bnd.service.Registry;
27import aQute.bnd.service.RegistryPlugin;
28import aQute.lib.deployer.FileRepo;
29import aQute.lib.io.IO;
30import aQute.lib.osgi.Jar;
31import aQute.libg.reporter.Reporter;
32import aQute.libg.sax.SAXUtil;
33import aQute.libg.sax.filters.MergeContentFilter;
34import aQute.libg.version.Version;
35
36public class LocalOBR extends OBR implements Refreshable, RegistryPlugin {
37
38 public static final String PROP_LOCAL_DIR = "local";
39 public static final String PROP_READONLY = "readonly";
40
41 private final FileRepo storageRepo = new FileRepo();
42
43 private Registry registry;
44 private File storageDir;
45 private File localIndex;
46
47 private List<URL> indexUrls;
48
49 public void setRegistry(Registry registry) {
50 this.registry = registry;
51 }
52
53 @Override
54 public void setReporter(Reporter reporter) {
55 super.setReporter(reporter);
56 storageRepo.setReporter(reporter);
57 }
58
59 @Override
60 public void setProperties(Map<String, String> map) {
61 super.setProperties(map);
62
63 // Load essential properties
64 String localDirPath = map.get(PROP_LOCAL_DIR);
65 if (localDirPath == null)
66 throw new IllegalArgumentException(String.format("Attribute '%s' must be set on LocalOBR plugin.", PROP_LOCAL_DIR));
67 storageDir = new File(localDirPath);
68 if (!storageDir.isDirectory())
69 throw new IllegalArgumentException(String.format("Local path '%s' does not exist or is not a directory.", localDirPath));
70
71 // Configure the storage repository
72 Map<String, String> storageRepoConfig = new HashMap<String, String>(2);
73 storageRepoConfig.put(FileRepo.LOCATION, localDirPath);
74 storageRepoConfig.put(FileRepo.READONLY, map.get(PROP_READONLY));
75 storageRepo.setProperties(storageRepoConfig);
76
77 // Set the local index and cache directory locations
78 localIndex = new File(storageDir, REPOSITORY_FILE_NAME);
79 if (localIndex.exists() && !localIndex.isFile())
80 throw new IllegalArgumentException(String.format("Cannot build local repository index: '%s' already exists but is not a plain file.", localIndex.getAbsolutePath()));
81 cacheDir = new File(storageDir, ".obrcache");
82 if (cacheDir.exists() && !cacheDir.isDirectory())
83 throw new IllegalArgumentException(String.format("Cannot create repository cache: '%s' already exists but is not directory.", cacheDir.getAbsolutePath()));
84 }
85
86 @Override
87 protected void initialiseIndexes() throws Exception {
88 if (!localIndex.exists()) {
89 regenerateIndex();
90 }
91 try {
92 Collection<URL> remotes = super.getOBRIndexes();
93 indexUrls = new ArrayList<URL>(remotes.size() + 1);
94 indexUrls.add(localIndex.toURI().toURL());
95 indexUrls.addAll(remotes);
96 } catch (IOException e) {
97 throw new IllegalArgumentException("Error initialising local index URL", e);
98 }
99 }
100
101 private void regenerateIndex() throws Exception {
102 BundleIndexer indexer = registry.getPlugin(BundleIndexer.class);
103 if (indexer == null)
104 throw new IllegalStateException("Cannot index repository: no Bundle Indexer service or plugin found.");
105
106 Set<File> allFiles = new HashSet<File>();
107 gatherFiles(allFiles);
108
109 FileOutputStream out = null;
110 try {
111 out = new FileOutputStream(localIndex);
112 if (!allFiles.isEmpty()) {
113 Map<String, String> config = new HashMap<String, String>();
114 config.put(BundleIndexer.REPOSITORY_NAME, this.getName());
115 config.put(BundleIndexer.ROOT_URL, localIndex.toURI().toURL().toString());
116 indexer.index(allFiles, out, config);
117 } else {
118 ByteArrayInputStream emptyRepo = new ByteArrayInputStream("<?xml version='1.0' encoding='UTF-8'?>\n<repository lastmodified='0'/>".getBytes());
119 IO.copy(emptyRepo, out);
120 }
121 } finally {
122 out.close();
123 }
124 }
125
126 private void gatherFiles(Set<File> allFiles) throws Exception {
127 List<String> bsns = storageRepo.list(null);
128 if (bsns != null) for (String bsn : bsns) {
129 List<Version> versions = storageRepo.versions(bsn);
130 if (versions != null) for (Version version : versions) {
131 File file = storageRepo.get(bsn, version.toString(), Strategy.HIGHEST, null);
132 if (file != null)
133 allFiles.add(file);
134 }
135 }
136 }
137
138 @Override
139 public List<URL> getOBRIndexes() {
140 return indexUrls;
141 }
142
143 @Override
144 public boolean canWrite() {
145 return storageRepo.canWrite();
146 }
147
148 @Override
149 public synchronized File put(Jar jar) throws Exception {
150 File newFile = storageRepo.put(jar);
151
152 // Index the new file
153 BundleIndexer indexer = registry.getPlugin(BundleIndexer.class);
154 if (indexer == null)
155 throw new IllegalStateException("Cannot index repository: no Bundle Indexer service or plugin found.");
156 ByteArrayOutputStream newIndexBuffer = new ByteArrayOutputStream();
157 indexer.index(Collections.singleton(newFile), newIndexBuffer, null);
158
159 // Merge into main index
160 File tempIndex = File.createTempFile("repository", ".xml");
161 FileOutputStream tempIndexOutput = new FileOutputStream(tempIndex);
162 MergeContentFilter merger = new MergeContentFilter();
163 XMLReader reader = SAXUtil.buildPipeline(new StreamResult(tempIndexOutput), new UniqueResourceFilter(), merger);
164
165 try {
166 // Parse the newly generated index
167 reader.parse(new InputSource(new ByteArrayInputStream(newIndexBuffer.toByteArray())));
168
169 // Parse the existing index (which may be empty/missing)
170 try {
171 reader.parse(new InputSource(new FileInputStream(localIndex)));
172 } catch (Exception e) {
173 reporter.warning("Existing local index is invalid or missing, overwriting (%s).", localIndex.getAbsolutePath());
174 }
175
176 merger.closeRootAndDocument();
177 } finally {
178 tempIndexOutput.flush();
179 tempIndexOutput.close();
180 }
181 IO.copy(tempIndex, localIndex);
182
183 // Re-read the index
184 reset();
185 init();
186
187 return newFile;
188 }
189
190 public boolean refresh() {
191 reset();
192 return true;
193 }
194
195 public File getRoot() {
196 return storageDir;
197 }
198}