/* * Copyright 2015 the original author or authors. * * Licensed 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.gradle.internal.resource.transfer; import org.gradle.api.Nullable; import org.gradle.internal.resource.local.LocalResource; import org.gradle.internal.resource.metadata.ExternalResourceMetaData; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; public class DefaultExternalResourceConnector implements ExternalResourceConnector { private static final String SYSPROP_KEY = "gradle.externalresources.recordstats"; private final static ExternalResourceAccessStats.Mode STATS_MODE = ExternalResourceAccessStats.Mode.valueOf(System.getProperty(SYSPROP_KEY, "none")); private final static ExternalResourceAccessStats STATS = STATS_MODE.create(); private final ExternalResourceAccessor accessor; private final ExternalResourceLister lister; private final ExternalResourceUploader uploader; public DefaultExternalResourceConnector(ExternalResourceAccessor accessor, ExternalResourceLister lister, ExternalResourceUploader uploader) { this.accessor = accessor; this.lister = lister; this.uploader = uploader; } public static ExternalResourceAccessStats getStatistics() { return STATS; } @Nullable @Override public ExternalResourceReadResponse openResource(URI location, boolean revalidate) { STATS.resource(location); return accessor.openResource(location, revalidate); } @Nullable @Override public ExternalResourceMetaData getMetaData(URI location, boolean revalidate) { STATS.metadata(location); return accessor.getMetaData(location, revalidate); } @Nullable @Override public List<String> list(URI parent) { STATS.list(parent); return lister.list(parent); } @Override public void upload(LocalResource resource, URI destination) throws IOException { STATS.upload(destination); uploader.upload(resource, destination); } public interface ExternalResourceAccessStats { enum Mode { none, count, trace; public ExternalResourceAccessStats create() { switch (this) { case none: return NoOpStats.INSTANCE; case count: return new CountingStats(); case trace: return new MemoizingStats(); } throw new UnsupportedOperationException(); } } void resource(URI location); void metadata(URI location); void list(URI parent); void upload(URI destination); void reset(); } private static class NoOpStats implements ExternalResourceAccessStats { public static final NoOpStats INSTANCE = new NoOpStats(); @Override public void resource(URI location) { } @Override public void metadata(URI location) { } @Override public void list(URI parent) { } @Override public void upload(URI destination) { } @Override public void reset() { } @Override public String toString() { return "External resources access stats are not recorded. Run Gradle with -D" + SYSPROP_KEY + "=(count|trace) to record statistics"; } } private static class CountingStats implements ExternalResourceAccessStats { private final AtomicInteger resourceCount = new AtomicInteger(); private final AtomicInteger metadataCount = new AtomicInteger(); private final AtomicInteger listCount = new AtomicInteger(); private final AtomicInteger uploadCount = new AtomicInteger(); @Override public void resource(URI location) { resourceCount.incrementAndGet(); } @Override public void metadata(URI location) { metadataCount.incrementAndGet(); } @Override public void list(URI parent) { listCount.incrementAndGet(); } @Override public void upload(URI destination) { uploadCount.incrementAndGet(); } @Override public synchronized void reset() { resourceCount.set(0); metadataCount.set(0); listCount.set(0); uploadCount.set(0); } @Override public String toString() { final StringBuilder sb = new StringBuilder("External resources connector statistics: \n"); sb.append(" - Resources fetched : ").append(resourceCount.get()).append("\n"); sb.append(" - Metadata fetched : ").append(metadataCount.get()).append("\n"); sb.append(" - Lists : ").append(listCount.get()).append("\n"); sb.append(" - Uploads : ").append(uploadCount.get()).append("\n"); return sb.toString(); } } private static class MemoizingStats extends CountingStats { private final Map<URI, Integer> resources = new HashMap<URI, Integer>(); private final Map<URI, Integer> metadata = new HashMap<URI, Integer>(); private final Map<URI, Integer> lists = new HashMap<URI, Integer>(); private final Map<URI, Integer> uploads = new HashMap<URI, Integer>(); private synchronized void record(Map<URI, Integer> container, URI uri) { Integer count = container.get(uri); if (count == null) { container.put(uri, 1); } else { container.put(uri, count+1); } } @Override public void list(URI parent) { record(lists, parent); super.list(parent); } @Override public void metadata(URI location) { record(metadata, location); super.metadata(location); } @Override public void resource(URI location) { record(resources, location); super.resource(location); } @Override public void upload(URI destination) { record(uploads, destination); super.upload(destination); } @Override public synchronized void reset() { super.reset(); resources.clear(); metadata.clear(); lists.clear(); uploads.clear(); } @Override public String toString() { StringBuilder sb = new StringBuilder(super.toString()); statsFor("fetched resources", resources, sb, 10); statsFor("fetched metadata", metadata, sb, 10); statsFor("lists queries", lists, sb, 10); statsFor("uploaded URIs", uploads, sb, 10); return sb.toString(); } private void statsFor(String label, Map<URI, Integer> stats, StringBuilder sb, int max) { if (stats.isEmpty()) { return; } List<Map.Entry<URI, Integer>> entries = new ArrayList<Map.Entry<URI, Integer>>(stats.entrySet()); Collections.sort(entries, new Comparator<Map.Entry<URI, Integer>>() { @Override public int compare(Map.Entry<URI, Integer> o1, Map.Entry<URI, Integer> o2) { return o2.getValue() - o1.getValue(); } }); sb.append("Top ").append(max).append(" most ").append(label).append("\n"); int cpt = 0; for (Map.Entry<URI, Integer> entry : entries) { sb.append(" ").append(entry.getKey()).append(" (").append(entry.getValue()).append(" times)\n"); if (++cpt==max) { break; } } } } }