X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=blobdiff_plain;f=altoslib%2FAltosMapStore.java;fp=altoslib%2FAltosMapStore.java;h=a10a16652020555c2e3ed726c91d084c2675ea46;hp=0000000000000000000000000000000000000000;hb=643c2fb03833d658320f476ef731bbb06fe3cc31;hpb=e41786fb384892961a6547e17812a24314ce9623 diff --git a/altoslib/AltosMapStore.java b/altoslib/AltosMapStore.java new file mode 100644 index 00000000..a10a1665 --- /dev/null +++ b/altoslib/AltosMapStore.java @@ -0,0 +1,243 @@ +/* + * Copyright © 2014 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_7; + +import java.io.*; +import java.net.*; +import java.util.*; + +public class AltosMapStore { + String url; + public File file; + LinkedList listeners = new LinkedList(); + + int status; + + public int status() { + return status; + } + + public synchronized void add_listener(AltosMapStoreListener listener) { + if (!listeners.contains(listener)) + listeners.add(listener); + } + + public synchronized void remove_listener(AltosMapStoreListener listener) { + listeners.remove(listener); + } + + private synchronized void notify_listeners(int status) { + this.status = status; + for (AltosMapStoreListener listener : listeners) + listener.notify_store(this, status); + } + + static Object forbidden_lock = new Object(); + static long forbidden_time; + static boolean forbidden_set; + + private int fetch_url() { + URL u; + + try { + u = new URL(url); + } catch (java.net.MalformedURLException e) { + return AltosMapTile.bad_request; + } + + byte[] data; + URLConnection uc = null; + try { + uc = u.openConnection(); + String type = uc.getContentType(); + int contentLength = uc.getContentLength(); + if (uc instanceof HttpURLConnection) { + int response = ((HttpURLConnection) uc).getResponseCode(); + switch (response) { + case HttpURLConnection.HTTP_FORBIDDEN: + case HttpURLConnection.HTTP_PAYMENT_REQUIRED: + case HttpURLConnection.HTTP_UNAUTHORIZED: + synchronized (forbidden_lock) { + forbidden_time = System.nanoTime(); + forbidden_set = true; + return AltosMapTile.forbidden; + } + } + } + InputStream in = new BufferedInputStream(uc.getInputStream()); + int bytesRead = 0; + int offset = 0; + data = new byte[contentLength]; + while (offset < contentLength) { + bytesRead = in.read(data, offset, data.length - offset); + if (bytesRead == -1) + break; + offset += bytesRead; + } + in.close(); + + if (offset != contentLength) + return AltosMapTile.failed; + + } catch (IOException e) { + return AltosMapTile.failed; + } + + try { + FileOutputStream out = new FileOutputStream(file); + out.write(data); + out.flush(); + out.close(); + } catch (FileNotFoundException e) { + return AltosMapTile.bad_request; + } catch (IOException e) { + if (file.exists()) + file.delete(); + return AltosMapTile.bad_request; + } + return AltosMapTile.success; + } + + static Object fetch_lock = new Object(); + + static final long forbidden_interval = 60l * 1000l * 1000l * 1000l; + static final long google_maps_ratelimit_ms = 1200; + + static Object loader_lock = new Object(); + + static LinkedList waiting = new LinkedList(); + static LinkedList running = new LinkedList(); + + static final int concurrent_loaders = 128; + + static void start_loaders() { + while (!waiting.isEmpty() && running.size() < concurrent_loaders) { + AltosMapStore s = waiting.remove(); + running.add(s); + Thread lt = s.make_loader_thread(); + lt.start(); + } + } + + void finish_loader() { + synchronized(loader_lock) { + running.remove(this); + start_loaders(); + } + } + + void add_loader() { + synchronized(loader_lock) { + waiting.add(this); + start_loaders(); + } + } + + class loader implements Runnable { + + public void run() { + try { + if (file.exists()) { + notify_listeners(AltosMapTile.success); + return; + } + + synchronized(forbidden_lock) { + if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) { + notify_listeners(AltosMapTile.forbidden); + return; + } + } + + int new_status; + + if (!AltosVersion.has_google_maps_api_key()) { + synchronized (fetch_lock) { + long startTime = System.nanoTime(); + new_status = fetch_url(); + if (new_status == AltosMapTile.success) { + long duration_ms = (System.nanoTime() - startTime) / 1000000; + if (duration_ms < google_maps_ratelimit_ms) { + try { + Thread.sleep(google_maps_ratelimit_ms - duration_ms); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + } + } else { + new_status = fetch_url(); + } + notify_listeners(new_status); + } finally { + finish_loader(); + } + } + } + + private Thread make_loader_thread() { + return new Thread(new loader()); + } + + private void load() { + add_loader(); + } + + private AltosMapStore (String url, File file) { + this.url = url; + this.file = file; + + if (file.exists()) + status = AltosMapTile.success; + else { + status = AltosMapTile.loading; + load(); + } + } + + public int hashCode() { + return url.hashCode(); + } + + public boolean equals(Object o) { + if (o == null) + return false; + + if (!(o instanceof AltosMapStore)) + return false; + + AltosMapStore other = (AltosMapStore) o; + return url.equals(other.url); + } + + static HashMap stores = new HashMap(); + + public static AltosMapStore get(String url, File file) { + AltosMapStore store; + synchronized(stores) { + if (stores.containsKey(url)) { + store = stores.get(url); + } else { + store = new AltosMapStore(url, file); + stores.put(url, store); + } + } + return store; + } +}