View Javadoc

1   /*
2    * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.properties.resource.maven.repository;
17  
18  import java.io.File;
19  import java.net.MalformedURLException;
20  import java.net.URI;
21  import java.net.URL;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.apache.commons.lang.StringUtils;
26  import org.apache.maven.repository.internal.DefaultArtifactDescriptorReader;
27  import org.apache.maven.repository.internal.DefaultVersionRangeResolver;
28  import org.apache.maven.repository.internal.DefaultVersionResolver;
29  import org.apache.maven.repository.internal.MavenRepositorySystemSession;
30  import org.sonatype.aether.RepositorySystem;
31  import org.sonatype.aether.RepositorySystemSession;
32  import org.sonatype.aether.artifact.Artifact;
33  import org.sonatype.aether.collection.CollectRequest;
34  import org.sonatype.aether.connector.file.FileRepositoryConnectorFactory;
35  import org.sonatype.aether.connector.wagon.WagonProvider;
36  import org.sonatype.aether.connector.wagon.WagonRepositoryConnectorFactory;
37  import org.sonatype.aether.graph.Dependency;
38  import org.sonatype.aether.graph.DependencyFilter;
39  import org.sonatype.aether.graph.DependencyNode;
40  import org.sonatype.aether.impl.ArtifactDescriptorReader;
41  import org.sonatype.aether.impl.VersionRangeResolver;
42  import org.sonatype.aether.impl.VersionResolver;
43  import org.sonatype.aether.impl.internal.DefaultServiceLocator;
44  import org.sonatype.aether.repository.Authentication;
45  import org.sonatype.aether.repository.LocalRepository;
46  import org.sonatype.aether.repository.RemoteRepository;
47  import org.sonatype.aether.resolution.DependencyRequest;
48  import org.sonatype.aether.resolution.DependencyResolutionException;
49  import org.sonatype.aether.resolution.DependencyResult;
50  import org.sonatype.aether.spi.connector.RepositoryConnectorFactory;
51  import org.sonatype.aether.util.graph.PreorderNodeListGenerator;
52  
53  import de.smartics.properties.resource.domain.ArtifactId;
54  import de.smartics.properties.resource.domain.ArtifactRef;
55  import de.smartics.properties.resource.domain.ClassPathEnvironment;
56  
57  /**
58   * The repository to access artifacts to resolve for property descriptor
59   * information.
60   */
61  public final class MavenRepository
62  { // NOPMD
63    // ********************************* Fields *********************************
64  
65    // --- constants ------------------------------------------------------------
66  
67    // --- members --------------------------------------------------------------
68  
69    /**
70     * The remote artifact repository.
71     */
72    private final RemoteRepository remoteRepository;
73  
74    /**
75     * The local artifact repository, where the remote artifacts are stored.
76     */
77    private final LocalRepository localRepository;
78  
79    /**
80     * The internal repository system as the interface to the repository access
81     * implementation.
82     */
83    private final RepositorySystem repositorySystem;
84  
85    /**
86     * The list of dependency filters to apply to the dependency request.
87     */
88    private final List<DependencyFilter> dependencyFilters;
89  
90    /**
91     * The flag to control accessing the local repository only.
92     */
93    private final boolean offline;
94  
95    // ****************************** Initializer *******************************
96  
97    // ****************************** Constructors ******************************
98  
99    /**
100    * Convenience constructor without dependency filters and no authentication.
101    *
102    * @param remoteRepositoryUrl the URL to the remote repository of artifacts.
103    * @param localRepositoryPath the directory path to the local repository of
104    *          artifacts.
105    */
106   public MavenRepository(final String remoteRepositoryUrl,
107       final String localRepositoryPath)
108   {
109     this(remoteRepositoryUrl, null, localRepositoryPath,
110         new ArrayList<DependencyFilter>(), false);
111   }
112 
113   /**
114    * Convenience constructor without dependency filters.
115    *
116    * @param remoteRepositoryUrl the URL to the remote repository of artifacts.
117    * @param localRepositoryPath the directory path to the local repository of
118    *          artifacts.
119    */
120   public MavenRepository(final String remoteRepositoryUrl,
121       final Authentication auth, final String localRepositoryPath)
122   {
123     this(remoteRepositoryUrl, auth, localRepositoryPath,
124         new ArrayList<DependencyFilter>(), false);
125   }
126 
127   /**
128    * Default constructor.
129    *
130    * @param remoteRepositoryUrl the URL to the remote repository of artifacts.
131    * @param localRepositoryPath the directory path to the local repository of
132    *          artifacts.
133    * @param dependencyFilters the list of dependency filters to apply to the
134    *          dependency request.
135    */
136   public MavenRepository(final String remoteRepositoryUrl,
137       final Authentication auth, final String localRepositoryPath,
138       final List<DependencyFilter> dependencyFilters, final boolean offline)
139   {
140     this.remoteRepository = createRemoteRepository(remoteRepositoryUrl, auth);
141     this.localRepository = createLocalRepository(localRepositoryPath);
142     this.repositorySystem = createRepositorySystem();
143     this.dependencyFilters = new ArrayList<DependencyFilter>(dependencyFilters);
144     this.offline = offline;
145   }
146 
147   // ****************************** Inner Classes *****************************
148 
149   // ********************************* Methods ********************************
150 
151   // --- init -----------------------------------------------------------------
152 
153   private static RemoteRepository createRemoteRepository(
154       final String remoteRepositoryUrl, final Authentication auth)
155   {
156     final RemoteRepository repo =
157         new RemoteRepository("central", "default", remoteRepositoryUrl);
158     if (auth != null)
159     {
160       repo.setAuthentication(auth);
161     }
162     return repo;
163   }
164 
165   private static LocalRepository createLocalRepository(
166       final String localRepositoryPath)
167   {
168     return new LocalRepository(localRepositoryPath);
169   }
170 
171   private static RepositorySystem createRepositorySystem()
172   {
173     final DefaultServiceLocator locator = new DefaultServiceLocator();
174 
175     locator.addService(RepositoryConnectorFactory.class,
176         FileRepositoryConnectorFactory.class);
177     locator.addService(RepositoryConnectorFactory.class,
178         WagonRepositoryConnectorFactory.class);
179     locator.addService(VersionResolver.class, DefaultVersionResolver.class);
180     locator.addService(VersionRangeResolver.class,
181         DefaultVersionRangeResolver.class);
182     locator.addService(ArtifactDescriptorReader.class,
183         DefaultArtifactDescriptorReader.class);
184 
185     locator.setServices(WagonProvider.class, new RepositoryWagonProvider());
186 
187     return locator.getService(RepositorySystem.class);
188   }
189 
190   // --- get&set --------------------------------------------------------------
191 
192   // --- business -------------------------------------------------------------
193 
194   /**
195    * Creates a session to communicate with the repository.
196    *
197    * @return a new session instance.
198    */
199   public RepositorySystemSession createSession()
200   {
201     final MavenRepositorySystemSession session =
202         new MavenRepositorySystemSession();
203 
204     session.setLocalRepositoryManager(repositorySystem
205         .newLocalRepositoryManager(localRepository));
206     session.setOffline(offline);
207 
208     session.setRepositoryListener(new ResourceRepositoryLogListener());
209     // session.setTransferListener( new ConsoleTransferListener() );
210 
211     return session;
212   }
213 
214   /**
215    * Resolves the artifact so that it is locally accessible.
216    *
217    * @param session the session used for communication.
218    * @param artifact the artifact to resolve.
219    * @return the reference to the resolved artifact that is now stored locally
220    *         ready for access.
221    * @throws DependencyResolutionException if the dependency tree could not be
222    *           built or any dependency artifact could not be resolved.
223    */
224   public ClassPathEnvironment resolve(final RepositorySystemSession session,
225       final Artifact artifact) throws DependencyResolutionException
226   {
227     final Dependency dependency = createDependency(artifact);
228 
229     final DependencyRequest dependencyRequest = createRequest(dependency);
230 
231     try
232     {
233       final DependencyNode rootNode =
234           repositorySystem.resolveDependencies(session, dependencyRequest)
235               .getRoot();
236       final PreorderNodeListGenerator generator =
237           new PreorderNodeListGenerator();
238       rootNode.accept(generator);
239 
240       final ClassPathEnvironment resources = createResult(generator);
241       return resources;
242     }
243     catch (final NullPointerException e) // NOPMD aether problem
244     {
245       // Only occurs if a parent dependency of the resource cannot be resolved
246       // TODO We should log this information somehow...
247       throw new DependencyResolutionException(new DependencyResult(
248           dependencyRequest), e);
249     }
250   }
251 
252   /**
253    * Returns the URL to the remote repository.
254    *
255    * @return the URL to the remote repository.
256    */
257   public String getRemoteRepositoryUrl()
258   {
259     return remoteRepository.getUrl();
260   }
261 
262   private static Dependency createDependency(final Artifact artifact)
263   {
264     final Dependency dependency = new Dependency(artifact, "runtime");
265     return dependency;
266   }
267 
268   private DependencyRequest createRequest(final Dependency dependency)
269   {
270     final CollectRequest collectRequest = new CollectRequest();
271     collectRequest.setRoot(dependency);
272     collectRequest.addRepository(remoteRepository);
273 
274     final DependencyRequest dependencyRequest = new DependencyRequest();
275     dependencyRequest.setCollectRequest(collectRequest);
276     for (final DependencyFilter filter : dependencyFilters)
277     {
278       dependencyRequest.setFilter(filter);
279     }
280     return dependencyRequest;
281   }
282 
283   private static ClassPathEnvironment createResult(
284       final PreorderNodeListGenerator generator)
285   {
286     final ClassPathEnvironment env = new ClassPathEnvironment();
287     final List<DependencyNode> nodes = generator.getNodes();
288 
289     // TODO: Should we copy the artifacts to a local folder?
290     for (final DependencyNode node : nodes)
291     {
292       final ArtifactRef artifactRef = createArtifact(node);
293       env.addArchiveArtifactRef(artifactRef);
294     }
295 
296     // final PropertySetClassesLoader declarationLoader =
297     // new PropertySetClassesLoader();
298     // final Set<Class<?>> declarations =
299     // declarationLoader.getPropertySetTypes(urls);
300     //
301     // final PropertiesFilesLoader definitionLoader = new
302     // PropertiesFilesLoader();
303     // final Set<String> definitions =
304     // definitionLoader.getPropertiesFiles(urls);
305     //
306     // final PropertiesResources resources =
307     // new PropertiesResources(env, declarations, definitions);
308     // return resources;
309     return env;
310   }
311 
312   private static URL toUrl(final File file) throws IllegalStateException
313   {
314     final URI uri = file.toURI();
315     try
316     {
317       final URL url = uri.toURL();
318       return url;
319     }
320     catch (final MalformedURLException e)
321     {
322       throw new IllegalStateException("Cannot resolve URL: "
323                                       + uri.toASCIIString(), e); // FIXME
324     }
325   }
326 
327   private static ArtifactRef createArtifact(final DependencyNode node)
328   {
329     final Artifact artifact = node.getDependency().getArtifact();
330     final ArtifactId.Builder builder = new ArtifactId.Builder();
331     final String classifier = normalize(artifact);
332     builder.withGroupId(artifact.getGroupId())
333         .withName(artifact.getArtifactId()).withVersion(artifact.getVersion())
334         .withArchiveType(artifact.getExtension()).withClassifier(classifier);
335     final ArtifactId id = builder.build();
336     final File file = artifact.getFile();
337     final URL url = toUrl(file);
338     final ArtifactRef ref = new ArtifactRef(id, url);
339     return ref;
340   }
341 
342   private static String normalize(final Artifact artifact)
343   {
344     String classifier = artifact.getClassifier();
345     if (StringUtils.isBlank(classifier))
346     {
347       classifier = null;
348     }
349     return classifier;
350   }
351 
352   // --- object basics --------------------------------------------------------
353 
354 }