IMPROVEMENT: Ease performance auditing (IVY-655)

git-svn-id: https://svn.apache.org/repos/asf/ant/ivy/core/trunk@601077 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Xavier Hanin 2007-12-04 20:39:16 +00:00
parent 56e3bfe5bb
commit 06151bfd4b
22 changed files with 722 additions and 50 deletions

View File

@ -62,6 +62,7 @@ for detailed view of each issue, please consult http://issues.apache.org/jira/br
- NEW: Add a task/code to create M2 POM files from Ivy configurations (IVY-416)
- NEW: [Build] Publish the ivy sources (IVY-646) (thanks to Nicolas Lalevée)
- IMPROVEMENT: Ease performance auditing (IVY-655)
- IMPROVEMENT: Maven Dependency Management is not used to determine artifact version (IVY-616) (thanks to Jim Bonanno)
- IMPROVEMENT: split the cache into an downloaded artifacts cache and a metadata cache (IVY-628)
- IMPROVEMENT: add publish triggers to event system (IVY-650) (thanks to Jason Trump)

View File

@ -50,30 +50,33 @@ The event available in Ivy are the following ones:
<li>conf</li>comma separated list of configurations which will be resolved
</ul>
</td>
<td>Fired before a module dependencies will be resolved</td>
<td>Fired at the beginning of the resolve process, before a module dependencies and transitive dependencies are resolved.</td>
</tr>
<tr><td>pre-resolve-dependency</td>
<td>
<ul>
<li>organisation</li>the organisation of the dependency resolved
<li>module</li>the name of the dependency resolved
<li>revision</li>the revision asked for the dependency
<li>req-revision</li>the requested revision for the dependency <span class="since">since 2.0</span> (provided for consistency with post-resolve-dependency)
<li>revision</li>the requested revision for the dependency
<li>resolver</li>the name of the resolver used to resolve the dependency
</ul>
</td>
<td>Fired before each dependency is resolved in a single resolve call</td>
<td>Fired before each dependency is resolved. In this case resolved means resolving the actual revision if the requested revision is a version constraint and not a static version, and downloading all necessary metadata information.</td>
</tr>
<tr><td>post-resolve-dependency</td>
<td>
<ul>
<li>organisation</li>the organisation of the dependency resolved
<li>module</li>the name of the dependency resolved
<li>revision</li>the revision of the dependency resolved, or the revision asked if the resolution was not successful
<li>req-revision</li>the requested revision for the dependency <span class="since">since 2.0</span>
<li>revision</li>the revision of the dependency resolved, or the requested revision if the resolution was not successful
<li>resolved</li>true if the resolution was successful, false otherwise
<li>duration</li>the time elapsed to resolve the dependency (in ms) <span class="since">since 2.0</span>
<li>resolver</li>the name of the resolver used to resolve the dependency
</ul>
</td>
<td>Fired after each dependency resolved in a single resolve call</td>
<td>Fired after each dependency is resolved</td>
</tr>
<tr><td>post-resolve</td>
<td>
@ -82,9 +85,15 @@ The event available in Ivy are the following ones:
<li>module</li>the name of the module for which the dependencies have been resolved
<li>revision</li>the revision of the module for which the dependencies have been resolved
<li>conf</li>comma separated list of configurations resolved
<li>resolve-id</li>the identifier of the resolution process <span class="since">since 2.0</span>
<li>nb-dependencies</li>total number of dependencies, including transitive and evicted ones <span class="since">since 2.0</span>
<li>nb-artifacts</li>total number of artifacts resolved, excluding metadata artifacts <span class="since">since 2.0</span>
<li>resolve-duration</li>the time (in ms) elapsed to resolve dependencies, before dowloading artifacts <span class="since">since 2.0</span>
<li>download-duration</li>the time (in ms) elapsed to download all artifacts, excluding metadata artifacts downloaded during the first phase of the resolution process <span class="since">since 2.0</span>
<li>download-size</li>the total size (in bytes) of all downloaded artifacts, excluding metadata artifacts. Only artifacts actually downloaded (not in cache or used from their original location) are considered <span class="since">since 2.0</span>
</ul>
</td>
<td>Fired after a module dependencies has been resolved</td>
<td>Fired at the end of the resolve process, when all module dependencies have been resolved</td>
</tr>
<tr><td>pre-download-artifact</td>
<td>
@ -95,6 +104,7 @@ The event available in Ivy are the following ones:
<li>artifact</li>the name of the the artifact which is about to be downloaded
<li>type</li>the type of the the artifact which is about to be downloaded
<li>ext</li>the extension of the the artifact which is about to be downloaded
<li>metadata</li>true if the artifact to be downloaded is a metadata artifact, false for published artifacts <span class="since">since 2.0</span>
<li>resolver</li>the name of the resolver used to download the artifact
<li>origin</li>the origin location from which it will be downloaded
<li>local</li>true if it's a local artifact, false otherwise
@ -111,15 +121,47 @@ The event available in Ivy are the following ones:
<li>artifact</li>the name of the the artifact which was just downloaded
<li>type</li>the type of the the artifact which was just downloaded
<li>ext</li>the extension of the the artifact which was just downloaded
<li>metadata</li>true if the downloaded artifact is a metadata artifact, false for published artifacts <span class="since">since 2.0</span>
<li>resolver</li>the name of the resolver used to download the artifact
<li>origin</li>the origin location from which it was downloaded
<li>local</li>true if it's a local artifact, false otherwise
<li>size</li>the size in bytes of the downloaded artifact
<li>duration</li>the time elapsed to download the artifact (in ms) <span class="since">since 2.0</span>
<li>file</li>the file to which it has been downloaded
</ul>
</td>
<td>Fired after an artifact has been downloaded from a repository to the cache</td>
</tr>
<tr><td>pre-retrieve <br><span class="since">since 2.0</span></td>
<td>
<ul>
<li>organisation</li>the organisation of the module for which the dependencies will be retrieved
<li>module</li>the name of the module for which the dependencies will be retrieved
<li>revision</li>the revision of the module for which the dependencies will be retrieved
<li>conf</li>comma separated list of configurations which will be retrieved
<li>symlink</li>true if Ivy will use symbolic links instead of copies on supported platforms, false otherwise
<li>sync</li>true if the retrieve process will be performed in sync mode, false otherwise
</ul>
</td>
<td>Fired at the beginning of the retrieve process.</td>
</tr>
<tr><td>post-retrieve <br><span class="since">since 2.0</span></td>
<td>
<ul>
<li>organisation</li>the organisation of the module for which the dependencies have been retrieved
<li>module</li>the name of the module for which the dependencies will be retrieved
<li>revision</li>the revision of the module for which the dependencies have been retrieved
<li>conf</li>comma separated list of configurations which have been retrieved
<li>symlink</li>true if Ivy used symbolic links instead of copies on supported platforms, false otherwise
<li>sync</li>true if the retrieve process has been performed in sync mode, false otherwise
<li>duration</li>the time elapsed in ms during the retrieve process
<li>size</li>the total size of artifacts which have actually been copied (or symlinked)
<li>nbCopied</li>the number of artifacts copied or symlinked
<li>nbUptodate</li>the number of artifacts which were already present and up to date at the destination location
</ul>
</td>
<td>Fired at the end of the retrieve process.</td>
</tr>
<tr><td>pre-publish-artifact <br><span class="since">since 2.0</span></td>
<td>
<ul>
@ -182,6 +224,7 @@ Ivy comes with two built-in triggers:
<tbody>
<tr><td>ant-build</td><td>Triggers an ant build.</td></tr>
<tr><td>ant-call</td><td>Calls a target in the current ant build.</td></tr>
<tr><td>log</td><td>Logs a message on the console or in a log file.</td></tr>
</tbody>
</table>
@ -247,7 +290,17 @@ Here is how the target can look like:
<unzip src="${dep.file}" dest="${basedir}/out"/>
</target>
</code>
</textarea>
<hr/>
<code type="xml">
<triggers>
<log file="ivy.log"
message='downloaded "${origin}" to "${file}" (${duration}ms - ${size}B)'
event="post-download-artifact" filter="status=successful"/>
</triggers>
</code>
Logs any successful artifact download, with information on the source and destination, and details on download size and duration.
The file attribute is optional, the log trigger will output messages to console if it isn't provided.</textarea>
<script type="text/javascript">xooki.postProcess();</script>
</body>
</html>

View File

@ -280,7 +280,7 @@ public class Ivy {
resolveEngine = new ResolveEngine(settings, eventManager, sortEngine);
}
if (retrieveEngine == null) {
retrieveEngine = new RetrieveEngine(settings);
retrieveEngine = new RetrieveEngine(settings, eventManager);
}
if (deliverEngine == null) {
deliverEngine = new DeliverEngine(settings);

View File

@ -32,6 +32,7 @@ public abstract class DownloadEvent extends IvyEvent {
protected void addArtifactAttributes(Artifact artifact) {
addMridAttributes(artifact.getModuleRevisionId());
addAttributes(artifact.getAttributes());
addAttribute("metadata", String.valueOf(artifact.isMetadata()));
}
public Artifact getArtifact() {

View File

@ -38,12 +38,14 @@ public class EndArtifactDownloadEvent extends DownloadEvent {
this.report = report;
addAttribute("resolver", this.resolver.getName());
addAttribute("status", this.report.getDownloadStatus().toString());
addAttribute("details", this.report.getDownloadDetails());
addAttribute("size", String.valueOf(this.report.getSize()));
addAttribute("file", dest.getAbsolutePath());
addAttribute("duration", String.valueOf(this.report.getDownloadTimeMillis()));
ArtifactOrigin origin = report.getArtifactOrigin();
if (origin != null) {
addAttribute("origin", this.report.getArtifactOrigin().getLocation());
addAttribute("local", String.valueOf(this.report.getArtifactOrigin().isLocal()));
addAttribute("origin", origin.getLocation());
addAttribute("local", String.valueOf(origin.isLocal()));
} else {
addAttribute("origin", "");
addAttribute("local", "");

View File

@ -26,10 +26,14 @@ public class EndResolveDependencyEvent extends ResolveDependencyEvent {
private ResolvedModuleRevision module;
private long duration;
public EndResolveDependencyEvent(DependencyResolver resolver, DependencyDescriptor dd,
ResolvedModuleRevision module) {
ResolvedModuleRevision module, long duration) {
super(NAME, resolver, dd);
this.module = module;
this.duration = duration;
addAttribute("duration", String.valueOf(duration));
if (this.module != null) {
// override revision from the dependency descriptor
addAttribute("revision", this.module.getDescriptor().getResolvedModuleRevisionId()
@ -43,5 +47,21 @@ public class EndResolveDependencyEvent extends ResolveDependencyEvent {
public ResolvedModuleRevision getModule() {
return module;
}
/**
* Returns the time elapsed to resolve the dependency.
* <p>
* The time elapsed to resolve a dependency includes the time required to locate the the actual
* revision if the dependency descriptor use a version constraint, and to download the module
* metadata if necessary. It doesn't include any conflict management operations nor transitive
* dependency management. It's basically the time elapsed since the corresponding
* {@link StartResolveDependencyEvent}
* </p>
*
* @return the time elapsed to resolve the dependency.
*/
public long getDuration() {
return duration;
}
}

View File

@ -28,6 +28,12 @@ public class EndResolveEvent extends ResolveEvent {
public EndResolveEvent(ModuleDescriptor md, String[] confs, ResolveReport report) {
super(NAME, md, confs);
this.report = report;
addAttribute("resolve-id", String.valueOf(report.getResolveId()));
addAttribute("nb-dependencies", String.valueOf(report.getDependencies().size()));
addAttribute("nb-artifacts", String.valueOf(report.getArtifacts().size()));
addAttribute("resolve-duration", String.valueOf(report.getResolveTime()));
addAttribute("download-duration", String.valueOf(report.getDownloadTime()));
addAttribute("download-size", String.valueOf(report.getDownloadSize()));
}
public ResolveReport getReport() {

View File

@ -34,6 +34,7 @@ public class ResolveDependencyEvent extends IvyEvent {
addAttribute("resolver", this.resolver.getName());
addMridAttributes(this.dd.getDependencyRevisionId());
addAttributes(this.dd.getExtraAttributes());
addAttribute("req-revision", dd.getDependencyRevisionId().getRevision());
}
public DependencyDescriptor getDependencyDescriptor() {

View File

@ -0,0 +1,83 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.ivy.core.event.retrieve;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.retrieve.RetrieveOptions;
public class EndRetrieveEvent extends RetrieveEvent {
public static final String NAME = "post-retrieve";
private long duration;
private int nbCopied;
private int nbUpToDate;
private long totalCopiedSize;
public EndRetrieveEvent(ModuleRevisionId mrid, String[] confs, long elapsedTime,
int targetsCopied, int targetsUpToDate, long totalCopiedSize, RetrieveOptions options) {
super(NAME, mrid, confs, options);
this.duration = elapsedTime;
this.nbCopied = targetsCopied;
this.nbUpToDate = targetsUpToDate;
this.totalCopiedSize = totalCopiedSize;
addAttribute("duration", String.valueOf(elapsedTime));
addAttribute("size", String.valueOf(totalCopiedSize));
addAttribute("nbCopied", String.valueOf(targetsCopied));
addAttribute("nbUptodate", String.valueOf(targetsUpToDate));
}
/**
* Duration of the retrieve operation, in ms.
*
* @return Duration of the retrieve operation, in ms.
*/
public long getDuration() {
return duration;
}
/**
* Number of artifacts which were copied (or symlinked) during the retrieve
*
* @return Number of artifacts which were copied during the retrieve.
*/
public int getNbCopied() {
return nbCopied;
}
/**
* Number of artifacts which were not copied since they were already present and up to date.
*
* @return Number of artifacts which were not copied since they were already present and up to
* date.
*/
public int getNbUpToDate() {
return nbUpToDate;
}
/**
* Total size of all copied (or symlinked) artifacts, in bytes.
* @return Total size of all copied (or symlinked) artifacts, in bytes.
*/
public long getTotalCopiedSize() {
return totalCopiedSize;
}
}

View File

@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.ivy.core.event.retrieve;
import org.apache.ivy.core.event.IvyEvent;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.retrieve.RetrieveOptions;
public class RetrieveEvent extends IvyEvent {
private ModuleRevisionId mrid;
private RetrieveOptions options;
protected RetrieveEvent(String name, ModuleRevisionId mrid,
String[] confs, RetrieveOptions options) {
super(name);
this.mrid = mrid;
addMridAttributes(mrid);
addConfsAttribute(confs);
addAttribute("symlink", String.valueOf(options.isMakeSymlinks()));
addAttribute("sync", String.valueOf(options.isSync()));
this.options = options;
}
public ModuleRevisionId getModuleRevisionId() {
return mrid;
}
public RetrieveOptions getOptions() {
return options;
}
}

View File

@ -0,0 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.ivy.core.event.retrieve;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.retrieve.RetrieveOptions;
public class StartRetrieveEvent extends RetrieveEvent {
public static final String NAME = "pre-retrieve";
public StartRetrieveEvent(ModuleRevisionId mrid, String[] confs, RetrieveOptions options) {
super(NAME, mrid, confs, options);
}
}

View File

@ -119,8 +119,13 @@ public class ArtifactDownloadReport {
if (downloadStatus == DownloadStatus.SUCCESSFUL) {
return "[SUCCESSFUL ] " + artifact + " (" + downloadTimeMillis + "ms)";
} else if (downloadStatus == DownloadStatus.FAILED) {
return "[FAILED ] " + artifact + ": " + downloadDetails
if (downloadDetails == MISSING_ARTIFACT) {
return "[NOT FOUND ] " + artifact
+ " (" + downloadTimeMillis + "ms)";
} else {
return "[FAILED ] " + artifact + ": " + downloadDetails
+ " (" + downloadTimeMillis + "ms)";
}
} else if (downloadStatus == DownloadStatus.NO) {
return "[NOT REQUIRED] " + artifact;
} else {

View File

@ -43,13 +43,12 @@ public class ResolveReport {
private Map confReports = new LinkedHashMap();
private List problemMessages;
private List problemMessages = new ArrayList();
private List dependencies; // the list of all dependencies resolved, ordered from the more
private List dependencies = new ArrayList();
// the list of all dependencies resolved, ordered from the more dependent to the less dependent
// dependent to the less dependent
private List artifacts;
private List artifacts = new ArrayList();
private long resolveTime;
@ -57,6 +56,8 @@ public class ResolveReport {
private String resolveId;
private long downloadSize;
public ResolveReport(ModuleDescriptor md) {
this(md, ResolveOptions.getDefaultResolveId(md));
}
@ -239,6 +240,23 @@ public class ResolveReport {
return downloadTime;
}
public void setDownloadSize(long size) {
this.downloadSize = size;
}
/**
* The total size of downloaded artifacts, in bytes.
* <p>
* This only includes artifacts actually downloaded to cache (DownloadStatus.SUCCESSFUL), and
* not artifacts already in cache or used at their original location.
* </p>
*
* @return The total size of downloaded artifacts, in bytes.
*/
public long getDownloadSize() {
return downloadSize;
}
public String getResolveId() {
return resolveId;
}

View File

@ -217,11 +217,13 @@ public class IvyNode implements Comparable {
try {
Message.debug("\tusing " + resolver + " to resolve " + getId());
DependencyDescriptor dependencyDescriptor = getDependencyDescriptor(parent);
long start = System.currentTimeMillis();
data.getEventManager().fireIvyEvent(
new StartResolveDependencyEvent(resolver, dependencyDescriptor));
module = resolver.getDependency(dependencyDescriptor, data);
data.getEventManager().fireIvyEvent(
new EndResolveDependencyEvent(resolver, dependencyDescriptor, module));
new EndResolveDependencyEvent(resolver, dependencyDescriptor, module,
System.currentTimeMillis() - start));
if (module != null) {
data.getCacheManager().saveResolver(module.getDescriptor(),
module.getResolver().getName());

View File

@ -306,7 +306,8 @@ public class ResolveEngine {
public void outputReport(ResolveReport report, ResolutionCacheManager cacheMgr)
throws IOException {
Message.info(":: resolution report ::");
Message.info(":: resolution report :: resolve " + report.getResolveTime() + "ms"
+ " :: artifacts dl " + report.getDownloadTime() + "ms");
report.setProblemMessages(Message.getProblems());
// output report
report.output(settings.getReportOutputters(), cacheMgr);
@ -321,6 +322,7 @@ public class ResolveEngine {
eventManager.fireIvyEvent(new PrepareDownloadEvent((Artifact[]) report.getArtifacts()
.toArray(new Artifact[report.getArtifacts().size()])));
long totalSize = 0;
for (int i = 0; i < dependencies.length; i++) {
checkInterrupted();
// download artifacts required in all asked configurations
@ -333,8 +335,10 @@ public class ResolveEngine {
ArtifactDownloadReport[] adrs = dReport.getArtifactsReports();
for (int j = 0; j < adrs.length; j++) {
if (adrs[j].getDownloadStatus() == DownloadStatus.FAILED) {
Message.warn("\t[NOT FOUND ] " + adrs[j].getArtifact());
Message.warn("\t" + adrs[j]);
resolver.reportFailure(adrs[j].getArtifact());
} else if (adrs[j].getDownloadStatus() == DownloadStatus.SUCCESSFUL) {
totalSize += adrs[j].getSize();
}
}
// update concerned reports
@ -354,6 +358,7 @@ public class ResolveEngine {
}
}
report.setDownloadTime(System.currentTimeMillis() - start);
report.setDownloadSize(totalSize);
}
/**

View File

@ -38,6 +38,9 @@ import org.apache.ivy.core.IvyPatternHelper;
import org.apache.ivy.core.cache.CacheManager;
import org.apache.ivy.core.cache.RepositoryCacheManager;
import org.apache.ivy.core.cache.ResolutionCacheManager;
import org.apache.ivy.core.event.EventManager;
import org.apache.ivy.core.event.retrieve.EndRetrieveEvent;
import org.apache.ivy.core.event.retrieve.StartRetrieveEvent;
import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.core.module.descriptor.DefaultArtifact;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
@ -52,10 +55,15 @@ import org.apache.ivy.util.FileUtil;
import org.apache.ivy.util.Message;
public class RetrieveEngine {
private static final int KILO = 1024;
private RetrieveEngineSettings settings;
public RetrieveEngine(RetrieveEngineSettings settings) {
private EventManager eventManager;
public RetrieveEngine(RetrieveEngineSettings settings, EventManager eventManager) {
this.settings = settings;
this.eventManager = eventManager;
}
/**
@ -80,6 +88,9 @@ public class RetrieveEngine {
RepositoryCacheManager cacheManager = getCacheManager(options);
String[] confs = getConfs(mrid, options);
Message.info("\tconfs: " + Arrays.asList(confs));
if (this.eventManager != null) {
this.eventManager.fireIvyEvent(new StartRetrieveEvent(mrid, confs, options));
}
try {
Map artifactsToCopy = determineArtifactsToCopy(mrid, destFilePattern, options);
@ -95,6 +106,7 @@ public class RetrieveEngine {
// do retrieve
int targetsCopied = 0;
int targetsUpToDate = 0;
long totalCopiedSize = 0;
for (Iterator iter = artifactsToCopy.keySet().iterator(); iter.hasNext();) {
Artifact artifact = (Artifact) iter.next();
File archive;
@ -123,6 +135,7 @@ public class RetrieveEngine {
} else {
FileUtil.copy(archive, destFile, null, true);
}
totalCopiedSize += destFile.length();
targetsCopied++;
} else {
Message.verbose("\t\tto " + destFile + " [NOT REQUIRED]");
@ -157,12 +170,19 @@ public class RetrieveEngine {
}
}
}
long elapsedTime = System.currentTimeMillis() - start;
Message.info("\t"
+ targetsCopied
+ " artifacts copied"
+ (settings.isCheckUpToDate() ? (", " + targetsUpToDate + " already retrieved")
: ""));
Message.verbose("\tretrieve done (" + (System.currentTimeMillis() - start) + "ms)");
: "")
+ " (" + (totalCopiedSize / KILO) + "kB/" + elapsedTime + "ms)");
Message.verbose("\tretrieve done (" + (elapsedTime) + "ms)");
if (this.eventManager != null) {
this.eventManager.fireIvyEvent(new EndRetrieveEvent(
mrid, confs, elapsedTime, targetsCopied, targetsUpToDate, totalCopiedSize,
options));
}
return targetsCopied;
} catch (Exception ex) {

View File

@ -50,3 +50,4 @@ range-vm = org.apache.ivy.plugins.version.VersionRangeMatcher
ant-build = org.apache.ivy.ant.AntBuildTrigger
ant-call = org.apache.ivy.ant.AntCallTrigger
log = org.apache.ivy.plugins.trigger.LogTrigger

View File

@ -72,6 +72,11 @@ public class TransferEvent extends IvyEvent {
*/
public static final int TRANSFER_ERROR = 4;
/**
* Used to check event type validity: should always be 0 <= type <= LAST_EVENT_TYPE
*/
private static final int LAST_EVENT_TYPE = TRANSFER_ERROR;
/**
* Indicates GET transfer (from the repository)
*/
@ -109,20 +114,26 @@ public class TransferEvent extends IvyEvent {
private long totalLength;
private boolean isTotalLengthSet = false;
/**
* This attribute is used to store the time at which the event enters a type.
* <p>
* The array should better be seen as a Map from event type (int) to the time at which the event
* entered that type, 0 if it never entered this type.
* </p>
*/
private long[] timeTracking = new long[LAST_EVENT_TYPE + 1];
public TransferEvent(final Repository repository, final Resource resource, final int eventType,
final int requestType) {
super(getName(eventType));
this.repository = repository;
addAttribute("repository", this.repository.getName());
this.resource = resource;
addAttribute("resource", this.resource.getName());
setResource(resource);
setEventType(eventType);
setRequestType(requestType);
addAttribute("request-type", requestType == REQUEST_GET ? "get" : "put");
}
public TransferEvent(final Repository repository, final Resource resource,
@ -202,6 +213,7 @@ public class TransferEvent extends IvyEvent {
}
this.requestType = requestType;
addAttribute("request-type", requestType == REQUEST_GET ? "get" : "put");
}
/**
@ -216,23 +228,19 @@ public class TransferEvent extends IvyEvent {
* The eventType to set.
*/
protected void setEventType(final int eventType) {
switch (eventType) {
case TRANSFER_INITIATED:
break;
case TRANSFER_STARTED:
break;
case TRANSFER_COMPLETED:
break;
case TRANSFER_PROGRESS:
break;
case TRANSFER_ERROR:
break;
default:
throw new IllegalArgumentException("Illegal event type: " + eventType);
checkEventType(eventType);
if (this.eventType != eventType) {
this.eventType = eventType;
timeTracking[eventType] = System.currentTimeMillis();
if (eventType > TRANSFER_INITIATED) {
addAttribute("total-duration",
String.valueOf(getElapsedTime(TRANSFER_INITIATED, eventType)));
if (eventType > TRANSFER_STARTED) {
addAttribute("duration",
String.valueOf(getElapsedTime(TRANSFER_STARTED, eventType)));
}
}
}
this.eventType = eventType;
}
/**
@ -241,6 +249,7 @@ public class TransferEvent extends IvyEvent {
*/
protected void setResource(final Resource resource) {
this.resource = resource;
addAttribute("resource", this.resource.getName());
}
/**
@ -285,5 +294,61 @@ public class TransferEvent extends IvyEvent {
public void setTotalLengthSet(boolean isTotalLengthSet) {
this.isTotalLengthSet = isTotalLengthSet;
}
public Repository getRepository() {
return repository;
}
/**
* Returns the elapsed time (in ms) between when the event entered one type until it entered
* another event time.
* <p>
* This is especially useful to get the elapsed transfer time:
* <pre>
* getElapsedTime(TransferEvent.TRANSFER_STARTED, TransferEvent.TRANSFER_COMPLETED);
* </pre>
* </p>
* <p>
* Special cases:
* <ul>
* <li>returns -1 if the event never entered the fromEventType or the toEventType.</li>
* <li>returns 0 if the event entered toEventType before fromEventType</li>
* </ul>
* </p>
*
* @param fromEventType
* the event type constant from which time should be measured
* @param toEventType
* the event type constant to which time should be measured
* @return the elapsed time (in ms) between when the event entered fromEventType until it
* entered toEventType.
* @throws IllegalArgumentException
* if either type is not a known constant event type.
*/
public long getElapsedTime(int fromEventType, int toEventType) {
checkEventType(fromEventType);
checkEventType(toEventType);
long start = timeTracking[fromEventType];
long end = timeTracking[toEventType];
if (start == 0 || end == 0) {
return -1;
} else if (end < start) {
return 0;
} else {
return end - start;
}
}
/**
* Checks the given event type is a valid event type, throws an {@link IllegalArgumentException}
* if it isn't
*
* @param eventType
* the event type to check
*/
private void checkEventType(int eventType) {
if (eventType < 0 || eventType > LAST_EVENT_TYPE) {
throw new IllegalArgumentException("invalid event type " + eventType);
}
}
}

View File

@ -0,0 +1,127 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.ivy.plugins.trigger;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import org.apache.ivy.core.IvyPatternHelper;
import org.apache.ivy.core.event.IvyEvent;
import org.apache.ivy.core.resolve.ResolveProcessException;
import org.apache.ivy.util.Message;
/**
* A trigger performing logging.
* <p>
* The implementation is widely inspired by Ant Echo task.
* </p>
*/
public class LogTrigger extends AbstractTrigger {
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
private String message = "";
private File file = null;
private boolean append = true;
/** encoding; set to null or empty means 'default' */
private String encoding = "";
public void progress(IvyEvent event) {
log(IvyPatternHelper.substituteVariables(message, event.getAttributes()));
}
/**
* Logs the given message.
*
* @param message the message to log
*/
protected void log(String message) {
if (file == null) {
Message.info(message);
} else {
Writer out = null;
try {
// we add a line separator here for consistency with Message.info which creates a
// new line each time.
// we use the system dependent line separator to ease reading the log file
message += LINE_SEPARATOR;
String filename = file.getAbsolutePath();
if (encoding == null || encoding.length() == 0) {
out = new FileWriter(filename, append);
} else {
out = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(filename, append), encoding));
}
out.write(message, 0, message.length());
} catch (IOException e) {
throw new ResolveProcessException(e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
throw new ResolveProcessException(e);
}
}
}
}
}
/**
* Message to write.
*
* @param msg Sets the value for the message variable.
*/
public void setMessage(String msg) {
this.message = msg;
}
/**
* File to write to.
* @param file the file to write to, if not set, echo to
* standard Ivy logging
*/
public void setFile(String file) {
this.file = new File(file);
}
/**
* If true, append to existing file.
* @param append if true, append to existing file, default
* is false.
*/
public void setAppend(boolean append) {
this.append = append;
}
/**
* Declare the encoding to use when outputting to a file;
* Use "" for the platform's default encoding.
* @param encoding the character encoding to use.
*/
public void setEncoding(String encoding) {
this.encoding = encoding;
}
}

View File

@ -193,16 +193,75 @@ public final class FileUtil {
}
}
/**
* Reads the whole BufferedReader line by line, using \n as line separator for each line.
* <p>
* Note that this method will add a final \n to the last line even though there is no new line
* character at the end of last line in the original reader.
* </p>
* <p>
* The BufferedReader is closed when this method returns.
* </p>
*
* @param in
* the {@link BufferedReader} to read from
* @return a String with the whole content read from the {@link BufferedReader}
* @throws IOException
* if an IO problems occur during reading
*/
public static String readEntirely(BufferedReader in) throws IOException {
StringBuffer buf = new StringBuffer();
try {
StringBuffer buf = new StringBuffer();
String line = in.readLine();
while (line != null) {
buf.append(line + "\n");
line = in.readLine();
String line = in.readLine();
while (line != null) {
buf.append(line + "\n");
line = in.readLine();
}
return buf.toString();
} finally {
in.close();
}
}
/**
* Reads the entire content of the file and returns it as a String.
*
* @param f
* the file to read from
* @return a String with the file content
* @throws IOException
* if an IO problems occurs during reading
*/
public static String readEntirely(File f) throws IOException {
return readEntirely(new FileInputStream(f));
}
/**
* Reads the entire content of the {@link InputStream} and returns it as a String.
* <p>
* The input stream is closed when this method returns.
* </p>
*
* @param is
* the {@link InputStream} to read from
* @return a String with the input stream content
* @throws IOException
* if an IO problems occurs during reading
*/
public static String readEntirely(InputStream is) throws IOException {
try {
StringBuffer sb = new StringBuffer();
byte[] buffer = new byte[BUFFER_SIZE];
int c;
while ((c = is.read(buffer)) != -1) {
sb.append(new String(buffer, 0, c));
}
return sb.toString();
} finally {
is.close();
}
in.close();
return buf.toString();
}
public static String concat(String dir, String file) {

View File

@ -19,12 +19,18 @@ package org.apache.ivy.core.retrieve;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
import org.apache.ivy.Ivy;
import org.apache.ivy.core.IvyPatternHelper;
import org.apache.ivy.core.cache.CacheManager;
import org.apache.ivy.core.event.IvyEvent;
import org.apache.ivy.core.event.IvyListener;
import org.apache.ivy.core.event.retrieve.EndRetrieveEvent;
import org.apache.ivy.core.event.retrieve.StartRetrieveEvent;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.report.ResolveReport;
import org.apache.ivy.core.resolve.ResolveOptions;
@ -82,6 +88,37 @@ public class RetrieveTest extends TestCase {
"jar", "jar", "default")).exists());
}
public void testEvent() throws Exception {
ResolveReport report = ivy.resolve(new File(
"test/repositories/1/org1/mod1.1/ivys/ivy-1.0.xml").toURL(),
getResolveOptions(new String[] {"*"}));
final List events = new ArrayList();
ivy.getEventManager().addIvyListener(new IvyListener() {
public void progress(IvyEvent event) {
events.add(event);
}
});
ModuleDescriptor md = report.getModuleDescriptor();
String pattern = "build/test/retrieve/[module]/[conf]/[artifact]-[revision].[ext]";
ivy.retrieve(md.getModuleRevisionId(), pattern, getRetrieveOptions());
assertEquals(2, events.size());
assertTrue(events.get(0) instanceof StartRetrieveEvent);
assertTrue(events.get(1) instanceof EndRetrieveEvent);
EndRetrieveEvent ev = (EndRetrieveEvent) events.get(1);
assertEquals(1, ev.getNbCopied());
assertEquals(0, ev.getNbUpToDate());
events.clear();
ivy.retrieve(md.getModuleRevisionId(), pattern, getRetrieveOptions());
assertEquals(2, events.size());
assertTrue(events.get(0) instanceof StartRetrieveEvent);
assertTrue(events.get(1) instanceof EndRetrieveEvent);
ev = (EndRetrieveEvent) events.get(1);
assertEquals(0, ev.getNbCopied());
assertEquals(1, ev.getNbUpToDate());
}
public void testRetrieveOverwrite() throws Exception {
// mod1.1 depends on mod1.2
ResolveReport report = ivy.resolve(new File(

View File

@ -0,0 +1,91 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.ivy.plugins.trigger;
import java.io.File;
import java.util.Date;
import junit.framework.TestCase;
import org.apache.ivy.core.event.resolve.StartResolveEvent;
import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.util.FileUtil;
import org.apache.ivy.util.Message;
import org.apache.ivy.util.MockMessageLogger;
public class LogTriggerTest extends TestCase {
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
private StartResolveEvent ev;
private LogTrigger trigger;
private File testDir;
protected void setUp() {
ev = new StartResolveEvent(
DefaultModuleDescriptor.newBasicInstance(ModuleRevisionId.parse("o#A;1"), new Date()),
new String[] {"c"});
trigger = new LogTrigger();
trigger.setEvent(ev.getName());
testDir = new File("build/test/trigger");
testDir.mkdirs();
}
protected void tearDown() throws Exception {
FileUtil.forceDelete(testDir);
}
public void testMessage() throws Exception {
trigger.setMessage("msg: ${organisation} ${module} ${revision}");
MockMessageLogger mockLogger = new MockMessageLogger();
Message.setDefaultLogger(mockLogger);
trigger.progress(ev);
mockLogger.assertLogInfoContains("msg: o A 1");
}
public void testFile() throws Exception {
trigger.setMessage("msg: ${organisation} ${module} ${revision}");
File f = new File(testDir, "test.log");
trigger.setFile(f.getPath());
trigger.progress(ev);
assertTrue(f.exists());
assertEquals("msg: o A 1" + LINE_SEPARATOR, FileUtil.readEntirely(f));
trigger.progress(ev);
assertEquals("msg: o A 1" + LINE_SEPARATOR + "msg: o A 1" + LINE_SEPARATOR,
FileUtil.readEntirely(f));
}
public void testFileNoAppend() throws Exception {
trigger.setMessage("msg: ${organisation} ${module} ${revision}");
File f = new File(testDir, "test.log");
trigger.setFile(f.getPath());
trigger.setAppend(false);
trigger.progress(ev);
trigger.progress(ev);
assertTrue(f.exists());
assertEquals("msg: o A 1" + LINE_SEPARATOR, FileUtil.readEntirely(f));
}
}