Changeset 1581 in josm for trunk


Ignore:
Timestamp:
2009-05-07T17:55:45+02:00 (16 years ago)
Author:
stoecker
Message:

fix #2429 - patch by Gubaer - fix upload cancel

Location:
trunk/src/org/openstreetmap/josm/io
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/io/OsmApi.java

    r1578 r1581  
    2323import java.util.Properties;
    2424import java.util.StringTokenizer;
     25import java.util.concurrent.FutureTask;
    2526
    2627import javax.xml.parsers.SAXParserFactory;
     
    3334import org.openstreetmap.josm.data.osm.Way;
    3435import org.openstreetmap.josm.data.osm.visitor.CreateOsmChangeVisitor;
     36import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    3537import org.xml.sax.Attributes;
    3638import org.xml.sax.InputSource;
     
    3941
    4042/**
    41  * Class that encapsulates the communications with the OSM API. 
    42  * 
    43  * All interaction with the server-side OSM API should go through this class. 
     43 * Class that encapsulates the communications with the OSM API.
     44 *
     45 * All interaction with the server-side OSM API should go through this class.
    4446 *
    4547 * It is conceivable to extract this into an interface later and create various
    4648 * classes implementing the interface, to be able to talk to various kinds of servers.
    47  * 
     49 *
    4850 */
    4951public class OsmApi extends OsmConnection {
     
    6365     */
    6466    private String minVersion = null;
    65    
     67
    6668    /**
    6769     * Maximum API version accepted by server, from capabilities response
    6870     */
    6971    private String maxVersion = null;
    70    
     72
    7173    /**
    7274     * Maximum downloadable area from server (degrees squared), from capabilities response
     
    7476     */
    7577    private String maxArea = null;
    76    
     78
    7779    /**
    7880     * true if successfully initialized
    7981     */
    8082    private boolean initialized = false;
    81    
     83
    8284    private StringWriter swriter = new StringWriter();
    8385    private OsmWriter osmWriter = new OsmWriter(new PrintWriter(swriter), true, null);
     
    108110        return "";
    109111    }
    110    
    111     /** 
     112
     113    /**
    112114     * Returns the OSM protocol version we use to talk to the server.
    113115     * @return protocol version, or null if not yet negotiated.
     
    116118        return version;
    117119    }
    118    
     120
    119121    /**
    120122     * Returns true if the negotiated version supports changesets.
     
    124126        return ((version != null) && (version.compareTo("0.6")>=0));
    125127    }
    126    
     128
    127129    /**
    128130     * Initializes this component by negotiating a protocol version with the server.
     
    141143            } else {
    142144                System.err.println(tr("This version of JOSM is incompatible with the configured server."));
    143                 System.err.println(tr("It supports protocol versions 0.5 and 0.6, while the server says it supports {0} to {1}.", 
     145                System.err.println(tr("It supports protocol versions 0.5 and 0.6, while the server says it supports {0} to {1}.",
    144146                    minVersion, maxVersion));
    145147                initialized = false;
    146148            }
    147             System.out.println(tr("Communications with {0} established using protocol version {1}", 
    148                 Main.pref.get("osm-server.url"), 
     149            System.out.println(tr("Communications with {0} established using protocol version {1}",
     150                Main.pref.get("osm-server.url"),
    149151                version));
    150152            osmWriter.setVersion(version);
     
    154156        }
    155157    }
    156    
     158
    157159    /**
    158160     * Makes an XML string from an OSM primitive. Uses the OsmWriter class.
     
    171173        return swriter.toString();
    172174    }
    173    
     175
    174176    /**
    175177     * Helper that makes an int from the first whitespace separated token in a string.
     
    183185            return Integer.parseInt(t.nextToken());
    184186        } catch (Exception x) {
    185             throw new OsmTransferException("Cannot read numeric value from response");
     187            throw new OsmTransferException(tr("Cannot read numeric value from response"));
    186188        }
    187189    }
     
    198200            return Long.parseLong(t.nextToken());
    199201        } catch (Exception x) {
    200             throw new OsmTransferException("Cannot read numeric value from response");
    201         }
    202     }   
    203    
     202            throw new OsmTransferException(tr("Cannot read numeric value from response"));
     203        }
     204    }
     205
    204206    /**
    205207     * Returns the base URL for API requests, including the negotiated version number.
     
    213215        }
    214216        rv.append("/");
    215         // this works around a ruby (or lighttpd) bug where two consecutive slashes in 
     217        // this works around a ruby (or lighttpd) bug where two consecutive slashes in
    216218        // an URL will cause a "404 not found" response.
    217219        int p; while ((p = rv.indexOf("//", 6)) > -1) { rv.delete(p, p + 1); }
     
    219221    }
    220222
    221     /** 
     223    /**
    222224     * Creates an OSM primitive on the server. The OsmPrimitive object passed in
    223225     * is modified by giving it the server-assigned id.
    224      * 
     226     *
    225227     * @param osm the primitive
    226228     * @throws OsmTransferException if something goes wrong
     
    230232        osm.version = 1;
    231233    }
    232    
     234
    233235    /**
    234236     * Modifies an OSM primitive on the server. For protocols greater than 0.5,
    235      * the OsmPrimitive object passed in is modified by giving it the server-assigned 
     237     * the OsmPrimitive object passed in is modified by giving it the server-assigned
    236238     * version.
    237      * 
     239     *
    238240     * @param osm the primitive
    239241     * @throws OsmTransferException if something goes wrong
     
    247249            osm.version = parseInt(sendRequest("PUT", which(osm)+"/" + osm.id, toXml(osm, true)));
    248250        }
    249     }   
    250    
     251    }
     252
    251253    /**
    252254     * Deletes an OSM primitive on the server.
     
    257259        // legacy mode does not require payload. normal mode (0.6 and up) requires payload for version matching.
    258260        sendRequest("DELETE", which(osm)+"/" + osm.id, version.equals("0.5") ? null : toXml(osm, false));
    259     }   
    260    
     261    }
     262
    261263    /**
    262264     * Creates a new changeset on the server to use for subsequent calls.
     
    276278    /**
    277279     * Closes a changeset on the server.
    278      * 
     280     *
    279281     * @throws OsmTransferException if something goes wrong.
    280282     */
     
    286288
    287289    /**
    288      * Uploads a list of changes in "diff" form the the server.
     290     * Uploads a list of changes in "diff" form to the server.
     291     *
    289292     * @param list the list of changed OSM Primitives
    290293     * @return list of processed primitives
    291      * @throws OsmTransferException if something is wrong.
    292      */
    293     public Collection<OsmPrimitive> uploadDiff(Collection<OsmPrimitive> list) throws OsmTransferException {
    294    
     294     * @throws OsmTransferException if something is wrong
     295     * @throws OsmTransferCancelledException  if the upload was cancelled by the user
     296     */
     297    public Collection<OsmPrimitive> uploadDiff(final Collection<OsmPrimitive> list) throws OsmTransferException {
     298
    295299        if (changeset == null) {
    296300            throw new OsmTransferException(tr("No changeset present for diff upload"));
    297301        }
    298        
    299         CreateOsmChangeVisitor duv = new CreateOsmChangeVisitor(changeset, this);
    300        
    301         ArrayList<OsmPrimitive> processed = new ArrayList<OsmPrimitive>();
    302    
    303         for (OsmPrimitive osm : list) {
    304             int progress = Main.pleaseWaitDlg.progress.getValue();
    305             Main.pleaseWaitDlg.currentAction.setText(tr("Preparing..."));
    306             if (cancel) throw new OsmTransferCancelledException();
    307             osm.visit(duv);
    308             Main.pleaseWaitDlg.progress.setValue(progress+1);
    309         }
    310    
    311         Main.pleaseWaitDlg.currentAction.setText(tr("Uploading..."));
    312         if (cancel) throw new OsmTransferCancelledException();
    313    
    314         String diff = duv.getDocument();
    315         String diffresult = sendRequest("POST", "changeset/" + changeset.id + "/upload", diff); 
     302
     303
     304        final ArrayList<OsmPrimitive> processed = new ArrayList<OsmPrimitive>();
     305
     306        // this is the asynchronous update task
     307        //
     308        class UploadDiffTask extends  PleaseWaitRunnable {
     309
     310            private boolean uploadCancelled = false;
     311            private boolean uploadFailed = false;
     312            private Throwable lastThrowable = null;
     313
     314            public UploadDiffTask(String title) {
     315                super(title,false /* don't ignore exceptions */);
     316            }
     317
     318            @Override protected void realRun() throws SAXException, IOException {
     319                CreateOsmChangeVisitor duv = new CreateOsmChangeVisitor(changeset, OsmApi.this);
     320
     321                for (OsmPrimitive osm : list) {
     322                    int progress = Main.pleaseWaitDlg.progress.getValue();
     323                    Main.pleaseWaitDlg.currentAction.setText(tr("Preparing..."));
     324                    osm.visit(duv);
     325                    Main.pleaseWaitDlg.progress.setValue(progress+1);
     326                }
     327
     328                Main.pleaseWaitDlg.currentAction.setText(tr("Uploading..."));
     329
     330                String diff = duv.getDocument();
     331                try {
     332                    String diffresult = sendRequest("POST", "changeset/" + changeset.id + "/upload", diff);
     333                    DiffResultReader.parseDiffResult(diffresult, list, processed, duv.getNewIdMap(), Main.pleaseWaitDlg);
     334                } catch (Exception sxe) {
     335                    if (isUploadCancelled()) {
     336                        // ignore exceptions thrown because the connection is aborted,
     337                        // i.e. IOExceptions or SocketExceptions
     338                        //
     339                        System.out.println("Ignoring exception caught because upload is cancelled. Exception is: " + sxe.toString());
     340                        return;
     341                    }
     342                    uploadFailed = true;
     343                    // remember last exception and don't throw it. If it was thrown again it would
     344                    // have to be encapsulated in a RuntimeException which would be nested in yet
     345                    // another RuntimeException by parent classes.
     346                    // Rather check isUploadFailed() and retrieve getLastThrowable() after the task
     347                    // is completed
     348                    //
     349                    lastThrowable = sxe;
     350                }
     351            }
     352
     353            @Override protected void finish() {
     354                // do nothing
     355            }
     356
     357            @Override protected void cancel() {
     358                activeConnection.disconnect();
     359                uploadCancelled = true;
     360            }
     361
     362            public boolean isUploadCancelled() {
     363                return uploadCancelled;
     364            }
     365
     366            public boolean isUploadFailed() {
     367                return uploadFailed;
     368            }
     369
     370            public Throwable getLastThrowable() {
     371                return lastThrowable;
     372            }
     373        }
     374
     375        UploadDiffTask uploadTask = new UploadDiffTask(tr("Uploading data"));
     376
     377        // run  data upload as asynchronous task
     378        //
    316379        try {
    317             DiffResultReader.parseDiffResult(diffresult, list, processed, duv.getNewIdMap(), Main.pleaseWaitDlg);
    318         } catch (Exception sxe) {
    319             throw new OsmTransferException(tr("Error processing changeset upload response"), sxe);
    320         }
     380            Void result = null;
     381            FutureTask<Void> task = new FutureTask<Void>(uploadTask, result);
     382            task.run();
     383            task.get(); // wait for the task to complete, no return value expected, though
     384        }  catch(Throwable e) {
     385            if (uploadTask.isUploadCancelled()) {
     386                throw new OsmTransferCancelledException();
     387            }
     388            throw new OsmTransferException(e);
     389        }
     390
     391        // handle failed upload
     392        //
     393        if (uploadTask.isUploadFailed()) {
     394            if (uploadTask.getLastThrowable() != null && uploadTask.getLastThrowable() instanceof OsmTransferException) {
     395                OsmTransferException e = (OsmTransferException)uploadTask.getLastThrowable();
     396                throw e;
     397            }
     398            // shouldn't happen, but just in case
     399            //
     400            throw new OsmTransferException(tr("Data upload failed for unknown reason"));
     401        }
     402
     403        // handle cancelled upload
     404        //
     405        if (uploadTask.isUploadCancelled()) {
     406            throw new OsmTransferCancelledException();
     407        }
     408
    321409        return processed;
    322410    }
     411
     412
    323413
    324414    private void sleepAndListen() throws OsmTransferCancelledException {
     
    339429     * This method will automatically re-try any requests that are answered with a 5xx
    340430     * error code, or that resulted in a timeout exception from the TCP layer.
    341      * 
     431     *
    342432     * @param requestMethod The http method used when talking with the server.
    343433     * @param urlSuffix The suffix to add at the server url, not including the version number,
    344434     *    but including any object ids (e.g. "/way/1234/history").
    345435     * @param requestBody the body of the HTTP request, if any.
    346      * 
     436     *
    347437     * @return the body of the HTTP response, if and only if the response code was "200 OK".
    348      * @exception OsmTransferException if the HTTP return code was not 200 (and retries have 
    349      *    been exhausted), or rewrapping a Java exception. 
     438     * @exception OsmTransferException if the HTTP return code was not 200 (and retries have
     439     *    been exhausted), or rewrapping a Java exception.
    350440     */
    351441    private String sendRequest(String requestMethod, String urlSuffix,
     
    353443
    354444        if (!initialized) throw new OsmTransferException(tr("Not initialized"));
    355        
     445
    356446        StringBuffer responseBody = new StringBuffer();
    357447        StringBuffer statusMessage = new StringBuffer();
    358448
    359         int retries = 5; // configurable? 
    360        
     449        int retries = 5; // configurable?
     450
    361451        while(true) { // the retry loop
    362452            try {
     
    386476                    out.close();
    387477                }
    388                
     478
    389479                activeConnection.connect();
    390480                System.out.println(activeConnection.getResponseMessage());
    391481                int retCode = activeConnection.getResponseCode();
    392                
     482
    393483                if (retCode >= 500) {
    394484                    if (retries-- > 0) {
     
    400490                // populate return fields.
    401491                responseBody.setLength(0);
    402                
     492
    403493                // If the API returned an error code like 403 forbidden, getInputStream
    404494                // will fail with an IOException.
     
    410500                }
    411501                BufferedReader in = new BufferedReader(new InputStreamReader(i));
    412                
     502
    413503                String s;
    414504                while((s = in.readLine()) != null) {
     
    430520                }
    431521                activeConnection.disconnect();
    432                
     522
    433523                if (retCode != 200) {
    434524                    throw new OsmTransferException(statusMessage.toString());
  • trunk/src/org/openstreetmap/josm/io/OsmServerWriter.java

    r1575 r1581  
    138138
    139139    private void dealWithTransferException (OsmTransferException e) {
     140        if (e instanceof OsmTransferCancelledException) {
     141            // ignore - don't bother the user with yet another message that he
     142            // has successfully cancelled the data upload
     143            //
     144            return;
     145        }
     146       
    140147        JOptionPane.showMessageDialog(Main.parent,
    141148            /* tr("Error during upload: ") + */ e.getMessage());
Note: See TracChangeset for help on using the changeset viewer.