Ignore:
Timestamp:
2014-08-20T03:07:15+02:00 (6 years ago)
Author:
Don-vip
Message:

fix #8885 (see #4614) - add offline mode with new command line argument --offline which can take one of several of these values (comma separated):

  • josm_website: to disable all accesses to JOSM website (when not cached, disables Getting Started page, help, plugin list, styles, imagery, presets, rules)
  • osm_api: to disable all accesses to OSM API (disables download, upload, changeset queries, history, user message notification)
  • all: alias to disable all values. Currently equivalent to "josm_website,osm_api"

Plus improved javadoc, fixed EDT violations, and fixed a bug with HTTP redirection sent without "Location" header

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

Legend:

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

    r7082 r7434  
    8080    }
    8181
     82    private boolean needsUpdate() {
     83        if (isOffline()) {
     84            return false;
     85        }
     86        return Main.pref.getInteger("cache." + ident, 0) + updateInterval < System.currentTimeMillis()/1000
     87                || !isCacheValid();
     88    }
     89
     90    private boolean isOffline() {
     91        try {
     92            checkOfflineAccess();
     93            return false;
     94        } catch (OfflineAccessException e) {
     95            return true;
     96        }
     97    }
     98
     99    protected void checkOfflineAccess() {
     100        // To be overriden by subclasses
     101    }
     102
    82103    /**
    83104     * Updates data if required
     
    85106     */
    86107    public byte[] updateIfRequired() throws T {
    87         if (Main.pref.getInteger("cache." + ident, 0) + updateInterval < System.currentTimeMillis()/1000
    88                 || !isCacheValid())
     108        if (needsUpdate())
    89109            return updateForce();
    90110        return getData();
     
    96116     */
    97117    public String updateIfRequiredString() throws T {
    98         if (Main.pref.getInteger("cache." + ident, 0) + updateInterval < System.currentTimeMillis()/1000
    99                 || !isCacheValid())
     118        if (needsUpdate())
    100119            return updateForceString();
    101120        return getDataString();
     
    138157     */
    139158    public String getDataString() throws T {
    140         return new String(getData(), StandardCharsets.UTF_8);
     159        byte[] array = getData();
     160        if (array == null) {
     161            return null;
     162        }
     163        return new String(array, StandardCharsets.UTF_8);
    141164    }
    142165
    143166    /**
    144      * Tries to load the data using the given ident from disk. If this fails, data will be updated
     167     * Tries to load the data using the given ident from disk. If this fails, data will be updated, unless run in offline mode
    145168     */
    146169    private void loadFromDisk() throws T {
     
    149172            input.read(this.data);
    150173        } catch (IOException e) {
    151             this.data = updateForce();
     174            if (!isOffline()) {
     175                this.data = updateForce();
     176            }
    152177        }
    153178    }
     
    166191
    167192    /**
    168      * Flushes the data from memory. Class automatically reloads it from disk or updateData() if
    169      * required
     193     * Flushes the data from memory. Class automatically reloads it from disk or updateData() if required
    170194     */
    171195    public void flushData() {
  • trunk/src/org/openstreetmap/josm/io/CachedFile.java

    r7282 r7434  
    2424
    2525import org.openstreetmap.josm.Main;
     26import org.openstreetmap.josm.tools.CheckParameterUtil;
    2627import org.openstreetmap.josm.tools.Pair;
    2728import org.openstreetmap.josm.tools.Utils;
     
    2930/**
    3031 * Downloads a file and caches it on disk in order to reduce network load.
    31  * 
     32 *
    3233 * Supports URLs, local files, and a custom scheme (<code>resource:</code>) to get
    3334 * resources from the current *.jar file. (Local caching is only done for URLs.)
     
    4950         * consider the cache stale and try to download the file again.
    5051         */
    51         MaxAge, 
     52        MaxAge,
    5253        /**
    5354         * Similar to MaxAge, considers the cache stale when a certain age is
     
    5657         * as a full download.
    5758         */
    58         IfModifiedSince 
     59        IfModifiedSince
    5960    }
    6061    protected String name;
     
    6364    protected String httpAccept;
    6465    protected CachingStrategy cachingStrategy;
    65    
     66
    6667    protected File cacheFile = null;
    6768    boolean initialized = false;
     
    9899        return this;
    99100    }
    100    
     101
    101102    /**
    102103     * Set maximum age of cache file. Only applies to URLs.
     
    201202                cacheFile = checkLocal(url);
    202203            }
    203         } catch (java.net.MalformedURLException e) {
     204        } catch (MalformedURLException e) {
    204205            if (name.startsWith("resource://")) {
    205206                return null;
     
    211212        }
    212213        if (cacheFile == null)
    213             throw new IOException();
     214            throw new IOException("Unable to get cache file for "+name);
    214215        return cacheFile;
    215216    }
    216    
     217
    217218    /**
    218219     * Looks for a certain entry inside a zip file and returns the entry path.
     
    292293     * Clear the cache for the given resource.
    293294     * This forces a fresh download.
    294      * @param name the URL 
     295     * @param name the URL
    295296     */
    296297    public static void cleanup(String name) {
     
    341342    private File checkLocal(URL url) throws IOException {
    342343        String prefKey = getPrefKey(url, destDir);
     344        String urlStr = url.toExternalForm();
    343345        long age = 0L;
    344346        long lMaxAge = maxAge;
     
    346348        File localFile = null;
    347349        List<String> localPathEntry = new ArrayList<>(Main.pref.getCollection(prefKey));
     350        boolean offline = false;
     351        try {
     352            checkOfflineAccess(urlStr);
     353        } catch (OfflineAccessException e) {
     354            offline = true;
     355        }
    348356        if (localPathEntry.size() == 2) {
    349357            localFile = new File(localPathEntry.get(1));
    350             if(!localFile.exists())
     358            if (!localFile.exists()) {
    351359                localFile = null;
    352             else {
     360            } else {
    353361                if ( maxAge == DEFAULT_MAXTIME
    354362                        || maxAge <= 0 // arbitrary value <= 0 is deprecated
     
    357365                }
    358366                age = System.currentTimeMillis() - Long.parseLong(localPathEntry.get(0));
    359                 if (age < lMaxAge*1000) {
     367                if (offline || age < lMaxAge*1000) {
    360368                    return localFile;
    361369                }
     
    373381            destDirFile.mkdirs();
    374382        }
    375        
    376         String a = url.toString().replaceAll("[^A-Za-z0-9_.-]", "_");
     383
     384        // No local file + offline => nothing to do
     385        if (offline) {
     386            return null;
     387        }
     388
     389        String a = urlStr.replaceAll("[^A-Za-z0-9_.-]", "_");
    377390        String localPath = "mirror_" + a;
    378391        destDirFile = new File(destDir, localPath + ".tmp");
     
    380393            HttpURLConnection con = connectFollowingRedirect(url, httpAccept, ifModifiedSince);
    381394            if (ifModifiedSince != null && con.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
    382                 Main.debug("304 Not Modified ("+url+")");
    383                 if (localFile == null) throw new AssertionError();
    384                 Main.pref.putCollection(prefKey,
     395                if (Main.isDebugEnabled()) {
     396                    Main.debug("304 Not Modified ("+urlStr+")");
     397                }
     398                if (localFile == null)
     399                    throw new AssertionError();
     400                Main.pref.putCollection(prefKey,
    385401                        Arrays.asList(Long.toString(System.currentTimeMillis()), localPathEntry.get(1)));
    386402                return localFile;
    387             } 
     403            }
    388404            try (
    389405                InputStream bis = new BufferedInputStream(con.getInputStream());
     
    398414            }
    399415            localFile = new File(destDir, localPath);
    400             if(Main.platform.rename(destDirFile, localFile)) {
    401                 Main.pref.putCollection(prefKey, 
     416            if (Main.platform.rename(destDirFile, localFile)) {
     417                Main.pref.putCollection(prefKey,
    402418                        Arrays.asList(Long.toString(System.currentTimeMillis()), localFile.toString()));
    403419            } else {
     
    407423        } catch (IOException e) {
    408424            if (age >= lMaxAge*1000 && age < lMaxAge*1000*2) {
    409                 Main.warn(tr("Failed to load {0}, use cached file and retry next time: {1}", url, e));
     425                Main.warn(tr("Failed to load {0}, use cached file and retry next time: {1}", urlStr, e));
    410426                return localFile;
    411427            } else {
     
    415431
    416432        return localFile;
     433    }
     434
     435    private static void checkOfflineAccess(String urlString) {
     436        OnlineResource.JOSM_WEBSITE.checkOfflineAccess(urlString, Main.getJOSMWebsite());
     437        OnlineResource.OSM_API.checkOfflineAccess(urlString, Main.pref.get("osm-server.url", OsmApi.DEFAULT_API_URL));
    417438    }
    418439
     
    424445     * is going from a http to a https URL, see <a href="https://bugs.openjdk.java.net/browse/JDK-4620571">bug report</a>.
    425446     * <p>
    426      * This can causes problems when downloading from certain GitHub URLs.
     447     * This can cause problems when downloading from certain GitHub URLs.
    427448     *
    428449     * @param downloadUrl The resource URL to download
     
    432453     * @throws MalformedURLException If a redirected URL is wrong
    433454     * @throws IOException If any I/O operation goes wrong
     455     * @throws OfflineAccessException if resource is accessed in offline mode, in any protocol
    434456     * @since 6867
    435457     */
    436458    public static HttpURLConnection connectFollowingRedirect(URL downloadUrl, String httpAccept, Long ifModifiedSince) throws MalformedURLException, IOException {
     459        CheckParameterUtil.ensureParameterNotNull(downloadUrl, "downloadUrl");
     460        String downloadString = downloadUrl.toExternalForm();
     461
     462        checkOfflineAccess(downloadString);
     463
    437464        HttpURLConnection con = null;
    438465        int numRedirects = 0;
    439466        while(true) {
    440467            con = Utils.openHttpConnection(downloadUrl);
     468            if (con == null) {
     469                throw new IOException("Cannot open http connection to "+downloadString);
     470            }
    441471            if (ifModifiedSince != null) {
    442472                con.setIfModifiedSince(ifModifiedSince);
     
    445475            con.setConnectTimeout(Main.pref.getInteger("socket.timeout.connect",15)*1000);
    446476            con.setReadTimeout(Main.pref.getInteger("socket.timeout.read",30)*1000);
    447             Main.debug("GET "+downloadUrl);
     477            if (Main.isDebugEnabled()) {
     478                Main.debug("GET "+downloadString);
     479            }
    448480            if (httpAccept != null) {
    449                 Main.debug("Accept: "+httpAccept);
     481                if (Main.isTraceEnabled()) {
     482                    Main.trace("Accept: "+httpAccept);
     483                }
    450484                con.setRequestProperty("Accept", httpAccept);
    451485            }
     
    466500            case HttpURLConnection.HTTP_SEE_OTHER:
    467501                String redirectLocation = con.getHeaderField("Location");
    468                 if (downloadUrl == null) {
    469                     /* I18n: argument is HTTP response code */ String msg = tr("Unexpected response from HTTP server. Got {0} response without ''Location'' header. Can''t redirect. Aborting.", con.getResponseCode());
     502                if (redirectLocation == null) {
     503                    /* I18n: argument is HTTP response code */
     504                    String msg = tr("Unexpected response from HTTP server. Got {0} response without ''Location'' header."+
     505                            " Can''t redirect. Aborting.", con.getResponseCode());
    470506                    throw new IOException(msg);
    471507                }
    472508                downloadUrl = new URL(redirectLocation);
     509                downloadString = downloadUrl.toExternalForm();
    473510                // keep track of redirect attempts to break a redirect loops if it happens
    474511                // to occur for whatever reason
     
    478515                    throw new IOException(msg);
    479516                }
    480                 Main.info(tr("Download redirected to ''{0}''", downloadUrl));
     517                Main.info(tr("Download redirected to ''{0}''", downloadString));
    481518                break;
    482519            default:
    483                 String msg = tr("Failed to read from ''{0}''. Server responded with status code {1}.", downloadUrl, con.getResponseCode());
     520                String msg = tr("Failed to read from ''{0}''. Server responded with status code {1}.", downloadString, con.getResponseCode());
    484521                throw new IOException(msg);
    485522            }
    486523        }
    487524    }
    488 
    489525}
  • trunk/src/org/openstreetmap/josm/io/MessageNotifier.java

    r6897 r7434  
    4040        // Hide default constructor for utils classes
    4141    }
    42    
     42
    4343    /** Property defining if this task is enabled or not */
    4444    public static final BooleanProperty PROP_NOTIFIER_ENABLED = new BooleanProperty("message.notifier.enabled", true);
    4545    /** Property defining the update interval in minutes */
    4646    public static final IntegerProperty PROP_INTERVAL = new IntegerProperty("message.notifier.interval", 5);
    47    
     47
    4848    private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor();
    49    
     49
    5050    private static final Runnable WORKER = new Worker();
    51    
     51
    5252    private static ScheduledFuture<?> task = null;
    53        
     53
    5454    private static class Worker implements Runnable {
    5555
     
    8282        }
    8383    }
    84    
     84
    8585    /**
    8686     * Starts the message notifier task if not already started and if user is fully identified
     
    8888    public static void start() {
    8989        int interval = PROP_INTERVAL.get();
    90         if (!isRunning() && interval > 0 && isUserEnoughIdentified()) {
     90        if (Main.isOffline(OnlineResource.OSM_API)) {
     91            Main.info(tr("{0} not available (offline mode)", tr("Message notifier")));
     92        } else if (!isRunning() && interval > 0 && isUserEnoughIdentified()) {
    9193            task = EXECUTOR.scheduleAtFixedRate(WORKER, 0, interval * 60, TimeUnit.SECONDS);
    9294            Main.info("Message notifier active (checks every "+interval+" minute"+(interval>1?"s":"")+")");
     
    104106        }
    105107    }
    106    
     108
    107109    /**
    108110     * Determines if the message notifier is currently running
     
    112114        return task != null;
    113115    }
    114    
     116
    115117    /**
    116118     * Determines if user set enough information in JOSM preferences to make the request to OSM API without
  • trunk/src/org/openstreetmap/josm/io/OsmApi.java

    r7082 r7434  
    9898    }
    9999
     100    private static String getServerUrlFromPref() {
     101        return Main.pref.get("osm-server.url", DEFAULT_API_URL);
     102    }
     103
    100104    /**
    101105     * Replies the {@link OsmApi} for the URL given by the preference <code>osm-server.url</code>
     
    104108     */
    105109    public static OsmApi getOsmApi() {
    106         String serverUrl = Main.pref.get("osm-server.url", DEFAULT_API_URL);
    107         return getOsmApi(serverUrl);
     110        return getOsmApi(getServerUrlFromPref());
    108111    }
    109112
     
    180183    private class CapabilitiesCache extends CacheCustomContent<OsmTransferException> {
    181184
     185        private static final String CAPABILITIES = "capabilities";
     186
    182187        ProgressMonitor monitor;
    183188        boolean fastFail;
    184189
    185190        public CapabilitiesCache(ProgressMonitor monitor, boolean fastFail) {
    186             super("capabilities" + getBaseUrl().hashCode(), CacheCustomContent.INTERVAL_WEEKLY);
     191            super(CAPABILITIES + getBaseUrl().hashCode(), CacheCustomContent.INTERVAL_WEEKLY);
    187192            this.monitor = monitor;
    188193            this.fastFail = fastFail;
     
    190195
    191196        @Override
     197        protected void checkOfflineAccess() {
     198            OnlineResource.OSM_API.checkOfflineAccess(getBaseUrl(getServerUrlFromPref(), "0.6")+CAPABILITIES, getServerUrlFromPref());
     199        }
     200
     201        @Override
    192202        protected byte[] updateData() throws OsmTransferException {
    193             return sendRequest("GET", "capabilities", null, monitor, false, fastFail).getBytes(StandardCharsets.UTF_8);
     203            return sendRequest("GET", CAPABILITIES, null, monitor, false, fastFail).getBytes(StandardCharsets.UTF_8);
    194204        }
    195205    }
     
    217227        if (initialized)
    218228            return;
     229        if (Main.isOffline(OnlineResource.OSM_API)) {
     230            // At this point this is an error because all automatic or UI actions requiring OSM API should have been disabled earlier
     231            throw new OfflineAccessException(tr("{0} not available (offline mode)", OnlineResource.OSM_API.getLocName()));
     232        }
    219233        cancel = false;
    220234        try {
     
    324338    }
    325339
    326     /**
    327      * Returns the base URL for API requests, including the negotiated version number.
    328      * @return base URL string
    329      */
    330     public String getBaseUrl() {
     340    private static String getBaseUrl(String serverUrl, String version) {
    331341        StringBuilder rv = new StringBuilder(serverUrl);
    332342        if (version != null) {
     
    339349        int p; while ((p = rv.indexOf("//", rv.indexOf("://")+2)) > -1) { rv.delete(p, p + 1); }
    340350        return rv.toString();
     351    }
     352
     353    /**
     354     * Returns the base URL for API requests, including the negotiated version number.
     355     * @return base URL string
     356     */
     357    public String getBaseUrl() {
     358        return getBaseUrl(serverUrl, version);
    341359    }
    342360
  • trunk/src/org/openstreetmap/josm/io/OsmServerReader.java

    r7267 r7434  
    118118    protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor, String reason, boolean uncompressAccordingToContentDisposition) throws OsmTransferException {
    119119        try {
     120            OnlineResource.JOSM_WEBSITE.checkOfflineAccess(urlStr, Main.getJOSMWebsite());
     121            OnlineResource.OSM_API.checkOfflineAccess(urlStr, Main.pref.get("osm-server.url", OsmApi.DEFAULT_API_URL));
     122
    120123            URL url = null;
    121124            try {
Note: See TracChangeset for help on using the changeset viewer.