001/*
002 * Bnd Nexus Search Plugin
003 * Copyright (C) 2017  Michael N. Lipp
004 * 
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Affero General Public License as published by 
007 * the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 * 
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
013 * for more details.
014 * 
015 * You should have received a copy of the GNU Affero General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package de.mnl.osgi.bnd.repository.maven.provider;
020
021import aQute.bnd.build.Workspace;
022import aQute.bnd.http.HttpClient;
023import aQute.bnd.osgi.repository.BaseRepository;
024import aQute.bnd.osgi.repository.BridgeRepository;
025import aQute.bnd.osgi.repository.BridgeRepository.ResourceInfo;
026import aQute.bnd.service.Plugin;
027import aQute.bnd.service.Refreshable;
028import aQute.bnd.service.Registry;
029import aQute.bnd.service.RegistryPlugin;
030import aQute.bnd.service.RepositoryListenerPlugin;
031import aQute.bnd.service.RepositoryPlugin;
032import aQute.bnd.util.repository.DownloadListenerPromise;
033import aQute.bnd.version.Version;
034import aQute.lib.converter.Converter;
035import aQute.lib.io.IO;
036import aQute.maven.api.Archive;
037import aQute.service.reporter.Reporter;
038import de.mnl.osgi.bnd.repository.maven.nexussearch.NexusSearchConfiguration;
039import de.mnl.osgi.bnd.repository.maven.nexussearch.NexusSearchOsgiRepository;
040import java.io.File;
041import java.io.InputStream;
042import java.net.MalformedURLException;
043import java.net.URL;
044import java.util.Collection;
045import java.util.List;
046import java.util.Map;
047import java.util.SortedSet;
048import org.osgi.resource.Capability;
049import org.osgi.resource.Requirement;
050import org.osgi.service.repository.Repository;
051import org.osgi.util.promise.Promise;
052
053/**
054 * Provide a {@link NexusSearchOsgiRepository} as a bnd repository. The interesting
055 * work is done by the {@link NexusSearchOsgiRepository}.
056 */
057public class NexusSearchRepositoryProvider extends BaseRepository
058        implements Repository, Plugin, RegistryPlugin, RepositoryPlugin,
059        Refreshable {
060//      private final static Logger logger = LoggerFactory.getLogger(
061//                      NexusSearchRepositoryProvider.class);
062    private static final String MAVEN_REPO_LOCAL
063        = System.getProperty("maven.repo.local", "~/.m2/repository");
064
065    private boolean initialized = false;
066    private File localRepo;
067    private NexusSearchConfiguration configuration;
068    private String name = "OssSonatype";
069    private Registry registry = null;
070    private Reporter reporter;
071    private NexusSearchOsgiRepository osgiRepository = null;
072    private URL server = null;
073    private BridgeRepository bridge;
074
075    @Override
076    public void setProperties(Map<String, String> properties) throws Exception {
077        configuration
078            = Converter.cnv(NexusSearchConfiguration.class, properties);
079        name = configuration.name("OssSonatype");
080        server = new URL(configuration.server() != null
081            ? configuration.server()
082            : "https://oss.sonatype.org");
083    }
084
085    @Override
086    public void setRegistry(Registry registry) {
087        this.registry = registry;
088    }
089
090    @Override
091    public void setReporter(Reporter reporter) {
092        this.reporter = reporter;
093    }
094
095    @Override
096    public String getName() {
097        return name;
098    }
099
100    @Override
101    public PutResult put(InputStream stream, PutOptions options)
102            throws Exception {
103        throw new IllegalStateException("Read-only repository");
104    }
105
106    @Override
107    public boolean canWrite() {
108        return false;
109    }
110
111    /**
112     * Performs initialization. Initialization must be delayed because the
113     * precise sequence of injecting dependencies seems to be undefined.
114     * @throws MalformedURLException 
115     */
116    private synchronized void init() {
117        if (initialized) {
118            return;
119        }
120        initialized = true;
121        String queryString = configuration.query();
122        Workspace workspace = registry.getPlugin(Workspace.class);
123        HttpClient client = registry.getPlugin(HttpClient.class);
124        File obrIndexFile = workspace.getFile(getLocation());
125        File mvnReposFile = workspace.getFile(
126            "cnf/cache/nexus-search-" + name + "-repositories.xml");
127        localRepo = IO.getFile(configuration.local(MAVEN_REPO_LOCAL));
128        try {
129            osgiRepository = new NexusSearchOsgiRepository(
130                name, server, localRepo, obrIndexFile, mvnReposFile,
131                queryString, configuration.searchBreadth(3),
132                configuration.chunkSize(500), reporter, client);
133            bridge = new BridgeRepository(osgiRepository);
134        } catch (Exception e) {
135            throw new RuntimeException(e);
136        }
137    }
138
139    @Override
140    public File getRoot() throws Exception {
141        return localRepo;
142    }
143
144    @Override
145    public boolean refresh() throws Exception {
146        init();
147        if (!osgiRepository.refresh()) {
148            return false;
149        }
150        bridge = new BridgeRepository(osgiRepository);
151        for (RepositoryListenerPlugin listener : registry
152            .getPlugins(RepositoryListenerPlugin.class)) {
153            try {
154                listener.repositoryRefreshed(this);
155            } catch (Exception e) {
156                reporter.exception(e, "Updating listener plugin %s", listener);
157            }
158        }
159        return true;
160    }
161
162    @Override
163    public File get(String bsn, Version version, Map<String, String> properties,
164            DownloadListener... listeners) throws Exception {
165        init();
166        ResourceInfo resource = bridge.getInfo(bsn, version);
167        if (resource == null) {
168            return null;
169        }
170
171        String name = resource.getInfo().name();
172        Archive archive = Archive.valueOf(name);
173
174        Promise<File> p = osgiRepository.mavenRepository().get(archive);
175
176        if (listeners.length == 0) {
177            return p.getValue();
178        }
179
180        new DownloadListenerPromise(reporter,
181            name + ": get " + bsn + ";" + version, p, listeners);
182        return osgiRepository.mavenRepository().toLocalFile(archive);
183    }
184
185    @Override
186    public List<String> list(String pattern) throws Exception {
187        init();
188        return bridge.list(pattern);
189    }
190
191    @Override
192    public SortedSet<Version> versions(String bsn) throws Exception {
193        init();
194        return bridge.versions(bsn);
195    }
196
197    @Override
198    public String getLocation() {
199        return "cnf/cache/nexus-search-" + name + ".xml";
200    }
201
202    @Override
203    public Map<Requirement, Collection<Capability>> findProviders(
204            Collection<? extends Requirement> requirements) {
205        init();
206        return osgiRepository.findProviders(requirements);
207    }
208}