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_10;
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);
38 listener.notify_store(this, status);
41 public synchronized void remove_listener(AltosMapStoreListener listener) {
42 listeners.remove(listener);
45 private synchronized void notify_listeners(int status) {
47 for (AltosMapStoreListener listener : listeners)
48 listener.notify_store(this, status);
51 static Object forbidden_lock = new Object();
52 static long forbidden_time;
53 static boolean forbidden_set;
55 private int fetch_url() {
60 } catch (java.net.MalformedURLException e) {
61 return AltosMapTile.bad_request;
65 URLConnection uc = null;
67 uc = u.openConnection();
68 String type = uc.getContentType();
69 int contentLength = uc.getContentLength();
70 if (uc instanceof HttpURLConnection) {
71 int response = ((HttpURLConnection) uc).getResponseCode();
73 case HttpURLConnection.HTTP_FORBIDDEN:
74 case HttpURLConnection.HTTP_PAYMENT_REQUIRED:
75 case HttpURLConnection.HTTP_UNAUTHORIZED:
76 synchronized (forbidden_lock) {
77 forbidden_time = System.nanoTime();
79 return AltosMapTile.forbidden;
83 InputStream in = new BufferedInputStream(uc.getInputStream());
86 data = new byte[contentLength];
87 while (offset < contentLength) {
88 bytesRead = in.read(data, offset, data.length - offset);
95 if (offset != contentLength)
96 return AltosMapTile.failed;
98 } catch (IOException e) {
99 return AltosMapTile.failed;
103 FileOutputStream out = new FileOutputStream(file);
107 } catch (FileNotFoundException e) {
108 return AltosMapTile.bad_request;
109 } catch (IOException e) {
112 return AltosMapTile.bad_request;
114 return AltosMapTile.fetched;
117 static Object fetch_lock = new Object();
119 static final long forbidden_interval = 60l * 1000l * 1000l * 1000l;
120 static final long google_maps_ratelimit_ms = 1200;
122 static Object fetcher_lock = new Object();
124 static LinkedList<AltosMapStore> waiting = new LinkedList<AltosMapStore>();
125 static LinkedList<AltosMapStore> running = new LinkedList<AltosMapStore>();
127 static final int concurrent_fetchers = 128;
129 static void start_fetchers() {
130 while (!waiting.isEmpty() && running.size() < concurrent_fetchers) {
131 AltosMapStore s = waiting.remove();
133 Thread lt = s.make_fetcher_thread();
138 void finish_fetcher() {
139 synchronized(fetcher_lock) {
140 running.remove(this);
146 synchronized(fetcher_lock) {
152 class fetcher implements Runnable {
157 notify_listeners(AltosMapTile.fetched);
161 synchronized(forbidden_lock) {
162 if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) {
163 notify_listeners(AltosMapTile.forbidden);
170 if (!AltosVersion.has_google_maps_api_key()) {
171 synchronized (fetch_lock) {
172 long startTime = System.nanoTime();
173 new_status = fetch_url();
174 if (new_status == AltosMapTile.fetched) {
175 long duration_ms = (System.nanoTime() - startTime) / 1000000;
176 if (duration_ms < google_maps_ratelimit_ms) {
178 Thread.sleep(google_maps_ratelimit_ms - duration_ms);
179 } catch (InterruptedException e) {
180 Thread.currentThread().interrupt();
186 new_status = fetch_url();
188 notify_listeners(new_status);
195 private Thread make_fetcher_thread() {
196 return new Thread(new fetcher());
199 private void fetch() {
203 private AltosMapStore (String url, File file) {
208 status = AltosMapTile.fetched;
210 status = AltosMapTile.fetching;
215 public int hashCode() {
216 return url.hashCode();
219 public boolean equals(Object o) {
223 if (!(o instanceof AltosMapStore))
226 AltosMapStore other = (AltosMapStore) o;
227 return url.equals(other.url);
230 static HashMap<String,AltosMapStore> stores = new HashMap<String,AltosMapStore>();
232 public static AltosMapStore get(String url, File file) {
234 synchronized(stores) {
235 if (stores.containsKey(url)) {
236 store = stores.get(url);
238 store = new AltosMapStore(url, file);
239 stores.put(url, store);