Ticket #7914: 7914.patch

File 7914.patch, 15.7 KB (added by Don-vip, 14 years ago)
  • core/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java

     
    1212import java.util.LinkedHashSet;
    1313import java.util.NoSuchElementException;
    1414import java.util.Set;
     15import java.util.concurrent.Callable;
     16import java.util.concurrent.CompletionService;
     17import java.util.concurrent.ExecutionException;
     18import java.util.concurrent.Executor;
     19import java.util.concurrent.ExecutorCompletionService;
     20import java.util.concurrent.Executors;
    1521
    1622import org.openstreetmap.josm.data.osm.DataSet;
    1723import org.openstreetmap.josm.data.osm.DataSetMerger;
     
    253259     * @param idPackage  the package of ids
    254260     * @return the request string
    255261     */
    256     protected String buildRequestString(OsmPrimitiveType type, Set<Long> idPackage) {
     262    protected static String buildRequestString(OsmPrimitiveType type, Set<Long> idPackage) {
    257263        StringBuilder sb = new StringBuilder();
    258264        sb.append(type.getAPIName()).append("s?")
    259265        .append(type.getAPIName()).append("s=");
     
    276282     * @param id the id
    277283     * @return the request string
    278284     */
    279     protected String buildRequestString(OsmPrimitiveType type, long id) {
     285    protected static String buildRequestString(OsmPrimitiveType type, long id) {
    280286        StringBuilder sb = new StringBuilder();
    281287        sb.append(type.getAPIName()).append("s?")
    282288        .append(type.getAPIName()).append("s=")
     
    284290        return sb.toString();
    285291    }
    286292
    287     /**
    288      * invokes a Multi Get for a set of ids and a given {@link OsmPrimitiveType}.
    289      * The retrieved primitives are merged to {@link #outputDataSet}.
    290      *
    291      * @param type the type
    292      * @param pkg the package of ids
    293      * @exception OsmTransferException thrown if an error occurs while communicating with the API server
    294      *
    295      */
    296     protected void multiGetIdPackage(OsmPrimitiveType type, Set<Long> pkg, ProgressMonitor progressMonitor) throws OsmTransferException {
    297         String request = buildRequestString(type, pkg);
    298         final InputStream in = getInputStream(request, NullProgressMonitor.INSTANCE);
    299         if (in == null) return;
    300         progressMonitor.subTask(tr("Downloading OSM data..."));
    301         try {
    302             DataSet loaded = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(pkg.size(), false));
    303             rememberNodesOfIncompleteWaysToLoad(loaded);
    304             merge(loaded);
    305         } catch(Exception e) {
    306             throw new OsmTransferException(e);
    307         }
    308     }
    309 
    310     /**
    311      * invokes a Multi Get for a single id and a given {@link OsmPrimitiveType}.
    312      * The retrieved primitive is merged to {@link #outputDataSet}.
    313      *
    314      * @param type the type
    315      * @param id the id
    316      * @exception OsmTransferException thrown if an error occurs while communicating with the API server
    317      *
    318      */
    319     protected void singleGetId(OsmPrimitiveType type, long id, ProgressMonitor progressMonitor) throws OsmTransferException {
    320         String request = buildRequestString(type, id);
    321         final InputStream in = getInputStream(request, NullProgressMonitor.INSTANCE);
    322         if (in == null)
    323             return;
    324         progressMonitor.subTask(tr("Downloading OSM data..."));
    325         try {
    326             DataSet loaded = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
    327             rememberNodesOfIncompleteWaysToLoad(loaded);
    328             merge(loaded);
    329         } catch(Exception e) {
    330             throw new OsmTransferException(e);
    331         }
    332     }
    333 
    334     /**
    335      * invokes a sequence of Multi Gets for individual ids in a set of ids and a given {@link OsmPrimitiveType}.
    336      * The retrieved primitives are merged to {@link #outputDataSet}.
    337      *
    338      * This method is used if one of the ids in pkg doesn't exist (the server replies with return code 404).
    339      * If the set is fetched with this method it is possible to find out which of the ids doesn't exist.
    340      * Unfortunatelly, the server does not provide an error header or an error body for a 404 reply.
    341      *
    342      * @param type the type
    343      * @param pkg the set of ids
    344      * @exception OsmTransferException thrown if an error occurs while communicating with the API server
    345      *
    346      */
    347     protected void singleGetIdPackage(OsmPrimitiveType type, Set<Long> pkg, ProgressMonitor progressMonitor) throws OsmTransferException {
    348         for (long id : pkg) {
    349             try {
    350                 String msg = "";
    351                 switch(type) {
    352                 case NODE: msg = tr("Fetching node with id {0} from ''{1}''", id, OsmApi.getOsmApi().getBaseUrl()); break;
    353                 case WAY: msg = tr("Fetching way with id {0} from ''{1}''", id, OsmApi.getOsmApi().getBaseUrl()); break;
    354                 case RELATION: msg = tr("Fetching relation with id {0} from ''{1}''", id, OsmApi.getOsmApi().getBaseUrl()); break;
    355                 }
    356                 progressMonitor.setCustomText(msg);
    357                 singleGetId(type, id, progressMonitor);
    358             } catch(OsmApiException e) {
    359                 if (e.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
    360                     System.out.println(tr("Server replied with response code 404 for id {0}. Skipping.", Long.toString(id)));
    361                     missingPrimitives.add(new SimplePrimitiveId(id, type));
    362                     continue;
    363                 }
    364                 throw e;
    365             }
    366         }
    367     }
    368 
    369293    protected void rememberNodesOfIncompleteWaysToLoad(DataSet from) {
    370294        for (Way w: from.getWays()) {
    371295            if (w.hasIncompleteNodes()) {
     
    396320     * @param type the  type
    397321     * @exception OsmTransferException thrown if an error occurs while communicating with the API server
    398322     */
    399     protected void fetchPrimitives(Set<Long> ids, OsmPrimitiveType type, ProgressMonitor progressMonitor) throws OsmTransferException{
     323    protected void fetchPrimitives(Set<Long> ids, OsmPrimitiveType type, ProgressMonitor progressMonitor) throws OsmTransferException {
    400324        String msg = "";
    401325        switch(type) {
    402326        case NODE: msg = tr("Fetching a package of nodes from ''{0}''", OsmApi.getOsmApi().getBaseUrl()); break;
     
    405329        }
    406330        progressMonitor.setTicksCount(ids.size());
    407331        progressMonitor.setTicks(0);
     332        // The complete set containg all primitives to fetch
    408333        Set<Long> toFetch = new HashSet<Long>(ids);
    409         while(! toFetch.isEmpty() && !isCanceled()) {
    410             Set<Long> pkg = extractIdPackage(toFetch);
     334        // Build a list of fetchers that will  download smaller sets containing only MAX_IDS_PER_REQUEST (200) primitives each
     335        // On a multicore architecture, we will run up to MAX_DOWNLOAD_THREADS concurrent fetchers.
     336        Executor exec = Executors.newFixedThreadPool(
     337                Math.min(OsmApi.MAX_DOWNLOAD_THREADS, Runtime.getRuntime().availableProcessors()));
     338        CompletionService<FetchResult> ecs = new ExecutorCompletionService<FetchResult>(exec);
     339        int n = 0;
     340        while (!toFetch.isEmpty()) {
     341            ecs.submit(new Fetcher(type, extractIdPackage(toFetch), progressMonitor));
     342            n++;
     343        }
     344        // Run the fetchers
     345        for (int i = 0; i < n && !isCanceled(); i++) {
    411346            progressMonitor.subTask(msg + "... " + progressMonitor.getTicks() + "/" + progressMonitor.getTicksCount());
    412347            try {
    413                 multiGetIdPackage(type, pkg, progressMonitor);
    414             } catch(OsmApiException e) {
    415                 if (e.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
    416                     System.out.println(tr("Server replied with response code 404, retrying with an individual request for each object."));
    417                     singleGetIdPackage(type, pkg, progressMonitor);
    418                 } else
    419                     throw e;
     348                FetchResult result = ecs.take().get();
     349                if (result.missingPrimitives != null) {
     350                    missingPrimitives.addAll(result.missingPrimitives);
     351                }
     352                if (result.dataSet != null) {
     353                    rememberNodesOfIncompleteWaysToLoad(result.dataSet);
     354                    merge(result.dataSet);
     355                }
     356            } catch (InterruptedException e) {
     357                e.printStackTrace();
     358            } catch (ExecutionException e) {
     359                e.printStackTrace();
    420360            }
    421361        }
    422362    }
     
    467407    public Set<PrimitiveId> getMissingPrimitives() {
    468408        return missingPrimitives;
    469409    }
     410   
     411    protected static class FetchResult {
     412        public final DataSet dataSet;
     413        public final Set<PrimitiveId> missingPrimitives;
     414        public FetchResult(DataSet dataSet, Set<PrimitiveId> missingPrimitives) {
     415            this.dataSet = dataSet;
     416            this.missingPrimitives = missingPrimitives;
     417        }
     418    }
     419   
     420    protected static class Fetcher extends OsmServerReader implements Callable<FetchResult> {
     421
     422        private final Set<Long> pkg;
     423        private final OsmPrimitiveType type;
     424        private final ProgressMonitor progressMonitor;
     425
     426        public Fetcher(OsmPrimitiveType type, Set<Long> extractIdPackage, ProgressMonitor progressMonitor) {
     427            this.pkg = extractIdPackage;
     428            this.type = type;
     429            this.progressMonitor = progressMonitor;
     430        }
     431
     432        @Override
     433        public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
     434            return fetch(progressMonitor).dataSet;
     435        }
     436       
     437        @Override
     438        public FetchResult call() throws Exception {
     439            return fetch(progressMonitor);
     440        }
     441       
     442        private FetchResult fetch(ProgressMonitor progressMonitor) throws OsmTransferException {
     443            try {
     444                return multiGetIdPackage(type, pkg, progressMonitor);
     445            } catch (OsmApiException e) {
     446                if (e.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
     447                    System.out.println(tr("Server replied with response code 404, retrying with an individual request for each object."));
     448                    return singleGetIdPackage(type, pkg, progressMonitor);
     449                } else {
     450                    throw e;
     451                }
     452            }
     453        }
     454       
     455        /**
     456         * invokes a Multi Get for a set of ids and a given {@link OsmPrimitiveType}.
     457         * The retrieved primitives are merged to {@link #outputDataSet}.
     458         *
     459         * @param type the type
     460         * @param pkg the package of ids
     461         * @exception OsmTransferException thrown if an error occurs while communicating with the API server
     462         *
     463         */
     464        protected FetchResult multiGetIdPackage(OsmPrimitiveType type, Set<Long> pkg, ProgressMonitor progressMonitor) throws OsmTransferException {
     465            String request = buildRequestString(type, pkg);
     466            final InputStream in = getInputStream(request, NullProgressMonitor.INSTANCE);
     467            if (in == null) return null;
     468            progressMonitor.subTask(tr("Downloading OSM data..."));
     469            try {
     470                return new FetchResult(OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(pkg.size(), false)), null);
     471            } catch (Exception e) {
     472                throw new OsmTransferException(e);
     473            }
     474        }
     475
     476        /**
     477         * invokes a Multi Get for a single id and a given {@link OsmPrimitiveType}.
     478         * The retrieved primitive is merged to {@link #outputDataSet}.
     479         *
     480         * @param type the type
     481         * @param id the id
     482         * @exception OsmTransferException thrown if an error occurs while communicating with the API server
     483         *
     484         */
     485        protected DataSet singleGetId(OsmPrimitiveType type, long id, ProgressMonitor progressMonitor) throws OsmTransferException {
     486            String request = buildRequestString(type, id);
     487            final InputStream in = getInputStream(request, NullProgressMonitor.INSTANCE);
     488            if (in == null) return null;
     489            progressMonitor.subTask(tr("Downloading OSM data..."));
     490            try {
     491                return OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
     492            } catch (Exception e) {
     493                throw new OsmTransferException(e);
     494            }
     495        }
     496
     497        /**
     498         * invokes a sequence of Multi Gets for individual ids in a set of ids and a given {@link OsmPrimitiveType}.
     499         * The retrieved primitives are merged to {@link #outputDataSet}.
     500         *
     501         * This method is used if one of the ids in pkg doesn't exist (the server replies with return code 404).
     502         * If the set is fetched with this method it is possible to find out which of the ids doesn't exist.
     503         * Unfortunatelly, the server does not provide an error header or an error body for a 404 reply.
     504         *
     505         * @param type the type
     506         * @param pkg the set of ids
     507         * @return
     508         * @exception OsmTransferException thrown if an error occurs while communicating with the API server
     509         *
     510         */
     511        protected FetchResult singleGetIdPackage(OsmPrimitiveType type, Set<Long> pkg, ProgressMonitor progressMonitor) throws OsmTransferException {
     512            FetchResult result = new FetchResult(new DataSet(), new HashSet<PrimitiveId>());
     513            String baseUrl = OsmApi.getOsmApi().getBaseUrl();
     514            for (long id : pkg) {
     515                try {
     516                    String msg = "";
     517                    switch (type) {
     518                        case NODE:     msg = tr("Fetching node with id {0} from ''{1}''",     id, baseUrl); break;
     519                        case WAY:      msg = tr("Fetching way with id {0} from ''{1}''",      id, baseUrl); break;
     520                        case RELATION: msg = tr("Fetching relation with id {0} from ''{1}''", id, baseUrl); break;
     521                    }
     522                    progressMonitor.setCustomText(msg);
     523                    result.dataSet.mergeFrom(singleGetId(type, id, progressMonitor));
     524                } catch (OsmApiException e) {
     525                    if (e.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
     526                        System.out.println(tr("Server replied with response code 404 for id {0}. Skipping.", Long.toString(id)));
     527                        result.missingPrimitives.add(new SimplePrimitiveId(id, type));
     528                    } else {
     529                        throw e;
     530                    }
     531                }
     532            }
     533            return result;
     534        }
     535    }
    470536}
  • core/src/org/openstreetmap/josm/io/OsmApi.java

     
    5454    /** max number of retries to send a request in case of HTTP 500 errors or timeouts */
    5555    static public final int DEFAULT_MAX_NUM_RETRIES = 5;
    5656
     57    /**
     58     * max number of concurrent download threads, imposed by
     59     * <a href="http://wiki.openstreetmap.org/wiki/API_usage_policy#Technical_Usage_Requirements">
     60     * OSM API usage policy.</a>
     61     */
     62    static public final int MAX_DOWNLOAD_THREADS = 2;
     63
    5764    /** the collection of instantiated OSM APIs */
    5865    private static HashMap<String, OsmApi> instances = new HashMap<String, OsmApi>();
    5966