Merge branch 'master' of ssh://git.indexdata.com/home/git/private/mkjsf
[mkjsf-moved-to-github.git] / src / main / java / com / indexdata / mkjsf / pazpar2 / Pz2Bean.java
1 package com.indexdata.mkjsf.pazpar2;\r
2 \r
3 import java.io.Serializable;\r
4 import java.util.ArrayList;\r
5 import java.util.Arrays;\r
6 import java.util.HashMap;\r
7 import java.util.List;\r
8 import java.util.Map;\r
9 import java.util.StringTokenizer;\r
10 \r
11 import javax.annotation.PostConstruct;\r
12 import javax.enterprise.context.SessionScoped;\r
13 import javax.inject.Inject;\r
14 import javax.inject.Named;\r
15 \r
16 import org.apache.log4j.Logger;\r
17 \r
18 import com.indexdata.mkjsf.config.Configurable;\r
19 import com.indexdata.mkjsf.config.Configuration;\r
20 import com.indexdata.mkjsf.config.ConfigurationReader;\r
21 import com.indexdata.mkjsf.controls.ResultsPager;\r
22 import com.indexdata.mkjsf.errors.ConfigurationError;\r
23 import com.indexdata.mkjsf.errors.ConfigurationException;\r
24 import com.indexdata.mkjsf.errors.ErrorCentral;\r
25 import com.indexdata.mkjsf.errors.ErrorHelper;\r
26 import com.indexdata.mkjsf.pazpar2.commands.CommandParameter;\r
27 import com.indexdata.mkjsf.pazpar2.commands.Pazpar2Commands;\r
28 import com.indexdata.mkjsf.pazpar2.data.RecordResponse;\r
29 import com.indexdata.mkjsf.pazpar2.data.ResponseDataObject;\r
30 import com.indexdata.mkjsf.pazpar2.data.ResponseParser;\r
31 import com.indexdata.mkjsf.pazpar2.data.Responses;\r
32 import com.indexdata.mkjsf.pazpar2.sp.auth.ServiceProxyUser;\r
33 import com.indexdata.mkjsf.pazpar2.state.StateListener;\r
34 import com.indexdata.mkjsf.pazpar2.state.StateManager;\r
35 import com.indexdata.mkjsf.utils.Utils;\r
36 \r
37 @Named("pz2") @SessionScoped\r
38 public class Pz2Bean implements Pz2Interface, StateListener, Configurable, Serializable {\r
39 \r
40   private static final String MODULE_NAME = "service";\r
41   private static String SERVICE_TYPE_TBD = "TBD", SERVICE_TYPE_PZ2 = "PZ2", SERVICE_TYPE_SP = "SP";\r
42   private static final List<String> serviceTypes = \r
43                 Arrays.asList(SERVICE_TYPE_PZ2,SERVICE_TYPE_SP,SERVICE_TYPE_TBD);\r
44   private String serviceType = SERVICE_TYPE_TBD;\r
45   private List<String> serviceProxyUrls = new ArrayList<String>();\r
46   public static final String SERVICE_PROXY_URL_LIST = "SERVICE_PROXY_URL_LIST";\r
47   private List<String> pazpar2Urls = new ArrayList<String>();\r
48   public static final String PAZPAR2_URL_LIST = "PAZPAR2_URL_LIST";\r
49 \r
50 \r
51   private static final long serialVersionUID = 3440277287081557861L;\r
52   private static Logger logger = Logger.getLogger(Pz2Bean.class);\r
53   private static Logger responseLogger = Logger.getLogger("com.indexdata.mkjsf.pazpar2.responses");   \r
54   protected Pz2Client pz2Client = null;\r
55   protected ServiceProxyClient spClient = null;\r
56   protected SearchClient searchClient = null;  \r
57     \r
58   @Inject ConfigurationReader configurator;\r
59   @Inject StateManager stateMgr;\r
60   @Inject Pazpar2Commands pzreq;\r
61   @Inject Responses pzresp;\r
62   @Inject ErrorCentral errors;\r
63   @Inject ServiceProxyUser user;\r
64   \r
65   protected ResultsPager pager = null; \r
66   \r
67   protected ErrorHelper errorHelper = null;\r
68               \r
69   public Pz2Bean () {\r
70     logger.info("Instantiating pz2 bean [" + Utils.objectId(this) + "]");    \r
71   }\r
72   \r
73   @PostConstruct\r
74   public void postConstruct() {    \r
75     logger.debug("Pz2Bean post-construct: Configurator is " + configurator);\r
76     logger.debug(Utils.objectId(this) + " will instantiate a Pz2Client next.");\r
77     pz2Client = new Pz2Client();\r
78     configureClient(pz2Client,configurator);\r
79     spClient = new ServiceProxyClient();\r
80     configureClient(spClient,configurator);\r
81     try {\r
82       this.configure(configurator);\r
83     } catch (ConfigurationException e) {\r
84       logger.error("There was a problem configuring the Pz2Bean (\"pz2\")");\r
85       e.printStackTrace();\r
86     }    \r
87     stateMgr.addStateListener(this);    \r
88   }  \r
89   \r
90   public void configureClient(SearchClient client, ConfigurationReader configReader) {\r
91     logger.debug(Utils.objectId(this) + " will configure search client for the session");\r
92     try {\r
93       client.configure(configReader);            \r
94     } catch (ConfigurationException e) {\r
95       logger.debug("Pz2Bean adding configuration error");\r
96       errors.addConfigurationError(new ConfigurationError("Search Client","Configuration",e.getMessage()));                \r
97     } \r
98     logger.info(configReader.document());\r
99     pzresp.resetAllSessionData();    \r
100   }\r
101   \r
102   public void resetSearchAndRecordCommands () {\r
103     pzreq.getRecord().removeParametersInState();\r
104     pzreq.getSearch().removeParametersInState();   \r
105   }\r
106 \r
107     \r
108   public void doSearch(String query) {\r
109     pzreq.getSearch().setParameter(new CommandParameter("query","=",query));     \r
110     doSearch();\r
111   }\r
112 \r
113   public void doSearch() { \r
114     stateMgr.hasPendingStateChange("search",false);\r
115     pzresp.resetSearchResponses();\r
116     // resets some record and show command parameters without \r
117     // changing state or creating state change feedback\r
118     pzreq.getRecord().removeParametersInState();        \r
119     pzreq.getShow().setParameterInState(new CommandParameter("start","=",0));    \r
120     logger.debug(Utils.objectId(this) + " is searching using "+pzreq.getCommand("search").getUrlEncodedParameterValue("query"));\r
121     doCommand("search");    \r
122   }\r
123       \r
124   /**\r
125    * Refreshes 'show', 'stat', 'termlist', and 'bytarget' data object from pazpar2\r
126    * \r
127    * @return Number of activeclients at the time of the 'show' command.\r
128    */\r
129   public String update () {\r
130     logger.debug("Updating show,stat,termlist,bytarget from pazpar2");\r
131     return update("show,stat,termlist,bytarget");\r
132   }\r
133   \r
134   public boolean validateUpdateRequest(String commands) {\r
135     if (errors.hasConfigurationErrors()) {\r
136       logger.error("The command(s) " + commands + " are cancelled due to configuration errors.");\r
137       return false;\r
138     } else if (!commands.equals("search") && pzresp.getSearch().hasApplicationError()) {\r
139       logger.error("The command(s) " + commands + " are cancelled because the latest search command had an error.");\r
140       return false;\r
141     } else if (!commandsAreValid(commands)) {\r
142       logger.debug("The command(s) " + commands + " are cancelled because the were not found to be ready/valid.");\r
143       return false;\r
144     } else if (!hasQuery() &&  !(commands.equals("record") && pzreq.getCommand("record").hasParameterValue("recordquery"))) {\r
145       logger.debug("The command(s) " + commands + " are held off because there's not yet a query.");\r
146       return false;\r
147     } else {\r
148       return true;\r
149     }\r
150     \r
151     \r
152   }\r
153    \r
154   /**\r
155    * Refreshes the data objects listed in 'commands' from pazpar2\r
156    * \r
157    * @param commands\r
158    * @return Number of activeclients at the time of the 'show' command\r
159    */\r
160   public String update (String commands) {\r
161     logger.info("Request to update: " + commands);\r
162     try {\r
163       if (! validateUpdateRequest(commands)) {\r
164         return "0";\r
165       } else {\r
166         handleQueryStateChanges(commands);\r
167         if (!commands.equals("search") && pzresp.getSearch().hasApplicationError()) {\r
168           logger.error("The command(s) " + commands + " are cancelled because the latest search command had an error.");\r
169           return "0";\r
170         } else {\r
171           logger.debug("Processing request for " + commands); \r
172           List<CommandThread> threadList = new ArrayList<CommandThread>();\r
173           StringTokenizer tokens = new StringTokenizer(commands,",");\r
174           while (tokens.hasMoreElements()) {          \r
175             threadList.add(new CommandThread(pzreq.getCommand(tokens.nextToken()),searchClient));            \r
176           }\r
177           for (CommandThread thread : threadList) {\r
178             thread.start();\r
179           }\r
180           for (CommandThread thread : threadList) {\r
181             try {\r
182               thread.join();\r
183             } catch (InterruptedException e) {\r
184               e.printStackTrace();\r
185             }\r
186           }\r
187           for (CommandThread thread : threadList) {\r
188              String commandName = thread.getCommand().getCommandName();\r
189              ClientCommandResponse response = (ClientCommandResponse) thread.getCommandResponse();\r
190              responseLogger.debug("Response was: " + response.getResponseString());\r
191              ResponseDataObject responseObject = ResponseParser.getParser().getDataObject(response);\r
192              if (ResponseParser.docTypes.contains(responseObject.getType())) {\r
193                pzresp.put(commandName, responseObject);\r
194              } else {\r
195                if (commandName.equals("record") && \r
196                    (pzreq.getRecord().hasParameterValue("offset") ||\r
197                     pzreq.getRecord().hasParameterValue("checksum"))) {\r
198                  RecordResponse recordResponse = new RecordResponse();\r
199                  recordResponse.setType("record");\r
200                  recordResponse.setXml(responseObject.getXml());\r
201                  recordResponse.setAttribute("activeclients", "0");\r
202                  pzresp.put(commandName, recordResponse);\r
203                }\r
204              }\r
205           }\r
206           if (commands.equals("record")) {\r
207             return pzresp.getRecord().getActiveClients();\r
208           } else {\r
209             return pzresp.getActiveClients();\r
210           }\r
211         }\r
212       }  \r
213     } catch (ClassCastException cce) {\r
214       cce.printStackTrace();    \r
215       return "";\r
216     } catch (NullPointerException npe) {\r
217       npe.printStackTrace();\r
218       return "";\r
219     } catch (Exception e) {\r
220       e.printStackTrace();\r
221       return "";\r
222     }\r
223     \r
224   }\r
225   \r
226   public boolean commandsAreValid(String commands) {\r
227     if (commands.equals("record")) {\r
228       if (!pzreq.getCommand("record").hasParameterValue("id")) {\r
229         logger.debug("Skips sending record command due to lacking id parameter");\r
230         return false;\r
231       }\r
232     }\r
233     return true;\r
234   }\r
235                                 \r
236   public String toggleRecord (String recId) {\r
237     if (hasRecord(recId)) {\r
238       pzreq.getRecord().removeParameters();  \r
239       pzresp.put("record", new RecordResponse());\r
240       return "";\r
241     } else {\r
242       pzreq.getRecord().setId(recId);\r
243       return doCommand("record");\r
244     }\r
245   }\r
246   \r
247   @Override\r
248   public boolean hasRecord (String recId) {\r
249     return pzreq.getCommand("record").hasParameters() && pzresp.getRecord().getRecId().equals(recId);\r
250   }\r
251         \r
252   public String getCurrentStateKey () {    \r
253     return stateMgr.getCurrentState().getKey();\r
254   }\r
255       \r
256   public void setCurrentStateKey(String key) {       \r
257     stateMgr.setCurrentStateKey(key);\r
258   }\r
259       \r
260   protected boolean hasQuery() {        \r
261     return pzreq.getCommand("search").hasParameterValue("query"); \r
262   }\r
263     \r
264     \r
265   @Override\r
266   public ResultsPager getPager () {\r
267     if (pager == null) {\r
268       pager = new ResultsPager(pzresp);      \r
269     } \r
270     return pager;      \r
271   }\r
272   \r
273   @Override\r
274   public ResultsPager setPager (int pageRange) {\r
275     pager =  new ResultsPager(pzresp,pageRange,pzreq);\r
276     return pager;\r
277   }\r
278     \r
279   protected void handleQueryStateChanges (String commands) {\r
280     if (stateMgr.hasPendingStateChange("search") && hasQuery()) { \r
281       logger.info("Triggered search: Found pending search change, doing search before updating " + commands);      \r
282       doSearch();\r
283     } \r
284     if (stateMgr.hasPendingStateChange("record") && ! commands.equals("record")) {        \r
285       logger.debug("Found pending record ID change. Doing record before updating " + commands);\r
286       stateMgr.hasPendingStateChange("record",false);\r
287       if (pzreq.getCommand("record").hasParameterValue("id")) {\r
288         update("record");\r
289       } else {         \r
290         pzresp.put("record", new RecordResponse());\r
291       }\r
292     }\r
293   }\r
294   \r
295   protected String doCommand(String commandName) {             \r
296     logger.debug(pzreq.getCommand(commandName).getEncodedQueryString() + ": Results for "+ pzreq.getCommand("search").getEncodedQueryString());\r
297     return update(commandName);\r
298   }\r
299   \r
300   @Override\r
301   public void stateUpdated(String commandName) {\r
302     logger.debug("State change reported for [" + commandName + "]");\r
303     if (commandName.equals("show")) {\r
304       logger.debug("Updating show");\r
305       update(commandName);\r
306     } \r
307   }\r
308   \r
309   public void setServiceProxyUrl(String url) {\r
310     searchClient = spClient;\r
311     setServiceType(SERVICE_TYPE_SP);\r
312     setServiceUrl(url);\r
313   }\r
314   \r
315   public String getServiceProxyUrl () {\r
316     if (isServiceProxyService()) {\r
317       return spClient.getServiceUrl();\r
318     } else {\r
319       return "";\r
320     }\r
321   }\r
322   \r
323   public void setPazpar2Url(String url) {\r
324     searchClient = pz2Client;\r
325     setServiceType(SERVICE_TYPE_PZ2);\r
326     setServiceUrl(url);\r
327   }\r
328   \r
329   public String getPazpar2Url() {\r
330     if (isPazpar2Service()) {\r
331       return pz2Client.getServiceUrl();\r
332     } else {\r
333       return "";\r
334     }\r
335   }\r
336 \r
337   \r
338   @Override\r
339   public void setServiceUrl(String url) {\r
340     if (url!=null && searchClient != null && !url.equals(searchClient.getServiceUrl())) {\r
341       pzreq.getRecord().removeParametersInState();\r
342       pzreq.getSearch().removeParametersInState();\r
343       pzresp.resetAllSessionData();\r
344       user.clear();\r
345       searchClient.setServiceUrl(url);\r
346     }    \r
347   }\r
348   \r
349   public String getServiceUrl() {\r
350     return (searchClient!=null ? searchClient.getServiceUrl() : "");\r
351   }\r
352   \r
353   public boolean getServiceUrlIsDefined() {\r
354     return (searchClient != null && searchClient.hasServiceUrl());\r
355   }\r
356   \r
357   public List<String> getServiceProxyUrls() {\r
358     List<String> urls = new ArrayList<String>();\r
359     urls.add("");\r
360     urls.addAll(serviceProxyUrls);\r
361     return urls;\r
362   }\r
363   \r
364   public List<String> getPazpar2Urls () {\r
365     List<String> urls = new ArrayList<String>();\r
366     urls.add("");\r
367     urls.addAll(pazpar2Urls);\r
368     return urls;\r
369   }\r
370   \r
371   public String getServiceType () {\r
372     return serviceType;\r
373   }\r
374   \r
375   public boolean isPazpar2Service () {\r
376     return serviceType.equals(SERVICE_TYPE_PZ2);\r
377   }\r
378   \r
379   public boolean isServiceProxyService() {\r
380     return serviceType.equals(SERVICE_TYPE_SP);\r
381   }\r
382   \r
383   public boolean serviceIsToBeDecided () {\r
384     return serviceType.equals(SERVICE_TYPE_TBD);\r
385   }\r
386   \r
387   public ServiceProxyClient getSpClient () {\r
388     return spClient;\r
389   }  \r
390   \r
391   @Override\r
392   public boolean getAuthenticationRequired () {\r
393     return spClient.isAuthenticatingClient();\r
394   }\r
395 \r
396   @Override\r
397   public String getCheckHistory () {\r
398     return ":pz2watch:stateForm:windowlocationhash";\r
399   }\r
400     \r
401   @Override\r
402   public String getWatchActiveclients () {\r
403     return ":pz2watch:activeclientsForm:activeclientsField";\r
404   }\r
405   \r
406   @Override\r
407   public String getWatchActiveclientsRecord () {\r
408     return ":pz2watch:activeclientsForm:activeclientsFieldRecord";\r
409   }\r
410 \r
411   @Override\r
412   public void configure(ConfigurationReader reader)\r
413       throws ConfigurationException {\r
414     Configuration config = reader.getConfiguration(this);\r
415     if (config == null) {\r
416       serviceType = SERVICE_TYPE_TBD;\r
417     } else {\r
418       String service = config.get("TYPE");\r
419       if (service == null || service.length()==0) {\r
420         serviceType = SERVICE_TYPE_TBD;\r
421       } else if (serviceTypes.contains(service.toUpperCase())) {        \r
422         setServiceType(service.toUpperCase());\r
423       } else {\r
424         logger.error("Unknown serviceType type in configuration [" + service + "], can be one of " + serviceTypes);\r
425         serviceType = SERVICE_TYPE_TBD;\r
426       }\r
427       serviceProxyUrls = config.getMultiProperty(SERVICE_PROXY_URL_LIST,",");\r
428       pazpar2Urls = config.getMultiProperty(PAZPAR2_URL_LIST, ",");\r
429     }\r
430     logger.info("Service Type is configured to " + serviceType);\r
431     \r
432   }\r
433 \r
434   @Override\r
435   public Map<String, String> getDefaults() {\r
436     return new HashMap<String,String>();\r
437   }\r
438 \r
439   @Override\r
440   public String getModuleName() {\r
441     return MODULE_NAME;\r
442   }\r
443 \r
444   @Override\r
445   public List<String> documentConfiguration() {\r
446     return new ArrayList<String>();\r
447   }\r
448 \r
449   @Override\r
450   public void setServiceTypePZ2() {\r
451     setServiceType(SERVICE_TYPE_PZ2);    \r
452   }\r
453 \r
454   @Override\r
455   public void setServiceTypeSP() {\r
456     setServiceType(SERVICE_TYPE_SP);        \r
457   }\r
458 \r
459   @Override\r
460   public void setServiceTypeTBD() {\r
461     setServiceType(SERVICE_TYPE_TBD);    \r
462   }\r
463   \r
464   private void setServiceType(String type) {\r
465     if (!serviceType.equals(type)  &&\r
466         !serviceType.equals(SERVICE_TYPE_TBD)) {\r
467       resetSearchAndRecordCommands();\r
468       pzresp.resetAllSessionData();\r
469     }\r
470     serviceType = type;\r
471     if (serviceType.equals(SERVICE_TYPE_PZ2)) {\r
472       searchClient = pz2Client;\r
473       logger.info("Setting a Pazpar2 client to serve requests.");\r
474     } else if (serviceType.equals(SERVICE_TYPE_SP)) {\r
475       searchClient = spClient;\r
476       logger.info("Setting a Service Proxy client to serve requests.");\r
477     } else {\r
478       logger.info("Clearing search client. No client defined to serve requests at this point.");\r
479       searchClient = null;\r
480     }\r
481   }\r
482   \r
483 }\r