altoslib/altosuilib: Update library version to 7
[fw/altos] / altoslib / AltosMapStore.java
1 /*
2  * Copyright © 2014 Keith Packard <keithp@keithp.com>
3  *
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.
7  *
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.
12  *
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.
16  */
17
18 package org.altusmetrum.altoslib_7;
19
20 import java.io.*;
21 import java.net.*;
22 import java.util.*;
23
24 public class AltosMapStore {
25         String                                  url;
26         public File                             file;
27         LinkedList<AltosMapStoreListener>       listeners = new LinkedList<AltosMapStoreListener>();
28
29         int                                     status;
30
31         public int status() {
32                 return status;
33         }
34
35         public synchronized void add_listener(AltosMapStoreListener listener) {
36                 if (!listeners.contains(listener))
37                         listeners.add(listener);
38         }
39
40         public synchronized void remove_listener(AltosMapStoreListener listener) {
41                 listeners.remove(listener);
42         }
43
44         private synchronized void notify_listeners(int status) {
45                 this.status = status;
46                 for (AltosMapStoreListener listener : listeners)
47                         listener.notify_store(this, status);
48         }
49
50         static Object   forbidden_lock = new Object();
51         static long     forbidden_time;
52         static boolean  forbidden_set;
53
54         private int fetch_url() {
55                 URL u;
56
57                 try {
58                         u = new URL(url);
59                 } catch (java.net.MalformedURLException e) {
60                         return AltosMapTile.bad_request;
61                 }
62
63                 byte[] data;
64                 URLConnection uc = null;
65                 try {
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();
71                                 switch (response) {
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();
77                                                 forbidden_set = true;
78                                                 return AltosMapTile.forbidden;
79                                         }
80                                 }
81                         }
82                         InputStream in = new BufferedInputStream(uc.getInputStream());
83                         int bytesRead = 0;
84                         int offset = 0;
85                         data = new byte[contentLength];
86                         while (offset < contentLength) {
87                                 bytesRead = in.read(data, offset, data.length - offset);
88                                 if (bytesRead == -1)
89                                         break;
90                                 offset += bytesRead;
91                         }
92                         in.close();
93
94                         if (offset != contentLength)
95                                 return AltosMapTile.failed;
96
97                 } catch (IOException e) {
98                         return AltosMapTile.failed;
99                 }
100
101                 try {
102                         FileOutputStream out = new FileOutputStream(file);
103                         out.write(data);
104                         out.flush();
105                         out.close();
106                 } catch (FileNotFoundException e) {
107                         return AltosMapTile.bad_request;
108                 } catch (IOException e) {
109                         if (file.exists())
110                                 file.delete();
111                         return AltosMapTile.bad_request;
112                 }
113                 return AltosMapTile.success;
114         }
115
116         static Object   fetch_lock = new Object();
117
118         static final long       forbidden_interval = 60l * 1000l * 1000l * 1000l;
119         static final long       google_maps_ratelimit_ms = 1200;
120
121         class loader implements Runnable {
122
123                 public void run() {
124                         if (file.exists()) {
125                                 notify_listeners(AltosMapTile.success);
126                                 return;
127                         }
128
129                         synchronized(forbidden_lock) {
130                                 if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) {
131                                         notify_listeners(AltosMapTile.forbidden);
132                                         return;
133                                 }
134                         }
135
136                         int new_status;
137
138                         if (!AltosVersion.has_google_maps_api_key()) {
139                                 synchronized (fetch_lock) {
140                                         long startTime = System.nanoTime();
141                                         new_status = fetch_url();
142                                         if (new_status == AltosMapTile.success) {
143                                                 long duration_ms = (System.nanoTime() - startTime) / 1000000;
144                                                 if (duration_ms < google_maps_ratelimit_ms) {
145                                                         try {
146                                                                 Thread.sleep(google_maps_ratelimit_ms - duration_ms);
147                                                         } catch (InterruptedException e) {
148                                                                 Thread.currentThread().interrupt();
149                                                         }
150                                                 }
151                                         }
152                                 }
153                         } else {
154                                 new_status = fetch_url();
155                         }
156                         notify_listeners(new_status);
157                 }
158         }
159
160         private void load() {
161                 loader  l = new loader();
162                 Thread  lt = new Thread(l);
163                 lt.start();
164         }
165
166         private AltosMapStore (String url, File file) {
167                 this.url = url;
168                 this.file = file;
169
170                 if (file.exists())
171                         status = AltosMapTile.success;
172                 else {
173                         status = AltosMapTile.loading;
174                         load();
175                 }
176         }
177
178         public boolean equals(AltosMapStore other) {
179                 return url.equals(other.url);
180         }
181
182         static HashMap<String,AltosMapStore> stores = new HashMap<String,AltosMapStore>();
183
184         public static AltosMapStore get(String url, File file) {
185                 AltosMapStore   store;
186                 synchronized(stores) {
187                         if (stores.containsKey(url)) {
188                                 store = stores.get(url);
189                         } else {
190                                 store = new AltosMapStore(url, file);
191                                 stores.put(url, store);
192                         }
193                 }
194                 return store;
195         }
196 }