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;
040
041import java.io.File;
042import java.io.InputStream;
043import java.net.MalformedURLException;
044import java.net.URL;
045import java.util.Collection;
046import java.util.List;
047import java.util.Map;
048import java.util.SortedSet;
049
050import org.osgi.resource.Capability;
051import org.osgi.resource.Requirement;
052import org.osgi.service.repository.Repository;
053import org.osgi.util.promise.Promise;
054
055/**
056 * Provide a {@link NexusSearchOsgiRepository} as a bnd repository. The interesting
057 * work is done by the {@link NexusSearchOsgiRepository}.
058 */
059public class NexusSearchRepositoryProvider extends BaseRepository 
060        implements Repository, Plugin, RegistryPlugin, RepositoryPlugin, Refreshable {
061//      private final static Logger logger = LoggerFactory.getLogger(
062//                      NexusSearchRepositoryProvider.class);
063        private static final String MAVEN_REPO_LOCAL
064                = System.getProperty("maven.repo.local", "~/.m2/repository");
065
066        private boolean initialized = false;
067        private File localRepo;
068        private NexusSearchConfiguration configuration;
069        private String name = "OssSonatype";
070        private Registry registry = null;
071        private Reporter reporter;
072        private NexusSearchOsgiRepository osgiRepository = null;
073        private URL server = null;
074        private BridgeRepository bridge;
075        
076        @Override
077        public void setProperties(Map<String, String> properties) throws Exception {
078                configuration = Converter.cnv(NexusSearchConfiguration.class, properties);
079                name = configuration.name("OssSonatype");
080                server = new URL(configuration.server() != null 
081                                ? configuration.server() : "https://oss.sonatype.org");
082        }
083
084        @Override
085        public void setRegistry(Registry registry) {
086                this.registry = registry;
087        }
088
089        @Override
090        public void setReporter(Reporter reporter) {
091                this.reporter = reporter;
092        }
093
094        @Override
095        public String getName() {
096                return name;
097        }
098
099        @Override
100        public PutResult put(InputStream stream, PutOptions options) throws Exception {
101                throw new IllegalStateException("Read-only repository");
102        }
103
104        @Override
105        public boolean canWrite() {
106                return false;
107        }
108
109        /**
110         * Performs initialization. Initialization must be delayed because the
111         * precise sequence of injecting dependencies seems to be undefined.
112         * @throws MalformedURLException 
113         */
114        private synchronized void init() {
115                if (initialized) {
116                        return;
117                }
118                initialized = true;
119                String queryString = configuration.query();
120                Workspace workspace = registry.getPlugin(Workspace.class);
121                HttpClient client = registry.getPlugin(HttpClient.class);
122                File obrIndexFile = workspace.getFile(getLocation());
123                File mvnReposFile = workspace.getFile(
124                                "cnf/cache/nexus-search-" + name + "-repositories.xml");
125                localRepo = IO.getFile(configuration.local(MAVEN_REPO_LOCAL));
126                try {
127                        osgiRepository = new NexusSearchOsgiRepository(
128                                        name, server, localRepo, obrIndexFile, mvnReposFile, 
129                                        queryString, configuration.searchBreadth(3),
130                                        configuration.chunkSize(500), reporter, client);
131                        bridge = new BridgeRepository(osgiRepository);
132                } catch (Exception e) {
133                        throw new RuntimeException(e);
134                }
135        }
136        
137        @Override
138        public File getRoot() throws Exception {
139                return localRepo;
140        }
141
142        @Override
143        public boolean refresh() throws Exception {
144                init();
145                if (!osgiRepository.refresh()) {
146                        return false;
147                }
148                bridge = new BridgeRepository(osgiRepository);
149                for (RepositoryListenerPlugin listener : registry.getPlugins(RepositoryListenerPlugin.class)) {
150                        try {
151                                listener.repositoryRefreshed(this);
152                        } catch (Exception e) {
153                                reporter.exception(e, "Updating listener plugin %s", listener);
154                        }
155                }
156                return true;
157        }
158
159        @Override
160        public File get(String bsn, Version version, Map<String, String> properties, 
161                        DownloadListener... listeners) throws Exception {
162                init();
163                ResourceInfo resource = bridge.getInfo(bsn, version);
164                if (resource == null)
165                        return null;
166
167                String name = resource.getInfo().name();
168                Archive archive = Archive.valueOf(name);
169
170                Promise<File> p = osgiRepository.mavenRepository().get(archive);
171
172                if (listeners.length == 0)
173                        return p.getValue();
174
175                new DownloadListenerPromise(reporter, name + ": get " + bsn + ";" + version, p, listeners);
176                return osgiRepository.mavenRepository().toLocalFile(archive);
177        }
178
179        @Override
180        public List<String> list(String pattern) throws Exception {
181                init();
182                return bridge.list(pattern);
183        }
184
185        @Override
186        public SortedSet<Version> versions(String bsn) throws Exception {
187                init();
188                return bridge.versions(bsn);
189        }
190
191        @Override
192        public String getLocation() {
193                return "cnf/cache/nexus-search-" + name + ".xml";
194        }
195
196        @Override
197        public Map<Requirement, Collection<Capability>> findProviders(
198                        Collection<? extends Requirement> requirements) {
199                init();
200                return osgiRepository.findProviders(requirements);
201        }
202}