package org.bndtools.core.templating.repobased; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.bndtools.templating.Template; import org.bndtools.templating.TemplateEngine; import org.bndtools.templating.TemplateLoader; import org.bndtools.utils.collections.CollectionUtils; import org.osgi.framework.Constants; import org.osgi.framework.namespace.IdentityNamespace; import org.osgi.resource.Capability; import org.osgi.resource.Namespace; import org.osgi.resource.Requirement; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; import org.osgi.service.component.annotations.ReferencePolicyOption; import org.osgi.service.repository.Repository; import org.osgi.util.function.Function; import org.osgi.util.promise.Deferred; import org.osgi.util.promise.Promise; import org.osgi.util.promise.Promises; import org.osgi.util.promise.Success; import aQute.bnd.build.Workspace; import aQute.bnd.deployer.repository.FixedIndexedRepo; import aQute.bnd.osgi.resource.CapReqBuilder; import aQute.bnd.osgi.resource.ResourceUtils; import aQute.bnd.osgi.resource.ResourceUtils.IdentityCapability; import aQute.service.reporter.Reporter; import bndtools.central.Central; import bndtools.preferences.BndPreferences; @Component(name = "org.bndtools.templating.repos", property = { "source=workspace", Constants.SERVICE_DESCRIPTION + "=Load templates from the Workspace and Repositories", Constants.SERVICE_RANKING + "=" + ReposTemplateLoader.RANKING }) public class ReposTemplateLoader implements TemplateLoader { static final int RANKING = Integer.MAX_VALUE; private static final String NS_TEMPLATE = "org.bndtools.template"; private final ConcurrentMap<String,TemplateEngine> engines = new ConcurrentHashMap<>(); // for testing Workspace workspace = null; private ExecutorService executor; @Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY) void setExecutorService(ExecutorService executor) { this.executor = executor; } @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC) void addTemplateEngine(TemplateEngine engine, Map<String,Object> svcProps) { String name = (String) svcProps.get("name"); engines.put(name, engine); } void removeTemplateEngine(@SuppressWarnings("unused") TemplateEngine engine, Map<String,Object> svcProps) { String name = (String) svcProps.get("name"); engines.remove(name); } @Activate void activate() { if (executor == null) executor = Executors.newCachedThreadPool(); } @Override public Promise<List<Template>> findTemplates(String templateType, final Reporter reporter) { String filterStr = String.format("(%s=%s)", NS_TEMPLATE, templateType); final Requirement requirement = new CapReqBuilder(NS_TEMPLATE).addDirective(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filterStr).buildSyntheticRequirement(); // Try to get the repositories and BundleLocator from the workspace List<Repository> workspaceRepos; BundleLocator tmpLocator; try { if (workspace == null) workspace = Central.getWorkspace(); workspaceRepos = workspace.getPlugins(Repository.class); tmpLocator = new RepoPluginsBundleLocator(workspace.getRepositories()); } catch (Exception e) { workspaceRepos = Collections.emptyList(); tmpLocator = new DirectDownloadBundleLocator(); } final BundleLocator locator = tmpLocator; // Setup the repos List<Repository> repos = new ArrayList<>(workspaceRepos.size() + 1); repos.addAll(workspaceRepos); addPreferenceConfiguredRepos(repos, reporter); // Generate a Promise<List<Template>> for each repository and add to an accumulator Promise<List<Template>> accumulator = Promises.resolved((List<Template>) new LinkedList<Template>()); for (final Repository repo : repos) { final Deferred<List<Template>> deferred = new Deferred<>(); final Promise<List<Template>> current = deferred.getPromise(); accumulator = accumulator.then(new Success<List<Template>,List<Template>>() { @Override public Promise<List<Template>> call(Promise<List<Template>> resolved) throws Exception { final List<Template> prefix = resolved.getValue(); return current.map(new Function<List<Template>,List<Template>>() { @Override public List<Template> apply(List<Template> t) { return CollectionUtils.append(prefix, t); } }); } }); executor.submit(new Runnable() { @Override public void run() { List<Template> templates = new LinkedList<>(); Map<Requirement,Collection<Capability>> providerMap = repo.findProviders(Collections.singleton(requirement)); if (providerMap != null) { Collection<Capability> candidates = providerMap.get(requirement); if (candidates != null) { for (Capability cap : candidates) { IdentityCapability idcap = ResourceUtils.getIdentityCapability(cap.getResource()); Object id = idcap.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE); Object ver = idcap.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE); try { String engineName = (String) cap.getAttributes().get("engine"); if (engineName == null) engineName = "stringtemplate"; TemplateEngine engine = engines.get(engineName); if (engine != null) templates.add(new CapabilityBasedTemplate(cap, locator, engine)); else reporter.error("Error loading template from resource '%s' version %s: no Template Engine available matching '%s'", id, ver, engineName); } catch (Exception e) { reporter.error("Error loading template from resource '%s' version %s: %s", id, ver, e.getMessage()); } } } } deferred.resolve(templates); } }); } return accumulator; } private static void addPreferenceConfiguredRepos(List<Repository> repos, Reporter reporter) { BndPreferences bndPrefs = null; try { bndPrefs = new BndPreferences(); } catch (Exception e) { // e.printStackTrace(); } if (bndPrefs != null && bndPrefs.getEnableTemplateRepo()) { List<String> repoUris = bndPrefs.getTemplateRepoUriList(); try { FixedIndexedRepo prefsRepo = loadRepo(repoUris); repos.add(prefsRepo); } catch (IOException | URISyntaxException ex) { reporter.exception(ex, "Error loading preference repository: %s", repoUris); } } } private static FixedIndexedRepo loadRepo(List<String> uris) throws IOException, URISyntaxException { FixedIndexedRepo repo = new FixedIndexedRepo(); StringBuilder sb = new StringBuilder(); for (Iterator<String> iter = uris.iterator(); iter.hasNext();) { sb.append(iter.next()); if (iter.hasNext()) sb.append(','); } repo.setLocations(sb.toString()); return repo; } }