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 }