package org.osgi.service.indexer.impl;
import static org.osgi.framework.FrameworkUtil.createFilter;
import java.io.File;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.URL;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.zip.Deflater;
import java.util.zip.GZIPOutputStream;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.indexer.Capability;
import org.osgi.service.indexer.Requirement;
import org.osgi.service.indexer.Resource;
import org.osgi.service.indexer.ResourceAnalyzer;
import org.osgi.service.indexer.ResourceIndexer;
import org.osgi.service.indexer.ResourceRecognizer;
import org.osgi.service.indexer.impl.types.TypedAttribute;
import org.osgi.service.indexer.impl.util.AddOnlyList;
import org.osgi.service.indexer.impl.util.Indent;
import org.osgi.service.indexer.impl.util.Pair;
import org.osgi.service.indexer.impl.util.Tag;
import org.osgi.service.log.LogService;
public class RepoIndex implements ResourceIndexer {
static final String REPOSITORY_INCREMENT_OVERRIDE = "-repository.increment.override";
private final BundleAnalyzer bundleAnalyzer;
private final OSGiFrameworkAnalyzer frameworkAnalyzer;
private final SCRAnalyzer scrAnalyzer;
private final BlueprintAnalyzer blueprintAnalyzer;
private final DefaultAnalyzer defaultAnalyzer;
private final LogService log;
private final List<ResourceRecognizer> recognizers = new LinkedList<ResourceRecognizer>();
private final List<Pair<ResourceAnalyzer, Filter>> analyzers = new LinkedList<Pair<ResourceAnalyzer,Filter>>();
public RepoIndex() {
this(new ConsoleLogSvc());
}
public RepoIndex(LogService log) {
this.log = log;
this.bundleAnalyzer = new BundleAnalyzer(log);
this.frameworkAnalyzer = new OSGiFrameworkAnalyzer(log);
this.scrAnalyzer = new SCRAnalyzer(log);
this.blueprintAnalyzer = new BlueprintAnalyzer(log);
this.defaultAnalyzer = new DefaultAnalyzer(log);
addRecognizer(new DefaultRecognizer(log));
addRecognizer(new MetaTypeRecognizer(log));
try {
Filter jarFilter = createFilter("(|(" + Resource.MIMETYPE + "=" + MimeType.Jar.toString() + ")(" + Resource.MIMETYPE + "=" + MimeType.Bundle.toString() + "))");
Filter fileFilter = createFilter("(!(|(" + Resource.MIMETYPE + "=" + MimeType.Jar.toString() + ")(" + Resource.MIMETYPE + "=" + MimeType.Bundle.toString() + ")))");
addAnalyzer(bundleAnalyzer, jarFilter);
addAnalyzer(frameworkAnalyzer, jarFilter);
addAnalyzer(scrAnalyzer, jarFilter);
addAnalyzer(blueprintAnalyzer, jarFilter);
addAnalyzer(defaultAnalyzer, fileFilter);
} catch (InvalidSyntaxException e) {
// Can't happen...?
throw new RuntimeException("Unexpected internal error compiling filter");
}
}
public final void addAnalyzer(ResourceAnalyzer analyzer, Filter filter) {
synchronized (analyzers) {
analyzers.add(Pair.create(analyzer, filter));
}
}
public final void removeAnalyzer(ResourceAnalyzer analyzer, Filter filter) {
synchronized (analyzers) {
analyzers.remove(Pair.create(analyzer, filter));
}
}
public final void addRecognizer(ResourceRecognizer recognizer) {
synchronized (recognizers) {
recognizers.add(recognizer);
}
}
public final void removeRecognizer(ResourceRecognizer recognizer) {
synchronized (recognizers) {
recognizers.remove(recognizer);
}
}
public void index(Set<File> files, OutputStream out, Map<String, String> config) throws Exception {
if (config == null)
config = new HashMap<String, String>(0);
Indent indent;
PrintWriter pw;
if (config.get(ResourceIndexer.PRETTY) != null) {
indent = Indent.PRETTY;
pw = new PrintWriter(out);
} else {
indent = Indent.NONE;
pw = new PrintWriter(new GZIPOutputStream(out, Deflater.BEST_COMPRESSION));
}
pw.print(Schema.XML_PROCESSING_INSTRUCTION);
Tag repoTag = new Tag(Schema.ELEM_REPOSITORY);
String repoName = config.get(REPOSITORY_NAME);
if (repoName == null)
repoName = REPOSITORYNAME_DEFAULT;
repoTag.addAttribute(Schema.ATTR_NAME, repoName);
String increment = config.get(REPOSITORY_INCREMENT_OVERRIDE);
if (increment == null)
increment = Long.toString(System.currentTimeMillis());
repoTag.addAttribute(Schema.ATTR_INCREMENT, increment);
repoTag.addAttribute(Schema.ATTR_XML_NAMESPACE, Schema.NAMESPACE);
repoTag.printOpen(indent, pw, false);
for (File file : files) {
Tag resourceTag = generateResource(file, config);
resourceTag.print(indent.next(), pw);
}
repoTag.printClose(indent, pw);
pw.flush(); pw.close();
}
public void indexFragment(Set<File> files, Writer out, Map<String, String> config) throws Exception {
PrintWriter pw;
if (out instanceof PrintWriter)
pw = (PrintWriter) out;
else
pw = new PrintWriter(out);
for (File file : files) {
Tag resourceTag = generateResource(file, config);
resourceTag.print(Indent.PRETTY, pw);
}
}
private Tag generateResource(File file, Map<String, String> config) throws Exception {
Resource resource = null;
synchronized (recognizers) {
for(ResourceRecognizer recognizer : recognizers){
resource = recognizer.recognizeResource(file, resource);
}
}
if(resource == null){
throw new IllegalStateException("Failed to recognize resource: " + file.getAbsolutePath());
}
List<Capability> caps = new AddOnlyList<Capability>(new LinkedList<Capability>());
List<Requirement> reqs = new AddOnlyList<Requirement>(new LinkedList<Requirement>());
// Read config settings and save in thread local state
if (config != null) {
URL rootURL;
String rootURLStr = config.get(ResourceIndexer.ROOT_URL);
if (rootURLStr != null) {
File rootDir = new File(rootURLStr);
if (rootDir.isDirectory())
rootURL = rootDir.toURI().toURL();
else
rootURL = new URL(rootURLStr);
}
else
rootURL = new File(System.getProperty("user.dir")).toURI().toURL();
String urlTemplate = config.get(ResourceIndexer.URL_TEMPLATE);
bundleAnalyzer.setStateLocal(new GeneratorState(rootURL, urlTemplate));
defaultAnalyzer.setStateLocal(new GeneratorState(rootURL, urlTemplate));
} else {
bundleAnalyzer.setStateLocal(null);
defaultAnalyzer.setStateLocal(null);
}
// Iterate over the analyzers
try {
synchronized (analyzers) {
for (Pair<ResourceAnalyzer, Filter> entry : analyzers) {
ResourceAnalyzer analyzer = entry.getFirst();
Filter filter = entry.getSecond();
if (filter == null || filter.match(resource.getProperties())) {
try {
analyzer.analyzeResource(resource, caps, reqs);
} catch (Exception e) {
log(LogService.LOG_ERROR, MessageFormat.format("Error calling analyzer \"{0}\" on resource {1}.", analyzer.getClass().getName(), resource.getLocation()), e);
}
}
}
}
} finally {
bundleAnalyzer.setStateLocal(null);
}
Tag resourceTag = new Tag(Schema.ELEM_RESOURCE);
for (Capability cap : caps) {
Tag capTag = new Tag(Schema.ELEM_CAPABILITY);
capTag.addAttribute(Schema.ATTR_NAMESPACE, cap.getNamespace());
appendAttributeAndDirectiveTags(capTag, cap.getAttributes(), cap.getDirectives());
resourceTag.addContent(capTag);
}
for (Requirement req : reqs) {
Tag reqTag = new Tag(Schema.ELEM_REQUIREMENT);
reqTag.addAttribute(Schema.ATTR_NAMESPACE, req.getNamespace());
appendAttributeAndDirectiveTags(reqTag, req.getAttributes(), req.getDirectives());
resourceTag.addContent(reqTag);
}
return resourceTag;
}
private void log(int level, String message, Throwable t) {
if (log != null)
log.log(level, message, t);
}
private static void appendAttributeAndDirectiveTags(Tag parentTag, Map<String, Object> attribs, Map<String, String> directives) {
for (Entry<String, Object> attribEntry : attribs.entrySet()) {
Tag attribTag = new Tag(Schema.ELEM_ATTRIBUTE);
attribTag.addAttribute(Schema.ATTR_NAME, attribEntry.getKey());
TypedAttribute typedAttrib = TypedAttribute.create(attribEntry.getKey(), attribEntry.getValue());
parentTag.addContent(typedAttrib.toXML());
}
for (Entry<String, String> directiveEntry : directives.entrySet()) {
Tag directiveTag = new Tag(Schema.ELEM_DIRECTIVE);
directiveTag.addAttribute(Schema.ATTR_NAME, directiveEntry.getKey());
directiveTag.addAttribute(Schema.ATTR_VALUE, directiveEntry.getValue());
parentTag.addContent(directiveTag);
}
}
}