001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020 package org.apache.geronimo.mavenplugins.car;
021
022 import org.apache.geronimo.kernel.config.InvalidConfigException;
023 import org.apache.geronimo.kernel.config.NoSuchConfigException;
024 import org.apache.geronimo.kernel.config.ConfigurationData;
025 import org.apache.geronimo.kernel.repository.Artifact;
026 import org.apache.geronimo.kernel.repository.ArtifactManager;
027 import org.apache.geronimo.kernel.repository.ArtifactResolver;
028 import org.apache.geronimo.kernel.repository.DefaultArtifactManager;
029 import org.apache.geronimo.kernel.repository.Environment;
030 import org.apache.geronimo.kernel.repository.FileWriteMonitor;
031 import org.apache.geronimo.kernel.repository.Dependency;
032 import org.apache.geronimo.kernel.repository.WritableListableRepository;
033 import org.apache.geronimo.kernel.repository.Repository;
034 import org.apache.geronimo.system.repository.Maven2Repository;
035 import org.apache.geronimo.system.configuration.RepositoryConfigurationStore;
036 import org.apache.geronimo.system.resolver.ExplicitDefaultArtifactResolver;
037
038 import org.codehaus.mojo.pluginsupport.dependency.DependencyTree;
039
040 import org.apache.maven.artifact.repository.ArtifactRepository;
041
042 import java.io.File;
043 import java.io.FileInputStream;
044 import java.io.IOException;
045 import java.io.InputStream;
046 import java.io.BufferedInputStream;
047 import java.io.BufferedReader;
048 import java.io.FileReader;
049 import java.io.InputStreamReader;
050
051 import java.util.Iterator;
052 import java.util.HashSet;
053 import java.util.LinkedHashSet;
054 import java.util.Set;
055 import java.util.Collections;
056 import java.util.HashMap;
057 import java.util.zip.ZipEntry;
058 import java.util.jar.JarFile;
059
060 import org.codehaus.plexus.util.FileUtils;
061
062 /**
063 * Installs Geronimo module CAR files into a target repository to support assembly.
064 *
065 * @version $Rev: 524717 $ $Date: 2007-04-01 23:39:19 -0400 (Sun, 01 Apr 2007) $
066 * @goal install-modules
067 */
068 public class InstallModulesMojo
069 extends AbstractCarMojo
070 {
071 /**
072 * The location of the target repository.
073 *
074 * @parameter expression="${project.build.directory}/repository"
075 * @required
076 */
077 private File targetRepositoryDirectory = null;
078
079 /**
080 * Configuration to be installed specified as groupId/artifactId/version/type
081 * if none specified, plugin will install all dependencies of type "car"
082 *
083 * @parameter
084 * @optional
085 */
086 private String artifact = null;
087
088 /**
089 * Location of the source repository for the dependencies
090 *
091 * @parameter expression="${localRepository}"
092 * @required
093 */
094 private ArtifactRepository sourceRepository = null;
095
096 /**
097 * The location where the properties mapping will be generated.
098 *
099 * @parameter expression="${project.build.directory}/explicit-versions.properties"
100 * @required
101 */
102 private File explicitResolutionProperties = null;
103
104 /**
105 * The Geronimo repository artifact resolver.
106 * <p/>
107 * <p/>
108 * Using a custom name here to prevent problems that happen when Plexus
109 * injects the Maven resolver into the base-class.
110 * </p>
111 */
112 private ArtifactResolver geronimoArtifactResolver;
113
114 private WritableListableRepository targetRepo;
115
116 private RepositoryConfigurationStore targetStore;
117
118 private WritableListableRepository sourceRepo;
119
120 private RepositoryConfigurationStore sourceStore;
121
122 /**
123 * Set of artifacts which have already been installed, so we can skip any processing.
124 */
125 private Set installedArtifacts = new HashSet();
126
127 protected void doExecute() throws Exception {
128 DependencyTree dependencies = dependencyHelper.getDependencies(project);
129 generateExplicitVersionProperties(explicitResolutionProperties, dependencies);
130
131 //
132 // TODO: Check if we need to use the Maven2RepositoryAdapter here or not...
133 //
134
135 Maven2RepositoryAdapter.ArtifactLookup lookup = new ArtifactLookupImpl(new HashMap());
136 sourceRepo = new Maven2RepositoryAdapter(dependencies, lookup);
137 // sourceRepo = new Maven2RepositoryAdapter(new File(sourceRepository.getBasedir()));
138 sourceStore = new RepositoryConfigurationStore(sourceRepo);
139
140 FileUtils.forceMkdir(targetRepositoryDirectory);
141
142 targetRepo = new Maven2Repository(targetRepositoryDirectory);
143 targetStore = new RepositoryConfigurationStore(targetRepo);
144
145 ArtifactManager artifactManager = new DefaultArtifactManager();
146 geronimoArtifactResolver = new ExplicitDefaultArtifactResolver(
147 explicitResolutionProperties.getPath(),
148 artifactManager,
149 Collections.singleton(sourceRepo),
150 null);
151
152 if (artifact != null) {
153 install(Artifact.create(artifact));
154 } else {
155 Iterator iter = getDependencies().iterator();
156 while (iter.hasNext()) {
157
158 Artifact artifact = mavenToGeronimoArtifact((org.apache.maven.artifact.Artifact) iter.next());
159 if (isModuleArtifact(artifact)) {
160 install(artifact);
161 }
162 }
163 }
164 }
165
166 /**
167 * Retrieves all artifact dependencies.
168 *
169 * @return A HashSet of artifacts
170 */
171 protected Set getDependencies() {
172 Set dependenciesSet = new HashSet();
173
174 org.apache.maven.artifact.Artifact artifact = project.getArtifact();
175 if (artifact != null && artifact.getFile() != null) {
176 dependenciesSet.add(artifact);
177 }
178
179 Set projectArtifacts = project.getArtifacts();
180 if (projectArtifacts != null) {
181 dependenciesSet.addAll(projectArtifacts);
182 }
183
184 return dependenciesSet;
185 }
186
187 /**
188 * Install the given artifact into the target Geronimo repository.
189 *
190 * @param artifact The artifact to be installed; must not be null
191 * @throws Exception Failed to install artifact
192 */
193 private void install(final Artifact artifact) throws Exception {
194 assert artifact != null;
195
196 if (installedArtifacts.contains(artifact)) {
197 log.debug("Skipping artifact; already installed: " + artifact);
198 } else {
199 // The artifact must exist in the source repository
200 if (!sourceRepo.contains(artifact)) {
201 throw new Exception("Missing artifact in source repository: " + artifact);
202 }
203
204 if (isModuleArtifact(artifact)) {
205 installModule(artifact);
206 } else {
207 installDependency(artifact);
208 }
209 }
210 }
211
212 /**
213 * Install a Geornimo module artifact.
214 *
215 * @param artifact The Geronimo module artifact to be installed; must not be null, must be a module
216 * @throws Exception Failed to insall Geronimo module artifact
217 */
218 private void installModule(final Artifact artifact) throws Exception {
219 assert artifact != null;
220 assert isModuleArtifact(artifact);
221
222 boolean install = true;
223
224 // The source store must contain the module artifact
225 if (!sourceStore.containsConfiguration(artifact)) {
226 throw new Exception("Missing module artifact in source repository: " + artifact);
227 }
228
229 // If the target store already contains the module, check if we need to reinstall it
230 if (targetStore.containsConfiguration(artifact)) {
231 if (hasModuleChanged(artifact)) {
232 log.debug("Old module exists in target store; uninstalling: " + artifact);
233 targetStore.uninstall(artifact);
234 } else {
235 log.debug("Same module exists in target store; skipping: " + artifact);
236 install = false;
237 }
238 }
239
240 // Copy the configuration into the target configuration store
241 if (install) {
242 log.info("Installing module: " + artifact);
243
244 File file = sourceRepo.getLocation(artifact);
245 InputStream input = new BufferedInputStream(new FileInputStream(file));
246
247 try {
248 FileWriteMonitor monitor = new FileWriteMonitor() {
249 public void writeStarted(final String file, final int bytes) {
250 log.debug("Installing module: " + file + " (" + bytes + " bytes)");
251 }
252
253 public void writeProgress(int bytes) {
254 // empty
255 }
256
257 public void writeComplete(int bytes) {
258 // empty
259 }
260 };
261
262 targetStore.install(input, (int) file.length(), artifact, monitor);
263
264 installedArtifacts.add(artifact);
265 }
266 finally {
267 input.close();
268 }
269 }
270
271 // Install all dependencies of this module
272 installModuleDependencies(artifact);
273 }
274
275 /**
276 * Install all of the dependencies of the given Geronimo module artifact.
277 *
278 * @param artifact The Geronimo module artifact to be installed; must not be null, must be a module
279 * @throws Exception Failed to install Geronimo module dependencies
280 */
281 private void installModuleDependencies(final Artifact artifact) throws Exception {
282 assert artifact != null;
283 assert isModuleArtifact(artifact);
284
285 log.debug("Installing module dependencies for artifact: " + artifact);
286
287 try {
288 ConfigurationData config = targetStore.loadConfiguration(artifact);
289 Environment env = config.getEnvironment();
290 LinkedHashSet deps = new LinkedHashSet();
291
292 Iterator iter = env.getDependencies().iterator();
293 while (iter.hasNext()) {
294 Dependency dep = (Dependency) iter.next();
295 deps.add(dep.getArtifact());
296 }
297
298 installDependencies(deps);
299 }
300 catch (IOException e) {
301 throw new InvalidConfigException("Unable to load module: " + artifact, e);
302 }
303 catch (NoSuchConfigException e) {
304 throw new InvalidConfigException("Unable to load module: " + artifact, e);
305 }
306 }
307
308 /**
309 * Install a dependency artifact into the Geronimo repository.
310 *
311 * @param artifact The artifact to be installed; must not be null, or a module artifact
312 * @throws Exception Failed to install artifact dependencies
313 */
314 private void installDependency(final Artifact artifact) throws Exception {
315 assert artifact != null;
316 assert !isModuleArtifact(artifact);
317
318 boolean install = true;
319
320 // If the dep already exists, then check if we need to reinstall it
321 if (targetRepo.contains(artifact)) {
322 if (hasDependencyChanged(artifact)) {
323 File file = targetRepo.getLocation(artifact);
324 log.debug("Old dependency exists in target repo; deleting: " + file);
325 FileUtils.forceDelete(file);
326 } else {
327 log.debug("Same dependency exists in target repo; skipping: " + artifact);
328 install = false;
329 }
330 }
331
332 if (install) {
333 log.info("Installing dependency: " + artifact);
334
335 // Copy the artifact into the target repo
336 File file = sourceRepo.getLocation(artifact);
337 InputStream input = new BufferedInputStream(new FileInputStream(file));
338 try {
339 FileWriteMonitor monitor = new FileWriteMonitor() {
340 public void writeStarted(final String file, final int bytes) {
341 log.debug("Copying dependency: " + file + " (" + bytes + " bytes)");
342 }
343
344 public void writeProgress(int bytes) {
345 // empty
346 }
347
348 public void writeComplete(int bytes) {
349 // empty
350 }
351 };
352
353 targetRepo.copyToRepository(input, (int) file.length(), artifact, monitor);
354
355 installedArtifacts.add(artifact);
356 }
357 finally {
358 input.close();
359 }
360 }
361
362 // Install all dependencies of this artifact
363 installDependencies(sourceRepo.getDependencies(artifact));
364 }
365
366 /**
367 * Install a set of dependency artifacts into the Geronimo repository.
368 *
369 * @param dependencies The set of artifacts to be installed; must not be null.
370 * @throws Exception Failed to install artifacts
371 */
372 private void installDependencies(final Set/*<Artifact>*/ dependencies) throws Exception {
373 assert dependencies != null;
374
375 Set resolved = geronimoArtifactResolver.resolveInClassLoader(dependencies);
376 Iterator iter = resolved.iterator();
377
378 while (iter.hasNext()) {
379 Artifact a = (Artifact) iter.next();
380 install(a);
381 }
382 }
383
384 /**
385 * Check if a module has changed by comparing the checksum in the source and target repos.
386 *
387 * @param module The module to inspect; must not be null.
388 * @return Returns true if the module has changed
389 * @throws IOException Failed to load checksum
390 */
391 private boolean hasModuleChanged(final Artifact module) throws IOException {
392 assert module != null;
393
394 String sourceChecksum = loadChecksum(sourceRepo, module);
395 String targetChecksum = loadChecksum(targetRepo, module);
396
397 return !sourceChecksum.equals(targetChecksum);
398 }
399
400 /**
401 * Load the <tt>config.ser</tt> checksum for the given artifact.
402 *
403 * @param repo The repository to resolve the artifacts location; must not be null.
404 * @param artifact The artifact to retrieve a checksum for; must not be null.
405 * @return Thr artifacts checksum
406 * @throws IOException Failed to load checksums
407 */
408 private String loadChecksum(final Repository repo, final Artifact artifact) throws IOException {
409 assert repo != null;
410 assert artifact != null;
411
412 File file = repo.getLocation(artifact);
413 BufferedReader reader;
414
415 if (file.isDirectory()) {
416 File serFile = new File(file, "META-INF/config.ser.sha1");
417 reader = new BufferedReader(new FileReader(serFile));
418 } else {
419 JarFile jarFile = new JarFile(file);
420 ZipEntry entry = jarFile.getEntry("META-INF/config.ser.sha1");
421 reader = new BufferedReader(new InputStreamReader(jarFile.getInputStream(entry)));
422 }
423
424 String checksum = reader.readLine();
425 reader.close();
426
427 return checksum;
428 }
429
430 /**
431 * Check if a dependency has changed by checking the file size and last modified for source and target.
432 *
433 * @param artifact The artifact to check; must not be null
434 * @return True if the dependency has changed
435 */
436 private boolean hasDependencyChanged(final Artifact artifact) {
437 assert artifact != null;
438
439 File source = sourceRepo.getLocation(artifact);
440 File target = targetRepo.getLocation(artifact);
441
442 return (source.length() != target.length()) ||
443 (source.lastModified() > target.lastModified());
444 }
445 }