2 * Copyright © 2014 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
18 package org.altusmetrum.altoslib_8;
24 public class AltosMapStore {
27 LinkedList<AltosMapStoreListener> listeners = new LinkedList<AltosMapStoreListener>();
35 public synchronized void add_listener(AltosMapStoreListener listener) {
36 if (!listeners.contains(listener))
37 listeners.add(listener);
40 public synchronized void remove_listener(AltosMapStoreListener listener) {
41 listeners.remove(listener);
44 private synchronized void notify_listeners(int status) {
46 for (AltosMapStoreListener listener : listeners)
47 listener.notify_store(this, status);
50 static Object forbidden_lock = new Object();
51 static long forbidden_time;
52 static boolean forbidden_set;
54 private int fetch_url() {
59 } catch (java.net.MalformedURLException e) {
60 return AltosMapTile.bad_request;
64 URLConnection uc = null;
66 uc = u.openConnection();
67 String type = uc.getContentType();
68 int contentLength = uc.getContentLength();
69 if (uc instanceof HttpURLConnection) {
70 int response = ((HttpURLConnection) uc).getResponseCode();
72 case HttpURLConnection.HTTP_FORBIDDEN:
73 case HttpURLConnection.HTTP_PAYMENT_REQUIRED:
74 case HttpURLConnection.HTTP_UNAUTHORIZED:
75 synchronized (forbidden_lock) {
76 forbidden_time = System.nanoTime();
78 return AltosMapTile.forbidden;
82 InputStream in = new BufferedInputStream(uc.getInputStream());
85 data = new byte[contentLength];
86 while (offset < contentLength) {
87 bytesRead = in.read(data, offset, data.length - offset);
94 if (offset != contentLength)
95 return AltosMapTile.failed;
97 } catch (IOException e) {
98 return AltosMapTile.failed;
102 FileOutputStream out = new FileOutputStream(file);
106 } catch (FileNotFoundException e) {
107 return AltosMapTile.bad_request;
108 } catch (IOException e) {
111 return AltosMapTile.bad_request;
113 return AltosMapTile.success;
116 static Object fetch_lock = new Object();
118 static final long forbidden_interval = 60l * 1000l * 1000l * 1000l;
119 static final long google_maps_ratelimit_ms = 1200;
121 static Object loader_lock = new Object();
123 static LinkedList<AltosMapStore> waiting = new LinkedList<AltosMapStore>();
124 static LinkedList<AltosMapStore> running = new LinkedList<AltosMapStore>();
126 static final int concurrent_loaders = 128;
128 static void start_loaders() {
129 while (!waiting.isEmpty() && running.size() < concurrent_loaders) {
130 AltosMapStore s = waiting.remove();
132 Thread lt = s.make_loader_thread();
137 void finish_loader() {
138 synchronized(loader_lock) {
139 running.remove(this);
145 synchronized(loader_lock) {
151 class loader implements Runnable {
156 notify_listeners(AltosMapTile.success);
160 synchronized(forbidden_lock) {
161 if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) {
162 notify_listeners(AltosMapTile.forbidden);
169 if (!AltosVersion.has_google_maps_api_key()) {
170 synchronized (fetch_lock) {
171 long startTime = System.nanoTime();
172 new_status = fetch_url();
173 if (new_status == AltosMapTile.success) {
174 long duration_ms = (System.nanoTime() - startTime) / 1000000;
175 if (duration_ms < google_maps_ratelimit_ms) {
177 Thread.sleep(google_maps_ratelimit_ms - duration_ms);
178 } catch (InterruptedException e) {
179 Thread.currentThread().interrupt();
185 new_status = fetch_url();
187 notify_listeners(new_status);
194 private Thread make_loader_thread() {
195 return new Thread(new loader());
198 private void load() {
202 private AltosMapStore (String url, File file) {
207 status = AltosMapTile.success;
209 status = AltosMapTile.loading;
214 public int hashCode() {
215 return url.hashCode();
218 public boolean equals(Object o) {
222 if (!(o instanceof AltosMapStore))
225 AltosMapStore other = (AltosMapStore) o;
226 return url.equals(other.url);
229 static HashMap<String,AltosMapStore> stores = new HashMap<String,AltosMapStore>();
231 public static AltosMapStore get(String url, File file) {
233 synchronized(stores) {
234 if (stores.containsKey(url)) {
235 store = stores.get(url);
237 store = new AltosMapStore(url, file);
238 stores.put(url, store);