source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/OAIResumptionToken.java@ 28965

Last change on this file since 28965 was 28880, checked in by kjdon, 10 years ago

added a field in data for initial time that resumption token started - so we can check if a collection has changed since the token was issued. Currently just the token name as the current time is used for token name. added method to manually expire a token - if a collection has changed since the token was issued that expire the token as they should reissue the request from the beginning.

File size: 11.1 KB
Line 
1/*
2 * OAIResumptionToken.java
3 * Copyright (C) 2014 New Zealand Digital Library, http://www.nzdl.org
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20package org.greenstone.gsdl3.util;
21
22import org.greenstone.util.GlobalProperties;
23
24import org.w3c.dom.*;
25
26import java.io.File;
27import java.net.URL;
28import java.util.HashMap;
29import java.util.Iterator;
30import java.util.Map;
31import java.util.Set;
32
33
34// import file Logger.java
35import org.apache.log4j.*;
36
37/** */
38public class OAIResumptionToken {
39
40
41
42 // Other fields we save
43 public static final String CURRENT_SET = "current_set";
44 public static final String CURRENT_CURSOR = "current_cursor";
45
46 // for token XML
47 public static final String TOKEN = "token";
48 public static final String TOKEN_LIST = "tokenList";
49 public static final String NAME = "name";
50 public static final String EXPIRATION = "expiration";
51 public static final String INITIAL_TIME = "initial";
52 //public static final String FROM
53 public static final String FILE_SEPARATOR = File.separator;
54 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.GSXML.class.getName());
55
56 public static XMLConverter converter = new XMLConverter();
57 public static Element resumption_token_elem = null;
58 //used when saving the token file
59 public static File resumption_token_file = null;
60
61 private static HashMap<String, HashMap<String,String>> stored_tokens = new HashMap<String, HashMap<String,String>>();
62
63 private static HashMap<String, Long> expiration_data = new HashMap<String, Long>();
64
65 /** initialize stored resumption tokens and clear any expired ones*/
66 public static void init() {
67 if (!findOrCreateTokenFile()) {
68 // can't read in old tokens
69 logger.error("Can't find token file, so we can't initialise tokens");
70 return;
71 }
72
73 Document token_doc = converter.getDOM(resumption_token_file, "utf-8");
74 Element token_elem = null;
75 if (token_doc != null) {
76 token_elem = token_doc.getDocumentElement();
77 } else {
78 logger.error("Failed to parse oai resumption token file OAIResumptionToken.xml.");
79 resetTokenFile();
80 return;
81 }
82
83 // read in stored tokens
84 NodeList tokens = token_elem.getElementsByTagName(TOKEN);
85 for(int i=0; i<tokens.getLength(); i++) {
86 Element token = (Element)tokens.item(i);
87 String set = token.getAttribute(OAIXML.SET);
88 if (set.isEmpty()) {
89 set = null;
90 }
91 String meta_prefix = token.getAttribute(OAIXML.METADATA_PREFIX);
92 if (meta_prefix.isEmpty()) {
93 meta_prefix = null;
94 }
95 String from = token.getAttribute(OAIXML.FROM);
96 if (from.isEmpty()) {
97 from = null;
98 }
99 String until = token.getAttribute(OAIXML.UNTIL);
100 if (until.isEmpty()) {
101 until = null;
102 }
103 storeToken(token.getAttribute(NAME), set, meta_prefix, from, until, token.getAttribute(EXPIRATION));
104
105 }
106 }
107 // store the token read in from saved tokens file
108 protected static void storeToken(String name, String set, String metadata_prefix, String from, String until, String expiration) {
109 HashMap<String, String> data = new HashMap<String, String>();
110 data.put(OAIXML.FROM, from);
111 data.put(OAIXML.UNTIL, until);
112 data.put(OAIXML.SET, set);
113 data.put(OAIXML.METADATA_PREFIX, metadata_prefix);
114 stored_tokens.put(name, data);
115 expiration_data.put(name, new Long(expiration));
116 }
117
118 /** generate the token, and store all the data for it */
119 public static String createAndStoreResumptionToken(String set, String metadata_prefix, String from, String until, String cursor, String current_set, String current_cursor) {
120 HashMap<String, String> data = new HashMap<String, String>();
121 long expiration_date = System.currentTimeMillis();
122 String base_token_name = ""+expiration_date;
123 stored_tokens.put(base_token_name, data);
124 expiration_date += (OAIXML.getTokenExpiration());
125 expiration_data.put(base_token_name, new Long(expiration_date));
126 String token_name = base_token_name+":" + cursor + ":" + current_set +":"+ current_cursor;
127 data.put(OAIXML.FROM, from);
128 data.put(OAIXML.UNTIL, until);
129 data.put(OAIXML.SET, set);
130 data.put(OAIXML.METADATA_PREFIX, metadata_prefix);
131 return token_name;
132
133 }
134
135 public static long getExpirationDate(String token) {
136 if (token.indexOf(":") != -1) {
137 token = token.substring(0, token.indexOf(":"));
138 }
139 return expiration_data.get(token).longValue();
140 }
141
142 public static String updateToken(String token, String cursor, String current_set, String current_cursor) {
143 if (token.indexOf(":") != -1) {
144 token = token.substring(0, token.indexOf(":"));
145 }
146
147 // when we are generating a new token, we update the expiration date to allow the new token to have the full length of time
148 long exp_date = System.currentTimeMillis();
149 exp_date += (OAIXML.getTokenExpiration());
150 expiration_data.put(token, exp_date);
151
152 token = token + ":" + cursor + ":" + current_set + ":" + current_cursor;
153 return token;
154 }
155
156 /** check that this token is currently valid */
157 public static boolean isValidToken(String token) {
158 // we clear expired tokens each time we check one.
159 clearExpiredTokens();
160 // take off the set/cursor parts to check for the main key
161 if (token.indexOf(":") != -1) {
162 token = token.substring(0, token.indexOf(":"));
163 logger.error("looking up "+token);
164 }
165 if (stored_tokens.containsKey(token)) {
166 return true;
167 }
168 return false;
169 }
170
171 public static HashMap<String, String> getTokenData(String token) {
172 // find the base name
173 String base_name = token;
174 if (token.indexOf(":") != -1) {
175 base_name = token.substring(0, token.indexOf(":"));
176 logger.error("getting data for "+base_name);
177 }
178 HashMap<String, String> data = new HashMap<String, String>(stored_tokens.get(base_name));
179 if (data == null) {
180 logger.error("data was null!!");
181 return null;
182 }
183 // add in cursor, etc from the token name
184 if (base_name != token) {
185 String[] parts = token.split(":");
186 if (parts.length != 4) {
187 // something wrong!!
188 }
189 data.put(OAIXML.CURSOR, parts[1]);
190 data.put(CURRENT_SET, parts[2]);
191 data.put(CURRENT_CURSOR, parts[3]);
192 data.put(INITIAL_TIME, base_name);
193 }
194 return data;
195
196 }
197
198 // used to manually expire a particular token - eg when one of its collections has changed since token was issued.
199 public static void expireToken(String token) {
200 if (token.indexOf(":") != -1) {
201 token = token.substring(0, token.indexOf(":"));
202 }
203 expiration_data.remove(token);
204 stored_tokens.remove(token);
205 }
206 // read through all stored expiry dates, and delete any tokens that are too
207 // old
208 public static void clearExpiredTokens() {
209
210 Set<Map.Entry<String,Long>> token_set = expiration_data.entrySet();
211 Iterator<Map.Entry<String,Long>> i = token_set.iterator();
212 int size = expiration_data.size();
213 logger.error("start tokens "+size);
214 Long time_now = System.currentTimeMillis();
215 while (i.hasNext()==true) {
216 Map.Entry<String,Long> entry = i.next();
217 String key = entry.getKey();
218 Long exp = entry.getValue();
219 if (exp < time_now) {
220 logger.error("token "+key+" is expired, "+ OAIXML.getTime(exp));
221 i.remove();
222 // also remove the token from the stored tokens
223 stored_tokens.remove(key);
224 }
225 }
226 size = expiration_data.size();
227 logger.error("end tokens "+size);
228 }
229
230 protected static boolean findOrCreateTokenFile() {
231
232 try {
233 URL token_file_url = Class.forName("org.greenstone.gsdl3.OAIServer").getClassLoader().getResource("OAIResumptionToken.xml");
234 if (token_file_url != null) {
235 resumption_token_file = new File(token_file_url.toURI());
236 if (resumption_token_file != null && resumption_token_file.exists()) {
237 return true;
238 }
239 else {
240 logger.error("Resumption token file found, "+ token_file_url.toURI()+" but couldn't create a file from it.");
241 }
242 }
243 } catch (Exception e) {
244 logger.error("couldn't find or ResumptionToken.xml "+e.getMessage());
245 }
246 // if we have got here, have't managed to load file via class loader -
247 // it may not exist yet.
248 // try and create a new empty one
249 if (resetTokenFile()) {
250 return true;
251 }
252 return false;
253 }
254
255 // If there was no file, or something went wrong with the file, use this to
256 // create a new one.
257 public static boolean resetTokenFile() {
258 resumption_token_file = new File(GlobalProperties.getGSDL3Home() +
259 FILE_SEPARATOR + "WEB-INF" +
260 FILE_SEPARATOR + "classes" +
261 FILE_SEPARATOR + "OAIResumptionToken.xml");
262 try {
263 if (!resumption_token_file.exists()) {
264
265 if (!resumption_token_file.createNewFile()) {
266 logger.error("Couldn't create a new resumption token file "+ resumption_token_file.getPath());
267 resumption_token_file = null;
268 return false;
269 }
270 }
271 } catch (Exception e) {
272 logger.error("Couldn't create a new resumption token file "+ resumption_token_file.getPath()+", "+e.getMessage());
273 resumption_token_file = null;
274 return false;
275 }
276 Document doc = converter.newDOM();
277 Element elem = doc.createElement(TOKEN_LIST);
278 if (converter.writeDOM(elem, resumption_token_file)) {
279 return true;
280 }
281 resumption_token_file = null;
282 return false;
283 }
284
285
286 public static boolean saveTokensToFile() {
287 clearExpiredTokens();
288 if (resumption_token_file == null) {
289 logger.error("no available resumption token file, not storing tokens");
290 return false;
291 }
292
293 // Create an XML representation of the stored tokens
294 Document doc = converter.newDOM();
295 Element token_list = doc.createElement(TOKEN_LIST);
296
297 if (stored_tokens.size() == 0) {
298 // nothing to store, store an empty file
299 converter.writeDOM(token_list, resumption_token_file);
300 return true;
301 }
302 Set<String> keys = stored_tokens.keySet();
303 Iterator<String> i = keys.iterator();
304 while(i.hasNext()) {
305 String token = i.next();
306 HashMap<String, String> data = stored_tokens.get(token);
307 Element token_elem = doc.createElement(TOKEN);
308 token_elem.setAttribute(NAME, token);
309 token_elem.setAttribute(OAIXML.FROM, data.get(OAIXML.FROM));
310 token_elem.setAttribute(OAIXML.UNTIL, data.get(OAIXML.UNTIL));
311 token_elem.setAttribute(OAIXML.SET, data.get(OAIXML.SET));
312 token_elem.setAttribute(OAIXML.METADATA_PREFIX, data.get(OAIXML.METADATA_PREFIX));
313 token_elem.setAttribute(EXPIRATION, ""+expiration_data.get(token));
314 token_list.appendChild(token_elem);
315 }
316 converter.writeDOM(token_list, resumption_token_file);
317 return true;
318 }
319
320}
Note: See TracBrowser for help on using the repository browser.