001 /** 002 * Copyright 2008-2013 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.codehaus.mojo.wagon.shared; 017 018 /* 019 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license 020 * agreements. See the NOTICE file distributed with this work for additional information regarding 021 * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the 022 * "License"); you may not use this file except in compliance with the License. You may obtain a 023 * copy of the License at 024 * 025 * http://www.apache.org/licenses/LICENSE-2.0 026 * 027 * Unless required by applicable law or agreed to in writing, software distributed under the License 028 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 029 * or implied. See the License for the specific language governing permissions and limitations under 030 * the License. 031 */ 032 033 import static com.google.common.base.Preconditions.checkState; 034 import static com.google.common.collect.Lists.newArrayList; 035 import static com.google.common.collect.Maps.newTreeMap; 036 import static java.lang.String.format; 037 import static java.lang.System.currentTimeMillis; 038 import static java.util.Collections.shuffle; 039 import static org.apache.commons.io.FileUtils.touch; 040 import static org.codehaus.plexus.util.StringUtils.isBlank; 041 import static org.kuali.common.util.FormatUtils.getCount; 042 import static org.kuali.common.util.FormatUtils.getRate; 043 import static org.kuali.common.util.FormatUtils.getSize; 044 import static org.kuali.common.util.FormatUtils.getTime; 045 import static org.kuali.common.util.base.Exceptions.illegalState; 046 import static org.kuali.common.util.execute.impl.ConcurrentExecutables.executeConcurrently; 047 048 import java.io.File; 049 import java.io.IOException; 050 import java.util.List; 051 import java.util.SortedMap; 052 053 import org.apache.maven.plugin.logging.Log; 054 import org.apache.maven.wagon.Wagon; 055 import org.apache.maven.wagon.WagonException; 056 import org.kuali.common.util.Counter; 057 import org.kuali.common.util.LongCounter; 058 import org.kuali.common.util.execute.Executable; 059 import org.kuali.common.util.file.CanonicalFile; 060 import org.kuali.common.util.inform.PercentCompleteInformer; 061 062 import com.google.common.collect.Lists; 063 064 /** 065 * @plexus.component role="org.codehaus.mojo.wagon.shared.WagonDownload" role-hint="default" 066 */ 067 068 public class DefaultWagonDownload implements WagonDownload { 069 070 @Override 071 public List<String> getFileList(Wagon wagon, WagonFileSet fileSet, Log logger) throws WagonException { 072 logger.info("Scanning repository - " + wagon.getRepository().getUrl()); 073 074 PercentCompleteInformer informer = new PercentCompleteInformer(250); 075 WagonDirectoryScanner dirScan = new WagonDirectoryScanner(); 076 dirScan.setLogger(logger); 077 dirScan.setWagon(wagon); 078 dirScan.setExcludes(fileSet.getExcludes()); 079 dirScan.setIncludes(fileSet.getIncludes()); 080 dirScan.setCaseSensitive(fileSet.isCaseSensitive()); 081 dirScan.setDirectory(fileSet.getDirectory()); 082 dirScan.setInformer(informer); 083 if (fileSet.isUseDefaultExcludes()) { 084 dirScan.addDefaultExcludes(); 085 } 086 087 long start = currentTimeMillis(); 088 informer.start(); 089 dirScan.scan(); 090 long directoriesScanned = informer.getProgress(); 091 informer.stop(); 092 logger.info(format("dirs scanned -> %s [%s]", directoriesScanned, getTime(currentTimeMillis() - start))); 093 logger.info(format("files located -> %s", dirScan.getFilesIncluded().size())); 094 095 return dirScan.getFilesIncluded(); 096 } 097 098 @Override 099 public void download(Wagon wagon, WagonFileSet remoteFileSet, Log logger, boolean skipExisting) throws WagonException { 100 101 List<String> fileList = getFileList(wagon, remoteFileSet, logger); 102 103 String url = wagon.getRepository().getUrl(); 104 url = url.endsWith("/") ? url : url + "/"; 105 106 if (fileList.size() == 0) { 107 logger.info("Nothing to download."); 108 return; 109 } 110 111 SortedMap<String, CanonicalFile> skipped = newTreeMap(); 112 SortedMap<String, CanonicalFile> downloads = newTreeMap(); 113 for (String remoteFile : fileList) { 114 CanonicalFile destination = new CanonicalFile(remoteFileSet.getDownloadDirectory() + "/" + remoteFile); 115 if (!isBlank(remoteFileSet.getDirectory())) { 116 remoteFile = remoteFileSet.getDirectory() + "/" + remoteFile; 117 } 118 if (skipExisting && destination.exists()) { 119 skipped.put(remoteFile, destination); 120 } else { 121 downloads.put(remoteFile, destination); 122 } 123 } 124 125 if (skipped.size() > 0) { 126 logger.info(format("skipping %s files that already exist on the local file system", skipped.size())); 127 } 128 logger.info(format("downloading %s files ", downloads.size())); 129 List<Executable> executables = newArrayList(); 130 long total = Math.max(100, downloads.size() / 10); 131 PercentCompleteInformer informer = new PercentCompleteInformer(total); 132 Counter counter = new Counter(); 133 LongCounter bytesCounter = new LongCounter(); 134 long start = currentTimeMillis(); 135 for (String remoteFile : downloads.keySet()) { 136 CanonicalFile destination = downloads.get(remoteFile); 137 WagonDownloadExecutable executable = WagonDownloadExecutable.builder().withDestination(destination).withRemoteFile(remoteFile).withWagon(wagon).withCounter(counter) 138 .withTotal(downloads.size()).withStart(start).withBytesCounter(bytesCounter).withInformer(informer).build(); 139 executables.add(executable); 140 } 141 shuffle(executables); 142 informer.start(); 143 executeConcurrently(executables, 10); 144 informer.stop(); 145 long elapsed = currentTimeMillis() - start; 146 checkState(counter.getValue() == downloads.size(), "download counter is %s but should be %s", counter.getValue(), downloads.size()); 147 List<File> files = Lists.<File> newArrayList(downloads.values()); 148 long bytes = getBytes(files); 149 Object[] args = { getCount(files.size()), getSize(bytes), getRate(elapsed, bytes), getTime(elapsed) }; 150 logger.info(format("files: %s size: %s rate: %s elapsed: %s", args)); 151 } 152 153 protected long getBytes(List<File> files) { 154 long bytes = 0; 155 for (File file : files) { 156 bytes += file.length(); 157 } 158 return bytes; 159 } 160 161 protected void touchQuietly(File file) { 162 try { 163 touch(file); 164 } catch (IOException e) { 165 throw illegalState(e); 166 } 167 } 168 169 /** 170 * 171 * @param wagon 172 * - a Wagon instance 173 * @param resource 174 * - Remote resource to check 175 * @throws WagonException 176 */ 177 @Override 178 public boolean exists(Wagon wagon, String resource) throws WagonException { 179 return wagon.resourceExists(resource); 180 } 181 182 }