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